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 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<F>(
94 &mut self,
95 evm_state: impl IntoIterator<Item = (Address, Account)>,
96 inspect: F,
97 ) -> Vec<(Address, TransitionAccount)>
98 where
99 F: FnMut(&Address, &Account),
100 {
101 self.apply_evm_state_iter(evm_state, inspect).collect()
102 }
103
104 #[inline]
106 pub(crate) fn apply_evm_state_iter<'a, F, T>(
107 &'a mut self,
108 evm_state: T,
109 mut inspect: F,
110 ) -> impl Iterator<Item = (Address, TransitionAccount)> + use<'a, F, T>
111 where
112 F: FnMut(&Address, &Account),
113 T: IntoIterator<Item = (Address, Account)>,
114 {
115 evm_state.into_iter().filter_map(move |(address, account)| {
116 inspect(&address, &account);
117 self.apply_account_state(address, account)
118 .map(|transition| (address, transition))
119 })
120 }
121
122 #[cfg(feature = "std")]
124 pub fn pretty_print(&self) -> String {
125 let mut output = String::new();
126 output.push_str("CacheState:\n");
127 output.push_str(&format!(
128 " (state_clear_enabled: {}, ",
129 self.has_state_clear
130 ));
131 output.push_str(&format!("accounts: {} total)\n", self.accounts.len()));
132
133 let mut accounts: Vec<_> = self.accounts.iter().collect();
135 accounts.sort_by_key(|(addr, _)| *addr);
136
137 let mut contracts = self.contracts.clone();
138
139 for (address, account) in accounts {
140 output.push_str(&format!(" [{address}]:\n"));
141 output.push_str(&format!(" status: {:?}\n", account.status));
142
143 if let Some(plain_account) = &account.account {
144 let code_hash = plain_account.info.code_hash;
145 output.push_str(&format!(" balance: {}\n", plain_account.info.balance));
146 output.push_str(&format!(" nonce: {}\n", plain_account.info.nonce));
147 output.push_str(&format!(" code_hash: {code_hash}\n"));
148
149 if let Some(code) = &plain_account.info.code {
150 if !code.is_empty() {
151 contracts.insert(code_hash, code.clone());
152 }
153 }
154
155 if !plain_account.storage.is_empty() {
156 output.push_str(&format!(
157 " storage: {} slots\n",
158 plain_account.storage.len()
159 ));
160 let mut storage: Vec<_> = plain_account.storage.iter().collect();
162 storage.sort_by_key(|(key, _)| *key);
163
164 for (key, value) in storage.iter() {
165 output.push_str(&format!(" [{key:#x}]: {value:#x}\n"));
166 }
167 }
168 } else {
169 output.push_str(" account: None (destroyed or non-existent)\n");
170 }
171 }
172
173 if !contracts.is_empty() {
174 output.push_str(&format!(" contracts: {} total\n", contracts.len()));
175 for (hash, bytecode) in contracts.iter() {
176 let len = bytecode.len();
177 output.push_str(&format!(" [{hash}]: {len} bytes\n"));
178 }
179 }
180
181 output.push_str("}\n");
182 output
183 }
184
185 pub(crate) fn apply_account_state(
189 &mut self,
190 address: Address,
191 account: Account,
192 ) -> Option<TransitionAccount> {
193 if !account.is_touched() {
195 return None;
196 }
197
198 let this_account = self
199 .accounts
200 .get_mut(&address)
201 .expect("All accounts should be present inside cache");
202
203 if account.is_selfdestructed() {
206 return this_account.selfdestruct();
207 }
208
209 let is_created = account.is_created();
210 let is_empty = account.is_empty();
211
212 let changed_storage = account
214 .storage
215 .into_iter()
216 .filter(|(_, slot)| slot.is_changed())
217 .map(|(key, slot)| (key, slot.into()))
218 .collect();
219
220 if is_created {
229 return Some(this_account.newly_created(account.info, changed_storage));
230 }
231
232 if is_empty {
237 if self.has_state_clear {
238 this_account.touch_empty_eip161()
240 } else {
241 this_account.touch_create_pre_eip161(changed_storage)
244 }
245 } else {
246 Some(this_account.change(account.info, changed_storage))
247 }
248 }
249}