Skip to main content

foundry_evm_core/backend/
in_memory_db.rs

1//! In-memory database.
2
3use crate::state_snapshot::StateSnapshots;
4use alloy_primitives::{Address, B256, U256};
5use foundry_fork_db::DatabaseError;
6use revm::{
7    Database, DatabaseCommit,
8    bytecode::Bytecode,
9    database::{CacheDB, DatabaseRef, EmptyDB},
10    primitives::AddressMap,
11    state::{Account, AccountInfo},
12};
13
14/// Type alias for an in-memory database.
15///
16/// See [`EmptyDBWrapper`].
17pub type FoundryEvmInMemoryDB = CacheDB<EmptyDBWrapper>;
18
19/// In-memory [`Database`] for Anvil.
20///
21/// This acts like a wrapper type for [`FoundryEvmInMemoryDB`] but is capable of applying snapshots.
22#[derive(Debug)]
23pub struct MemDb {
24    pub inner: FoundryEvmInMemoryDB,
25    pub state_snapshots: StateSnapshots<FoundryEvmInMemoryDB>,
26}
27
28impl Default for MemDb {
29    fn default() -> Self {
30        Self { inner: CacheDB::new(Default::default()), state_snapshots: Default::default() }
31    }
32}
33
34impl DatabaseRef for MemDb {
35    type Error = DatabaseError;
36
37    fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
38        DatabaseRef::basic_ref(&self.inner, address)
39    }
40
41    fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
42        DatabaseRef::code_by_hash_ref(&self.inner, code_hash)
43    }
44
45    fn storage_ref(&self, address: Address, index: U256) -> Result<U256, Self::Error> {
46        DatabaseRef::storage_ref(&self.inner, address, index)
47    }
48
49    fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
50        DatabaseRef::block_hash_ref(&self.inner, number)
51    }
52}
53
54impl Database for MemDb {
55    type Error = DatabaseError;
56
57    fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
58        // Note: this will always return `Some(AccountInfo)`, See `EmptyDBWrapper`
59        Database::basic(&mut self.inner, address)
60    }
61
62    fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
63        Database::code_by_hash(&mut self.inner, code_hash)
64    }
65
66    fn storage(&mut self, address: Address, index: U256) -> Result<U256, Self::Error> {
67        Database::storage(&mut self.inner, address, index)
68    }
69
70    fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
71        Database::block_hash(&mut self.inner, number)
72    }
73}
74
75impl DatabaseCommit for MemDb {
76    fn commit(&mut self, changes: AddressMap<Account>) {
77        DatabaseCommit::commit(&mut self.inner, changes)
78    }
79}
80
81/// An empty database that always returns default values when queried.
82///
83/// This is just a simple wrapper for `revm::EmptyDB` but implements `DatabaseError` instead, this
84/// way we can unify all different `Database` impls
85///
86/// This will also _always_ return `Some(AccountInfo)`:
87///
88/// The [`Database`] implementation for `CacheDB` manages an `AccountState` for the
89/// `DbAccount`, this will be set to `AccountState::NotExisting` if the account does not exist yet.
90/// This is because there's a distinction between "non-existing" and "empty",
91/// see <https://github.com/bluealloy/revm/blob/8f4348dc93022cffb3730d9db5d3ab1aad77676a/crates/revm/src/db/in_memory_db.rs#L81-L83>.
92/// If an account is `NotExisting`, `Database::basic_ref` will always return `None` for the
93/// requested `AccountInfo`.
94///
95/// To prevent this, we ensure that a missing account is never marked as `NotExisting` by always
96/// returning `Some` with this type, which will then insert a default [`AccountInfo`] instead
97/// of one marked as `AccountState::NotExisting`.
98#[derive(Clone, Debug, Default)]
99pub struct EmptyDBWrapper(EmptyDB);
100
101impl DatabaseRef for EmptyDBWrapper {
102    type Error = DatabaseError;
103
104    fn basic_ref(&self, _address: Address) -> Result<Option<AccountInfo>, Self::Error> {
105        // Note: this will always return `Some(AccountInfo)`, for the reason explained above
106        Ok(Some(AccountInfo::default()))
107    }
108
109    fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
110        Ok(self.0.code_by_hash_ref(code_hash)?)
111    }
112    fn storage_ref(&self, address: Address, index: U256) -> Result<U256, Self::Error> {
113        Ok(self.0.storage_ref(address, index)?)
114    }
115
116    fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
117        Ok(self.0.block_hash_ref(number)?)
118    }
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124    use alloy_primitives::b256;
125    use revm::database::InMemoryDB;
126
127    /// Ensures the `Database(Ref)` implementation for `revm::CacheDB` works as expected
128    ///
129    /// Demonstrates how calling `Database::basic` works if an account does not exist
130    #[test]
131    fn cache_db_insert_basic_non_existing() {
132        let mut db = InMemoryDB::default();
133        let address = Address::random();
134        // call `basic` on a non-existing account
135        let info = Database::basic(&mut db, address).unwrap();
136        assert!(info.is_none());
137
138        let mut info = info.unwrap_or_default();
139        info.balance = U256::from(500u64);
140
141        // insert the modified account info
142        db.insert_account_info(address, info);
143
144        // now we can call `basic` again and it should return the inserted account info
145        let info = Database::basic(&mut db, address).unwrap();
146        assert!(info.is_some());
147    }
148
149    /// Demonstrates how to insert a new account but not mark it as non-existing
150    #[test]
151    fn cache_db_insert_basic_default() {
152        let mut db = InMemoryDB::default();
153        let address = Address::random();
154
155        // We use `basic_ref` here to ensure that the account is not marked as `NotExisting`.
156        let info = DatabaseRef::basic_ref(&db, address).unwrap();
157        assert!(info.is_none());
158        let mut info = info.unwrap_or_default();
159        info.balance = U256::from(500u64);
160
161        // insert the modified account info
162        db.insert_account_info(address, info.clone());
163
164        let loaded = Database::basic(&mut db, address).unwrap();
165        assert!(loaded.is_some());
166        assert_eq!(loaded.unwrap(), info)
167    }
168
169    /// Demonstrates that `Database::basic` for `MemDb` will always return the `AccountInfo`
170    #[test]
171    fn mem_db_insert_basic_default() {
172        let mut db = MemDb::default();
173        let address = Address::from_word(b256!(
174            "0x000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa96045"
175        ));
176
177        let info = Database::basic(&mut db, address).unwrap();
178        // We know info exists, as MemDb always returns `Some(AccountInfo)` due to the
179        // `EmptyDbWrapper`.
180        assert!(info.is_some());
181        let mut info = info.unwrap();
182        info.balance = U256::from(500u64);
183
184        // insert the modified account info
185        db.inner.insert_account_info(address, info.clone());
186
187        let loaded = Database::basic(&mut db, address).unwrap();
188        assert!(loaded.is_some());
189        assert_eq!(loaded.unwrap(), info)
190    }
191}