revm_database/
in_memory_db.rs

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