revm_database/states/
cache_account.rs

1use super::{
2    plain_account::PlainStorage, AccountStatus, BundleAccount, PlainAccount,
3    StorageWithOriginalValues, TransitionAccount,
4};
5use primitives::{HashMap, StorageKey, StorageValue, U256};
6use state::AccountInfo;
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 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 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 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 fn new_destroyed() -> Self {
76        Self {
77            account: None,
78            status: AccountStatus::Destroyed,
79        }
80    }
81
82    /// Creates changed account.
83    pub 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 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    /// Account got touched and before EIP161 state clear this account is considered created.
120    pub fn touch_create_pre_eip161(
121        &mut self,
122        storage: StorageWithOriginalValues,
123    ) -> Option<TransitionAccount> {
124        let previous_status = self.status;
125
126        let had_no_info = self
127            .account
128            .as_ref()
129            .map(|a| a.info.is_empty())
130            .unwrap_or_default();
131        self.status = self.status.on_touched_created_pre_eip161(had_no_info)?;
132
133        let plain_storage = storage.iter().map(|(k, v)| (*k, v.present_value)).collect();
134        let previous_info = self.account.take().map(|a| a.info);
135
136        self.account = Some(PlainAccount::new_empty_with_storage(plain_storage));
137
138        Some(TransitionAccount {
139            info: Some(AccountInfo::default()),
140            status: self.status,
141            previous_info,
142            previous_status,
143            storage,
144            storage_was_destroyed: false,
145        })
146    }
147
148    /// Touch empty account, related to EIP-161 state clear.
149    ///
150    /// This account returns the Transition that is used to create the BundleState.
151    pub fn touch_empty_eip161(&mut self) -> Option<TransitionAccount> {
152        let previous_status = self.status;
153
154        // Set account to None.
155        let previous_info = self.account.take().map(|acc| acc.info);
156
157        // Set account state to Destroyed as we need to clear the storage if it exist.
158        self.status = self.status.on_touched_empty_post_eip161();
159
160        if matches!(
161            previous_status,
162            AccountStatus::LoadedNotExisting
163                | AccountStatus::Destroyed
164                | AccountStatus::DestroyedAgain
165        ) {
166            None
167        } else {
168            Some(TransitionAccount {
169                info: None,
170                status: self.status,
171                previous_info,
172                previous_status,
173                storage: HashMap::default(),
174                storage_was_destroyed: true,
175            })
176        }
177    }
178
179    /// Consumes self and make account as destroyed.
180    ///
181    /// Sets account as None and set status to Destroyer or DestroyedAgain.
182    pub fn selfdestruct(&mut self) -> Option<TransitionAccount> {
183        // Account should be None after selfdestruct so we can take it.
184        let previous_info = self.account.take().map(|a| a.info);
185        let previous_status = self.status;
186
187        self.status = self.status.on_selfdestructed();
188
189        if previous_status == AccountStatus::LoadedNotExisting {
190            None
191        } else {
192            Some(TransitionAccount {
193                info: None,
194                status: self.status,
195                previous_info,
196                previous_status,
197                storage: HashMap::default(),
198                storage_was_destroyed: true,
199            })
200        }
201    }
202
203    /// Newly created account.
204    pub fn newly_created(
205        &mut self,
206        new_info: AccountInfo,
207        new_storage: StorageWithOriginalValues,
208    ) -> TransitionAccount {
209        let previous_status = self.status;
210        let previous_info = self.account.take().map(|a| a.info);
211
212        let new_bundle_storage = new_storage
213            .iter()
214            .map(|(k, s)| (*k, s.present_value))
215            .collect();
216
217        self.status = self.status.on_created();
218        let transition_account = TransitionAccount {
219            info: Some(new_info.clone()),
220            status: self.status,
221            previous_status,
222            previous_info,
223            storage: new_storage,
224            storage_was_destroyed: false,
225        };
226        self.account = Some(PlainAccount {
227            info: new_info,
228            storage: new_bundle_storage,
229        });
230        transition_account
231    }
232
233    /// Increment balance by `balance` amount. Assume that balance will not
234    /// overflow or be zero.
235    ///
236    /// Note: Only if balance is zero we would return None as no transition would be made.
237    pub fn increment_balance(&mut self, balance: u128) -> Option<TransitionAccount> {
238        if balance == 0 {
239            return None;
240        }
241        let (_, transition) = self.account_info_change(|info| {
242            info.balance = info.balance.saturating_add(U256::from(balance));
243        });
244        Some(transition)
245    }
246
247    fn account_info_change<T, F: FnOnce(&mut AccountInfo) -> T>(
248        &mut self,
249        change: F,
250    ) -> (T, TransitionAccount) {
251        let previous_status = self.status;
252        let previous_info = self.account_info();
253        let mut account = self.account.take().unwrap_or_default();
254        let output = change(&mut account.info);
255        self.account = Some(account);
256
257        let had_no_nonce_and_code = previous_info
258            .as_ref()
259            .map(AccountInfo::has_no_code_and_nonce)
260            .unwrap_or_default();
261        self.status = self.status.on_changed(had_no_nonce_and_code);
262
263        (
264            output,
265            TransitionAccount {
266                info: self.account_info(),
267                status: self.status,
268                previous_info,
269                previous_status,
270                storage: HashMap::default(),
271                storage_was_destroyed: false,
272            },
273        )
274    }
275
276    /// Drain balance from account and return drained amount and transition.
277    ///
278    /// Used for DAO hardfork transition.
279    pub fn drain_balance(&mut self) -> (u128, TransitionAccount) {
280        self.account_info_change(|info| {
281            let output = info.balance;
282            info.balance = U256::ZERO;
283            output.try_into().unwrap()
284        })
285    }
286
287    /// Updates the account with new information and storage changes.
288    ///
289    /// Merges the provided storage values with the existing storage and updates the account status.
290    pub fn change(
291        &mut self,
292        new: AccountInfo,
293        storage: StorageWithOriginalValues,
294    ) -> TransitionAccount {
295        let previous_status = self.status;
296        let (previous_info, mut this_storage) = if let Some(account) = self.account.take() {
297            (Some(account.info), account.storage)
298        } else {
299            (None, Default::default())
300        };
301
302        this_storage.extend(storage.iter().map(|(k, s)| (*k, s.present_value)));
303        let changed_account = PlainAccount {
304            info: new,
305            storage: this_storage,
306        };
307
308        let had_no_nonce_and_code = previous_info
309            .as_ref()
310            .map(AccountInfo::has_no_code_and_nonce)
311            .unwrap_or_default();
312        self.status = self.status.on_changed(had_no_nonce_and_code);
313        self.account = Some(changed_account);
314
315        TransitionAccount {
316            info: self.account.as_ref().map(|a| a.info.clone()),
317            status: self.status,
318            previous_info,
319            previous_status,
320            storage,
321            storage_was_destroyed: false,
322        }
323    }
324}