revm/db/states/
transition_account.rs

1use super::{AccountRevert, BundleAccount, StorageWithOriginalValues};
2use crate::db::AccountStatus;
3use revm_interpreter::primitives::{hash_map, AccountInfo, Bytecode, B256, I256, U256};
4
5/// Account Created when EVM state is merged to cache state.
6/// And it is sent to Block state.
7///
8/// It is used when block state gets merged to bundle state to
9/// create needed Reverts.
10#[derive(Clone, Debug, PartialEq, Eq, Default)]
11pub struct TransitionAccount {
12    pub info: Option<AccountInfo>,
13    pub status: AccountStatus,
14    /// Previous account info is needed for account that got initially loaded.
15    /// Initially loaded account are not present inside bundle and are needed
16    /// to generate Reverts.
17    pub previous_info: Option<AccountInfo>,
18    /// Mostly needed when previous status Loaded/LoadedEmpty.
19    pub previous_status: AccountStatus,
20    /// Storage contains both old and new account
21    pub storage: StorageWithOriginalValues,
22    /// If there is transition that clears the storage we should mark it here and
23    /// delete all storages in BundleState. This flag is needed if we have transition
24    /// between Destroyed states from DestroyedChanged-> DestroyedAgain-> DestroyedChanged
25    /// in the end transition that we would have would be `DestroyedChanged->DestroyedChanged`
26    /// and with only that info we couldn't decide what to do.
27    pub storage_was_destroyed: bool,
28}
29
30impl TransitionAccount {
31    /// Create new LoadedEmpty account.
32    pub fn new_empty_eip161(storage: StorageWithOriginalValues) -> Self {
33        Self {
34            info: Some(AccountInfo::default()),
35            status: AccountStatus::InMemoryChange,
36            previous_info: None,
37            previous_status: AccountStatus::LoadedNotExisting,
38            storage,
39            storage_was_destroyed: false,
40        }
41    }
42
43    /// Return new contract bytecode if it is changed or newly created.
44    pub fn has_new_contract(&self) -> Option<(B256, &Bytecode)> {
45        let present_new_codehash = self.info.as_ref().map(|info| &info.code_hash);
46        let previous_codehash = self.previous_info.as_ref().map(|info| &info.code_hash);
47        if present_new_codehash != previous_codehash {
48            return self
49                .info
50                .as_ref()
51                .and_then(|info| info.code.as_ref().map(|c| (info.code_hash, c)));
52        }
53        None
54    }
55
56    /// Return the balance of account before transition.
57    pub fn previous_balance(&self) -> U256 {
58        self.previous_info
59            .as_ref()
60            .map(|info| info.balance)
61            .unwrap_or_default()
62    }
63
64    /// Return the balance of account after transition.
65    pub fn current_balance(&self) -> U256 {
66        self.info
67            .as_ref()
68            .map(|info| info.balance)
69            .unwrap_or_default()
70    }
71
72    /// Calculate the change in account's balance for this transition.
73    /// Returns `None` if delta does not fit in [I256].
74    pub fn balance_delta(&self) -> Option<I256> {
75        let previous_balance = self.previous_balance();
76        let current_balance = self.current_balance();
77        let delta = I256::try_from(previous_balance.abs_diff(current_balance)).ok()?;
78        if current_balance >= previous_balance {
79            Some(delta)
80        } else {
81            delta.checked_neg()
82        }
83    }
84
85    /// Update new values of transition. Don't override old values.
86    /// Both account info and old storages need to be left intact.
87    pub fn update(&mut self, other: Self) {
88        self.info.clone_from(&other.info);
89        self.status = other.status;
90
91        // if transition is from some to destroyed drop the storage.
92        // This need to be done here as it is one increment of the state.
93        if matches!(
94            other.status,
95            AccountStatus::Destroyed | AccountStatus::DestroyedAgain
96        ) {
97            self.storage = other.storage;
98            self.storage_was_destroyed = true;
99        } else {
100            // update changed values to this transition.
101            for (key, slot) in other.storage.into_iter() {
102                match self.storage.entry(key) {
103                    hash_map::Entry::Vacant(entry) => {
104                        entry.insert(slot);
105                    }
106                    hash_map::Entry::Occupied(mut entry) => {
107                        let value = entry.get_mut();
108                        // if new value is same as original value. Remove storage entry.
109                        if value.original_value() == slot.present_value() {
110                            entry.remove();
111                        } else {
112                            // if value is different, update transition present value;
113                            value.present_value = slot.present_value;
114                        }
115                    }
116                }
117            }
118        }
119    }
120
121    /// Consume Self and create account revert from it.
122    pub fn create_revert(self) -> Option<AccountRevert> {
123        let mut previous_account = self.original_bundle_account();
124        previous_account.update_and_create_revert(self)
125    }
126
127    /// Present bundle account
128    pub fn present_bundle_account(&self) -> BundleAccount {
129        BundleAccount {
130            info: self.info.clone(),
131            original_info: self.previous_info.clone(),
132            storage: self.storage.clone(),
133            status: self.status,
134        }
135    }
136
137    /// Original bundle account
138    fn original_bundle_account(&self) -> BundleAccount {
139        BundleAccount {
140            info: self.previous_info.clone(),
141            original_info: self.previous_info.clone(),
142            storage: StorageWithOriginalValues::default(),
143            status: self.previous_status,
144        }
145    }
146}