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, 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
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: usize,
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: usize,
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: usize,
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
228impl<T: DatabaseRef> DatabaseRef for WrapDatabaseRef<T> {
229    type Error = T::Error;
230
231    #[inline]
232    fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
233        self.0.basic_ref(address)
234    }
235
236    #[inline]
237    fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
238        self.0.code_by_hash_ref(code_hash)
239    }
240
241    #[inline]
242    fn storage_ref(
243        &self,
244        address: Address,
245        index: StorageKey,
246    ) -> Result<StorageValue, Self::Error> {
247        self.0.storage_ref(address, index)
248    }
249
250    #[inline]
251    fn storage_by_account_id_ref(
252        &self,
253        address: Address,
254        account_id: usize,
255        storage_key: StorageKey,
256    ) -> Result<StorageValue, Self::Error> {
257        self.0
258            .storage_by_account_id_ref(address, account_id, storage_key)
259    }
260
261    #[inline]
262    fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
263        self.0.block_hash_ref(number)
264    }
265}
266
267impl<T: Database + DatabaseCommit> DatabaseCommitExt for T {
268    // default implementation
269}
270
271/// EVM database commit interface.
272pub trait DatabaseCommitExt: Database + DatabaseCommit {
273    /// Iterates over received balances and increment all account balances.
274    ///
275    /// Update will create transitions for all accounts that are updated.
276    fn increment_balances(
277        &mut self,
278        balances: impl IntoIterator<Item = (Address, u128)>,
279    ) -> Result<(), Self::Error> {
280        // Make transition and update cache state
281        let transitions = balances
282            .into_iter()
283            .map(|(address, balance)| {
284                let mut original_account = match self.basic(address)? {
285                    Some(acc_info) => Account::from(acc_info),
286                    None => Account::new_not_existing(0),
287                };
288                original_account.info.balance = original_account
289                    .info
290                    .balance
291                    .saturating_add(U256::from(balance));
292                original_account.mark_touch();
293                Ok((address, original_account))
294            })
295            // Unfortunately must collect here to short circuit on error
296            .collect::<Result<Vec<_>, _>>()?;
297
298        self.commit_iter(&mut transitions.into_iter());
299        Ok(())
300    }
301
302    /// Drains balances from given account and return those values.
303    ///
304    /// It is used for DAO hardfork state change to move values from given accounts.
305    fn drain_balances(
306        &mut self,
307        addresses: impl IntoIterator<Item = Address>,
308    ) -> Result<Vec<u128>, Self::Error> {
309        // Make transition and update cache state
310        let addresses_iter = addresses.into_iter();
311        let (lower, _) = addresses_iter.size_hint();
312        let mut transitions = Vec::with_capacity(lower);
313        let balances = addresses_iter
314            .map(|address| {
315                let mut original_account = match self.basic(address)? {
316                    Some(acc_info) => Account::from(acc_info),
317                    None => Account::new_not_existing(0),
318                };
319                let balance = core::mem::take(&mut original_account.info.balance);
320                original_account.mark_touch();
321                transitions.push((address, original_account));
322                Ok(balance.try_into().unwrap())
323            })
324            .collect::<Result<Vec<_>, _>>()?;
325
326        self.commit_iter(&mut transitions.into_iter());
327        Ok(balances)
328    }
329}
330
331#[cfg(test)]
332mod tests {
333    use super::*;
334
335    /// Compile-time test that DatabaseCommit is dyn-compatible.
336    /// This mirrors Foundry's approach: `struct _ObjectSafe(dyn DatabaseExt);`
337    struct _DatabaseCommitObjectSafe(dyn DatabaseCommit);
338
339    /// Test that dyn DatabaseCommit works correctly.
340    #[test]
341    fn test_dyn_database_commit() {
342        use std::collections::HashMap as StdHashMap;
343
344        struct MockDb {
345            commits: Vec<StdHashMap<Address, Account>>,
346        }
347
348        impl DatabaseCommit for MockDb {
349            fn commit(&mut self, changes: AddressMap<Account>) {
350                let std_map: StdHashMap<_, _> = changes.into_iter().collect();
351                self.commits.push(std_map);
352            }
353        }
354
355        let mut db = MockDb { commits: vec![] };
356
357        // Test commit_iter on concrete types
358        let items: Vec<(Address, Account)> = vec![];
359        db.commit_iter(&mut items.into_iter());
360        assert_eq!(db.commits.len(), 1);
361
362        // Test commit() on trait objects
363        {
364            let db_dyn: &mut dyn DatabaseCommit = &mut db;
365            db_dyn.commit(AddressMap::default());
366        }
367        assert_eq!(db.commits.len(), 2);
368
369        // Test commit_iter on trait objects (now works directly!)
370        {
371            let db_dyn: &mut dyn DatabaseCommit = &mut db;
372            let items: Vec<(Address, Account)> = vec![];
373            db_dyn.commit_iter(&mut items.into_iter());
374        }
375        assert_eq!(db.commits.len(), 3);
376
377        // Test ergonomic commit_from_iter on trait objects
378        {
379            let db_dyn: &mut dyn DatabaseCommit = &mut db;
380            db_dyn.commit_from_iter(vec![]);
381        }
382        assert_eq!(db.commits.len(), 4);
383    }
384}