revm_database/states/
cache.rs1use super::{
2 plain_account::PlainStorage, transition_account::TransitionAccount, CacheAccount, PlainAccount,
3};
4use bytecode::Bytecode;
5use primitives::{Address, AddressMap, B256Map, HashMap};
6use state::{Account, AccountInfo};
7use std::vec::Vec;
8
9#[derive(Clone, Debug, PartialEq, Eq)]
18pub struct CacheState {
19 pub accounts: AddressMap<CacheAccount>,
21 pub contracts: B256Map<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 clear(&mut self) {
50 self.accounts.clear();
51 self.contracts.clear();
52 }
53
54 pub fn trie_account(&self) -> impl IntoIterator<Item = (Address, &PlainAccount)> {
58 self.accounts.iter().filter_map(|(address, account)| {
59 account
60 .account
61 .as_ref()
62 .map(|plain_acc| (*address, plain_acc))
63 })
64 }
65
66 pub fn insert_not_existing(&mut self, address: Address) {
68 self.accounts
69 .insert(address, CacheAccount::new_loaded_not_existing());
70 }
71
72 pub fn insert_account(&mut self, address: Address, info: AccountInfo) {
74 let account = if !info.is_empty() {
75 CacheAccount::new_loaded(info, HashMap::default())
76 } else {
77 CacheAccount::new_loaded_empty_eip161(HashMap::default())
78 };
79 self.accounts.insert(address, account);
80 }
81
82 pub fn insert_account_with_storage(
84 &mut self,
85 address: Address,
86 info: AccountInfo,
87 storage: PlainStorage,
88 ) {
89 let account = if !info.is_empty() {
90 CacheAccount::new_loaded(info, storage)
91 } else {
92 CacheAccount::new_loaded_empty_eip161(storage)
93 };
94 self.accounts.insert(address, account);
95 }
96
97 #[inline]
99 pub fn apply_evm_state<F>(
100 &mut self,
101 evm_state: impl IntoIterator<Item = (Address, Account)>,
102 inspect: F,
103 ) -> Vec<(Address, TransitionAccount)>
104 where
105 F: FnMut(&Address, &Account),
106 {
107 self.apply_evm_state_iter(evm_state, inspect).collect()
108 }
109
110 #[inline]
112 pub(crate) fn apply_evm_state_iter<'a, F, T>(
113 &'a mut self,
114 evm_state: T,
115 mut inspect: F,
116 ) -> impl Iterator<Item = (Address, TransitionAccount)> + use<'a, F, T>
117 where
118 F: FnMut(&Address, &Account),
119 T: IntoIterator<Item = (Address, Account)>,
120 {
121 evm_state.into_iter().filter_map(move |(address, account)| {
122 inspect(&address, &account);
123 self.apply_account_state(address, account)
124 .map(|transition| (address, transition))
125 })
126 }
127
128 #[cfg(feature = "std")]
130 pub fn pretty_print(&self) -> String {
131 let mut output = String::new();
132 output.push_str("CacheState:\n");
133 output.push_str(&format!(
134 " (state_clear_enabled: {}, ",
135 self.has_state_clear
136 ));
137 output.push_str(&format!("accounts: {} total)\n", self.accounts.len()));
138
139 let mut accounts: Vec<_> = self.accounts.iter().collect();
141 accounts.sort_by_key(|(addr, _)| *addr);
142
143 let mut contracts = self.contracts.clone();
144
145 for (address, account) in accounts {
146 output.push_str(&format!(" [{address}]:\n"));
147 output.push_str(&format!(" status: {:?}\n", account.status));
148
149 if let Some(plain_account) = &account.account {
150 let code_hash = plain_account.info.code_hash;
151 output.push_str(&format!(" balance: {}\n", plain_account.info.balance));
152 output.push_str(&format!(" nonce: {}\n", plain_account.info.nonce));
153 output.push_str(&format!(" code_hash: {code_hash}\n"));
154
155 if let Some(code) = &plain_account.info.code {
156 if !code.is_empty() {
157 contracts.insert(code_hash, code.clone());
158 }
159 }
160
161 if !plain_account.storage.is_empty() {
162 output.push_str(&format!(
163 " storage: {} slots\n",
164 plain_account.storage.len()
165 ));
166 let mut storage: Vec<_> = plain_account.storage.iter().collect();
168 storage.sort_by_key(|(key, _)| *key);
169
170 for (key, value) in storage.iter() {
171 output.push_str(&format!(" [{key:#x}]: {value:#x}\n"));
172 }
173 }
174 } else {
175 output.push_str(" account: None (destroyed or non-existent)\n");
176 }
177 }
178
179 if !contracts.is_empty() {
180 output.push_str(&format!(" contracts: {} total\n", contracts.len()));
181 for (hash, bytecode) in contracts.iter() {
182 let len = bytecode.len();
183 output.push_str(&format!(" [{hash}]: {len} bytes\n"));
184 }
185 }
186
187 output.push_str("}\n");
188 output
189 }
190
191 pub(crate) fn apply_account_state(
195 &mut self,
196 address: Address,
197 account: Account,
198 ) -> Option<TransitionAccount> {
199 if !account.is_touched() {
201 return None;
202 }
203
204 let this_account = self
205 .accounts
206 .get_mut(&address)
207 .expect("All accounts should be present inside cache");
208
209 if account.is_selfdestructed() {
212 return this_account.selfdestruct();
213 }
214
215 let is_created = account.is_created();
216 let is_empty = account.is_empty();
217
218 let changed_storage = account
220 .storage
221 .into_iter()
222 .filter(|(_, slot)| slot.is_changed())
223 .map(|(key, slot)| (key, slot.into()))
224 .collect();
225
226 if is_created {
235 return Some(this_account.newly_created(account.info, changed_storage));
236 }
237
238 if is_empty {
243 if self.has_state_clear {
244 this_account.touch_empty_eip161()
246 } else {
247 this_account.touch_create_pre_eip161(changed_storage)
250 }
251 } else {
252 Some(this_account.change(account.info, changed_storage))
253 }
254 }
255}