1use super::{
2 plain_account::PlainStorage, transition_account::TransitionAccount, CacheAccount, PlainAccount,
3};
4use bytecode::Bytecode;
5use primitives::{hash_map, Address, AddressMap, B256Map, HashMap};
6use state::{Account, AccountInfo, EvmStorage};
7use std::{borrow::Cow, 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 mut inspect: F,
95 ) -> Vec<(Address, TransitionAccount<Option<Cow<'_, EvmStorage>>>)>
96 where
97 F: FnMut(&Address, &Account),
98 {
99 self.apply_evm_state_iter(
100 evm_state
101 .into_iter()
102 .map(|(address, account)| (address, Cow::Owned(account))),
103 |address, account| {
104 inspect(address, account);
105 },
106 )
107 .collect()
108 }
109
110 #[inline]
112 pub(crate) fn apply_evm_state_iter<'a, 'b, F, T>(
113 &'b mut self,
114 evm_state: T,
115 mut inspect: F,
116 ) -> impl Iterator<Item = (Address, TransitionAccount<Option<Cow<'a, EvmStorage>>>)>
117 + use<'a, 'b, F, T>
118 where
119 F: FnMut(&Address, &Cow<'a, Account>),
120 T: IntoIterator<Item = (Address, Cow<'a, Account>)>,
121 {
122 evm_state.into_iter().filter_map(move |(address, account)| {
123 inspect(&address, &account);
124 self.apply_account_state(address, account)
125 .map(move |transition| (address, transition))
126 })
127 }
128
129 #[cfg(feature = "std")]
131 pub fn pretty_print(&self) -> String {
132 let mut output = String::new();
133 output.push_str("CacheState:\n");
134 output.push_str(&format!(" (accounts: {} total)\n", self.accounts.len()));
135
136 let mut accounts: Vec<_> = self.accounts.iter().collect();
138 accounts.sort_by_key(|(addr, _)| *addr);
139
140 let mut contracts = self.contracts.clone();
141
142 for (address, account) in accounts {
143 output.push_str(&format!(" [{address}]:\n"));
144 output.push_str(&format!(" status: {:?}\n", account.status));
145
146 if let Some(plain_account) = &account.account {
147 let code_hash = plain_account.info.code_hash;
148 output.push_str(&format!(" balance: {}\n", plain_account.info.balance));
149 output.push_str(&format!(" nonce: {}\n", plain_account.info.nonce));
150 output.push_str(&format!(" code_hash: {code_hash}\n"));
151
152 if let Some(code) = &plain_account.info.code {
153 if !code.is_empty() {
154 contracts.insert(code_hash, code.clone());
155 }
156 }
157
158 if !plain_account.storage.is_empty() {
159 output.push_str(&format!(
160 " storage: {} slots\n",
161 plain_account.storage.len()
162 ));
163 let mut storage: Vec<_> = plain_account.storage.iter().collect();
165 storage.sort_by_key(|(key, _)| *key);
166
167 for (key, value) in storage.iter() {
168 output.push_str(&format!(" [{key:#x}]: {value:#x}\n"));
169 }
170 }
171 } else {
172 output.push_str(" account: None (destroyed or non-existent)\n");
173 }
174 }
175
176 if !contracts.is_empty() {
177 output.push_str(&format!(" contracts: {} total\n", contracts.len()));
178 for (hash, bytecode) in contracts.iter() {
179 let len = bytecode.len();
180 output.push_str(&format!(" [{hash}]: {len} bytes\n"));
181 }
182 }
183
184 output.push_str("}\n");
185 output
186 }
187
188 pub(crate) fn apply_account_state<'a>(
192 &mut self,
193 address: Address,
194 account: Cow<'a, Account>,
195 ) -> Option<TransitionAccount<Option<Cow<'a, EvmStorage>>>> {
196 if !account.is_touched() {
198 return None;
199 }
200
201 let this_account = match self.accounts.entry(address) {
204 hash_map::Entry::Occupied(entry) => entry.into_mut(),
205 hash_map::Entry::Vacant(entry) => {
206 let cache_account = if account.is_loaded_as_not_existing() {
207 CacheAccount::new_loaded_not_existing()
208 } else {
209 let original = account.original_info();
210 if original.is_empty() {
211 CacheAccount::new_loaded_empty_eip161(HashMap::default())
212 } else {
213 CacheAccount::new_loaded(original, HashMap::default())
214 }
215 };
216 entry.insert(cache_account)
217 }
218 };
219
220 if account.is_selfdestructed() {
223 return this_account.selfdestruct();
224 }
225
226 let is_created = account.is_created();
227 let is_empty = account.is_empty();
228
229 if is_created {
238 return Some(this_account.newly_created(account));
239 }
240
241 if is_empty {
246 this_account.touch_empty_eip161()
249 } else {
250 Some(this_account.change(account))
251 }
252 }
253}