revm/db/states/
reverts.rs

1use super::{
2    changes::PlainStorageRevert, AccountStatus, BundleAccount, PlainStateReverts,
3    StorageWithOriginalValues,
4};
5use core::ops::{Deref, DerefMut};
6use revm_interpreter::primitives::{AccountInfo, Address, HashMap, U256};
7use std::vec::Vec;
8
9/// Contains reverts of multiple account in multiple transitions (Transitions as a block).
10#[derive(Clone, Debug, Default, PartialEq, Eq)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
12pub struct Reverts(Vec<Vec<(Address, AccountRevert)>>);
13
14impl Deref for Reverts {
15    type Target = Vec<Vec<(Address, AccountRevert)>>;
16
17    fn deref(&self) -> &Self::Target {
18        &self.0
19    }
20}
21
22impl DerefMut for Reverts {
23    fn deref_mut(&mut self) -> &mut Self::Target {
24        &mut self.0
25    }
26}
27
28impl Reverts {
29    /// Create new reverts
30    pub fn new(reverts: Vec<Vec<(Address, AccountRevert)>>) -> Self {
31        Self(reverts)
32    }
33
34    /// Sort account inside transition by their address.
35    pub fn sort(&mut self) {
36        for revert in &mut self.0 {
37            revert.sort_by_key(|(address, _)| *address);
38        }
39    }
40
41    /// Extend reverts with other reverts.
42    pub fn extend(&mut self, other: Reverts) {
43        self.0.extend(other.0);
44    }
45
46    /// Generate a [`PlainStateReverts`].
47    ///
48    /// Note that account are sorted by address.
49    pub fn to_plain_state_reverts(&self) -> PlainStateReverts {
50        let mut state_reverts = PlainStateReverts::with_capacity(self.0.len());
51        for reverts in &self.0 {
52            // pessimistically pre-allocate assuming _all_ accounts changed.
53            let mut accounts = Vec::with_capacity(reverts.len());
54            let mut storage = Vec::with_capacity(reverts.len());
55            for (address, revert_account) in reverts {
56                match &revert_account.account {
57                    AccountInfoRevert::RevertTo(acc) => {
58                        // cloning is cheap, because account info has 3 small
59                        // fields and a Bytes
60                        accounts.push((*address, Some(acc.clone())))
61                    }
62                    AccountInfoRevert::DeleteIt => accounts.push((*address, None)),
63                    AccountInfoRevert::DoNothing => (),
64                }
65                if revert_account.wipe_storage || !revert_account.storage.is_empty() {
66                    storage.push(PlainStorageRevert {
67                        address: *address,
68                        wiped: revert_account.wipe_storage,
69                        storage_revert: revert_account
70                            .storage
71                            .iter()
72                            .map(|(k, v)| (*k, *v))
73                            .collect::<Vec<_>>(),
74                    });
75                }
76            }
77            state_reverts.accounts.push(accounts);
78            state_reverts.storage.push(storage);
79        }
80        state_reverts
81    }
82
83    /// Consume reverts and create [`PlainStateReverts`].
84    ///
85    /// Note that account are sorted by address.
86    #[deprecated = "Use `to_plain_state_reverts` instead"]
87    pub fn into_plain_state_reverts(self) -> PlainStateReverts {
88        self.to_plain_state_reverts()
89    }
90}
91
92/// Assumption is that Revert can return full state from any future state to any past state.
93///
94/// It is created when new account state is applied to old account state.
95/// And it is used to revert new account state to the old account state.
96///
97/// AccountRevert is structured in this way as we need to save it inside database.
98/// And we need to be able to read it from database.
99#[derive(Clone, Default, Debug, PartialEq, Eq)]
100#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
101pub struct AccountRevert {
102    pub account: AccountInfoRevert,
103    pub storage: HashMap<U256, RevertToSlot>,
104    pub previous_status: AccountStatus,
105    pub wipe_storage: bool,
106}
107
108impl AccountRevert {
109    /// The approximate size of changes needed to store this account revert.
110    /// `1 + storage_reverts_len`
111    pub fn size_hint(&self) -> usize {
112        1 + self.storage.len()
113    }
114
115    /// Very similar to new_selfdestructed but it will add additional zeros (RevertToSlot::Destroyed)
116    /// for the storage that are set if account is again created.
117    pub fn new_selfdestructed_again(
118        status: AccountStatus,
119        account: AccountInfoRevert,
120        mut previous_storage: StorageWithOriginalValues,
121        updated_storage: StorageWithOriginalValues,
122    ) -> Self {
123        // Take present storage values as the storages that we are going to revert to.
124        // As those values got destroyed.
125        let mut previous_storage: HashMap<U256, RevertToSlot> = previous_storage
126            .drain()
127            .map(|(key, value)| (key, RevertToSlot::Some(value.present_value)))
128            .collect();
129        for (key, _) in updated_storage {
130            previous_storage
131                .entry(key)
132                .or_insert(RevertToSlot::Destroyed);
133        }
134        AccountRevert {
135            account,
136            storage: previous_storage,
137            previous_status: status,
138            wipe_storage: false,
139        }
140    }
141
142    /// Create revert for states that were before selfdestruct.
143    pub fn new_selfdestructed_from_bundle(
144        account_info_revert: AccountInfoRevert,
145        bundle_account: &mut BundleAccount,
146        updated_storage: &StorageWithOriginalValues,
147    ) -> Option<Self> {
148        match bundle_account.status {
149            AccountStatus::InMemoryChange
150            | AccountStatus::Changed
151            | AccountStatus::LoadedEmptyEIP161
152            | AccountStatus::Loaded => {
153                let mut ret = AccountRevert::new_selfdestructed_again(
154                    bundle_account.status,
155                    account_info_revert,
156                    bundle_account.storage.drain().collect(),
157                    updated_storage.clone(),
158                );
159                ret.wipe_storage = true;
160                Some(ret)
161            }
162            _ => None,
163        }
164    }
165
166    /// Create new selfdestruct revert.
167    pub fn new_selfdestructed(
168        status: AccountStatus,
169        account: AccountInfoRevert,
170        mut storage: StorageWithOriginalValues,
171    ) -> Self {
172        // Zero all present storage values and save present values to AccountRevert.
173        let previous_storage = storage
174            .iter_mut()
175            .map(|(key, value)| {
176                // take previous value and set ZERO as storage got destroyed.
177                (*key, RevertToSlot::Some(value.present_value))
178            })
179            .collect();
180
181        Self {
182            account,
183            storage: previous_storage,
184            previous_status: status,
185            wipe_storage: true,
186        }
187    }
188
189    /// Returns `true` if there is nothing to revert,
190    /// by checking that:
191    /// * both account info and storage have been left untouched
192    /// * we don't need to wipe storage
193    pub fn is_empty(&self) -> bool {
194        self.account == AccountInfoRevert::DoNothing
195            && self.storage.is_empty()
196            && !self.wipe_storage
197    }
198}
199
200/// Depending on previous state of account info this
201/// will tell us what to do on revert.
202#[derive(Clone, Default, Debug, PartialEq, Eq, Hash)]
203#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
204pub enum AccountInfoRevert {
205    #[default]
206    /// Nothing changed
207    DoNothing,
208    /// Account was created and on revert we need to remove it with all storage.
209    DeleteIt,
210    /// Account was changed and on revert we need to put old state.
211    RevertTo(AccountInfo),
212}
213
214/// So storage can have multiple types:
215/// * Zero, on revert remove plain state.
216/// * Value, on revert set this value
217/// * Destroyed, should be removed on revert but on Revert set it as zero.
218///
219/// Note: It is completely different state if Storage is Zero or Some or if Storage was
220/// Destroyed. Because if it is destroyed, previous values can be found in database or it can be zero.
221#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash)]
222#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
223pub enum RevertToSlot {
224    Some(U256),
225    Destroyed,
226}
227
228impl RevertToSlot {
229    pub fn to_previous_value(self) -> U256 {
230        match self {
231            RevertToSlot::Some(value) => value,
232            RevertToSlot::Destroyed => U256::ZERO,
233        }
234    }
235}