1use super::{
2 plain_account::PlainStorage, AccountStatus, BundleAccount, PlainAccount,
3 StorageWithOriginalValues, TransitionAccount,
4};
5use primitives::{HashMap, StorageKey, StorageValue, U256};
6use state::AccountInfo;
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 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 fn new_loaded_not_existing() -> Self {
60 Self {
61 account: None,
62 status: AccountStatus::LoadedNotExisting,
63 }
64 }
65
66 pub 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 fn new_destroyed() -> Self {
76 Self {
77 account: None,
78 status: AccountStatus::Destroyed,
79 }
80 }
81
82 pub 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 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_create_pre_eip161(
121 &mut self,
122 storage: StorageWithOriginalValues,
123 ) -> Option<TransitionAccount> {
124 let previous_status = self.status;
125
126 let had_no_info = self
127 .account
128 .as_ref()
129 .map(|a| a.info.is_empty())
130 .unwrap_or_default();
131 self.status = self.status.on_touched_created_pre_eip161(had_no_info)?;
132
133 let plain_storage = storage.iter().map(|(k, v)| (*k, v.present_value)).collect();
134 let previous_info = self.account.take().map(|a| a.info);
135
136 self.account = Some(PlainAccount::new_empty_with_storage(plain_storage));
137
138 Some(TransitionAccount {
139 info: Some(AccountInfo::default()),
140 status: self.status,
141 previous_info,
142 previous_status,
143 storage,
144 storage_was_destroyed: false,
145 })
146 }
147
148 pub fn touch_empty_eip161(&mut self) -> Option<TransitionAccount> {
152 let previous_status = self.status;
153
154 let previous_info = self.account.take().map(|acc| acc.info);
156
157 self.status = self.status.on_touched_empty_post_eip161();
159
160 if matches!(
161 previous_status,
162 AccountStatus::LoadedNotExisting
163 | AccountStatus::Destroyed
164 | AccountStatus::DestroyedAgain
165 ) {
166 None
167 } else {
168 Some(TransitionAccount {
169 info: None,
170 status: self.status,
171 previous_info,
172 previous_status,
173 storage: HashMap::default(),
174 storage_was_destroyed: true,
175 })
176 }
177 }
178
179 pub fn selfdestruct(&mut self) -> Option<TransitionAccount> {
183 let previous_info = self.account.take().map(|a| a.info);
185 let previous_status = self.status;
186
187 self.status = self.status.on_selfdestructed();
188
189 if previous_status == AccountStatus::LoadedNotExisting {
190 None
191 } else {
192 Some(TransitionAccount {
193 info: None,
194 status: self.status,
195 previous_info,
196 previous_status,
197 storage: HashMap::default(),
198 storage_was_destroyed: true,
199 })
200 }
201 }
202
203 pub fn newly_created(
205 &mut self,
206 new_info: AccountInfo,
207 new_storage: StorageWithOriginalValues,
208 ) -> TransitionAccount {
209 let previous_status = self.status;
210 let previous_info = self.account.take().map(|a| a.info);
211
212 let new_bundle_storage = new_storage
213 .iter()
214 .map(|(k, s)| (*k, s.present_value))
215 .collect();
216
217 self.status = self.status.on_created();
218 let transition_account = TransitionAccount {
219 info: Some(new_info.clone()),
220 status: self.status,
221 previous_status,
222 previous_info,
223 storage: new_storage,
224 storage_was_destroyed: false,
225 };
226 self.account = Some(PlainAccount {
227 info: new_info,
228 storage: new_bundle_storage,
229 });
230 transition_account
231 }
232
233 pub fn increment_balance(&mut self, balance: u128) -> Option<TransitionAccount> {
238 if balance == 0 {
239 return None;
240 }
241 let (_, transition) = self.account_info_change(|info| {
242 info.balance = info.balance.saturating_add(U256::from(balance));
243 });
244 Some(transition)
245 }
246
247 fn account_info_change<T, F: FnOnce(&mut AccountInfo) -> T>(
248 &mut self,
249 change: F,
250 ) -> (T, TransitionAccount) {
251 let previous_status = self.status;
252 let previous_info = self.account_info();
253 let mut account = self.account.take().unwrap_or_default();
254 let output = change(&mut account.info);
255 self.account = Some(account);
256
257 let had_no_nonce_and_code = previous_info
258 .as_ref()
259 .map(AccountInfo::has_no_code_and_nonce)
260 .unwrap_or_default();
261 self.status = self.status.on_changed(had_no_nonce_and_code);
262
263 (
264 output,
265 TransitionAccount {
266 info: self.account_info(),
267 status: self.status,
268 previous_info,
269 previous_status,
270 storage: HashMap::default(),
271 storage_was_destroyed: false,
272 },
273 )
274 }
275
276 pub fn drain_balance(&mut self) -> (u128, TransitionAccount) {
280 self.account_info_change(|info| {
281 let output = info.balance;
282 info.balance = U256::ZERO;
283 output.try_into().unwrap()
284 })
285 }
286
287 pub fn change(
291 &mut self,
292 new: AccountInfo,
293 storage: StorageWithOriginalValues,
294 ) -> TransitionAccount {
295 let previous_status = self.status;
296 let (previous_info, mut this_storage) = if let Some(account) = self.account.take() {
297 (Some(account.info), account.storage)
298 } else {
299 (None, Default::default())
300 };
301
302 this_storage.extend(storage.iter().map(|(k, s)| (*k, s.present_value)));
303 let changed_account = PlainAccount {
304 info: new,
305 storage: this_storage,
306 };
307
308 let had_no_nonce_and_code = previous_info
309 .as_ref()
310 .map(AccountInfo::has_no_code_and_nonce)
311 .unwrap_or_default();
312 self.status = self.status.on_changed(had_no_nonce_and_code);
313 self.account = Some(changed_account);
314
315 TransitionAccount {
316 info: self.account.as_ref().map(|a| a.info.clone()),
317 status: self.status,
318 previous_info,
319 previous_status,
320 storage,
321 storage_was_destroyed: false,
322 }
323 }
324}