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_empty_eip161(&mut self) -> Option<TransitionAccount> {
123 let previous_status = self.status;
124
125 let previous_info = self.account.take().map(|acc| acc.info);
127
128 self.status = self.status.on_touched_empty_post_eip161();
130
131 if matches!(
132 previous_status,
133 AccountStatus::LoadedNotExisting
134 | AccountStatus::Destroyed
135 | AccountStatus::DestroyedAgain
136 ) {
137 None
138 } else {
139 Some(TransitionAccount {
140 info: None,
141 status: self.status,
142 previous_info,
143 previous_status,
144 storage: HashMap::default(),
145 storage_was_destroyed: true,
146 })
147 }
148 }
149
150 pub fn selfdestruct(&mut self) -> Option<TransitionAccount> {
154 let previous_info = self.account.take().map(|a| a.info);
156 let previous_status = self.status;
157
158 self.status = self.status.on_selfdestructed();
159
160 if previous_status == AccountStatus::LoadedNotExisting {
161 None
162 } else {
163 Some(TransitionAccount {
164 info: None,
165 status: self.status,
166 previous_info,
167 previous_status,
168 storage: HashMap::default(),
169 storage_was_destroyed: true,
170 })
171 }
172 }
173
174 pub fn newly_created(
176 &mut self,
177 new_info: AccountInfo,
178 new_storage: StorageWithOriginalValues,
179 ) -> TransitionAccount {
180 let previous_status = self.status;
181 let previous_info = self.account.take().map(|a| a.info);
182
183 let new_bundle_storage = new_storage
184 .iter()
185 .map(|(k, s)| (*k, s.present_value))
186 .collect();
187
188 self.status = self.status.on_created();
189 let transition_account = TransitionAccount {
190 info: Some(new_info.clone()),
191 status: self.status,
192 previous_status,
193 previous_info,
194 storage: new_storage,
195 storage_was_destroyed: false,
196 };
197 self.account = Some(PlainAccount {
198 info: new_info,
199 storage: new_bundle_storage,
200 });
201 transition_account
202 }
203
204 pub fn increment_balance(&mut self, balance: u128) -> Option<TransitionAccount> {
209 if balance == 0 {
210 return None;
211 }
212 let (_, transition) = self.account_info_change(|info| {
213 info.balance = info.balance.saturating_add(U256::from(balance));
214 });
215 Some(transition)
216 }
217
218 fn account_info_change<T, F: FnOnce(&mut AccountInfo) -> T>(
219 &mut self,
220 change: F,
221 ) -> (T, TransitionAccount) {
222 let previous_status = self.status;
223 let previous_info = self.account_info();
224 let mut account = self.account.take().unwrap_or_default();
225 let output = change(&mut account.info);
226 self.account = Some(account);
227
228 let had_no_nonce_and_code = previous_info
229 .as_ref()
230 .map(AccountInfo::has_no_code_and_nonce)
231 .unwrap_or_default();
232 self.status = self.status.on_changed(had_no_nonce_and_code);
233
234 (
235 output,
236 TransitionAccount {
237 info: self.account_info(),
238 status: self.status,
239 previous_info,
240 previous_status,
241 storage: HashMap::default(),
242 storage_was_destroyed: false,
243 },
244 )
245 }
246
247 pub fn drain_balance(&mut self) -> (u128, TransitionAccount) {
251 self.account_info_change(|info| {
252 let output = info.balance;
253 info.balance = U256::ZERO;
254 output.try_into().unwrap()
255 })
256 }
257
258 pub fn change(
262 &mut self,
263 new: AccountInfo,
264 storage: StorageWithOriginalValues,
265 ) -> TransitionAccount {
266 let previous_status = self.status;
267 let (previous_info, mut this_storage) = if let Some(account) = self.account.take() {
268 (Some(account.info), account.storage)
269 } else {
270 (None, Default::default())
271 };
272
273 this_storage.extend(storage.iter().map(|(k, s)| (*k, s.present_value)));
274 let changed_account = PlainAccount {
275 info: new,
276 storage: this_storage,
277 };
278
279 let had_no_nonce_and_code = previous_info
280 .as_ref()
281 .map(AccountInfo::has_no_code_and_nonce)
282 .unwrap_or_default();
283 self.status = self.status.on_changed(had_no_nonce_and_code);
284 self.account = Some(changed_account);
285
286 TransitionAccount {
287 info: self.account.as_ref().map(|a| a.info.clone()),
288 status: self.status,
289 previous_info,
290 previous_status,
291 storage,
292 storage_was_destroyed: false,
293 }
294 }
295}