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}
24
25impl Default for CacheState {
26 fn default() -> Self {
27 Self::new()
28 }
29}
30
31impl CacheState {
32 pub fn new() -> Self {
34 Self {
35 accounts: HashMap::default(),
36 contracts: HashMap::default(),
37 }
38 }
39
40 pub fn clear(&mut self) {
42 self.accounts.clear();
43 self.contracts.clear();
44 }
45
46 pub fn trie_account(&self) -> impl IntoIterator<Item = (Address, &PlainAccount)> {
50 self.accounts.iter().filter_map(|(address, account)| {
51 account
52 .account
53 .as_ref()
54 .map(|plain_acc| (*address, plain_acc))
55 })
56 }
57
58 pub fn insert_not_existing(&mut self, address: Address) {
60 self.accounts
61 .insert(address, CacheAccount::new_loaded_not_existing());
62 }
63
64 pub fn insert_account(&mut self, address: Address, info: AccountInfo) {
66 let account = if !info.is_empty() {
67 CacheAccount::new_loaded(info, HashMap::default())
68 } else {
69 CacheAccount::new_loaded_empty_eip161(HashMap::default())
70 };
71 self.accounts.insert(address, account);
72 }
73
74 pub fn insert_account_with_storage(
76 &mut self,
77 address: Address,
78 info: AccountInfo,
79 storage: PlainStorage,
80 ) {
81 let account = if !info.is_empty() {
82 CacheAccount::new_loaded(info, storage)
83 } else {
84 CacheAccount::new_loaded_empty_eip161(storage)
85 };
86 self.accounts.insert(address, account);
87 }
88
89 #[inline]
91 pub fn apply_evm_state<F>(
92 &mut self,
93 evm_state: impl IntoIterator<Item = (Address, Account)>,
94 inspect: F,
95 ) -> Vec<(Address, TransitionAccount)>
96 where
97 F: FnMut(&Address, &Account),
98 {
99 self.apply_evm_state_iter(evm_state, inspect).collect()
100 }
101
102 #[inline]
104 pub(crate) fn apply_evm_state_iter<'a, F, T>(
105 &'a mut self,
106 evm_state: T,
107 mut inspect: F,
108 ) -> impl Iterator<Item = (Address, TransitionAccount)> + use<'a, F, T>
109 where
110 F: FnMut(&Address, &Account),
111 T: IntoIterator<Item = (Address, Account)>,
112 {
113 evm_state.into_iter().filter_map(move |(address, account)| {
114 inspect(&address, &account);
115 self.apply_account_state(address, account)
116 .map(|transition| (address, transition))
117 })
118 }
119
120 #[cfg(feature = "std")]
122 pub fn pretty_print(&self) -> String {
123 let mut output = String::new();
124 output.push_str("CacheState:\n");
125 output.push_str(&format!(" (accounts: {} total)\n", self.accounts.len()));
126
127 let mut accounts: Vec<_> = self.accounts.iter().collect();
129 accounts.sort_by_key(|(addr, _)| *addr);
130
131 let mut contracts = self.contracts.clone();
132
133 for (address, account) in accounts {
134 output.push_str(&format!(" [{address}]:\n"));
135 output.push_str(&format!(" status: {:?}\n", account.status));
136
137 if let Some(plain_account) = &account.account {
138 let code_hash = plain_account.info.code_hash;
139 output.push_str(&format!(" balance: {}\n", plain_account.info.balance));
140 output.push_str(&format!(" nonce: {}\n", plain_account.info.nonce));
141 output.push_str(&format!(" code_hash: {code_hash}\n"));
142
143 if let Some(code) = &plain_account.info.code {
144 if !code.is_empty() {
145 contracts.insert(code_hash, code.clone());
146 }
147 }
148
149 if !plain_account.storage.is_empty() {
150 output.push_str(&format!(
151 " storage: {} slots\n",
152 plain_account.storage.len()
153 ));
154 let mut storage: Vec<_> = plain_account.storage.iter().collect();
156 storage.sort_by_key(|(key, _)| *key);
157
158 for (key, value) in storage.iter() {
159 output.push_str(&format!(" [{key:#x}]: {value:#x}\n"));
160 }
161 }
162 } else {
163 output.push_str(" account: None (destroyed or non-existent)\n");
164 }
165 }
166
167 if !contracts.is_empty() {
168 output.push_str(&format!(" contracts: {} total\n", contracts.len()));
169 for (hash, bytecode) in contracts.iter() {
170 let len = bytecode.len();
171 output.push_str(&format!(" [{hash}]: {len} bytes\n"));
172 }
173 }
174
175 output.push_str("}\n");
176 output
177 }
178
179 pub(crate) fn apply_account_state(
183 &mut self,
184 address: Address,
185 account: Account,
186 ) -> Option<TransitionAccount> {
187 if !account.is_touched() {
189 return None;
190 }
191
192 let this_account = self
193 .accounts
194 .get_mut(&address)
195 .expect("All accounts should be present inside cache");
196
197 if account.is_selfdestructed() {
200 return this_account.selfdestruct();
201 }
202
203 let is_created = account.is_created();
204 let is_empty = account.is_empty();
205
206 let changed_storage = account
208 .storage
209 .into_iter()
210 .filter(|(_, slot)| slot.is_changed())
211 .map(|(key, slot)| (key, slot.into()))
212 .collect();
213
214 if is_created {
223 return Some(this_account.newly_created(account.info, changed_storage));
224 }
225
226 if is_empty {
231 this_account.touch_empty_eip161()
234 } else {
235 Some(this_account.change(account.info, changed_storage))
236 }
237 }
238}