revm/db/states/
cache.rs

1use super::{
2    plain_account::PlainStorage, transition_account::TransitionAccount, CacheAccount, PlainAccount,
3};
4use revm_interpreter::primitives::{
5    Account, AccountInfo, Address, Bytecode, EvmState, HashMap, B256,
6};
7use std::vec::Vec;
8
9/// Cache state contains both modified and original values.
10///
11/// Cache state is main state that revm uses to access state.
12/// It loads all accounts from database and applies revm output to it.
13///
14/// It generates transitions that is used to build BundleState.
15#[derive(Clone, Debug, PartialEq, Eq)]
16pub struct CacheState {
17    /// Block state account with account state.
18    pub accounts: HashMap<Address, CacheAccount>,
19    /// Created contracts.
20    // TODO add bytecode counter for number of bytecodes added/removed.
21    pub contracts: HashMap<B256, Bytecode>,
22    /// Has EIP-161 state clear enabled (Spurious Dragon hardfork).
23    pub has_state_clear: bool,
24}
25
26impl Default for CacheState {
27    fn default() -> Self {
28        Self::new(true)
29    }
30}
31
32impl CacheState {
33    /// New default state.
34    pub fn new(has_state_clear: bool) -> Self {
35        Self {
36            accounts: HashMap::default(),
37            contracts: HashMap::default(),
38            has_state_clear,
39        }
40    }
41
42    /// Set state clear flag. EIP-161.
43    pub fn set_state_clear_flag(&mut self, has_state_clear: bool) {
44        self.has_state_clear = has_state_clear;
45    }
46
47    /// Helper function that returns all accounts.
48    ///
49    /// Used inside tests to generate merkle tree.
50    pub fn trie_account(&self) -> impl IntoIterator<Item = (Address, &PlainAccount)> {
51        self.accounts.iter().filter_map(|(address, account)| {
52            account
53                .account
54                .as_ref()
55                .map(|plain_acc| (*address, plain_acc))
56        })
57    }
58
59    /// Insert not existing account.
60    pub fn insert_not_existing(&mut self, address: Address) {
61        self.accounts
62            .insert(address, CacheAccount::new_loaded_not_existing());
63    }
64
65    /// Insert Loaded (Or LoadedEmptyEip161 if account is empty) account.
66    pub fn insert_account(&mut self, address: Address, info: AccountInfo) {
67        let account = if !info.is_empty() {
68            CacheAccount::new_loaded(info, HashMap::default())
69        } else {
70            CacheAccount::new_loaded_empty_eip161(HashMap::default())
71        };
72        self.accounts.insert(address, account);
73    }
74
75    /// Similar to `insert_account` but with storage.
76    pub fn insert_account_with_storage(
77        &mut self,
78        address: Address,
79        info: AccountInfo,
80        storage: PlainStorage,
81    ) {
82        let account = if !info.is_empty() {
83            CacheAccount::new_loaded(info, storage)
84        } else {
85            CacheAccount::new_loaded_empty_eip161(storage)
86        };
87        self.accounts.insert(address, account);
88    }
89
90    /// Apply output of revm execution and create account transitions that are used to build BundleState.
91    pub fn apply_evm_state(&mut self, evm_state: EvmState) -> Vec<(Address, TransitionAccount)> {
92        let mut transitions = Vec::with_capacity(evm_state.len());
93        for (address, account) in evm_state {
94            if let Some(transition) = self.apply_account_state(address, account) {
95                transitions.push((address, transition));
96            }
97        }
98        transitions
99    }
100
101    /// Apply updated account state to the cached account.
102    /// Returns account transition if applicable.
103    fn apply_account_state(
104        &mut self,
105        address: Address,
106        account: Account,
107    ) -> Option<TransitionAccount> {
108        // not touched account are never changed.
109        if !account.is_touched() {
110            return None;
111        }
112
113        let this_account = self
114            .accounts
115            .get_mut(&address)
116            .expect("All accounts should be present inside cache");
117
118        // If it is marked as selfdestructed inside revm
119        // we need to changed state to destroyed.
120        if account.is_selfdestructed() {
121            return this_account.selfdestruct();
122        }
123
124        let is_created = account.is_created();
125        let is_empty = account.is_empty();
126
127        // transform evm storage to storage with previous value.
128        let changed_storage = account
129            .storage
130            .into_iter()
131            .filter(|(_, slot)| slot.is_changed())
132            .map(|(key, slot)| (key, slot.into()))
133            .collect();
134
135        // Note: it can happen that created contract get selfdestructed in same block
136        // that is why is_created is checked after selfdestructed
137        //
138        // Note: Create2 opcode (Petersburg) was after state clear EIP (Spurious Dragon)
139        //
140        // Note: It is possibility to create KECCAK_EMPTY contract with some storage
141        // by just setting storage inside CRATE constructor. Overlap of those contracts
142        // is not possible because CREATE2 is introduced later.
143        if is_created {
144            return Some(this_account.newly_created(account.info, changed_storage));
145        }
146
147        // Account is touched, but not selfdestructed or newly created.
148        // Account can be touched and not changed.
149        // And when empty account is touched it needs to be removed from database.
150        // EIP-161 state clear
151        if is_empty {
152            if self.has_state_clear {
153                // touch empty account.
154                this_account.touch_empty_eip161()
155            } else {
156                // if account is empty and state clear is not enabled we should save
157                // empty account.
158                this_account.touch_create_pre_eip161(changed_storage)
159            }
160        } else {
161            Some(this_account.change(account.info, changed_storage))
162        }
163    }
164}