revm_database/states/
bundle_account.rs

1use super::{
2    reverts::AccountInfoRevert, AccountRevert, AccountStatus, RevertToSlot, StorageSlot,
3    StorageWithOriginalValues, TransitionAccount,
4};
5use primitives::{HashMap, StorageKey, StorageValue};
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    /// Current account information.
22    pub info: Option<AccountInfo>,
23    /// Original account information before modifications.
24    pub original_info: Option<AccountInfo>,
25    /// Contains both original and present state.
26    /// When extracting changeset we compare if original value is different from present value.
27    /// If it is different we add it to changeset.
28    ///
29    /// If Account was destroyed we ignore original value and compare present state with StorageValue::ZERO.
30    pub storage: StorageWithOriginalValues,
31    /// Account status.
32    pub status: AccountStatus,
33}
34
35impl BundleAccount {
36    /// Create new BundleAccount.
37    pub fn new(
38        original_info: Option<AccountInfo>,
39        present_info: Option<AccountInfo>,
40        storage: StorageWithOriginalValues,
41        status: AccountStatus,
42    ) -> Self {
43        Self {
44            info: present_info,
45            original_info,
46            storage,
47            status,
48        }
49    }
50
51    /// The approximate size of changes needed to store this account.
52    ///
53    /// `1 + storage_len`
54    pub fn size_hint(&self) -> usize {
55        1 + self.storage.len()
56    }
57
58    /// Return storage slot if it exists.
59    ///
60    /// In case we know that account is newly created or destroyed, return `Some(StorageValue::ZERO)`
61    pub fn storage_slot(&self, slot: StorageKey) -> Option<StorageValue> {
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(StorageValue::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 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 = StorageValue::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(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 it.
126                    self.storage.remove(&key);
127                }
128            }
129        }
130        false
131    }
132
133    /// Update to new state and generate AccountRevert that if applied to new state will
134    /// revert it to previous state.
135    ///
136    /// If no revert is present, update is noop.
137    pub fn update_and_create_revert(
138        &mut self,
139        transition: TransitionAccount,
140    ) -> Option<AccountRevert> {
141        let updated_info = transition.info;
142        let updated_storage = transition.storage;
143        let updated_status = transition.status;
144
145        // The helper that extends this storage but preserves original value.
146        let extend_storage =
147            |this_storage: &mut StorageWithOriginalValues,
148             storage_update: StorageWithOriginalValues| {
149                for (key, value) in storage_update {
150                    this_storage.entry(key).or_insert(value).present_value = value.present_value;
151                }
152            };
153
154        let previous_storage_from_update =
155            |updated_storage: &StorageWithOriginalValues| -> HashMap<StorageKey, RevertToSlot> {
156                updated_storage
157                    .iter()
158                    .filter(|s| s.1.is_changed())
159                    .map(|(key, value)| {
160                        (*key, RevertToSlot::Some(value.previous_or_original_value))
161                    })
162                    .collect()
163            };
164
165        // Needed for some reverts.
166        let info_revert = if self.info != updated_info {
167            AccountInfoRevert::RevertTo(self.info.clone().unwrap_or_default())
168        } else {
169            AccountInfoRevert::DoNothing
170        };
171
172        let account_revert = match updated_status {
173            AccountStatus::Changed => {
174                let previous_storage = previous_storage_from_update(&updated_storage);
175                match self.status {
176                    AccountStatus::Changed | AccountStatus::Loaded => {
177                        // Extend the storage. original values is not used inside bundle.
178                        extend_storage(&mut self.storage, updated_storage);
179                    }
180                    AccountStatus::LoadedEmptyEIP161 => {
181                        // Do nothing.
182                        // Only change that can happen from LoadedEmpty to Changed is if balance
183                        // is send to account. So we are only checking account change here.
184                    }
185                    _ => unreachable!("Invalid state transfer to Changed from {self:?}"),
186                };
187                let previous_status = self.status;
188                self.status = AccountStatus::Changed;
189                self.info = updated_info;
190                Some(AccountRevert {
191                    account: info_revert,
192                    storage: previous_storage,
193                    previous_status,
194                    wipe_storage: false,
195                })
196            }
197            AccountStatus::InMemoryChange => {
198                let previous_storage = previous_storage_from_update(&updated_storage);
199                let in_memory_info_revert = match self.status {
200                    AccountStatus::Loaded | AccountStatus::InMemoryChange => {
201                        // From loaded (Or LoadedEmpty) to InMemoryChange can happen if there is balance change
202                        // or new created account but Loaded didn't have contract.
203                        extend_storage(&mut self.storage, updated_storage);
204                        info_revert
205                    }
206                    AccountStatus::LoadedEmptyEIP161 => {
207                        self.storage = updated_storage;
208                        info_revert
209                    }
210                    AccountStatus::LoadedNotExisting => {
211                        self.storage = updated_storage;
212                        AccountInfoRevert::DeleteIt
213                    }
214                    _ => unreachable!("Invalid change to InMemoryChange from {self:?}"),
215                };
216                let previous_status = self.status;
217                self.status = AccountStatus::InMemoryChange;
218                self.info = updated_info;
219                Some(AccountRevert {
220                    account: in_memory_info_revert,
221                    storage: previous_storage,
222                    previous_status,
223                    wipe_storage: false,
224                })
225            }
226            AccountStatus::Loaded
227            | AccountStatus::LoadedNotExisting
228            | AccountStatus::LoadedEmptyEIP161 => {
229                // No changeset, maybe just update data
230                // Do nothing for now.
231                None
232            }
233            AccountStatus::Destroyed => {
234                // Clear this storage and move it to the Revert.
235                let this_storage = self.storage.drain().collect();
236                let ret = match self.status {
237                    AccountStatus::InMemoryChange | AccountStatus::Changed | AccountStatus::Loaded | AccountStatus::LoadedEmptyEIP161 => {
238                        Some(AccountRevert::new_selfdestructed(self.status, info_revert, this_storage))
239                    }
240                    AccountStatus::LoadedNotExisting => {
241                        // Do nothing as we have LoadedNotExisting -> Destroyed (It is noop)
242                        None
243                    }
244                    _ => unreachable!("Invalid transition to Destroyed account from: {self:?} to {updated_info:?} {updated_status:?}"),
245                };
246
247                if ret.is_some() {
248                    self.status = AccountStatus::Destroyed;
249                    self.info = None;
250                }
251
252                // Set present to destroyed.
253                ret
254            }
255            AccountStatus::DestroyedChanged => {
256                // Previous block created account or changed.
257                // (It was destroyed on previous block or one before).
258
259                // Check common pre destroy paths.
260                // If common path is there it will drain the storage.
261                if let Some(revert_state) = AccountRevert::new_selfdestructed_from_bundle(
262                    info_revert.clone(),
263                    self,
264                    &updated_storage,
265                ) {
266                    // Set to destroyed and revert state.
267                    self.status = AccountStatus::DestroyedChanged;
268                    self.info = updated_info;
269                    self.storage = updated_storage;
270
271                    Some(revert_state)
272                } else {
273                    let ret = match self.status {
274                        AccountStatus::Destroyed | AccountStatus::LoadedNotExisting => {
275                            // From destroyed state new account is made
276                            Some(AccountRevert {
277                                account: AccountInfoRevert::DeleteIt,
278                                storage: previous_storage_from_update(&updated_storage),
279                                previous_status: self.status,
280                                wipe_storage: false,
281                            })
282                        }
283                        AccountStatus::DestroyedChanged => {
284                            // Account was destroyed in this transition. So we should clear present storage
285                            // and insert it inside revert.
286
287                            let previous_storage = if transition.storage_was_destroyed {
288                                let mut storage = core::mem::take(&mut self.storage)
289                                    .into_iter()
290                                    .map(|t| (t.0, RevertToSlot::Some(t.1.present_value)))
291                                    .collect::<HashMap<_, _>>();
292                                for key in updated_storage.keys() {
293                                    // As it is not existing inside Destroyed storage this means
294                                    // that previous values must be zero
295                                    storage.entry(*key).or_insert(RevertToSlot::Destroyed);
296                                }
297                                storage
298                            } else {
299                                previous_storage_from_update(&updated_storage)
300                            };
301
302                            Some(AccountRevert {
303                                account: info_revert,
304                                storage: previous_storage,
305                                previous_status: AccountStatus::DestroyedChanged,
306                                wipe_storage: false,
307                            })
308                        }
309                        AccountStatus::DestroyedAgain => {
310                            Some(AccountRevert::new_selfdestructed_again(
311                                // Destroyed again will set empty account.
312                                AccountStatus::DestroyedAgain,
313                                AccountInfoRevert::DeleteIt,
314                                HashMap::default(),
315                                updated_storage.clone(),
316                            ))
317                        }
318                        _ => unreachable!("Invalid state transfer to DestroyedNew from {self:?}"),
319                    };
320                    self.status = AccountStatus::DestroyedChanged;
321                    self.info = updated_info;
322                    // Extends current storage.
323                    extend_storage(&mut self.storage, updated_storage);
324
325                    ret
326                }
327            }
328            AccountStatus::DestroyedAgain => {
329                // Previous block created account
330                // (It was destroyed on previous block or one before).
331
332                // Check common pre destroy paths.
333                // This will drain the storage if it is common transition.
334                let ret = if let Some(revert_state) = AccountRevert::new_selfdestructed_from_bundle(
335                    info_revert,
336                    self,
337                    &HashMap::default(),
338                ) {
339                    Some(revert_state)
340                } else {
341                    match self.status {
342                        AccountStatus::Destroyed
343                        | AccountStatus::DestroyedAgain
344                        | AccountStatus::LoadedNotExisting => {
345                            // From destroyed to destroyed again is noop
346                            //
347                            // From DestroyedAgain to DestroyedAgain is noop
348                            //
349                            // From LoadedNotExisting to DestroyedAgain is noop
350                            // as account is destroyed again
351                            None
352                        }
353                        AccountStatus::DestroyedChanged => {
354                            // From destroyed changed to destroyed again.
355                            Some(AccountRevert::new_selfdestructed_again(
356                                // Destroyed again will set empty account.
357                                AccountStatus::DestroyedChanged,
358                                AccountInfoRevert::RevertTo(self.info.clone().unwrap_or_default()),
359                                self.storage.drain().collect(),
360                                HashMap::default(),
361                            ))
362                        }
363                        _ => unreachable!("Invalid state to DestroyedAgain from {self:?}"),
364                    }
365                };
366                // Set to destroyed and revert state.
367                self.status = AccountStatus::DestroyedAgain;
368                self.info = None;
369                self.storage.clear();
370                ret
371            }
372        };
373
374        account_revert.and_then(|acc| if acc.is_empty() { None } else { Some(acc) })
375    }
376}