Skip to main content

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, AddressMap, StorageKey, StorageValue, B256, U256};
12use state::{Account, AccountId, AccountInfo, Bytecode, TransactionId};
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
30pub use primitives;
31pub use state;
32
33#[cfg(feature = "asyncdb")]
34pub mod async_db;
35pub mod bal;
36pub mod either;
37pub mod empty_db;
38pub mod erased_error;
39pub mod try_commit;
40
41#[cfg(feature = "asyncdb")]
42pub use async_db::{DatabaseAsync, WrapDatabaseAsync};
43pub use empty_db::{EmptyDB, EmptyDBTyped};
44pub use erased_error::ErasedError;
45pub use try_commit::{ArcUpgradeError, TryDatabaseCommit};
46
47/// Database error marker is needed to implement From conversion for Error type.
48pub trait DBErrorMarker: core::error::Error + Send + Sync + 'static {}
49
50/// Implement marker for `()`.
51impl DBErrorMarker for Infallible {}
52impl DBErrorMarker for ErasedError {}
53
54/// EVM database interface.
55#[auto_impl(&mut, Box)]
56pub trait Database {
57    /// The database error type.
58    type Error: DBErrorMarker;
59
60    /// Gets basic account information.
61    fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error>;
62
63    /// Gets account code by its hash.
64    fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error>;
65
66    /// Gets storage value of address at index.
67    fn storage(&mut self, address: Address, index: StorageKey)
68        -> Result<StorageValue, Self::Error>;
69
70    /// Gets storage value of account by its id. By default call [`Database::storage`] method.
71    ///
72    /// If basic account sets account_id inside [`AccountInfo::account_id`], evm will call this
73    /// function with that given account_id. This can be useful if IndexMap is used to get faster access to the account.
74    #[inline]
75    fn storage_by_account_id(
76        &mut self,
77        address: Address,
78        account_id: AccountId,
79        storage_key: StorageKey,
80    ) -> Result<StorageValue, Self::Error> {
81        let _ = account_id;
82        self.storage(address, storage_key)
83    }
84
85    /// Gets block hash by block number.
86    fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error>;
87}
88
89/// EVM database commit interface.
90///
91/// # Dyn Compatibility
92///
93/// This trait is dyn-compatible. The `commit_iter` method uses `&mut dyn Iterator`
94/// which allows it to be called on trait objects while remaining in the vtable.
95#[auto_impl(&mut, Box)]
96pub trait DatabaseCommit {
97    /// Commit changes to the database.
98    fn commit(&mut self, changes: AddressMap<Account>);
99
100    /// Commit changes to the database with an iterator.
101    ///
102    /// Implementors of [`DatabaseCommit`] should override this method when possible for efficiency.
103    ///
104    /// Callers should prefer using [`DatabaseCommit::commit`] when they already have a [`AddressMap`].
105    ///
106    /// # Dyn Compatibility
107    ///
108    /// This method uses `&mut dyn Iterator` to remain object-safe and callable on trait objects.
109    /// For ergonomic usage with `impl IntoIterator`, use the inherent method
110    /// `commit_from_iter` on `dyn DatabaseCommit`.
111    fn commit_iter(&mut self, changes: &mut dyn Iterator<Item = (Address, Account)>) {
112        let changes: AddressMap<Account> = changes.collect();
113        self.commit(changes);
114    }
115}
116
117/// Inherent implementation for `dyn DatabaseCommit` trait objects.
118///
119/// This provides `commit_from_iter` as an ergonomic wrapper around the trait's
120/// `commit_iter` method, accepting `impl IntoIterator` for convenience.
121impl dyn DatabaseCommit {
122    /// Commit changes to the database with an iterator.
123    ///
124    /// This is an ergonomic wrapper that accepts `impl IntoIterator` and delegates
125    /// to the trait's [`commit_iter`](DatabaseCommit::commit_iter) method.
126    #[inline]
127    pub fn commit_from_iter(&mut self, changes: impl IntoIterator<Item = (Address, Account)>) {
128        self.commit_iter(&mut changes.into_iter())
129    }
130}
131
132/// EVM database interface.
133///
134/// Contains the same methods as [`Database`], but with `&self` receivers instead of `&mut self`.
135///
136/// Use [`WrapDatabaseRef`] to provide [`Database`] implementation for a type
137/// that only implements this trait.
138#[auto_impl(&, &mut, Box, Rc, Arc)]
139pub trait DatabaseRef {
140    /// The database error type.
141    type Error: DBErrorMarker;
142
143    /// Gets basic account information.
144    fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error>;
145
146    /// Gets account code by its hash.
147    fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error>;
148
149    /// Gets storage value of address at index.
150    fn storage_ref(&self, address: Address, index: StorageKey)
151        -> Result<StorageValue, Self::Error>;
152
153    /// Gets storage value of account by its id.
154    ///
155    /// Default implementation is to call [`DatabaseRef::storage_ref`] method.
156    #[inline]
157    fn storage_by_account_id_ref(
158        &self,
159        address: Address,
160        account_id: AccountId,
161        storage_key: StorageKey,
162    ) -> Result<StorageValue, Self::Error> {
163        let _ = account_id;
164        self.storage_ref(address, storage_key)
165    }
166
167    /// Gets block hash by block number.
168    fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error>;
169}
170
171/// Wraps a [`DatabaseRef`] to provide a [`Database`] implementation.
172#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
173pub struct WrapDatabaseRef<T: DatabaseRef>(pub T);
174
175impl<F: DatabaseRef> From<F> for WrapDatabaseRef<F> {
176    #[inline]
177    fn from(f: F) -> Self {
178        WrapDatabaseRef(f)
179    }
180}
181
182impl<T: DatabaseRef> Database for WrapDatabaseRef<T> {
183    type Error = T::Error;
184
185    #[inline]
186    fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
187        self.0.basic_ref(address)
188    }
189
190    #[inline]
191    fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
192        self.0.code_by_hash_ref(code_hash)
193    }
194
195    #[inline]
196    fn storage(
197        &mut self,
198        address: Address,
199        index: StorageKey,
200    ) -> Result<StorageValue, Self::Error> {
201        self.0.storage_ref(address, index)
202    }
203
204    #[inline]
205    fn storage_by_account_id(
206        &mut self,
207        address: Address,
208        account_id: AccountId,
209        storage_key: StorageKey,
210    ) -> Result<StorageValue, Self::Error> {
211        self.0
212            .storage_by_account_id_ref(address, account_id, storage_key)
213    }
214
215    #[inline]
216    fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
217        self.0.block_hash_ref(number)
218    }
219}
220
221impl<T: DatabaseRef + DatabaseCommit> DatabaseCommit for WrapDatabaseRef<T> {
222    #[inline]
223    fn commit(&mut self, changes: AddressMap<Account>) {
224        self.0.commit(changes)
225    }
226
227    #[inline]
228    fn commit_iter(&mut self, changes: &mut dyn Iterator<Item = (Address, Account)>) {
229        self.0.commit_iter(changes)
230    }
231}
232
233impl<T: DatabaseRef> DatabaseRef for WrapDatabaseRef<T> {
234    type Error = T::Error;
235
236    #[inline]
237    fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
238        self.0.basic_ref(address)
239    }
240
241    #[inline]
242    fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
243        self.0.code_by_hash_ref(code_hash)
244    }
245
246    #[inline]
247    fn storage_ref(
248        &self,
249        address: Address,
250        index: StorageKey,
251    ) -> Result<StorageValue, Self::Error> {
252        self.0.storage_ref(address, index)
253    }
254
255    #[inline]
256    fn storage_by_account_id_ref(
257        &self,
258        address: Address,
259        account_id: AccountId,
260        storage_key: StorageKey,
261    ) -> Result<StorageValue, Self::Error> {
262        self.0
263            .storage_by_account_id_ref(address, account_id, storage_key)
264    }
265
266    #[inline]
267    fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
268        self.0.block_hash_ref(number)
269    }
270}
271
272impl<T: Database + DatabaseCommit> DatabaseCommitExt for T {
273    // default implementation
274}
275
276/// EVM database commit interface.
277pub trait DatabaseCommitExt: Database + DatabaseCommit {
278    /// Iterates over received balances and increment all account balances.
279    ///
280    /// Update will create transitions for all accounts that are updated.
281    fn increment_balances(
282        &mut self,
283        balances: impl IntoIterator<Item = (Address, u128)>,
284    ) -> Result<(), Self::Error> {
285        // Make transition and update cache state
286        let transitions = balances
287            .into_iter()
288            .map(|(address, balance)| {
289                let mut original_account = match self.basic(address)? {
290                    Some(acc_info) => Account::from(acc_info),
291                    None => Account::new_not_existing(TransactionId::ZERO),
292                };
293                original_account.info.balance = original_account
294                    .info
295                    .balance
296                    .saturating_add(U256::from(balance));
297                original_account.mark_touch();
298                Ok((address, original_account))
299            })
300            // Unfortunately must collect here to short circuit on error
301            .collect::<Result<Vec<_>, _>>()?;
302
303        self.commit_iter(&mut transitions.into_iter());
304        Ok(())
305    }
306
307    /// Drains balances from given account and return those values.
308    ///
309    /// It is used for DAO hardfork state change to move values from given accounts.
310    fn drain_balances(
311        &mut self,
312        addresses: impl IntoIterator<Item = Address>,
313    ) -> Result<Vec<u128>, Self::Error> {
314        // Make transition and update cache state
315        let addresses_iter = addresses.into_iter();
316        let (lower, _) = addresses_iter.size_hint();
317        let mut transitions = Vec::with_capacity(lower);
318        let balances = addresses_iter
319            .map(|address| {
320                let mut original_account = match self.basic(address)? {
321                    Some(acc_info) => Account::from(acc_info),
322                    None => Account::new_not_existing(TransactionId::ZERO),
323                };
324                let balance = core::mem::take(&mut original_account.info.balance);
325                original_account.mark_touch();
326                transitions.push((address, original_account));
327                Ok(balance.try_into().unwrap())
328            })
329            .collect::<Result<Vec<_>, _>>()?;
330
331        self.commit_iter(&mut transitions.into_iter());
332        Ok(balances)
333    }
334}
335
336#[cfg(test)]
337mod tests {
338    use super::*;
339
340    /// Compile-time test that DatabaseCommit is dyn-compatible.
341    /// This mirrors Foundry's approach: `struct _ObjectSafe(dyn DatabaseExt);`
342    struct _DatabaseCommitObjectSafe(dyn DatabaseCommit);
343
344    /// Test that dyn DatabaseCommit works correctly.
345    #[test]
346    fn test_dyn_database_commit() {
347        use std::collections::HashMap as StdHashMap;
348
349        struct MockDb {
350            commits: Vec<StdHashMap<Address, Account>>,
351        }
352
353        impl DatabaseCommit for MockDb {
354            fn commit(&mut self, changes: AddressMap<Account>) {
355                let std_map: StdHashMap<_, _> = changes.into_iter().collect();
356                self.commits.push(std_map);
357            }
358        }
359
360        let mut db = MockDb { commits: vec![] };
361
362        // Test commit_iter on concrete types
363        let items: Vec<(Address, Account)> = vec![];
364        db.commit_iter(&mut items.into_iter());
365        assert_eq!(db.commits.len(), 1);
366
367        // Test commit() on trait objects
368        {
369            let db_dyn: &mut dyn DatabaseCommit = &mut db;
370            db_dyn.commit(AddressMap::default());
371        }
372        assert_eq!(db.commits.len(), 2);
373
374        // Test commit_iter on trait objects (now works directly!)
375        {
376            let db_dyn: &mut dyn DatabaseCommit = &mut db;
377            let items: Vec<(Address, Account)> = vec![];
378            db_dyn.commit_iter(&mut items.into_iter());
379        }
380        assert_eq!(db.commits.len(), 3);
381
382        // Test ergonomic commit_from_iter on trait objects
383        {
384            let db_dyn: &mut dyn DatabaseCommit = &mut db;
385            db_dyn.commit_from_iter(vec![]);
386        }
387        assert_eq!(db.commits.len(), 4);
388    }
389
390    #[test]
391    fn wrappers_forward_commit_iter() {
392        #[derive(Default)]
393        struct MockDb {
394            commits: usize,
395            commit_iters: usize,
396            committed_accounts: usize,
397        }
398
399        impl DatabaseCommit for MockDb {
400            fn commit(&mut self, changes: AddressMap<Account>) {
401                self.commits += 1;
402                self.committed_accounts += changes.len();
403            }
404
405            fn commit_iter(&mut self, changes: &mut dyn Iterator<Item = (Address, Account)>) {
406                self.commit_iters += 1;
407                self.committed_accounts += changes.count();
408            }
409        }
410
411        impl DatabaseRef for MockDb {
412            type Error = Infallible;
413
414            fn basic_ref(&self, _address: Address) -> Result<Option<AccountInfo>, Self::Error> {
415                Ok(None)
416            }
417
418            fn code_by_hash_ref(&self, _code_hash: B256) -> Result<Bytecode, Self::Error> {
419                Ok(Bytecode::default())
420            }
421
422            fn storage_ref(
423                &self,
424                _address: Address,
425                _index: StorageKey,
426            ) -> Result<StorageValue, Self::Error> {
427                Ok(StorageValue::ZERO)
428            }
429
430            fn block_hash_ref(&self, _number: u64) -> Result<B256, Self::Error> {
431                Ok(B256::ZERO)
432            }
433        }
434
435        fn changes() -> Vec<(Address, Account)> {
436            vec![(Address::with_last_byte(1), Account::default())]
437        }
438
439        let mut db = WrapDatabaseRef(MockDb::default());
440        db.commit_iter(&mut changes().into_iter());
441        assert_eq!(db.0.commits, 0);
442        assert_eq!(db.0.commit_iters, 1);
443        assert_eq!(db.0.committed_accounts, 1);
444
445        let mut db: ::either::Either<MockDb, MockDb> = ::either::Either::Left(MockDb::default());
446        db.commit_iter(&mut changes().into_iter());
447        let ::either::Either::Left(db) = db else {
448            unreachable!()
449        };
450        assert_eq!(db.commits, 0);
451        assert_eq!(db.commit_iters, 1);
452        assert_eq!(db.committed_accounts, 1);
453
454        let address = Address::with_last_byte(2);
455        let mut account = Account::default();
456        account.mark_touch();
457
458        let mut db = bal::BalDatabase::new(MockDb::default()).with_bal_builder();
459        db.commit_iter(&mut [(address, account)].into_iter());
460        assert_eq!(db.db.commits, 0);
461        assert_eq!(db.db.commit_iters, 1);
462        assert_eq!(db.db.committed_accounts, 1);
463
464        let bal = db.bal_state.take_built_bal().expect("BAL should be built");
465        assert!(bal.accounts.get(&address).is_some());
466    }
467}