1//! In-memory database.
23use crate::state_snapshot::StateSnapshots;
4use alloy_primitives::{Address, B256, U256};
5use foundry_fork_db::DatabaseError;
6use revm::{
7 db::{CacheDB, DatabaseRef, EmptyDB},
8 primitives::{Account, AccountInfo, Bytecode, HashMap as Map},
9 Database, DatabaseCommit,
10};
1112/// Type alias for an in-memory database.
13///
14/// See [`EmptyDBWrapper`].
15pub type FoundryEvmInMemoryDB = CacheDB<EmptyDBWrapper>;
1617/// In-memory [`Database`] for Anvil.
18///
19/// This acts like a wrapper type for [`FoundryEvmInMemoryDB`] but is capable of applying snapshots.
20#[derive(Debug)]
21pub struct MemDb {
22pub inner: FoundryEvmInMemoryDB,
23pub state_snapshots: StateSnapshots<FoundryEvmInMemoryDB>,
24}
2526impl Defaultfor MemDb {
27fn default() -> Self {
28Self { inner: CacheDB::new(Default::default()), state_snapshots: Default::default() }
29 }
30}
3132impl DatabaseRef for MemDb {
33type Error = DatabaseError;
3435fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
36 DatabaseRef::basic_ref(&self.inner, address)
37 }
3839fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
40 DatabaseRef::code_by_hash_ref(&self.inner, code_hash)
41 }
4243fn storage_ref(&self, address: Address, index: U256) -> Result<U256, Self::Error> {
44 DatabaseRef::storage_ref(&self.inner, address, index)
45 }
4647fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
48 DatabaseRef::block_hash_ref(&self.inner, number)
49 }
50}
5152impl Database for MemDb {
53type Error = DatabaseError;
5455fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
56// Note: this will always return `Some(AccountInfo)`, See `EmptyDBWrapper`
57Database::basic(&mut self.inner, address)
58 }
5960fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
61 Database::code_by_hash(&mut self.inner, code_hash)
62 }
6364fn storage(&mut self, address: Address, index: U256) -> Result<U256, Self::Error> {
65 Database::storage(&mut self.inner, address, index)
66 }
6768fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
69 Database::block_hash(&mut self.inner, number)
70 }
71}
7273impl DatabaseCommit for MemDb {
74fn commit(&mut self, changes: Map<Address, Account>) {
75 DatabaseCommit::commit(&mut self.inner, changes)
76 }
77}
7879/// An empty database that always returns default values when queried.
80///
81/// This is just a simple wrapper for `revm::EmptyDB` but implements `DatabaseError` instead, this
82/// way we can unify all different `Database` impls
83///
84/// This will also _always_ return `Some(AccountInfo)`:
85///
86/// The [`Database`] implementation for `CacheDB` manages an `AccountState` for the
87/// `DbAccount`, this will be set to `AccountState::NotExisting` if the account does not exist yet.
88/// This is because there's a distinction between "non-existing" and "empty",
89/// see <https://github.com/bluealloy/revm/blob/8f4348dc93022cffb3730d9db5d3ab1aad77676a/crates/revm/src/db/in_memory_db.rs#L81-L83>.
90/// If an account is `NotExisting`, `Database::basic_ref` will always return `None` for the
91/// requested `AccountInfo`.
92///
93/// To prevent this, we ensure that a missing account is never marked as `NotExisting` by always
94/// returning `Some` with this type, which will then insert a default [`AccountInfo`] instead
95/// of one marked as `AccountState::NotExisting`.
96#[derive(Clone, Debug, Default)]
97pub struct EmptyDBWrapper(EmptyDB);
9899impl DatabaseRef for EmptyDBWrapper {
100type Error = DatabaseError;
101102fn basic_ref(&self, _address: Address) -> Result<Option<AccountInfo>, Self::Error> {
103// Note: this will always return `Some(AccountInfo)`, for the reason explained above
104Ok(Some(AccountInfo::default()))
105 }
106107fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
108Ok(self.0.code_by_hash_ref(code_hash)?)
109 }
110fn storage_ref(&self, address: Address, index: U256) -> Result<U256, Self::Error> {
111Ok(self.0.storage_ref(address, index)?)
112 }
113114fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
115Ok(self.0.block_hash_ref(number)?)
116 }
117}
118119#[cfg(test)]
120mod tests {
121use super::*;
122use alloy_primitives::b256;
123124/// Ensures the `Database(Ref)` implementation for `revm::CacheDB` works as expected
125 ///
126 /// Demonstrates how calling `Database::basic` works if an account does not exist
127#[test]
128fn cache_db_insert_basic_non_existing() {
129let mut db = CacheDB::new(EmptyDB::default());
130let address = Address::random();
131// call `basic` on a non-existing account
132let info = Database::basic(&mut db, address).unwrap();
133assert!(info.is_none());
134let mut info = info.unwrap_or_default();
135 info.balance = U256::from(500u64);
136137// insert the modified account info
138db.insert_account_info(address, info);
139140// when fetching again, the `AccountInfo` is still `None` because the state of the account
141 // is `AccountState::NotExisting`, see <https://github.com/bluealloy/revm/blob/8f4348dc93022cffb3730d9db5d3ab1aad77676a/crates/revm/src/db/in_memory_db.rs#L217-L226>
142let info = Database::basic(&mut db, address).unwrap();
143assert!(info.is_none());
144 }
145146/// Demonstrates how to insert a new account but not mark it as non-existing
147#[test]
148fn cache_db_insert_basic_default() {
149let mut db = CacheDB::new(EmptyDB::default());
150let address = Address::random();
151152// We use `basic_ref` here to ensure that the account is not marked as `NotExisting`.
153let info = DatabaseRef::basic_ref(&db, address).unwrap();
154assert!(info.is_none());
155let mut info = info.unwrap_or_default();
156 info.balance = U256::from(500u64);
157158// insert the modified account info
159db.insert_account_info(address, info.clone());
160161let loaded = Database::basic(&mut db, address).unwrap();
162assert!(loaded.is_some());
163assert_eq!(loaded.unwrap(), info)
164 }
165166/// Demonstrates that `Database::basic` for `MemDb` will always return the `AccountInfo`
167#[test]
168fn mem_db_insert_basic_default() {
169let mut db = MemDb::default();
170let address = Address::from_word(b256!(
171"0x000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa96045"
172));
173174let info = Database::basic(&mut db, address).unwrap();
175// We know info exists, as MemDb always returns `Some(AccountInfo)` due to the
176 // `EmptyDbWrapper`.
177assert!(info.is_some());
178let mut info = info.unwrap();
179 info.balance = U256::from(500u64);
180181// insert the modified account info
182db.inner.insert_account_info(address, info.clone());
183184let loaded = Database::basic(&mut db, address).unwrap();
185assert!(loaded.is_some());
186assert_eq!(loaded.unwrap(), info)
187 }
188}