revm_database_interface/
async_db.rs

1//! Async database interface.
2use crate::{DBErrorMarker, Database, DatabaseRef};
3use core::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: DBErrorMarker;
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 storage value of account by its id.
37    ///
38    /// Default implementation is to call [`DatabaseAsync::storage_async`] method.
39    #[inline]
40    fn storage_by_account_id_async(
41        &mut self,
42        address: Address,
43        account_id: usize,
44        storage_key: StorageKey,
45    ) -> impl Future<Output = Result<StorageValue, Self::Error>> + Send {
46        let _ = account_id;
47        self.storage_async(address, storage_key)
48    }
49
50    /// Gets block hash by block number.
51    fn block_hash_async(
52        &mut self,
53        number: u64,
54    ) -> impl Future<Output = Result<B256, Self::Error>> + Send;
55}
56
57/// The async EVM database interface
58///
59/// Contains the same methods as [DatabaseRef], but it returns [Future] type instead.
60///
61/// Use [WrapDatabaseAsync] to provide [DatabaseRef] implementation for a type that only implements this trait.
62pub trait DatabaseAsyncRef {
63    /// The database error type
64    type Error: DBErrorMarker;
65
66    /// Gets basic account information.
67    fn basic_async_ref(
68        &self,
69        address: Address,
70    ) -> impl Future<Output = Result<Option<AccountInfo>, Self::Error>> + Send;
71
72    /// Gets account code by its hash.
73    fn code_by_hash_async_ref(
74        &self,
75        code_hash: B256,
76    ) -> impl Future<Output = Result<Bytecode, Self::Error>> + Send;
77
78    /// Gets storage value of address at index.
79    fn storage_async_ref(
80        &self,
81        address: Address,
82        index: StorageKey,
83    ) -> impl Future<Output = Result<StorageValue, Self::Error>> + Send;
84
85    /// Gets storage value of account by its id.
86    ///
87    /// Default implementation is to call [`DatabaseAsyncRef::storage_async_ref`] method.
88    #[inline]
89    fn storage_by_account_id_async_ref(
90        &self,
91        address: Address,
92        account_id: usize,
93        storage_key: StorageKey,
94    ) -> impl Future<Output = Result<StorageValue, Self::Error>> + Send {
95        let _ = account_id;
96        self.storage_async_ref(address, storage_key)
97    }
98
99    /// Gets block hash by block number.
100    fn block_hash_async_ref(
101        &self,
102        number: u64,
103    ) -> impl Future<Output = Result<B256, Self::Error>> + Send;
104}
105
106/// Wraps a [DatabaseAsync] or [DatabaseAsyncRef] to provide a [`Database`] implementation.
107#[derive(Debug)]
108pub struct WrapDatabaseAsync<T> {
109    db: T,
110    rt: HandleOrRuntime,
111}
112
113impl<T> WrapDatabaseAsync<T> {
114    /// Wraps a [DatabaseAsync] or [DatabaseAsyncRef] instance.
115    ///
116    /// Returns `None` if no tokio runtime is available or if the current runtime is a current-thread runtime.
117    pub fn new(db: T) -> Option<Self> {
118        let rt = match Handle::try_current() {
119            Ok(handle) => match handle.runtime_flavor() {
120                tokio::runtime::RuntimeFlavor::CurrentThread => return None,
121                _ => HandleOrRuntime::Handle(handle),
122            },
123            Err(_) => return None,
124        };
125        Some(Self { db, rt })
126    }
127
128    /// Wraps a [DatabaseAsync] or [DatabaseAsyncRef] instance, with a runtime.
129    ///
130    /// Refer to [tokio::runtime::Builder] on how to create a runtime if you are in synchronous world.
131    ///
132    /// If you are already using something like [tokio::main], call [`WrapDatabaseAsync::new`] instead.
133    pub fn with_runtime(db: T, runtime: Runtime) -> Self {
134        let rt = HandleOrRuntime::Runtime(runtime);
135        Self { db, rt }
136    }
137
138    /// Wraps a [DatabaseAsync] or [DatabaseAsyncRef] instance, with a runtime handle.
139    ///
140    /// This generally allows you to pass any valid runtime handle, refer to [tokio::runtime::Handle] on how
141    /// to obtain a handle.
142    ///
143    /// If you are already in asynchronous world, like [tokio::main], use [`WrapDatabaseAsync::new`] instead.
144    pub fn with_handle(db: T, handle: Handle) -> Self {
145        let rt = HandleOrRuntime::Handle(handle);
146        Self { db, rt }
147    }
148}
149
150impl<T: DatabaseAsync> Database for WrapDatabaseAsync<T> {
151    type Error = T::Error;
152
153    #[inline]
154    fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
155        self.rt.block_on(self.db.basic_async(address))
156    }
157
158    #[inline]
159    fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
160        self.rt.block_on(self.db.code_by_hash_async(code_hash))
161    }
162
163    #[inline]
164    fn storage(
165        &mut self,
166        address: Address,
167        index: StorageKey,
168    ) -> Result<StorageValue, Self::Error> {
169        self.rt.block_on(self.db.storage_async(address, index))
170    }
171
172    /// Gets storage value of account by its id.
173    ///
174    /// Default implementation is to call [`DatabaseRef::storage_ref`] method.
175    #[inline]
176    fn storage_by_account_id(
177        &mut self,
178        address: Address,
179        account_id: usize,
180        storage_key: StorageKey,
181    ) -> Result<StorageValue, Self::Error> {
182        self.rt.block_on(
183            self.db
184                .storage_by_account_id_async(address, account_id, storage_key),
185        )
186    }
187
188    #[inline]
189    fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
190        self.rt.block_on(self.db.block_hash_async(number))
191    }
192}
193
194impl<T: DatabaseAsyncRef> DatabaseRef for WrapDatabaseAsync<T> {
195    type Error = T::Error;
196
197    #[inline]
198    fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
199        self.rt.block_on(self.db.basic_async_ref(address))
200    }
201
202    #[inline]
203    fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
204        self.rt.block_on(self.db.code_by_hash_async_ref(code_hash))
205    }
206
207    #[inline]
208    fn storage_ref(
209        &self,
210        address: Address,
211        index: StorageKey,
212    ) -> Result<StorageValue, Self::Error> {
213        self.rt.block_on(self.db.storage_async_ref(address, index))
214    }
215
216    #[inline]
217    fn storage_by_account_id_ref(
218        &self,
219        address: Address,
220        account_id: usize,
221        storage_key: StorageKey,
222    ) -> Result<StorageValue, Self::Error> {
223        self.rt.block_on(
224            self.db
225                .storage_by_account_id_async_ref(address, account_id, storage_key),
226        )
227    }
228
229    #[inline]
230    fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
231        self.rt.block_on(self.db.block_hash_async_ref(number))
232    }
233}
234
235// Hold a tokio runtime handle or full runtime
236#[derive(Debug)]
237enum HandleOrRuntime {
238    Handle(Handle),
239    Runtime(Runtime),
240}
241
242impl HandleOrRuntime {
243    #[inline]
244    fn block_on<F>(&self, f: F) -> F::Output
245    where
246        F: Future + Send,
247        F::Output: Send,
248    {
249        match self {
250            Self::Handle(handle) => {
251                // We use a conservative approach: if we're currently in a multi-threaded
252                // tokio runtime context, we use block_in_place. This works because:
253                // 1. If we're in the SAME runtime, block_in_place prevents deadlock
254                // 2. If we're in a DIFFERENT runtime, block_in_place still works safely
255                //    (it just moves the work off the current worker thread before blocking)
256                //
257                // This approach is compatible with all tokio versions and doesn't require
258                // runtime identity comparison (Handle::id() is unstable feature).
259                let should_use_block_in_place = Handle::try_current()
260                    .ok()
261                    .map(|current| {
262                        // Only use block_in_place for multi-threaded runtimes
263                        // (block_in_place panics on current-thread runtime)
264                        !matches!(
265                            current.runtime_flavor(),
266                            tokio::runtime::RuntimeFlavor::CurrentThread
267                        )
268                    })
269                    .unwrap_or(false);
270
271                if should_use_block_in_place {
272                    // We're in a multi-threaded runtime context.
273                    // Use block_in_place to:
274                    // 1. Move the blocking operation off the async worker thread
275                    // 2. Prevent potential deadlock if this is the same runtime
276                    // 3. Allow other tasks to continue executing
277                    tokio::task::block_in_place(move || handle.block_on(f))
278                } else {
279                    // Safe to call block_on directly in these cases:
280                    // - We're outside any runtime context
281                    // - We're in a current-thread runtime (where block_in_place doesn't work)
282                    handle.block_on(f)
283                }
284            }
285            Self::Runtime(rt) => rt.block_on(f),
286        }
287    }
288}