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