revm_database_interface/
lib.rs

1//! Database interface.
2#![cfg_attr(not(test), warn(unused_crate_dependencies))]
3#![cfg_attr(not(feature = "std"), no_std)]
4
5#[cfg(not(feature = "std"))]
6extern crate alloc as std;
7
8use core::convert::Infallible;
9
10use auto_impl::auto_impl;
11use primitives::{address, Address, HashMap, StorageKey, StorageValue, B256, U256};
12use state::{Account, AccountInfo, Bytecode};
13use std::vec::Vec;
14
15/// Address with all `0xff..ff` in it. Used for testing.
16pub const FFADDRESS: Address = address!("0xffffffffffffffffffffffffffffffffffffffff");
17/// BENCH_TARGET address
18pub const BENCH_TARGET: Address = FFADDRESS;
19/// Common test balance used for benchmark addresses
20pub const TEST_BALANCE: U256 = U256::from_limbs([10_000_000_000_000_000, 0, 0, 0]);
21/// BENCH_TARGET_BALANCE balance
22pub const BENCH_TARGET_BALANCE: U256 = TEST_BALANCE;
23/// Address with all `0xee..ee` in it. Used for testing.
24pub const EEADDRESS: Address = address!("0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee");
25/// BENCH_CALLER address
26pub const BENCH_CALLER: Address = EEADDRESS;
27/// BENCH_CALLER_BALANCE balance
28pub const BENCH_CALLER_BALANCE: U256 = TEST_BALANCE;
29
30#[cfg(feature = "asyncdb")]
31pub mod async_db;
32pub mod bal;
33pub mod either;
34pub mod empty_db;
35pub mod erased_error;
36pub mod try_commit;
37
38#[cfg(feature = "asyncdb")]
39pub use async_db::{DatabaseAsync, WrapDatabaseAsync};
40pub use empty_db::{EmptyDB, EmptyDBTyped};
41pub use erased_error::ErasedError;
42pub use try_commit::{ArcUpgradeError, TryDatabaseCommit};
43
44/// Database error marker is needed to implement From conversion for Error type.
45pub trait DBErrorMarker: core::error::Error + Send + Sync + 'static {}
46
47/// Implement marker for `()`.
48impl DBErrorMarker for Infallible {}
49impl DBErrorMarker for ErasedError {}
50
51/// EVM database interface.
52#[auto_impl(&mut, Box)]
53pub trait Database {
54    /// The database error type.
55    type Error: DBErrorMarker;
56
57    /// Gets basic account information.
58    fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error>;
59
60    /// Gets account code by its hash.
61    fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error>;
62
63    /// Gets storage value of address at index.
64    fn storage(&mut self, address: Address, index: StorageKey)
65        -> Result<StorageValue, Self::Error>;
66
67    /// Gets storage value of account by its id. By default call [`Database::storage`] method.
68    ///
69    /// If basic account sets account_id inside [`AccountInfo::account_id`], evm will call this
70    /// function with that given account_id. This can be useful if IndexMap is used to get faster access to the account.
71    #[inline]
72    fn storage_by_account_id(
73        &mut self,
74        address: Address,
75        account_id: usize,
76        storage_key: StorageKey,
77    ) -> Result<StorageValue, Self::Error> {
78        let _ = account_id;
79        self.storage(address, storage_key)
80    }
81
82    /// Gets block hash by block number.
83    fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error>;
84}
85
86/// EVM database commit interface.
87///
88/// # Dyn Compatibility
89///
90/// This trait is dyn-compatible. The `commit_iter` method uses `&mut dyn Iterator`
91/// which allows it to be called on trait objects while remaining in the vtable.
92#[auto_impl(&mut, Box)]
93pub trait DatabaseCommit {
94    /// Commit changes to the database.
95    fn commit(&mut self, changes: HashMap<Address, Account>);
96
97    /// Commit changes to the database with an iterator.
98    ///
99    /// Implementors of [`DatabaseCommit`] should override this method when possible for efficiency.
100    ///
101    /// Callers should prefer using [`DatabaseCommit::commit`] when they already have a [`HashMap`].
102    ///
103    /// # Dyn Compatibility
104    ///
105    /// This method uses `&mut dyn Iterator` to remain object-safe and callable on trait objects.
106    /// For ergonomic usage with `impl IntoIterator`, use the inherent method
107    /// `commit_from_iter` on `dyn DatabaseCommit`.
108    fn commit_iter(&mut self, changes: &mut dyn Iterator<Item = (Address, Account)>) {
109        let changes: HashMap<Address, Account> = changes.collect();
110        self.commit(changes);
111    }
112}
113
114/// Inherent implementation for `dyn DatabaseCommit` trait objects.
115///
116/// This provides `commit_from_iter` as an ergonomic wrapper around the trait's
117/// `commit_iter` method, accepting `impl IntoIterator` for convenience.
118impl dyn DatabaseCommit {
119    /// Commit changes to the database with an iterator.
120    ///
121    /// This is an ergonomic wrapper that accepts `impl IntoIterator` and delegates
122    /// to the trait's [`commit_iter`](DatabaseCommit::commit_iter) method.
123    #[inline]
124    pub fn commit_from_iter(&mut self, changes: impl IntoIterator<Item = (Address, Account)>) {
125        self.commit_iter(&mut changes.into_iter())
126    }
127}
128
129/// EVM database interface.
130///
131/// Contains the same methods as [`Database`], but with `&self` receivers instead of `&mut self`.
132///
133/// Use [`WrapDatabaseRef`] to provide [`Database`] implementation for a type
134/// that only implements this trait.
135#[auto_impl(&, &mut, Box, Rc, Arc)]
136pub trait DatabaseRef {
137    /// The database error type.
138    type Error: DBErrorMarker;
139
140    /// Gets basic account information.
141    fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error>;
142
143    /// Gets account code by its hash.
144    fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error>;
145
146    /// Gets storage value of address at index.
147    fn storage_ref(&self, address: Address, index: StorageKey)
148        -> Result<StorageValue, Self::Error>;
149
150    /// Gets storage value of account by its id.
151    ///
152    /// Default implementation is to call [`DatabaseRef::storage_ref`] method.
153    #[inline]
154    fn storage_by_account_id_ref(
155        &self,
156        address: Address,
157        account_id: usize,
158        storage_key: StorageKey,
159    ) -> Result<StorageValue, Self::Error> {
160        let _ = account_id;
161        self.storage_ref(address, storage_key)
162    }
163
164    /// Gets block hash by block number.
165    fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error>;
166}
167
168/// Wraps a [`DatabaseRef`] to provide a [`Database`] implementation.
169#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
170pub struct WrapDatabaseRef<T: DatabaseRef>(pub T);
171
172impl<F: DatabaseRef> From<F> for WrapDatabaseRef<F> {
173    #[inline]
174    fn from(f: F) -> Self {
175        WrapDatabaseRef(f)
176    }
177}
178
179impl<T: DatabaseRef> Database for WrapDatabaseRef<T> {
180    type Error = T::Error;
181
182    #[inline]
183    fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
184        self.0.basic_ref(address)
185    }
186
187    #[inline]
188    fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
189        self.0.code_by_hash_ref(code_hash)
190    }
191
192    #[inline]
193    fn storage(
194        &mut self,
195        address: Address,
196        index: StorageKey,
197    ) -> Result<StorageValue, Self::Error> {
198        self.0.storage_ref(address, index)
199    }
200
201    #[inline]
202    fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
203        self.0.block_hash_ref(number)
204    }
205}
206
207impl<T: DatabaseRef + DatabaseCommit> DatabaseCommit for WrapDatabaseRef<T> {
208    #[inline]
209    fn commit(&mut self, changes: HashMap<Address, Account>) {
210        self.0.commit(changes)
211    }
212}
213
214impl<T: DatabaseRef> DatabaseRef for WrapDatabaseRef<T> {
215    type Error = T::Error;
216
217    #[inline]
218    fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
219        self.0.basic_ref(address)
220    }
221
222    #[inline]
223    fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
224        self.0.code_by_hash_ref(code_hash)
225    }
226
227    #[inline]
228    fn storage_ref(
229        &self,
230        address: Address,
231        index: StorageKey,
232    ) -> Result<StorageValue, Self::Error> {
233        self.0.storage_ref(address, index)
234    }
235
236    #[inline]
237    fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
238        self.0.block_hash_ref(number)
239    }
240}
241
242impl<T: Database + DatabaseCommit> DatabaseCommitExt for T {
243    // default implementation
244}
245
246/// EVM database commit interface.
247pub trait DatabaseCommitExt: Database + DatabaseCommit {
248    /// Iterates over received balances and increment all account balances.
249    ///
250    /// Update will create transitions for all accounts that are updated.
251    fn increment_balances(
252        &mut self,
253        balances: impl IntoIterator<Item = (Address, u128)>,
254    ) -> Result<(), Self::Error> {
255        // Make transition and update cache state
256        let transitions = balances
257            .into_iter()
258            .map(|(address, balance)| {
259                let mut original_account = match self.basic(address)? {
260                    Some(acc_info) => Account::from(acc_info),
261                    None => Account::new_not_existing(0),
262                };
263                original_account.info.balance = original_account
264                    .info
265                    .balance
266                    .saturating_add(U256::from(balance));
267                original_account.mark_touch();
268                Ok((address, original_account))
269            })
270            // Unfortunately must collect here to short circuit on error
271            .collect::<Result<Vec<_>, _>>()?;
272
273        self.commit_iter(&mut transitions.into_iter());
274        Ok(())
275    }
276
277    /// Drains balances from given account and return those values.
278    ///
279    /// It is used for DAO hardfork state change to move values from given accounts.
280    fn drain_balances(
281        &mut self,
282        addresses: impl IntoIterator<Item = Address>,
283    ) -> Result<Vec<u128>, Self::Error> {
284        // Make transition and update cache state
285        let addresses_iter = addresses.into_iter();
286        let (lower, _) = addresses_iter.size_hint();
287        let mut transitions = Vec::with_capacity(lower);
288        let balances = addresses_iter
289            .map(|address| {
290                let mut original_account = match self.basic(address)? {
291                    Some(acc_info) => Account::from(acc_info),
292                    None => Account::new_not_existing(0),
293                };
294                let balance = core::mem::take(&mut original_account.info.balance);
295                original_account.mark_touch();
296                transitions.push((address, original_account));
297                Ok(balance.try_into().unwrap())
298            })
299            .collect::<Result<Vec<_>, _>>()?;
300
301        self.commit_iter(&mut transitions.into_iter());
302        Ok(balances)
303    }
304}
305
306#[cfg(test)]
307mod tests {
308    use super::*;
309
310    /// Compile-time test that DatabaseCommit is dyn-compatible.
311    /// This mirrors Foundry's approach: `struct _ObjectSafe(dyn DatabaseExt);`
312    struct _DatabaseCommitObjectSafe(dyn DatabaseCommit);
313
314    /// Test that dyn DatabaseCommit works correctly.
315    #[test]
316    fn test_dyn_database_commit() {
317        use std::collections::HashMap as StdHashMap;
318
319        struct MockDb {
320            commits: Vec<StdHashMap<Address, Account>>,
321        }
322
323        impl DatabaseCommit for MockDb {
324            fn commit(&mut self, changes: HashMap<Address, Account>) {
325                let std_map: StdHashMap<_, _> = changes.into_iter().collect();
326                self.commits.push(std_map);
327            }
328        }
329
330        let mut db = MockDb { commits: vec![] };
331
332        // Test commit_iter on concrete types
333        let items: Vec<(Address, Account)> = vec![];
334        db.commit_iter(&mut items.into_iter());
335        assert_eq!(db.commits.len(), 1);
336
337        // Test commit() on trait objects
338        {
339            let db_dyn: &mut dyn DatabaseCommit = &mut db;
340            db_dyn.commit(HashMap::default());
341        }
342        assert_eq!(db.commits.len(), 2);
343
344        // Test commit_iter on trait objects (now works directly!)
345        {
346            let db_dyn: &mut dyn DatabaseCommit = &mut db;
347            let items: Vec<(Address, Account)> = vec![];
348            db_dyn.commit_iter(&mut items.into_iter());
349        }
350        assert_eq!(db.commits.len(), 3);
351
352        // Test ergonomic commit_from_iter on trait objects
353        {
354            let db_dyn: &mut dyn DatabaseCommit = &mut db;
355            db_dyn.commit_from_iter(vec![]);
356        }
357        assert_eq!(db.commits.len(), 4);
358    }
359}