Skip to main content

reth_trie_common/bundle_state/
reverts.rs

1use super::{
2    changes::PlainStorageRevert, AccountStatus, BundleAccount, PlainStateReverts,
3    StorageWithOriginalValues,
4};
5use alloc::vec::Vec;
6use alloy_primitives::{map::U256Map, Address, U256};
7use core::{
8    cmp::Ordering,
9    ops::{Deref, DerefMut},
10};
11use evm2::evm::AccountInfo;
12
13/// Contains reverts of multiple account in multiple transitions (Transitions as a block).
14#[derive(Clone, Debug, Default, Eq)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
16pub struct Reverts(Vec<Vec<(Address, AccountRevert)>>);
17
18impl Deref for Reverts {
19    type Target = Vec<Vec<(Address, AccountRevert)>>;
20
21    fn deref(&self) -> &Self::Target {
22        &self.0
23    }
24}
25
26impl DerefMut for Reverts {
27    fn deref_mut(&mut self) -> &mut Self::Target {
28        &mut self.0
29    }
30}
31
32impl Reverts {
33    /// Creates new reverts.
34    pub const fn new(reverts: Vec<Vec<(Address, AccountRevert)>>) -> Self {
35        Self(reverts)
36    }
37
38    /// Sorts account inside transition by their address.
39    pub fn sort(&mut self) {
40        for revert in &mut self.0 {
41            revert.sort_by_key(|(address, _)| *address);
42        }
43    }
44
45    /// Extends reverts with other reverts.
46    pub fn extend(&mut self, other: Reverts) {
47        self.0.extend(other.0);
48    }
49
50    /// Generates a [`PlainStateReverts`].
51    ///
52    /// Note that account are sorted by address.
53    pub fn to_plain_state_reverts(&self) -> PlainStateReverts {
54        let mut state_reverts = PlainStateReverts::with_capacity(self.0.len());
55        for reverts in &self.0 {
56            // Pessimistically pre-allocate assuming _all_ accounts changed.
57            let mut accounts = Vec::with_capacity(reverts.len());
58            let mut storage = Vec::with_capacity(reverts.len());
59            for (address, revert_account) in reverts {
60                match &revert_account.account {
61                    AccountInfoRevert::RevertTo(acc) => {
62                        // Cloning is cheap, because account info has 3 small
63                        // fields and a Bytes
64                        accounts.push((*address, Some(acc.clone())))
65                    }
66                    AccountInfoRevert::DeleteIt => accounts.push((*address, None)),
67                    AccountInfoRevert::DoNothing => (),
68                }
69                if revert_account.wipe_storage || !revert_account.storage.is_empty() {
70                    storage.push(PlainStorageRevert {
71                        address: *address,
72                        wiped: revert_account.wipe_storage,
73                        storage_revert: revert_account
74                            .storage
75                            .iter()
76                            .map(|(k, v)| (*k, *v))
77                            .collect::<Vec<_>>(),
78                    });
79                }
80            }
81            state_reverts.accounts.push(accounts);
82            state_reverts.storage.push(storage);
83        }
84        state_reverts
85    }
86
87    /// Compare two Reverts instances, ignoring the order of elements
88    pub fn content_eq(&self, other: &Self) -> bool {
89        if self.0.len() != other.0.len() {
90            return false;
91        }
92
93        for (self_transition, other_transition) in self.0.iter().zip(other.0.iter()) {
94            if self_transition.len() != other_transition.len() {
95                return false;
96            }
97
98            let mut self_transition = self_transition.clone();
99            let mut other_transition = other_transition.clone();
100            // Sort both transitions
101            self_transition.sort_by(|(addr1, revert1), (addr2, revert2)| {
102                addr1.cmp(addr2).then_with(|| revert1.cmp(revert2))
103            });
104            other_transition.sort_by(|(addr1, revert1), (addr2, revert2)| {
105                addr1.cmp(addr2).then_with(|| revert1.cmp(revert2))
106            });
107
108            // Compare sorted transitions
109            if self_transition != other_transition {
110                return false;
111            }
112        }
113
114        true
115    }
116
117    /// Consume reverts and create [`PlainStateReverts`].
118    ///
119    /// Note that account are sorted by address.
120    #[deprecated = "Use `to_plain_state_reverts` instead"]
121    pub fn into_plain_state_reverts(self) -> PlainStateReverts {
122        self.to_plain_state_reverts()
123    }
124}
125
126impl PartialEq for Reverts {
127    fn eq(&self, other: &Self) -> bool {
128        self.content_eq(other)
129    }
130}
131
132/// Assumption is that Revert can return full state from any future state to any past state.
133///
134/// # Note
135/// It is created when new account state is applied to old account state.
136///
137/// And it is used to revert new account state to the old account state.
138///
139/// [`AccountRevert`] is structured in this way as we need to save it inside database.
140///
141/// And we need to be able to read it from database.
142#[derive(Clone, Default, Debug, PartialEq, Eq)]
143#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
144pub struct AccountRevert {
145    /// Account information revert.
146    pub account: AccountInfoRevert,
147    /// Storage slots to revert.
148    pub storage: U256Map<RevertToSlot>,
149    /// Previous account status before the change.
150    pub previous_status: AccountStatus,
151    /// Whether to wipe the storage.
152    pub wipe_storage: bool,
153}
154
155impl AccountRevert {
156    /// The approximate size of changes needed to store this account revert.
157    ///
158    /// `1 + storage_reverts_len`
159    pub fn size_hint(&self) -> usize {
160        1 + self.storage.len()
161    }
162
163    /// Very similar to new_selfdestructed but it will add additional zeros
164    /// ([`RevertToSlot::Destroyed`]) for the storage that are set if account is again created.
165    pub fn new_selfdestructed_again(
166        status: AccountStatus,
167        account: AccountInfoRevert,
168        mut previous_storage: StorageWithOriginalValues,
169        updated_storage: StorageWithOriginalValues,
170    ) -> Self {
171        // Take present storage values as the storages that we are going to revert to.
172        // As those values got destroyed.
173        let mut previous_storage: U256Map<RevertToSlot> = previous_storage
174            .drain()
175            .map(|(key, value)| (key, RevertToSlot::Some(value.present_value)))
176            .collect();
177        for (key, _) in updated_storage {
178            previous_storage.entry(key).or_insert(RevertToSlot::Destroyed);
179        }
180        AccountRevert {
181            account,
182            storage: previous_storage,
183            previous_status: status,
184            wipe_storage: false,
185        }
186    }
187
188    /// Creates revert for states that were before selfdestruct.
189    pub fn new_selfdestructed_from_bundle(
190        account_info_revert: AccountInfoRevert,
191        bundle_account: &mut BundleAccount,
192        updated_storage: &StorageWithOriginalValues,
193    ) -> Option<Self> {
194        match bundle_account.status {
195            AccountStatus::InMemoryChange |
196            AccountStatus::Changed |
197            AccountStatus::LoadedEmptyEIP161 |
198            AccountStatus::Loaded => {
199                let mut ret = AccountRevert::new_selfdestructed_again(
200                    bundle_account.status,
201                    account_info_revert,
202                    core::mem::take(&mut bundle_account.storage),
203                    updated_storage.clone(),
204                );
205                ret.wipe_storage = true;
206                Some(ret)
207            }
208            _ => None,
209        }
210    }
211
212    /// Create new selfdestruct revert.
213    pub fn new_selfdestructed(
214        status: AccountStatus,
215        account: AccountInfoRevert,
216        mut storage: StorageWithOriginalValues,
217    ) -> Self {
218        // Zero all present storage values and save present values to AccountRevert.
219        let previous_storage = storage
220            .iter_mut()
221            .map(|(key, value)| {
222                // Take previous value and set ZERO as storage got destroyed.
223                (*key, RevertToSlot::Some(value.present_value))
224            })
225            .collect();
226
227        Self { account, storage: previous_storage, previous_status: status, wipe_storage: true }
228    }
229
230    /// Returns `true` if there is nothing to revert,
231    /// by checking that:
232    /// * both account info and storage have been left untouched
233    /// * we don't need to wipe storage
234    pub fn is_empty(&self) -> bool {
235        self.account == AccountInfoRevert::DoNothing &&
236            self.storage.is_empty() &&
237            !self.wipe_storage
238    }
239}
240
241/// Implements partial ordering for AccountRevert
242impl PartialOrd for AccountRevert {
243    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
244        Some(self.cmp(other))
245    }
246}
247
248/// Implements total ordering for AccountRevert
249impl Ord for AccountRevert {
250    fn cmp(&self, other: &Self) -> Ordering {
251        // First compare accounts
252        if let Some(ord) = self.account.partial_cmp(&other.account) {
253            if ord != Ordering::Equal {
254                return ord;
255            }
256        }
257
258        // Convert HashMaps to sorted vectors for comparison
259        let mut self_storage: Vec<_> = self.storage.iter().collect();
260        let mut other_storage: Vec<_> = other.storage.iter().collect();
261
262        // Sort by key and then by value
263        self_storage.sort_by(|(k1, v1), (k2, v2)| k1.cmp(k2).then_with(|| v1.cmp(v2)));
264        other_storage.sort_by(|(k1, v1), (k2, v2)| k1.cmp(k2).then_with(|| v1.cmp(v2)));
265
266        // Compare each element
267        for (self_entry, other_entry) in self_storage.iter().zip(other_storage.iter()) {
268            let key_ord = self_entry.0.cmp(other_entry.0);
269            if key_ord != Ordering::Equal {
270                return key_ord;
271            }
272            let value_ord = self_entry.1.cmp(other_entry.1);
273            if value_ord != Ordering::Equal {
274                return value_ord;
275            }
276        }
277
278        // If one vector is longer than the other, or if all elements are equal
279        self_storage
280            .len()
281            .cmp(&other_storage.len())
282            .then_with(|| self.previous_status.cmp(&other.previous_status))
283            .then_with(|| self.wipe_storage.cmp(&other.wipe_storage))
284    }
285}
286
287/// Depending on previous state of account info this
288/// will tell us what to do on revert.
289#[derive(Clone, Default, Debug, PartialEq, Eq, Hash)]
290#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
291pub enum AccountInfoRevert {
292    #[default]
293    /// Nothing changed
294    DoNothing,
295    /// Account was created and on revert we need to remove it with all storage.
296    DeleteIt,
297    /// Account was changed and on revert we need to put old state.
298    RevertTo(AccountInfo),
299}
300
301impl PartialOrd for AccountInfoRevert {
302    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
303        Some(self.cmp(other))
304    }
305}
306
307/// Orders by variant (`DoNothing`, `DeleteIt`, `RevertTo`), then by the [`AccountInfo`] fields that
308/// its `Eq` compares.
309impl Ord for AccountInfoRevert {
310    fn cmp(&self, other: &Self) -> Ordering {
311        match (self, other) {
312            (Self::RevertTo(a), Self::RevertTo(b)) => {
313                let order = a
314                    .balance
315                    .cmp(&b.balance)
316                    .then_with(|| a.nonce.cmp(&b.nonce))
317                    .then_with(|| a.code_hash.cmp(&b.code_hash));
318                #[cfg(feature = "account-ext")]
319                let order = order.then_with(|| a.extension.as_ref().cmp(b.extension.as_ref()));
320                order
321            }
322            (Self::DoNothing, Self::DoNothing) | (Self::DeleteIt, Self::DeleteIt) => {
323                Ordering::Equal
324            }
325            (Self::DoNothing, _) | (Self::DeleteIt, Self::RevertTo(_)) => Ordering::Less,
326            (_, Self::DoNothing) | (Self::RevertTo(_), Self::DeleteIt) => Ordering::Greater,
327        }
328    }
329}
330
331/// So storage can have multiple types:
332/// * Zero, on revert remove plain state.
333/// * Value, on revert set this value
334/// * Destroyed, should be removed on revert but on Revert set it as zero.
335///
336/// **Note**: It is completely different state if Storage is Zero or Some or if Storage was
337/// Destroyed.
338///
339/// Because if it is destroyed, previous values can be found in database or it can be zero.
340#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
341#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
342pub enum RevertToSlot {
343    /// Revert to this value.
344    Some(U256),
345    /// Storage was destroyed.
346    Destroyed,
347}
348
349impl RevertToSlot {
350    /// Returns the previous value to set on revert.
351    pub const fn to_previous_value(self) -> U256 {
352        match self {
353            RevertToSlot::Some(value) => value,
354            RevertToSlot::Destroyed => U256::ZERO,
355        }
356    }
357}
358
359#[cfg(test)]
360mod test {
361    use super::*;
362
363    #[test]
364    fn content_eq_ignores_order_of_same_address() {
365        let address = Address::with_last_byte(1);
366        let do_nothing = AccountRevert::default();
367        let delete_it =
368            AccountRevert { account: AccountInfoRevert::DeleteIt, ..Default::default() };
369        let a =
370            Reverts::new(vec![vec![(address, do_nothing.clone()), (address, delete_it.clone())]]);
371        let b = Reverts::new(vec![vec![(address, delete_it), (address, do_nothing)]]);
372        assert_eq!(a, b);
373    }
374}