revm_database/states/
transition_account.rs

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