revm/db/states/
cache_account.rs

1use super::{
2    plain_account::PlainStorage, AccountStatus, BundleAccount, PlainAccount,
3    StorageWithOriginalValues, TransitionAccount,
4};
5use revm_interpreter::primitives::{AccountInfo, U256};
6use revm_precompile::HashMap;
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    /// Create 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    /// Create 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    /// Create 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    /// Create account that is destroyed.
67    pub fn new_destroyed() -> Self {
68        Self {
69            account: None,
70            status: AccountStatus::Destroyed,
71        }
72    }
73
74    /// Create 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    /// Return 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    /// Return storage slot if it exist.
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    /// Fetch account info if it exist.
102    pub fn account_info(&self) -> Option<AccountInfo> {
103        self.account.as_ref().map(|a| a.info.clone())
104    }
105
106    /// Dissolve 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    /// Consume self and make account as destroyed.
172    ///
173    /// Set 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    pub fn change(
280        &mut self,
281        new: AccountInfo,
282        storage: StorageWithOriginalValues,
283    ) -> TransitionAccount {
284        let previous_status = self.status;
285        let previous_info = self.account.as_ref().map(|a| a.info.clone());
286        let mut this_storage = self
287            .account
288            .take()
289            .map(|acc| acc.storage)
290            .unwrap_or_default();
291
292        this_storage.extend(storage.iter().map(|(k, s)| (*k, s.present_value)));
293        let changed_account = PlainAccount {
294            info: new,
295            storage: this_storage,
296        };
297
298        let had_no_nonce_and_code = previous_info
299            .as_ref()
300            .map(AccountInfo::has_no_code_and_nonce)
301            .unwrap_or_default();
302        self.status = self.status.on_changed(had_no_nonce_and_code);
303        self.account = Some(changed_account);
304
305        TransitionAccount {
306            info: self.account.as_ref().map(|a| a.info.clone()),
307            status: self.status,
308            previous_info,
309            previous_status,
310            storage,
311            storage_was_destroyed: false,
312        }
313    }
314}