revm_database/states/
cache.rs1use super::{
2 plain_account::PlainStorage, transition_account::TransitionAccount, CacheAccount, PlainAccount,
3};
4use bytecode::Bytecode;
5use primitives::{Address, HashMap, B256};
6use state::{Account, AccountInfo};
7use std::vec::Vec;
8
9#[derive(Clone, Debug, PartialEq, Eq)]
18pub struct CacheState {
19 pub accounts: HashMap<Address, CacheAccount>,
21 pub contracts: HashMap<B256, Bytecode>,
23 pub has_state_clear: bool,
25}
26
27impl Default for CacheState {
28 fn default() -> Self {
29 Self::new(true)
30 }
31}
32
33impl CacheState {
34 pub fn new(has_state_clear: bool) -> Self {
36 Self {
37 accounts: HashMap::default(),
38 contracts: HashMap::default(),
39 has_state_clear,
40 }
41 }
42
43 pub fn set_state_clear_flag(&mut self, has_state_clear: bool) {
45 self.has_state_clear = has_state_clear;
46 }
47
48 pub fn trie_account(&self) -> impl IntoIterator<Item = (Address, &PlainAccount)> {
52 self.accounts.iter().filter_map(|(address, account)| {
53 account
54 .account
55 .as_ref()
56 .map(|plain_acc| (*address, plain_acc))
57 })
58 }
59
60 pub fn insert_not_existing(&mut self, address: Address) {
62 self.accounts
63 .insert(address, CacheAccount::new_loaded_not_existing());
64 }
65
66 pub fn insert_account(&mut self, address: Address, info: AccountInfo) {
68 let account = if !info.is_empty() {
69 CacheAccount::new_loaded(info, HashMap::default())
70 } else {
71 CacheAccount::new_loaded_empty_eip161(HashMap::default())
72 };
73 self.accounts.insert(address, account);
74 }
75
76 pub fn insert_account_with_storage(
78 &mut self,
79 address: Address,
80 info: AccountInfo,
81 storage: PlainStorage,
82 ) {
83 let account = if !info.is_empty() {
84 CacheAccount::new_loaded(info, storage)
85 } else {
86 CacheAccount::new_loaded_empty_eip161(storage)
87 };
88 self.accounts.insert(address, account);
89 }
90
91 #[inline]
93 pub fn apply_evm_state(
94 &mut self,
95 evm_state: impl IntoIterator<Item = (Address, Account)>,
96 ) -> Vec<(Address, TransitionAccount)> {
97 evm_state
98 .into_iter()
99 .filter_map(|(address, account)| {
100 self.apply_account_state(address, account)
101 .map(|transition| (address, transition))
102 })
103 .collect()
104 }
105
106 #[cfg(feature = "std")]
108 pub fn pretty_print(&self) -> String {
109 let mut output = String::new();
110 output.push_str("CacheState:\n");
111 output.push_str(&format!(
112 " (state_clear_enabled: {}, ",
113 self.has_state_clear
114 ));
115 output.push_str(&format!("accounts: {} total)\n", self.accounts.len()));
116
117 let mut accounts: Vec<_> = self.accounts.iter().collect();
119 accounts.sort_by_key(|(addr, _)| *addr);
120
121 let mut contracts = self.contracts.clone();
122
123 for (address, account) in accounts {
124 output.push_str(&format!(" [{address}]:\n"));
125 output.push_str(&format!(" status: {:?}\n", account.status));
126
127 if let Some(plain_account) = &account.account {
128 let code_hash = plain_account.info.code_hash;
129 output.push_str(&format!(" balance: {}\n", plain_account.info.balance));
130 output.push_str(&format!(" nonce: {}\n", plain_account.info.nonce));
131 output.push_str(&format!(" code_hash: {code_hash}\n"));
132
133 if let Some(code) = &plain_account.info.code {
134 if !code.is_empty() {
135 contracts.insert(code_hash, code.clone());
136 }
137 }
138
139 if !plain_account.storage.is_empty() {
140 output.push_str(&format!(
141 " storage: {} slots\n",
142 plain_account.storage.len()
143 ));
144 let mut storage: Vec<_> = plain_account.storage.iter().collect();
146 storage.sort_by_key(|(key, _)| *key);
147
148 for (key, value) in storage.iter() {
149 output.push_str(&format!(" [{key:#x}]: {value:#x}\n"));
150 }
151 }
152 } else {
153 output.push_str(" account: None (destroyed or non-existent)\n");
154 }
155 }
156
157 if !contracts.is_empty() {
158 output.push_str(&format!(" contracts: {} total\n", contracts.len()));
159 for (hash, bytecode) in contracts.iter() {
160 let len = bytecode.len();
161 output.push_str(&format!(" [{hash}]: {len} bytes\n"));
162 }
163 }
164
165 output.push_str("}\n");
166 output
167 }
168
169 fn apply_account_state(
173 &mut self,
174 address: Address,
175 account: Account,
176 ) -> Option<TransitionAccount> {
177 if !account.is_touched() {
179 return None;
180 }
181
182 let this_account = self
183 .accounts
184 .get_mut(&address)
185 .expect("All accounts should be present inside cache");
186
187 if account.is_selfdestructed() {
190 return this_account.selfdestruct();
191 }
192
193 let is_created = account.is_created();
194 let is_empty = account.is_empty();
195
196 let changed_storage = account
198 .storage
199 .into_iter()
200 .filter(|(_, slot)| slot.is_changed())
201 .map(|(key, slot)| (key, slot.into()))
202 .collect();
203
204 if is_created {
213 return Some(this_account.newly_created(account.info, changed_storage));
214 }
215
216 if is_empty {
221 if self.has_state_clear {
222 this_account.touch_empty_eip161()
224 } else {
225 this_account.touch_create_pre_eip161(changed_storage)
228 }
229 } else {
230 Some(this_account.change(account.info, changed_storage))
231 }
232 }
233}