revm_database/states/
bundle_account.rs

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