1use super::{
2 plain_account::PlainStorage, AccountStatus, BundleAccount, PlainAccount, TransitionAccount,
3};
4use primitives::{HashMap, StorageKey, StorageValue, U256};
5use state::{Account, AccountInfo, EvmStorage};
6use std::borrow::Cow;
7
8#[derive(Clone, Debug, PartialEq, Eq)]
11pub struct CacheAccount {
12 pub account: Option<PlainAccount>,
14 pub status: AccountStatus,
16}
17
18impl From<BundleAccount> for CacheAccount {
19 fn from(account: BundleAccount) -> Self {
20 CacheAccount::from(&account)
21 }
22}
23
24impl From<&BundleAccount> for CacheAccount {
25 fn from(account: &BundleAccount) -> Self {
26 let storage = account
27 .storage
28 .iter()
29 .map(|(k, v)| (*k, v.present_value))
30 .collect();
31 let plain_account = account
32 .account_info()
33 .map(|info| PlainAccount { info, storage });
34 Self {
35 account: plain_account,
36 status: account.status,
37 }
38 }
39}
40
41impl CacheAccount {
42 pub const fn new_loaded(info: AccountInfo, storage: PlainStorage) -> Self {
44 Self {
45 account: Some(PlainAccount { info, storage }),
46 status: AccountStatus::Loaded,
47 }
48 }
49
50 pub fn new_loaded_empty_eip161(storage: PlainStorage) -> Self {
52 Self {
53 account: Some(PlainAccount::new_empty_with_storage(storage)),
54 status: AccountStatus::LoadedEmptyEIP161,
55 }
56 }
57
58 pub const fn new_loaded_not_existing() -> Self {
60 Self {
61 account: None,
62 status: AccountStatus::LoadedNotExisting,
63 }
64 }
65
66 pub const fn new_newly_created(info: AccountInfo, storage: PlainStorage) -> Self {
68 Self {
69 account: Some(PlainAccount { info, storage }),
70 status: AccountStatus::InMemoryChange,
71 }
72 }
73
74 pub const fn new_destroyed() -> Self {
76 Self {
77 account: None,
78 status: AccountStatus::Destroyed,
79 }
80 }
81
82 pub const fn new_changed(info: AccountInfo, storage: PlainStorage) -> Self {
84 Self {
85 account: Some(PlainAccount { info, storage }),
86 status: AccountStatus::Changed,
87 }
88 }
89
90 pub const fn is_some(&self) -> bool {
92 matches!(
93 self.status,
94 AccountStatus::Changed
95 | AccountStatus::InMemoryChange
96 | AccountStatus::DestroyedChanged
97 | AccountStatus::Loaded
98 | AccountStatus::LoadedEmptyEIP161
99 )
100 }
101
102 pub fn storage_slot(&self, slot: StorageKey) -> Option<StorageValue> {
104 self.account
105 .as_ref()
106 .and_then(|a| a.storage.get(&slot).cloned())
107 }
108
109 pub fn account_info(&self) -> Option<AccountInfo> {
111 self.account.as_ref().map(|a| a.info.clone())
112 }
113
114 pub fn into_components(self) -> (Option<(AccountInfo, PlainStorage)>, AccountStatus) {
116 (self.account.map(|a| a.into_components()), self.status)
117 }
118
119 pub fn touch_empty_eip161<'a>(
123 &mut self,
124 ) -> Option<TransitionAccount<Option<Cow<'a, EvmStorage>>>> {
125 let previous_status = self.status;
126
127 let previous_info = self.account.take().map(|acc| acc.info);
129
130 self.status = self.status.on_touched_empty_post_eip161();
132
133 if matches!(
134 previous_status,
135 AccountStatus::LoadedNotExisting
136 | AccountStatus::Destroyed
137 | AccountStatus::DestroyedAgain
138 ) {
139 None
140 } else {
141 Some(TransitionAccount {
142 info: None,
143 status: self.status,
144 previous_info,
145 previous_status,
146 storage: None,
147 storage_was_destroyed: true,
148 })
149 }
150 }
151
152 pub fn selfdestruct<'a>(&mut self) -> Option<TransitionAccount<Option<Cow<'a, EvmStorage>>>> {
156 let previous_info = self.account.take().map(|a| a.info);
158 let previous_status = self.status;
159
160 self.status = self.status.on_selfdestructed();
161
162 if previous_status == AccountStatus::LoadedNotExisting {
163 None
164 } else {
165 Some(TransitionAccount {
166 info: None,
167 status: self.status,
168 previous_info,
169 previous_status,
170 storage: None,
171 storage_was_destroyed: true,
172 })
173 }
174 }
175
176 pub fn newly_created<'a>(
178 &mut self,
179 account: Cow<'a, Account>,
180 ) -> TransitionAccount<Option<Cow<'a, EvmStorage>>> {
181 let previous_status = self.status;
182 let previous_info = self.account.take().map(|a| a.info);
183
184 let new_bundle_storage = account
185 .storage
186 .iter()
187 .filter_map(|(k, s)| s.is_changed().then_some((*k, s.present_value)))
188 .collect();
189
190 self.status = self.status.on_created();
191 let (info, storage) = match account {
192 Cow::Borrowed(account) => (account.info.clone(), Cow::Borrowed(&account.storage)),
193 Cow::Owned(account) => (account.info, Cow::Owned(account.storage)),
194 };
195 let transition_account = TransitionAccount {
196 info: Some(info.clone()),
197 status: self.status,
198 previous_status,
199 previous_info,
200 storage: Some(storage),
201 storage_was_destroyed: false,
202 };
203 self.account = Some(PlainAccount {
204 info,
205 storage: new_bundle_storage,
206 });
207 transition_account
208 }
209
210 pub fn increment_balance(&mut self, balance: u128) -> Option<TransitionAccount> {
215 if balance == 0 {
216 return None;
217 }
218 let (_, transition) = self.account_info_change(|info| {
219 info.balance = info.balance.saturating_add(U256::from(balance));
220 });
221 Some(transition)
222 }
223
224 fn account_info_change<T, F: FnOnce(&mut AccountInfo) -> T>(
225 &mut self,
226 change: F,
227 ) -> (T, TransitionAccount) {
228 let previous_status = self.status;
229 let previous_info = self.account_info();
230 let mut account = self.account.take().unwrap_or_default();
231 let output = change(&mut account.info);
232 self.account = Some(account);
233
234 let had_no_nonce_and_code = previous_info
235 .as_ref()
236 .map(AccountInfo::has_no_code_and_nonce)
237 .unwrap_or_default();
238 self.status = self.status.on_changed(had_no_nonce_and_code);
239
240 (
241 output,
242 TransitionAccount {
243 info: self.account_info(),
244 status: self.status,
245 previous_info,
246 previous_status,
247 storage: HashMap::default(),
248 storage_was_destroyed: false,
249 },
250 )
251 }
252
253 pub fn drain_balance(&mut self) -> (u128, TransitionAccount) {
257 self.account_info_change(|info| {
258 let output = info.balance;
259 info.balance = U256::ZERO;
260 output.try_into().unwrap()
261 })
262 }
263
264 pub fn change<'a>(
268 &mut self,
269 account: Cow<'a, Account>,
270 ) -> TransitionAccount<Option<Cow<'a, EvmStorage>>> {
271 let previous_status = self.status;
272 let (previous_info, mut this_storage) = if let Some(account) = self.account.take() {
273 (Some(account.info), account.storage)
274 } else {
275 (None, Default::default())
276 };
277
278 let (info, storage) = match account {
279 Cow::Borrowed(account) => (account.info.clone(), Cow::Borrowed(&account.storage)),
280 Cow::Owned(account) => (account.info, Cow::Owned(account.storage)),
281 };
282
283 this_storage.extend(
284 storage
285 .iter()
286 .filter_map(|(k, s)| s.is_changed().then_some((*k, s.present_value))),
287 );
288 let changed_account = PlainAccount {
289 info,
290 storage: this_storage,
291 };
292
293 let had_no_nonce_and_code = previous_info
294 .as_ref()
295 .map(AccountInfo::has_no_code_and_nonce)
296 .unwrap_or_default();
297 self.status = self.status.on_changed(had_no_nonce_and_code);
298 self.account = Some(changed_account);
299
300 TransitionAccount {
301 info: self.account.as_ref().map(|a| a.info.clone()),
302 status: self.status,
303 previous_info,
304 previous_status,
305 storage: Some(storage),
306 storage_was_destroyed: false,
307 }
308 }
309}