revm_database/
in_memory_db.rs

1use core::convert::Infallible;
2use database_interface::{Database, DatabaseCommit, DatabaseRef, EmptyDB};
3use primitives::{address, hash_map::Entry, Address, HashMap, Log, B256, KECCAK_EMPTY, U256};
4use state::{Account, AccountInfo, Bytecode};
5use std::vec::Vec;
6
7/// A [Database] implementation that stores all state changes in memory.
8pub type InMemoryDB = CacheDB<EmptyDB>;
9
10/// A cache used in [CacheDB]. Its kept separate so it can be used independently.
11///
12/// Accounts and code are stored in two separate maps, the `accounts` map maps addresses to [DbAccount],
13/// whereas contracts are identified by their code hash, and are stored in the `contracts` map.
14/// The [DbAccount] holds the code hash of the contract, which is used to look up the contract in the `contracts` map.
15#[derive(Debug, Clone)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17pub struct Cache {
18    /// Account info where None means it is not existing. Not existing state is needed for Pre TANGERINE forks.
19    /// `code` is always `None`, and bytecode can be found in `contracts`.
20    pub accounts: HashMap<Address, DbAccount>,
21    /// Tracks all contracts by their code hash.
22    pub contracts: HashMap<B256, Bytecode>,
23    /// All logs that were committed via [DatabaseCommit::commit].
24    pub logs: Vec<Log>,
25    /// All cached block hashes from the [DatabaseRef].
26    pub block_hashes: HashMap<U256, B256>,
27}
28
29impl Default for Cache {
30    fn default() -> Self {
31        let mut contracts = HashMap::default();
32        contracts.insert(KECCAK_EMPTY, Bytecode::default());
33        contracts.insert(B256::ZERO, Bytecode::default());
34
35        Cache {
36            accounts: HashMap::default(),
37            contracts,
38            logs: Vec::default(),
39            block_hashes: HashMap::default(),
40        }
41    }
42}
43
44/// A [Database] implementation that stores all state changes in memory.
45///
46/// This implementation wraps a [DatabaseRef] that is used to load data ([AccountInfo]).
47#[derive(Debug, Clone)]
48#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
49pub struct CacheDB<ExtDB> {
50    /// The cache that stores all state changes.
51    pub cache: Cache,
52    /// The underlying database ([DatabaseRef]) that is used to load data.
53    ///
54    /// Note: This is read-only, data is never written to this database.
55    pub db: ExtDB,
56}
57
58impl<ExtDB: Default> Default for CacheDB<ExtDB> {
59    fn default() -> Self {
60        Self::new(ExtDB::default())
61    }
62}
63
64impl<ExtDb> CacheDB<CacheDB<ExtDb>> {
65    /// Flattens a nested cache by applying the outer cache to the inner cache.
66    ///
67    /// The behavior is as follows:
68    /// - Accounts are overridden with outer accounts
69    /// - Contracts are overridden with outer contracts
70    /// - Logs are appended
71    /// - Block hashes are overridden with outer block hashes
72    pub fn flatten(self) -> CacheDB<ExtDb> {
73        let CacheDB {
74            cache:
75                Cache {
76                    accounts,
77                    contracts,
78                    logs,
79                    block_hashes,
80                },
81            db: mut inner,
82        } = self;
83
84        inner.cache.accounts.extend(accounts);
85        inner.cache.contracts.extend(contracts);
86        inner.cache.logs.extend(logs);
87        inner.cache.block_hashes.extend(block_hashes);
88        inner
89    }
90
91    /// Discards the outer cache and return the inner cache.
92    pub fn discard_outer(self) -> CacheDB<ExtDb> {
93        self.db
94    }
95}
96
97impl<ExtDB> CacheDB<ExtDB> {
98    /// Creates a new cache with the given external database.
99    pub fn new(db: ExtDB) -> Self {
100        Self {
101            cache: Cache::default(),
102            db,
103        }
104    }
105
106    /// Inserts the account's code into the cache.
107    ///
108    /// Accounts objects and code are stored separately in the cache, this will take the code from the account and instead map it to the code hash.
109    ///
110    /// Note: This will not insert into the underlying external database.
111    pub fn insert_contract(&mut self, account: &mut AccountInfo) {
112        if let Some(code) = &account.code {
113            if !code.is_empty() {
114                if account.code_hash == KECCAK_EMPTY {
115                    account.code_hash = code.hash_slow();
116                }
117                self.cache
118                    .contracts
119                    .entry(account.code_hash)
120                    .or_insert_with(|| code.clone());
121            }
122        }
123        if account.code_hash.is_zero() {
124            account.code_hash = KECCAK_EMPTY;
125        }
126    }
127
128    /// Inserts account info but not override storage
129    pub fn insert_account_info(&mut self, address: Address, mut info: AccountInfo) {
130        self.insert_contract(&mut info);
131        self.cache.accounts.entry(address).or_default().info = info;
132    }
133
134    /// Wraps the cache in a [CacheDB], creating a nested cache.
135    pub fn nest(self) -> CacheDB<Self> {
136        CacheDB::new(self)
137    }
138}
139
140impl<ExtDB: DatabaseRef> CacheDB<ExtDB> {
141    /// Returns the account for the given address.
142    ///
143    /// If the account was not found in the cache, it will be loaded from the underlying database.
144    pub fn load_account(&mut self, address: Address) -> Result<&mut DbAccount, ExtDB::Error> {
145        let db = &self.db;
146        match self.cache.accounts.entry(address) {
147            Entry::Occupied(entry) => Ok(entry.into_mut()),
148            Entry::Vacant(entry) => Ok(entry.insert(
149                db.basic_ref(address)?
150                    .map(|info| DbAccount {
151                        info,
152                        ..Default::default()
153                    })
154                    .unwrap_or_else(DbAccount::new_not_existing),
155            )),
156        }
157    }
158
159    /// Inserts account storage without overriding account info
160    pub fn insert_account_storage(
161        &mut self,
162        address: Address,
163        slot: U256,
164        value: U256,
165    ) -> Result<(), ExtDB::Error> {
166        let account = self.load_account(address)?;
167        account.storage.insert(slot, value);
168        Ok(())
169    }
170
171    /// Replaces account storage without overriding account info
172    pub fn replace_account_storage(
173        &mut self,
174        address: Address,
175        storage: HashMap<U256, U256>,
176    ) -> Result<(), ExtDB::Error> {
177        let account = self.load_account(address)?;
178        account.account_state = AccountState::StorageCleared;
179        account.storage = storage.into_iter().collect();
180        Ok(())
181    }
182}
183
184impl<ExtDB> DatabaseCommit for CacheDB<ExtDB> {
185    fn commit(&mut self, changes: HashMap<Address, Account>) {
186        for (address, mut account) in changes {
187            if !account.is_touched() {
188                continue;
189            }
190            if account.is_selfdestructed() {
191                let db_account = self.cache.accounts.entry(address).or_default();
192                db_account.storage.clear();
193                db_account.account_state = AccountState::NotExisting;
194                db_account.info = AccountInfo::default();
195                continue;
196            }
197            let is_newly_created = account.is_created();
198            self.insert_contract(&mut account.info);
199
200            let db_account = self.cache.accounts.entry(address).or_default();
201            db_account.info = account.info;
202
203            db_account.account_state = if is_newly_created {
204                db_account.storage.clear();
205                AccountState::StorageCleared
206            } else if db_account.account_state.is_storage_cleared() {
207                // Preserve old account state if it already exists
208                AccountState::StorageCleared
209            } else {
210                AccountState::Touched
211            };
212            db_account.storage.extend(
213                account
214                    .storage
215                    .into_iter()
216                    .map(|(key, value)| (key, value.present_value())),
217            );
218        }
219    }
220}
221
222impl<ExtDB: DatabaseRef> Database for CacheDB<ExtDB> {
223    type Error = ExtDB::Error;
224
225    fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
226        let basic = match self.cache.accounts.entry(address) {
227            Entry::Occupied(entry) => entry.into_mut(),
228            Entry::Vacant(entry) => entry.insert(
229                self.db
230                    .basic_ref(address)?
231                    .map(|info| DbAccount {
232                        info,
233                        ..Default::default()
234                    })
235                    .unwrap_or_else(DbAccount::new_not_existing),
236            ),
237        };
238        Ok(basic.info())
239    }
240
241    fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
242        match self.cache.contracts.entry(code_hash) {
243            Entry::Occupied(entry) => Ok(entry.get().clone()),
244            Entry::Vacant(entry) => {
245                // If you return code bytes when basic fn is called this function is not needed.
246                Ok(entry.insert(self.db.code_by_hash_ref(code_hash)?).clone())
247            }
248        }
249    }
250
251    /// Get the value in an account's storage slot.
252    ///
253    /// It is assumed that account is already loaded.
254    fn storage(&mut self, address: Address, index: U256) -> Result<U256, Self::Error> {
255        match self.cache.accounts.entry(address) {
256            Entry::Occupied(mut acc_entry) => {
257                let acc_entry = acc_entry.get_mut();
258                match acc_entry.storage.entry(index) {
259                    Entry::Occupied(entry) => Ok(*entry.get()),
260                    Entry::Vacant(entry) => {
261                        if matches!(
262                            acc_entry.account_state,
263                            AccountState::StorageCleared | AccountState::NotExisting
264                        ) {
265                            Ok(U256::ZERO)
266                        } else {
267                            let slot = self.db.storage_ref(address, index)?;
268                            entry.insert(slot);
269                            Ok(slot)
270                        }
271                    }
272                }
273            }
274            Entry::Vacant(acc_entry) => {
275                // Acc needs to be loaded for us to access slots.
276                let info = self.db.basic_ref(address)?;
277                let (account, value) = if info.is_some() {
278                    let value = self.db.storage_ref(address, index)?;
279                    let mut account: DbAccount = info.into();
280                    account.storage.insert(index, value);
281                    (account, value)
282                } else {
283                    (info.into(), U256::ZERO)
284                };
285                acc_entry.insert(account);
286                Ok(value)
287            }
288        }
289    }
290
291    fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
292        match self.cache.block_hashes.entry(U256::from(number)) {
293            Entry::Occupied(entry) => Ok(*entry.get()),
294            Entry::Vacant(entry) => {
295                let hash = self.db.block_hash_ref(number)?;
296                entry.insert(hash);
297                Ok(hash)
298            }
299        }
300    }
301}
302
303impl<ExtDB: DatabaseRef> DatabaseRef for CacheDB<ExtDB> {
304    type Error = ExtDB::Error;
305
306    fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
307        match self.cache.accounts.get(&address) {
308            Some(acc) => Ok(acc.info()),
309            None => self.db.basic_ref(address),
310        }
311    }
312
313    fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
314        match self.cache.contracts.get(&code_hash) {
315            Some(entry) => Ok(entry.clone()),
316            None => self.db.code_by_hash_ref(code_hash),
317        }
318    }
319
320    fn storage_ref(&self, address: Address, index: U256) -> Result<U256, Self::Error> {
321        match self.cache.accounts.get(&address) {
322            Some(acc_entry) => match acc_entry.storage.get(&index) {
323                Some(entry) => Ok(*entry),
324                None => {
325                    if matches!(
326                        acc_entry.account_state,
327                        AccountState::StorageCleared | AccountState::NotExisting
328                    ) {
329                        Ok(U256::ZERO)
330                    } else {
331                        self.db.storage_ref(address, index)
332                    }
333                }
334            },
335            None => self.db.storage_ref(address, index),
336        }
337    }
338
339    fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
340        match self.cache.block_hashes.get(&U256::from(number)) {
341            Some(entry) => Ok(*entry),
342            None => self.db.block_hash_ref(number),
343        }
344    }
345}
346
347#[derive(Debug, Clone, Default)]
348#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
349pub struct DbAccount {
350    pub info: AccountInfo,
351    /// If account is selfdestructed or newly created, storage will be cleared.
352    pub account_state: AccountState,
353    /// Storage slots
354    pub storage: HashMap<U256, U256>,
355}
356
357impl DbAccount {
358    pub fn new_not_existing() -> Self {
359        Self {
360            account_state: AccountState::NotExisting,
361            ..Default::default()
362        }
363    }
364
365    pub fn info(&self) -> Option<AccountInfo> {
366        if matches!(self.account_state, AccountState::NotExisting) {
367            None
368        } else {
369            Some(self.info.clone())
370        }
371    }
372}
373
374impl From<Option<AccountInfo>> for DbAccount {
375    fn from(from: Option<AccountInfo>) -> Self {
376        from.map(Self::from).unwrap_or_else(Self::new_not_existing)
377    }
378}
379
380impl From<AccountInfo> for DbAccount {
381    fn from(info: AccountInfo) -> Self {
382        Self {
383            info,
384            account_state: AccountState::None,
385            ..Default::default()
386        }
387    }
388}
389
390#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
391#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
392pub enum AccountState {
393    /// Before Spurious Dragon hardfork there was a difference between empty and not existing.
394    /// And we are flagging it here.
395    NotExisting,
396    /// EVM touched this account. For newer hardfork this means it can be cleared/removed from state.
397    Touched,
398    /// EVM cleared storage of this account, mostly by selfdestruct, we don't ask database for storage slots
399    /// and assume they are U256::ZERO
400    StorageCleared,
401    /// EVM didn't interacted with this account
402    #[default]
403    None,
404}
405
406impl AccountState {
407    /// Returns `true` if EVM cleared storage of this account
408    pub fn is_storage_cleared(&self) -> bool {
409        matches!(self, AccountState::StorageCleared)
410    }
411}
412
413/// Custom benchmarking DB that only has account info for the zero address.
414///
415/// Any other address will return an empty account.
416#[derive(Debug, Default, Clone)]
417pub struct BenchmarkDB(pub Bytecode, B256);
418
419impl BenchmarkDB {
420    pub fn new_bytecode(bytecode: Bytecode) -> Self {
421        let hash = bytecode.hash_slow();
422        Self(bytecode, hash)
423    }
424}
425
426/// BYTECODE address
427pub const FFADDRESS: Address = address!("0xffffffffffffffffffffffffffffffffffffffff");
428pub const BENCH_TARGET: Address = FFADDRESS;
429pub const BENCH_TARGET_BALANCE: U256 = U256::from_limbs([10_000_000, 0, 0, 0]);
430/// CALLER address
431pub const EEADDRESS: Address = address!("0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee");
432pub const BENCH_CALLER: Address = EEADDRESS;
433pub const BENCH_CALLER_BALANCE: U256 = U256::from_limbs([10_000_000, 0, 0, 0]);
434
435impl Database for BenchmarkDB {
436    type Error = Infallible;
437    /// Get basic account information.
438    fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
439        if address == BENCH_TARGET {
440            return Ok(Some(AccountInfo {
441                nonce: 1,
442                balance: BENCH_TARGET_BALANCE,
443                code: Some(self.0.clone()),
444                code_hash: self.1,
445            }));
446        }
447        if address == BENCH_CALLER {
448            return Ok(Some(AccountInfo {
449                nonce: 0,
450                balance: BENCH_CALLER_BALANCE,
451                code: None,
452                code_hash: KECCAK_EMPTY,
453            }));
454        }
455        Ok(None)
456    }
457
458    /// Get account code by its hash
459    fn code_by_hash(&mut self, _code_hash: B256) -> Result<Bytecode, Self::Error> {
460        Ok(Bytecode::default())
461    }
462
463    /// Get storage value of address at index.
464    fn storage(&mut self, _address: Address, _index: U256) -> Result<U256, Self::Error> {
465        Ok(U256::default())
466    }
467
468    // History related
469    fn block_hash(&mut self, _number: u64) -> Result<B256, Self::Error> {
470        Ok(B256::default())
471    }
472}
473
474#[cfg(test)]
475mod tests {
476    use super::{CacheDB, EmptyDB};
477    use database_interface::Database;
478    use primitives::{Address, HashMap, U256};
479    use state::AccountInfo;
480
481    #[test]
482    fn test_insert_account_storage() {
483        let account = Address::with_last_byte(42);
484        let nonce = 42;
485        let mut init_state = CacheDB::new(EmptyDB::default());
486        init_state.insert_account_info(
487            account,
488            AccountInfo {
489                nonce,
490                ..Default::default()
491            },
492        );
493
494        let (key, value) = (U256::from(123), U256::from(456));
495        let mut new_state = CacheDB::new(init_state);
496        new_state
497            .insert_account_storage(account, key, value)
498            .unwrap();
499
500        assert_eq!(new_state.basic(account).unwrap().unwrap().nonce, nonce);
501        assert_eq!(new_state.storage(account, key), Ok(value));
502    }
503
504    #[test]
505    fn test_replace_account_storage() {
506        let account = Address::with_last_byte(42);
507        let nonce = 42;
508        let mut init_state = CacheDB::new(EmptyDB::default());
509        init_state.insert_account_info(
510            account,
511            AccountInfo {
512                nonce,
513                ..Default::default()
514            },
515        );
516
517        let (key0, value0) = (U256::from(123), U256::from(456));
518        let (key1, value1) = (U256::from(789), U256::from(999));
519        init_state
520            .insert_account_storage(account, key0, value0)
521            .unwrap();
522
523        let mut new_state = CacheDB::new(init_state);
524        new_state
525            .replace_account_storage(account, HashMap::from_iter([(key1, value1)]))
526            .unwrap();
527
528        assert_eq!(new_state.basic(account).unwrap().unwrap().nonce, nonce);
529        assert_eq!(new_state.storage(account, key0), Ok(U256::ZERO));
530        assert_eq!(new_state.storage(account, key1), Ok(value1));
531    }
532
533    #[cfg(feature = "serde")]
534    #[test]
535    fn test_serialize_deserialize_cachedb() {
536        let account = Address::with_last_byte(69);
537        let nonce = 420;
538        let mut init_state = CacheDB::new(EmptyDB::default());
539        init_state.insert_account_info(
540            account,
541            AccountInfo {
542                nonce,
543                ..Default::default()
544            },
545        );
546
547        let serialized = serde_json::to_string(&init_state).unwrap();
548        let deserialized: CacheDB<EmptyDB> = serde_json::from_str(&serialized).unwrap();
549
550        assert!(deserialized.cache.accounts.contains_key(&account));
551        assert_eq!(
552            deserialized
553                .cache
554                .accounts
555                .get(&account)
556                .unwrap()
557                .info
558                .nonce,
559            nonce
560        );
561    }
562}