revm_database_interface/
async_db.rs

1use core::future::Future;
2
3use crate::{DBErrorMarker, Database, DatabaseRef};
4use core::error::Error;
5use primitives::{Address, StorageKey, StorageValue, B256};
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: StorageKey,
35    ) -> impl Future<Output = Result<StorageValue, 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: StorageKey,
70    ) -> impl Future<Output = Result<StorageValue, 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(
138        &mut self,
139        address: Address,
140        index: StorageKey,
141    ) -> Result<StorageValue, Self::Error> {
142        self.rt.block_on(self.db.storage_async(address, index))
143    }
144
145    #[inline]
146    fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
147        self.rt.block_on(self.db.block_hash_async(number))
148    }
149}
150
151impl<T: DatabaseAsyncRef> DatabaseRef for WrapDatabaseAsync<T> {
152    type Error = T::Error;
153
154    #[inline]
155    fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
156        self.rt.block_on(self.db.basic_async_ref(address))
157    }
158
159    #[inline]
160    fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
161        self.rt.block_on(self.db.code_by_hash_async_ref(code_hash))
162    }
163
164    #[inline]
165    fn storage_ref(
166        &self,
167        address: Address,
168        index: StorageKey,
169    ) -> Result<StorageValue, Self::Error> {
170        self.rt.block_on(self.db.storage_async_ref(address, index))
171    }
172
173    #[inline]
174    fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
175        self.rt.block_on(self.db.block_hash_async_ref(number))
176    }
177}
178
179// Hold a tokio runtime handle or full runtime
180#[derive(Debug)]
181enum HandleOrRuntime {
182    Handle(Handle),
183    Runtime(Runtime),
184}
185
186impl HandleOrRuntime {
187    #[inline]
188    fn block_on<F>(&self, f: F) -> F::Output
189    where
190        F: Future + Send,
191        F::Output: Send,
192    {
193        match self {
194            Self::Handle(handle) => tokio::task::block_in_place(move || handle.block_on(f)),
195            Self::Runtime(rt) => rt.block_on(f),
196        }
197    }
198}