Skip to main content

revm_database/states/
transition_account.rs

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