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