Skip to main content

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, StorageKeyMap,
8    StorageValue, U256Map, B256, 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: U256Map<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: StorageKeyMap<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: AddressMap<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        Ok(self.load_account(address)?.info())
325    }
326
327    fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
328        match self.cache.contracts.entry(code_hash) {
329            Entry::Occupied(entry) => Ok(entry.get().clone()),
330            Entry::Vacant(entry) => {
331                // If you return code bytes when basic fn is called this function is not needed.
332                Ok(entry.insert(self.db.code_by_hash_ref(code_hash)?).clone())
333            }
334        }
335    }
336
337    /// Get the value in an account's storage slot.
338    ///
339    /// It is assumed that account is already loaded.
340    fn storage(
341        &mut self,
342        address: Address,
343        index: StorageKey,
344    ) -> Result<StorageValue, Self::Error> {
345        match self.cache.accounts.entry(address) {
346            Entry::Occupied(mut acc_entry) => {
347                let acc_entry = acc_entry.get_mut();
348                match acc_entry.storage.entry(index) {
349                    Entry::Occupied(entry) => Ok(*entry.get()),
350                    Entry::Vacant(entry) => {
351                        if matches!(
352                            acc_entry.account_state,
353                            AccountState::StorageCleared | AccountState::NotExisting
354                        ) {
355                            Ok(StorageValue::ZERO)
356                        } else {
357                            let slot = self.db.storage_ref(address, index)?;
358                            entry.insert(slot);
359                            Ok(slot)
360                        }
361                    }
362                }
363            }
364            Entry::Vacant(acc_entry) => {
365                // Acc needs to be loaded for us to access slots.
366                let info = self.db.basic_ref(address)?;
367                let (account, value) = if info.is_some() {
368                    let value = self.db.storage_ref(address, index)?;
369                    let mut account: DbAccount = info.into();
370                    account.storage.insert(index, value);
371                    (account, value)
372                } else {
373                    (info.into(), StorageValue::ZERO)
374                };
375                acc_entry.insert(account);
376                Ok(value)
377            }
378        }
379    }
380
381    fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
382        match self.cache.block_hashes.entry(U256::from(number)) {
383            Entry::Occupied(entry) => Ok(*entry.get()),
384            Entry::Vacant(entry) => {
385                let hash = self.db.block_hash_ref(number)?;
386                entry.insert(hash);
387                Ok(hash)
388            }
389        }
390    }
391}
392
393impl<ExtDB: DatabaseRef> DatabaseRef for CacheDB<ExtDB> {
394    type Error = ExtDB::Error;
395
396    fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
397        match self.cache.accounts.get(&address) {
398            Some(acc) => Ok(acc.info()),
399            None => self.db.basic_ref(address),
400        }
401    }
402
403    fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
404        match self.cache.contracts.get(&code_hash) {
405            Some(entry) => Ok(entry.clone()),
406            None => self.db.code_by_hash_ref(code_hash),
407        }
408    }
409
410    fn storage_ref(
411        &self,
412        address: Address,
413        index: StorageKey,
414    ) -> Result<StorageValue, Self::Error> {
415        match self.cache.accounts.get(&address) {
416            Some(acc_entry) => match acc_entry.storage.get(&index) {
417                Some(entry) => Ok(*entry),
418                None => {
419                    if matches!(
420                        acc_entry.account_state,
421                        AccountState::StorageCleared | AccountState::NotExisting
422                    ) {
423                        Ok(StorageValue::ZERO)
424                    } else {
425                        self.db.storage_ref(address, index)
426                    }
427                }
428            },
429            None => self.db.storage_ref(address, index),
430        }
431    }
432
433    fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
434        match self.cache.block_hashes.get(&U256::from(number)) {
435            Some(entry) => Ok(*entry),
436            None => self.db.block_hash_ref(number),
437        }
438    }
439}
440
441/// Database account representation.
442#[derive(Debug, Clone, Default)]
443#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
444pub struct DbAccount {
445    /// Basic account information.
446    pub info: AccountInfo,
447    /// If account is selfdestructed or newly created, storage will be cleared.
448    pub account_state: AccountState,
449    /// Storage slots
450    pub storage: StorageKeyMap<StorageValue>,
451}
452
453impl DbAccount {
454    /// Creates a new non-existing account.
455    pub fn new_not_existing() -> Self {
456        Self {
457            account_state: AccountState::NotExisting,
458            ..Default::default()
459        }
460    }
461
462    /// Returns account info if the account exists.
463    pub fn info(&self) -> Option<AccountInfo> {
464        if matches!(self.account_state, AccountState::NotExisting) {
465            None
466        } else {
467            Some(self.info.clone())
468        }
469    }
470
471    /// Updates the account information.
472    #[inline(always)]
473    pub fn update_info(&mut self, info: AccountInfo) {
474        self.info = info;
475    }
476
477    /// Updates the account state.
478    #[inline(always)]
479    pub fn update_account_state(&mut self, account_state: AccountState) {
480        self.account_state = account_state;
481    }
482}
483
484impl From<Option<AccountInfo>> for DbAccount {
485    fn from(from: Option<AccountInfo>) -> Self {
486        from.map(Self::from).unwrap_or_else(Self::new_not_existing)
487    }
488}
489
490impl From<AccountInfo> for DbAccount {
491    fn from(info: AccountInfo) -> Self {
492        Self {
493            info,
494            account_state: AccountState::None,
495            ..Default::default()
496        }
497    }
498}
499
500/// State of an account in the database.
501#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
502#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
503pub enum AccountState {
504    /// Before Spurious Dragon hardfork there was a difference between empty and not existing.
505    /// And we are flagging it here.
506    NotExisting,
507    /// EVM touched this account. For newer hardfork this means it can be cleared/removed from state.
508    Touched,
509    /// EVM cleared storage of this account, mostly by selfdestruct, we don't ask database for storage slots
510    /// and assume they are StorageValue::ZERO
511    StorageCleared,
512    /// EVM didn't interacted with this account
513    #[default]
514    None,
515}
516
517impl AccountState {
518    /// Returns `true` if EVM cleared storage of this account
519    pub fn is_storage_cleared(&self) -> bool {
520        matches!(self, AccountState::StorageCleared)
521    }
522}
523
524/// Custom benchmarking DB that only has account info for the zero address.
525///
526/// Any other address will return an empty account.
527#[derive(Debug, Default, Clone)]
528pub struct BenchmarkDB(pub Bytecode, B256);
529
530impl BenchmarkDB {
531    /// Creates a new benchmark database with the given bytecode.
532    pub fn new_bytecode(bytecode: Bytecode) -> Self {
533        let hash = bytecode.hash_slow();
534        Self(bytecode, hash)
535    }
536}
537
538impl Database for BenchmarkDB {
539    type Error = Infallible;
540    /// Get basic account information.
541    fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
542        if address == BENCH_TARGET {
543            return Ok(Some(AccountInfo {
544                nonce: 1,
545                balance: BENCH_TARGET_BALANCE,
546                code: Some(self.0.clone()),
547                code_hash: self.1,
548                ..Default::default()
549            }));
550        }
551        if address == BENCH_CALLER {
552            return Ok(Some(AccountInfo {
553                nonce: 0,
554                balance: BENCH_CALLER_BALANCE,
555                code: None,
556                code_hash: KECCAK_EMPTY,
557                ..Default::default()
558            }));
559        }
560        Ok(None)
561    }
562
563    /// Get account code by its hash
564    fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
565        if code_hash == self.1 {
566            Ok(self.0.clone())
567        } else {
568            Ok(Bytecode::default())
569        }
570    }
571
572    /// Get storage value of address at index.
573    fn storage(
574        &mut self,
575        _address: Address,
576        _index: StorageKey,
577    ) -> Result<StorageValue, Self::Error> {
578        Ok(StorageValue::default())
579    }
580
581    // History related
582    fn block_hash(&mut self, _number: u64) -> Result<B256, Self::Error> {
583        Ok(B256::default())
584    }
585}
586
587#[cfg(test)]
588mod tests {
589    use super::{CacheDB, EmptyDB};
590    use database_interface::Database;
591    use primitives::{Address, HashMap, StorageKey, StorageValue};
592    use state::AccountInfo;
593
594    #[test]
595    fn test_insert_account_storage() {
596        let account = Address::with_last_byte(42);
597        let nonce = 42;
598        let mut init_state = CacheDB::new(EmptyDB::default());
599        init_state.insert_account_info(
600            account,
601            AccountInfo {
602                nonce,
603                ..Default::default()
604            },
605        );
606
607        let (key, value) = (StorageKey::from(123), StorageValue::from(456));
608        let mut new_state = CacheDB::new(init_state);
609        new_state
610            .insert_account_storage(account, key, value)
611            .unwrap();
612
613        assert_eq!(new_state.basic(account).unwrap().unwrap().nonce, nonce);
614        assert_eq!(new_state.storage(account, key), Ok(value));
615    }
616
617    #[test]
618    fn test_replace_account_storage() {
619        let account = Address::with_last_byte(42);
620        let nonce = 42;
621        let mut init_state = CacheDB::new(EmptyDB::default());
622        init_state.insert_account_info(
623            account,
624            AccountInfo {
625                nonce,
626                ..Default::default()
627            },
628        );
629
630        let (key0, value0) = (StorageKey::from(123), StorageValue::from(456));
631        let (key1, value1) = (StorageKey::from(789), StorageValue::from(999));
632        init_state
633            .insert_account_storage(account, key0, value0)
634            .unwrap();
635
636        let mut new_state = CacheDB::new(init_state);
637        new_state
638            .replace_account_storage(account, HashMap::from_iter([(key1, value1)]))
639            .unwrap();
640
641        assert_eq!(new_state.basic(account).unwrap().unwrap().nonce, nonce);
642        assert_eq!(new_state.storage(account, key0), Ok(StorageValue::ZERO));
643        assert_eq!(new_state.storage(account, key1), Ok(value1));
644    }
645
646    #[cfg(feature = "std")]
647    #[test]
648    fn test_pretty_print_cachedb() {
649        use primitives::{Bytes, Log, LogData, B256, U256};
650
651        let account = Address::with_last_byte(55);
652        let mut cachedb = CacheDB::new(EmptyDB::default());
653        cachedb.insert_account_info(
654            account,
655            AccountInfo {
656                nonce: 7,
657                ..Default::default()
658            },
659        );
660        let key = StorageKey::from(1);
661        let value = StorageValue::from(2);
662        cachedb.insert_account_storage(account, key, value).unwrap();
663
664        // Add a log entry
665        let log = Log {
666            address: account,
667            data: LogData::new(Vec::new(), Bytes::from(vec![0x01u8]))
668                .expect("LogData should have <=4 topics"),
669        };
670        cachedb.cache.logs.push(log);
671
672        // Add a block hash entry
673        cachedb
674            .cache
675            .block_hashes
676            .insert(U256::from(123u64), B256::from([1u8; 32]));
677
678        let s = cachedb.pretty_print();
679        assert!(s.contains("CacheDB:"));
680        assert!(s.contains("accounts: 1 total"));
681        // storage line is expected to be present for the account
682        assert!(s.contains("storage: 1 slots"));
683
684        // logs and block_hashes should be reported with counts
685        assert!(s.contains("logs: 1 total"));
686        assert!(s.contains("block_hashes: 1 total"));
687    }
688
689    #[cfg(feature = "serde")]
690    #[test]
691    fn test_serialize_deserialize_cachedb() {
692        let account = Address::with_last_byte(69);
693        let nonce = 420;
694        let mut init_state = CacheDB::new(EmptyDB::default());
695        init_state.insert_account_info(
696            account,
697            AccountInfo {
698                nonce,
699                ..Default::default()
700            },
701        );
702
703        let serialized = serde_json::to_string(&init_state).unwrap();
704        let deserialized: CacheDB<EmptyDB> = serde_json::from_str(&serialized).unwrap();
705
706        assert!(deserialized.cache.accounts.contains_key(&account));
707        assert_eq!(
708            deserialized
709                .cache
710                .accounts
711                .get(&account)
712                .unwrap()
713                .info
714                .nonce,
715            nonce
716        );
717    }
718}