reth_trie_common/bundle_state/bundle_account.rs
1use super::{
2 reverts::AccountInfoRevert, AccountRevert, AccountStatus, RevertToSlot, StorageSlot,
3 StorageWithOriginalValues, TransitionAccount,
4};
5use alloy_primitives::{
6 map::{HashMap, U256Map},
7 U256,
8};
9use evm2::evm::AccountInfo;
10
11/// Account information focused on creating of database changesets
12/// and Reverts.
13///
14/// Status is needed as to know from what state we are applying the TransitionAccount.
15///
16/// Original account info is needed to know if there was a change.
17///
18/// Same thing for storage with original value.
19///
20/// On selfdestruct storage original value is ignored.
21#[derive(Clone, Debug, PartialEq, Eq)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
23pub struct BundleAccount {
24 /// Current account information.
25 pub info: Option<AccountInfo>,
26 /// Original account information before modifications.
27 pub original_info: Option<AccountInfo>,
28 /// Contains both original and present state.
29 /// When extracting changeset we compare if original value is different from present value.
30 /// If it is different we add it to changeset.
31 ///
32 /// If Account was destroyed we ignore original value and compare present state with
33 /// U256::ZERO.
34 pub storage: StorageWithOriginalValues,
35 /// Account status.
36 pub status: AccountStatus,
37}
38
39impl BundleAccount {
40 /// Create new BundleAccount.
41 pub const fn new(
42 original_info: Option<AccountInfo>,
43 present_info: Option<AccountInfo>,
44 storage: StorageWithOriginalValues,
45 status: AccountStatus,
46 ) -> Self {
47 Self { info: present_info, original_info, storage, status }
48 }
49
50 /// The approximate size of changes needed to store this account.
51 ///
52 /// `1 + storage_len`
53 pub fn size_hint(&self) -> usize {
54 1 + self.storage.len()
55 }
56
57 /// Return storage slot if it exists.
58 ///
59 /// In case we know that account is newly created or destroyed, return
60 /// `Some(U256::ZERO)`
61 pub fn storage_slot(&self, slot: U256) -> Option<U256> {
62 let slot = self.storage.get(&slot).map(|s| s.present_value);
63 if slot.is_some() {
64 slot
65 } else if self.status.is_storage_known() {
66 Some(U256::ZERO)
67 } else {
68 None
69 }
70 }
71
72 /// Fetch account info if it exists.
73 pub fn account_info(&self) -> Option<AccountInfo> {
74 self.info.clone()
75 }
76
77 /// Was this account destroyed.
78 pub const fn was_destroyed(&self) -> bool {
79 self.status.was_destroyed()
80 }
81
82 /// Return true of account info was changed.
83 pub fn is_info_changed(&self) -> bool {
84 self.info != self.original_info
85 }
86
87 /// Return true if contract was changed
88 pub fn is_contract_changed(&self) -> bool {
89 self.info.as_ref().map(|a| a.code_hash) != self.original_info.as_ref().map(|a| a.code_hash)
90 }
91
92 /// Revert account to previous state and return true if account can be removed.
93 pub fn revert(&mut self, revert: AccountRevert) -> bool {
94 self.status = revert.previous_status;
95
96 match revert.account {
97 AccountInfoRevert::DoNothing => (),
98 AccountInfoRevert::DeleteIt => {
99 self.info = None;
100 if self.original_info.is_none() {
101 self.storage = HashMap::default();
102 return true;
103 } else {
104 // Set all storage to zero but preserve original values.
105 self.storage.iter_mut().for_each(|(_, v)| {
106 v.present_value = U256::ZERO;
107 });
108 return false;
109 }
110 }
111 AccountInfoRevert::RevertTo(info) => self.info = Some(info),
112 };
113 // Revert storage
114 for (key, slot) in revert.storage {
115 match slot {
116 RevertToSlot::Some(value) => {
117 // Don't overwrite original values if present
118 // if storage is not present set original value as current value.
119 self.storage
120 .entry(key)
121 .or_insert_with(|| StorageSlot::new(value))
122 .present_value = value;
123 }
124 RevertToSlot::Destroyed => {
125 // If it was destroyed this means that storage was created and we need to remove
126 // it.
127 self.storage.remove(&key);
128 }
129 }
130 }
131 false
132 }
133
134 /// Update to new state and generate AccountRevert that if applied to new state will
135 /// revert it to previous state.
136 ///
137 /// If no revert is present, update is noop.
138 pub fn update_and_create_revert(
139 &mut self,
140 transition: TransitionAccount,
141 ) -> Option<AccountRevert> {
142 let updated_info = transition.info;
143 let updated_storage = transition.storage;
144 let updated_status = transition.status;
145
146 // The helper that extends this storage but preserves original value.
147 let extend_storage =
148 |this_storage: &mut StorageWithOriginalValues,
149 storage_update: StorageWithOriginalValues| {
150 this_storage.reserve(storage_update.len());
151 for (key, value) in storage_update {
152 this_storage.entry(key).or_insert(value).present_value = value.present_value;
153 }
154 };
155
156 let previous_storage_from_update =
157 |updated_storage: &StorageWithOriginalValues| -> U256Map<RevertToSlot> {
158 updated_storage
159 .iter()
160 .filter(|s| s.1.is_changed())
161 .map(|(key, value)| {
162 (*key, RevertToSlot::Some(value.previous_or_original_value))
163 })
164 .collect()
165 };
166
167 // Needed for some reverts.
168 let info_revert = if self.info != updated_info {
169 AccountInfoRevert::RevertTo(self.info.clone().unwrap_or_default())
170 } else {
171 AccountInfoRevert::DoNothing
172 };
173
174 let account_revert = match updated_status {
175 AccountStatus::Changed => {
176 let previous_storage = previous_storage_from_update(&updated_storage);
177 match self.status {
178 AccountStatus::Changed | AccountStatus::Loaded => {
179 // Extend the storage. original values is not used inside bundle.
180 extend_storage(&mut self.storage, updated_storage);
181 }
182 AccountStatus::LoadedEmptyEIP161 => {
183 // Do nothing.
184 // Only change that can happen from LoadedEmpty to Changed is if balance
185 // is send to account. So we are only checking account change here.
186 }
187 _ => unreachable!("Invalid state transfer to Changed from {self:?}"),
188 };
189 let previous_status = self.status;
190 self.status = AccountStatus::Changed;
191 self.info = updated_info;
192 Some(AccountRevert {
193 account: info_revert,
194 storage: previous_storage,
195 previous_status,
196 wipe_storage: false,
197 })
198 }
199 AccountStatus::InMemoryChange => {
200 let previous_storage = previous_storage_from_update(&updated_storage);
201 let in_memory_info_revert = match self.status {
202 AccountStatus::Loaded | AccountStatus::InMemoryChange => {
203 // From loaded (Or LoadedEmpty) to InMemoryChange can happen if there is
204 // balance change or new created account but Loaded
205 // didn't have contract.
206 extend_storage(&mut self.storage, updated_storage);
207 info_revert
208 }
209 AccountStatus::LoadedEmptyEIP161 => {
210 self.storage = updated_storage;
211 info_revert
212 }
213 AccountStatus::LoadedNotExisting => {
214 self.storage = updated_storage;
215 AccountInfoRevert::DeleteIt
216 }
217 _ => unreachable!("Invalid change to InMemoryChange from {self:?}"),
218 };
219 let previous_status = self.status;
220 self.status = AccountStatus::InMemoryChange;
221 self.info = updated_info;
222 Some(AccountRevert {
223 account: in_memory_info_revert,
224 storage: previous_storage,
225 previous_status,
226 wipe_storage: false,
227 })
228 }
229 AccountStatus::Loaded |
230 AccountStatus::LoadedNotExisting |
231 AccountStatus::LoadedEmptyEIP161 => {
232 // No changeset, maybe just update data
233 // Do nothing for now.
234 None
235 }
236 AccountStatus::Destroyed => {
237 // Clear this storage and move it to the Revert.
238 let this_storage = core::mem::take(&mut self.storage);
239 let ret = match self.status {
240 AccountStatus::InMemoryChange | AccountStatus::Changed | AccountStatus::Loaded | AccountStatus::LoadedEmptyEIP161 => {
241 Some(AccountRevert::new_selfdestructed(self.status, info_revert, this_storage))
242 }
243 AccountStatus::LoadedNotExisting => {
244 // Do nothing as we have LoadedNotExisting -> Destroyed (It is noop)
245 None
246 }
247 _ => unreachable!("Invalid transition to Destroyed account from: {self:?} to {updated_info:?} {updated_status:?}"),
248 };
249
250 if ret.is_some() {
251 self.status = AccountStatus::Destroyed;
252 self.info = None;
253 }
254
255 // Set present to destroyed.
256 ret
257 }
258 AccountStatus::DestroyedChanged => {
259 // Previous block created account or changed.
260 // (It was destroyed on previous block or one before).
261
262 // Check common pre destroy paths.
263 // If common path is there it will drain the storage.
264 if let Some(revert_state) = AccountRevert::new_selfdestructed_from_bundle(
265 info_revert.clone(),
266 self,
267 &updated_storage,
268 ) {
269 // Set to destroyed and revert state.
270 self.status = AccountStatus::DestroyedChanged;
271 self.info = updated_info;
272 self.storage = updated_storage;
273
274 Some(revert_state)
275 } else {
276 let ret = match self.status {
277 AccountStatus::Destroyed | AccountStatus::LoadedNotExisting => {
278 // From destroyed state new account is made
279 Some(AccountRevert {
280 account: AccountInfoRevert::DeleteIt,
281 storage: previous_storage_from_update(&updated_storage),
282 previous_status: self.status,
283 wipe_storage: false,
284 })
285 }
286 AccountStatus::DestroyedChanged => {
287 // Account was destroyed in this transition. So we should clear present
288 // storage and insert it inside revert.
289
290 let previous_storage = if transition.storage_was_destroyed {
291 let mut storage = core::mem::take(&mut self.storage)
292 .into_iter()
293 .map(|t| (t.0, RevertToSlot::Some(t.1.present_value)))
294 .collect::<U256Map<_>>();
295 for key in updated_storage.keys() {
296 // As it is not existing inside Destroyed storage this means
297 // that previous values must be zero
298 storage.entry(*key).or_insert(RevertToSlot::Destroyed);
299 }
300 storage
301 } else {
302 previous_storage_from_update(&updated_storage)
303 };
304
305 Some(AccountRevert {
306 account: info_revert,
307 storage: previous_storage,
308 previous_status: AccountStatus::DestroyedChanged,
309 wipe_storage: false,
310 })
311 }
312 AccountStatus::DestroyedAgain => {
313 Some(AccountRevert::new_selfdestructed_again(
314 // Destroyed again will set empty account.
315 AccountStatus::DestroyedAgain,
316 AccountInfoRevert::DeleteIt,
317 HashMap::default(),
318 updated_storage.clone(),
319 ))
320 }
321 _ => unreachable!("Invalid state transfer to DestroyedNew from {self:?}"),
322 };
323 self.status = AccountStatus::DestroyedChanged;
324 self.info = updated_info;
325 // Extends current storage.
326 extend_storage(&mut self.storage, updated_storage);
327
328 ret
329 }
330 }
331 AccountStatus::DestroyedAgain => {
332 // Previous block created account
333 // (It was destroyed on previous block or one before).
334
335 // Check common pre destroy paths.
336 // This will drain the storage if it is common transition.
337 let ret = if let Some(revert_state) = AccountRevert::new_selfdestructed_from_bundle(
338 info_revert,
339 self,
340 &HashMap::default(),
341 ) {
342 Some(revert_state)
343 } else {
344 match self.status {
345 AccountStatus::Destroyed |
346 AccountStatus::DestroyedAgain |
347 AccountStatus::LoadedNotExisting => {
348 // From destroyed to destroyed again is noop
349 //
350 // From DestroyedAgain to DestroyedAgain is noop
351 //
352 // From LoadedNotExisting to DestroyedAgain is noop
353 // as account is destroyed again
354 None
355 }
356 AccountStatus::DestroyedChanged => {
357 // From destroyed changed to destroyed again.
358 Some(AccountRevert::new_selfdestructed_again(
359 // Destroyed again will set empty account.
360 AccountStatus::DestroyedChanged,
361 AccountInfoRevert::RevertTo(self.info.clone().unwrap_or_default()),
362 core::mem::take(&mut self.storage),
363 HashMap::default(),
364 ))
365 }
366 _ => unreachable!("Invalid state to DestroyedAgain from {self:?}"),
367 }
368 };
369 // Set to destroyed and revert state.
370 self.status = AccountStatus::DestroyedAgain;
371 self.info = None;
372 self.storage.clear();
373 ret
374 }
375 };
376
377 account_revert.filter(|acc| !acc.is_empty())
378 }
379}