revm_database_interface/
async_db.rs

1//! Async database interface.
2use crate::{DBErrorMarker, Database, DatabaseRef};
3use core::{error::Error, future::Future};
4use primitives::{Address, StorageKey, StorageValue, B256};
5use state::{AccountInfo, Bytecode};
6use tokio::runtime::{Handle, Runtime};
7
8/// The async EVM database interface
9///
10/// Contains the same methods as [Database], but it returns [Future] type instead.
11///
12/// Use [WrapDatabaseAsync] to provide [Database] implementation for a type that only implements this trait.
13pub trait DatabaseAsync {
14    /// The database error type
15    type Error: Send + DBErrorMarker + Error;
16
17    /// Gets basic account information.
18    fn basic_async(
19        &mut self,
20        address: Address,
21    ) -> impl Future<Output = Result<Option<AccountInfo>, Self::Error>> + Send;
22
23    /// Gets account code by its hash.
24    fn code_by_hash_async(
25        &mut self,
26        code_hash: B256,
27    ) -> impl Future<Output = Result<Bytecode, Self::Error>> + Send;
28
29    /// Gets storage value of address at index.
30    fn storage_async(
31        &mut self,
32        address: Address,
33        index: StorageKey,
34    ) -> impl Future<Output = Result<StorageValue, Self::Error>> + Send;
35
36    /// Gets block hash by block number.
37    fn block_hash_async(
38        &mut self,
39        number: u64,
40    ) -> impl Future<Output = Result<B256, Self::Error>> + Send;
41}
42
43/// The async EVM database interface
44///
45/// Contains the same methods as [DatabaseRef], but it returns [Future] type instead.
46///
47/// Use [WrapDatabaseAsync] to provide [DatabaseRef] implementation for a type that only implements this trait.
48pub trait DatabaseAsyncRef {
49    /// The database error type
50    type Error: Send + DBErrorMarker + Error;
51
52    /// Gets basic account information.
53    fn basic_async_ref(
54        &self,
55        address: Address,
56    ) -> impl Future<Output = Result<Option<AccountInfo>, Self::Error>> + Send;
57
58    /// Gets account code by its hash.
59    fn code_by_hash_async_ref(
60        &self,
61        code_hash: B256,
62    ) -> impl Future<Output = Result<Bytecode, Self::Error>> + Send;
63
64    /// Gets storage value of address at index.
65    fn storage_async_ref(
66        &self,
67        address: Address,
68        index: StorageKey,
69    ) -> impl Future<Output = Result<StorageValue, Self::Error>> + Send;
70
71    /// Gets block hash by block number.
72    fn block_hash_async_ref(
73        &self,
74        number: u64,
75    ) -> impl Future<Output = Result<B256, Self::Error>> + Send;
76}
77
78/// Wraps a [DatabaseAsync] or [DatabaseAsyncRef] to provide a [`Database`] implementation.
79#[derive(Debug)]
80pub struct WrapDatabaseAsync<T> {
81    db: T,
82    rt: HandleOrRuntime,
83}
84
85impl<T> WrapDatabaseAsync<T> {
86    /// Wraps a [DatabaseAsync] or [DatabaseAsyncRef] instance.
87    ///
88    /// Returns `None` if no tokio runtime is available or if the current runtime is a current-thread runtime.
89    pub fn new(db: T) -> Option<Self> {
90        let rt = match Handle::try_current() {
91            Ok(handle) => match handle.runtime_flavor() {
92                tokio::runtime::RuntimeFlavor::CurrentThread => return None,
93                _ => HandleOrRuntime::Handle(handle),
94            },
95            Err(_) => return None,
96        };
97        Some(Self { db, rt })
98    }
99
100    /// Wraps a [DatabaseAsync] or [DatabaseAsyncRef] instance, with a runtime.
101    ///
102    /// Refer to [tokio::runtime::Builder] on how to create a runtime if you are in synchronous world.
103    ///
104    /// If you are already using something like [tokio::main], call [`WrapDatabaseAsync::new`] instead.
105    pub fn with_runtime(db: T, runtime: Runtime) -> Self {
106        let rt = HandleOrRuntime::Runtime(runtime);
107        Self { db, rt }
108    }
109
110    /// Wraps a [DatabaseAsync] or [DatabaseAsyncRef] instance, with a runtime handle.
111    ///
112    /// This generally allows you to pass any valid runtime handle, refer to [tokio::runtime::Handle] on how
113    /// to obtain a handle.
114    ///
115    /// If you are already in asynchronous world, like [tokio::main], use [`WrapDatabaseAsync::new`] instead.
116    pub fn with_handle(db: T, handle: Handle) -> Self {
117        let rt = HandleOrRuntime::Handle(handle);
118        Self { db, rt }
119    }
120}
121
122impl<T: DatabaseAsync> Database for WrapDatabaseAsync<T> {
123    type Error = T::Error;
124
125    #[inline]
126    fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
127        self.rt.block_on(self.db.basic_async(address))
128    }
129
130    #[inline]
131    fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
132        self.rt.block_on(self.db.code_by_hash_async(code_hash))
133    }
134
135    #[inline]
136    fn storage(
137        &mut self,
138        address: Address,
139        index: StorageKey,
140    ) -> Result<StorageValue, Self::Error> {
141        self.rt.block_on(self.db.storage_async(address, index))
142    }
143
144    #[inline]
145    fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
146        self.rt.block_on(self.db.block_hash_async(number))
147    }
148}
149
150impl<T: DatabaseAsyncRef> DatabaseRef for WrapDatabaseAsync<T> {
151    type Error = T::Error;
152
153    #[inline]
154    fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
155        self.rt.block_on(self.db.basic_async_ref(address))
156    }
157
158    #[inline]
159    fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
160        self.rt.block_on(self.db.code_by_hash_async_ref(code_hash))
161    }
162
163    #[inline]
164    fn storage_ref(
165        &self,
166        address: Address,
167        index: StorageKey,
168    ) -> Result<StorageValue, Self::Error> {
169        self.rt.block_on(self.db.storage_async_ref(address, index))
170    }
171
172    #[inline]
173    fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
174        self.rt.block_on(self.db.block_hash_async_ref(number))
175    }
176}
177
178// Hold a tokio runtime handle or full runtime
179#[derive(Debug)]
180enum HandleOrRuntime {
181    Handle(Handle),
182    Runtime(Runtime),
183}
184
185impl HandleOrRuntime {
186    #[inline]
187    fn block_on<F>(&self, f: F) -> F::Output
188    where
189        F: Future + Send,
190        F::Output: Send,
191    {
192        match self {
193            Self::Handle(handle) => tokio::task::block_in_place(move || handle.block_on(f)),
194            Self::Runtime(rt) => rt.block_on(f),
195        }
196    }
197}