revm_database/
in_memory_db.rs

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