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