revm_database_interface/
async_db.rs

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