revm_database/states/
cache_account.rs

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