Skip to main content

revm_database/states/
cache_account.rs

1use super::{
2    plain_account::PlainStorage, AccountStatus, BundleAccount, PlainAccount, TransitionAccount,
3};
4use primitives::{HashMap, StorageKey, StorageValue, U256};
5use state::{Account, AccountInfo, EvmStorage};
6use std::borrow::Cow;
7
8/// Cache account contains plain state that gets updated
9/// at every transaction when evm output is applied to CacheState.
10#[derive(Clone, Debug, PartialEq, Eq)]
11pub struct CacheAccount {
12    /// Account information and storage, if account exists.
13    pub account: Option<PlainAccount>,
14    /// Account status flags.
15    pub status: AccountStatus,
16}
17
18impl From<BundleAccount> for CacheAccount {
19    fn from(account: BundleAccount) -> Self {
20        CacheAccount::from(&account)
21    }
22}
23
24impl From<&BundleAccount> for CacheAccount {
25    fn from(account: &BundleAccount) -> Self {
26        let storage = account
27            .storage
28            .iter()
29            .map(|(k, v)| (*k, v.present_value))
30            .collect();
31        let plain_account = account
32            .account_info()
33            .map(|info| PlainAccount { info, storage });
34        Self {
35            account: plain_account,
36            status: account.status,
37        }
38    }
39}
40
41impl CacheAccount {
42    /// Creates new account that is loaded from database.
43    pub const fn new_loaded(info: AccountInfo, storage: PlainStorage) -> Self {
44        Self {
45            account: Some(PlainAccount { info, storage }),
46            status: AccountStatus::Loaded,
47        }
48    }
49
50    /// Creates new account that is loaded empty from database.
51    pub fn new_loaded_empty_eip161(storage: PlainStorage) -> Self {
52        Self {
53            account: Some(PlainAccount::new_empty_with_storage(storage)),
54            status: AccountStatus::LoadedEmptyEIP161,
55        }
56    }
57
58    /// Loaded not existing account.
59    pub const fn new_loaded_not_existing() -> Self {
60        Self {
61            account: None,
62            status: AccountStatus::LoadedNotExisting,
63        }
64    }
65
66    /// Creates new account that is newly created.
67    pub const fn new_newly_created(info: AccountInfo, storage: PlainStorage) -> Self {
68        Self {
69            account: Some(PlainAccount { info, storage }),
70            status: AccountStatus::InMemoryChange,
71        }
72    }
73
74    /// Creates account that is destroyed.
75    pub const fn new_destroyed() -> Self {
76        Self {
77            account: None,
78            status: AccountStatus::Destroyed,
79        }
80    }
81
82    /// Creates changed account.
83    pub const fn new_changed(info: AccountInfo, storage: PlainStorage) -> Self {
84        Self {
85            account: Some(PlainAccount { info, storage }),
86            status: AccountStatus::Changed,
87        }
88    }
89
90    /// Returns true if account is some.
91    pub const fn is_some(&self) -> bool {
92        matches!(
93            self.status,
94            AccountStatus::Changed
95                | AccountStatus::InMemoryChange
96                | AccountStatus::DestroyedChanged
97                | AccountStatus::Loaded
98                | AccountStatus::LoadedEmptyEIP161
99        )
100    }
101
102    /// Returns storage slot if it exists.
103    pub fn storage_slot(&self, slot: StorageKey) -> Option<StorageValue> {
104        self.account
105            .as_ref()
106            .and_then(|a| a.storage.get(&slot).cloned())
107    }
108
109    /// Fetches account info if it exists.
110    pub fn account_info(&self) -> Option<AccountInfo> {
111        self.account.as_ref().map(|a| a.info.clone())
112    }
113
114    /// Dissolves account into components.
115    pub fn into_components(self) -> (Option<(AccountInfo, PlainStorage)>, AccountStatus) {
116        (self.account.map(|a| a.into_components()), self.status)
117    }
118
119    /// Touch empty account, related to EIP-161 state clear.
120    ///
121    /// This account returns the Transition that is used to create the BundleState.
122    pub fn touch_empty_eip161<'a>(
123        &mut self,
124    ) -> Option<TransitionAccount<Option<Cow<'a, EvmStorage>>>> {
125        let previous_status = self.status;
126
127        // Set account to None.
128        let previous_info = self.account.take().map(|acc| acc.info);
129
130        // Set account state to Destroyed as we need to clear the storage if it exist.
131        self.status = self.status.on_touched_empty_post_eip161();
132
133        if matches!(
134            previous_status,
135            AccountStatus::LoadedNotExisting
136                | AccountStatus::Destroyed
137                | AccountStatus::DestroyedAgain
138        ) {
139            None
140        } else {
141            Some(TransitionAccount {
142                info: None,
143                status: self.status,
144                previous_info,
145                previous_status,
146                storage: None,
147                storage_was_destroyed: true,
148            })
149        }
150    }
151
152    /// Consumes self and make account as destroyed.
153    ///
154    /// Sets account as None and set status to Destroyer or DestroyedAgain.
155    pub fn selfdestruct<'a>(&mut self) -> Option<TransitionAccount<Option<Cow<'a, EvmStorage>>>> {
156        // Account should be None after selfdestruct so we can take it.
157        let previous_info = self.account.take().map(|a| a.info);
158        let previous_status = self.status;
159
160        self.status = self.status.on_selfdestructed();
161
162        if previous_status == AccountStatus::LoadedNotExisting {
163            None
164        } else {
165            Some(TransitionAccount {
166                info: None,
167                status: self.status,
168                previous_info,
169                previous_status,
170                storage: None,
171                storage_was_destroyed: true,
172            })
173        }
174    }
175
176    /// Newly created account.
177    pub fn newly_created<'a>(
178        &mut self,
179        account: Cow<'a, Account>,
180    ) -> TransitionAccount<Option<Cow<'a, EvmStorage>>> {
181        let previous_status = self.status;
182        let previous_info = self.account.take().map(|a| a.info);
183
184        let new_bundle_storage = account
185            .storage
186            .iter()
187            .filter_map(|(k, s)| s.is_changed().then_some((*k, s.present_value)))
188            .collect();
189
190        self.status = self.status.on_created();
191        let (info, storage) = match account {
192            Cow::Borrowed(account) => (account.info.clone(), Cow::Borrowed(&account.storage)),
193            Cow::Owned(account) => (account.info, Cow::Owned(account.storage)),
194        };
195        let transition_account = TransitionAccount {
196            info: Some(info.clone()),
197            status: self.status,
198            previous_status,
199            previous_info,
200            storage: Some(storage),
201            storage_was_destroyed: false,
202        };
203        self.account = Some(PlainAccount {
204            info,
205            storage: new_bundle_storage,
206        });
207        transition_account
208    }
209
210    /// Increment balance by `balance` amount. Assume that balance will not
211    /// overflow or be zero.
212    ///
213    /// Note: Only if balance is zero we would return None as no transition would be made.
214    pub fn increment_balance(&mut self, balance: u128) -> Option<TransitionAccount> {
215        if balance == 0 {
216            return None;
217        }
218        let (_, transition) = self.account_info_change(|info| {
219            info.balance = info.balance.saturating_add(U256::from(balance));
220        });
221        Some(transition)
222    }
223
224    fn account_info_change<T, F: FnOnce(&mut AccountInfo) -> T>(
225        &mut self,
226        change: F,
227    ) -> (T, TransitionAccount) {
228        let previous_status = self.status;
229        let previous_info = self.account_info();
230        let mut account = self.account.take().unwrap_or_default();
231        let output = change(&mut account.info);
232        self.account = Some(account);
233
234        let had_no_nonce_and_code = previous_info
235            .as_ref()
236            .map(AccountInfo::has_no_code_and_nonce)
237            .unwrap_or_default();
238        self.status = self.status.on_changed(had_no_nonce_and_code);
239
240        (
241            output,
242            TransitionAccount {
243                info: self.account_info(),
244                status: self.status,
245                previous_info,
246                previous_status,
247                storage: HashMap::default(),
248                storage_was_destroyed: false,
249            },
250        )
251    }
252
253    /// Drain balance from account and return drained amount and transition.
254    ///
255    /// Used for DAO hardfork transition.
256    pub fn drain_balance(&mut self) -> (u128, TransitionAccount) {
257        self.account_info_change(|info| {
258            let output = info.balance;
259            info.balance = U256::ZERO;
260            output.try_into().unwrap()
261        })
262    }
263
264    /// Updates the account with new information and storage changes.
265    ///
266    /// Merges the provided storage values with the existing storage and updates the account status.
267    pub fn change<'a>(
268        &mut self,
269        account: Cow<'a, Account>,
270    ) -> TransitionAccount<Option<Cow<'a, EvmStorage>>> {
271        let previous_status = self.status;
272        let (previous_info, mut this_storage) = if let Some(account) = self.account.take() {
273            (Some(account.info), account.storage)
274        } else {
275            (None, Default::default())
276        };
277
278        let (info, storage) = match account {
279            Cow::Borrowed(account) => (account.info.clone(), Cow::Borrowed(&account.storage)),
280            Cow::Owned(account) => (account.info, Cow::Owned(account.storage)),
281        };
282
283        this_storage.extend(
284            storage
285                .iter()
286                .filter_map(|(k, s)| s.is_changed().then_some((*k, s.present_value))),
287        );
288        let changed_account = PlainAccount {
289            info,
290            storage: this_storage,
291        };
292
293        let had_no_nonce_and_code = previous_info
294            .as_ref()
295            .map(AccountInfo::has_no_code_and_nonce)
296            .unwrap_or_default();
297        self.status = self.status.on_changed(had_no_nonce_and_code);
298        self.account = Some(changed_account);
299
300        TransitionAccount {
301            info: self.account.as_ref().map(|a| a.info.clone()),
302            status: self.status,
303            previous_info,
304            previous_status,
305            storage: Some(storage),
306            storage_was_destroyed: false,
307        }
308    }
309}