1//! In-memory database.
23use crate::state_snapshot::StateSnapshots;
4use alloy_primitives::{Address, B256, U256};
5use foundry_fork_db::DatabaseError;
6use revm::{
7 bytecode::Bytecode,
8 database::{CacheDB, DatabaseRef, EmptyDB},
9 primitives::HashMap as Map,
10 state::{Account, AccountInfo},
11 Database, DatabaseCommit,
12};
1314/// Type alias for an in-memory database.
15///
16/// See [`EmptyDBWrapper`].
17pub type FoundryEvmInMemoryDB = CacheDB<EmptyDBWrapper>;
1819/// 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 {
24pub inner: FoundryEvmInMemoryDB,
25pub state_snapshots: StateSnapshots<FoundryEvmInMemoryDB>,
26}
2728impl Defaultfor MemDb {
29fn default() -> Self {
30Self { inner: CacheDB::new(Default::default()), state_snapshots: Default::default() }
31 }
32}
3334impl DatabaseRef for MemDb {
35type Error = DatabaseError;
3637fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
38 DatabaseRef::basic_ref(&self.inner, address)
39 }
4041fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
42 DatabaseRef::code_by_hash_ref(&self.inner, code_hash)
43 }
4445fn storage_ref(&self, address: Address, index: U256) -> Result<U256, Self::Error> {
46 DatabaseRef::storage_ref(&self.inner, address, index)
47 }
4849fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
50 DatabaseRef::block_hash_ref(&self.inner, number)
51 }
52}
5354impl Database for MemDb {
55type Error = DatabaseError;
5657fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
58// Note: this will always return `Some(AccountInfo)`, See `EmptyDBWrapper`
59Database::basic(&mut self.inner, address)
60 }
6162fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
63 Database::code_by_hash(&mut self.inner, code_hash)
64 }
6566fn storage(&mut self, address: Address, index: U256) -> Result<U256, Self::Error> {
67 Database::storage(&mut self.inner, address, index)
68 }
6970fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
71 Database::block_hash(&mut self.inner, number)
72 }
73}
7475impl DatabaseCommit for MemDb {
76fn commit(&mut self, changes: Map<Address, Account>) {
77 DatabaseCommit::commit(&mut self.inner, changes)
78 }
79}
8081/// 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);
100101impl DatabaseRef for EmptyDBWrapper {
102type Error = DatabaseError;
103104fn basic_ref(&self, _address: Address) -> Result<Option<AccountInfo>, Self::Error> {
105// Note: this will always return `Some(AccountInfo)`, for the reason explained above
106Ok(Some(AccountInfo::default()))
107 }
108109fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
110Ok(self.0.code_by_hash_ref(code_hash)?)
111 }
112fn storage_ref(&self, address: Address, index: U256) -> Result<U256, Self::Error> {
113Ok(self.0.storage_ref(address, index)?)
114 }
115116fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
117Ok(self.0.block_hash_ref(number)?)
118 }
119}
120121#[cfg(test)]
122mod tests {
123use super::*;
124use alloy_primitives::b256;
125126/// Ensures the `Database(Ref)` implementation for `revm::CacheDB` works as expected
127 ///
128 /// Demonstrates how calling `Database::basic` works if an account does not exist
129#[test]
130fn cache_db_insert_basic_non_existing() {
131let mut db = CacheDB::new(EmptyDB::default());
132let address = Address::random();
133// call `basic` on a non-existing account
134let info = Database::basic(&mut db, address).unwrap();
135assert!(info.is_none());
136let mut info = info.unwrap_or_default();
137 info.balance = U256::from(500u64);
138139// insert the modified account info
140db.insert_account_info(address, info);
141142// when fetching again, the `AccountInfo` is still `None` because the state of the account
143 // is `AccountState::NotExisting`, see <https://github.com/bluealloy/revm/blob/8f4348dc93022cffb3730d9db5d3ab1aad77676a/crates/revm/src/db/in_memory_db.rs#L217-L226>
144let info = Database::basic(&mut db, address).unwrap();
145assert!(info.is_none());
146 }
147148/// Demonstrates how to insert a new account but not mark it as non-existing
149#[test]
150fn cache_db_insert_basic_default() {
151let mut db = CacheDB::new(EmptyDB::default());
152let address = Address::random();
153154// We use `basic_ref` here to ensure that the account is not marked as `NotExisting`.
155let info = DatabaseRef::basic_ref(&db, address).unwrap();
156assert!(info.is_none());
157let mut info = info.unwrap_or_default();
158 info.balance = U256::from(500u64);
159160// insert the modified account info
161db.insert_account_info(address, info.clone());
162163let loaded = Database::basic(&mut db, address).unwrap();
164assert!(loaded.is_some());
165assert_eq!(loaded.unwrap(), info)
166 }
167168/// Demonstrates that `Database::basic` for `MemDb` will always return the `AccountInfo`
169#[test]
170fn mem_db_insert_basic_default() {
171let mut db = MemDb::default();
172let address = Address::from_word(b256!(
173"0x000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa96045"
174));
175176let info = Database::basic(&mut db, address).unwrap();
177// We know info exists, as MemDb always returns `Some(AccountInfo)` due to the
178 // `EmptyDbWrapper`.
179assert!(info.is_some());
180let mut info = info.unwrap();
181 info.balance = U256::from(500u64);
182183// insert the modified account info
184db.inner.insert_account_info(address, info.clone());
185186let loaded = Database::basic(&mut db, address).unwrap();
187assert!(loaded.is_some());
188assert_eq!(loaded.unwrap(), info)
189 }
190}