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