revm_state/
lib.rs

1//! Optimism-specific constants, types, and helpers.
2#![cfg_attr(not(test), warn(unused_crate_dependencies))]
3#![cfg_attr(not(feature = "std"), no_std)]
4
5mod account_info;
6mod types;
7pub use bytecode;
8
9pub use account_info::AccountInfo;
10pub use bytecode::Bytecode;
11pub use primitives;
12pub use types::{EvmState, EvmStorage, TransientStorage};
13
14use bitflags::bitflags;
15use core::hash::Hash;
16use primitives::{HashMap, U256};
17use specification::hardfork::SpecId;
18
19#[derive(Debug, Clone, PartialEq, Eq, Default)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
21pub struct Account {
22    /// Balance, nonce, and code
23    pub info: AccountInfo,
24    /// Storage cache
25    pub storage: EvmStorage,
26    /// Account status flags
27    pub status: AccountStatus,
28}
29
30impl Account {
31    /// Creates new account and mark it as non existing.
32    pub fn new_not_existing() -> Self {
33        Self {
34            info: AccountInfo::default(),
35            storage: HashMap::default(),
36            status: AccountStatus::LoadedAsNotExisting,
37        }
38    }
39
40    /// Checks if account is empty and check if empty state before spurious dragon hardfork.
41    #[inline]
42    pub fn state_clear_aware_is_empty(&self, spec: SpecId) -> bool {
43        if SpecId::is_enabled_in(spec, SpecId::SPURIOUS_DRAGON) {
44            self.is_empty()
45        } else {
46            let loaded_not_existing = self.is_loaded_as_not_existing();
47            let is_not_touched = !self.is_touched();
48            loaded_not_existing && is_not_touched
49        }
50    }
51
52    /// Marks the account as self destructed.
53    pub fn mark_selfdestruct(&mut self) {
54        self.status |= AccountStatus::SelfDestructed;
55    }
56
57    /// Unmarks the account as self destructed.
58    pub fn unmark_selfdestruct(&mut self) {
59        self.status -= AccountStatus::SelfDestructed;
60    }
61
62    /// Is account marked for self destruct.
63    pub fn is_selfdestructed(&self) -> bool {
64        self.status.contains(AccountStatus::SelfDestructed)
65    }
66
67    /// Marks the account as touched
68    pub fn mark_touch(&mut self) {
69        self.status |= AccountStatus::Touched;
70    }
71
72    /// Unmarks the touch flag.
73    pub fn unmark_touch(&mut self) {
74        self.status -= AccountStatus::Touched;
75    }
76
77    /// If account status is marked as touched.
78    pub fn is_touched(&self) -> bool {
79        self.status.contains(AccountStatus::Touched)
80    }
81
82    /// Marks the account as newly created.
83    pub fn mark_created(&mut self) {
84        self.status |= AccountStatus::Created;
85    }
86
87    /// Unmarks the created flag.
88    pub fn unmark_created(&mut self) {
89        self.status -= AccountStatus::Created;
90    }
91
92    /// Marks the account as cold.
93    pub fn mark_cold(&mut self) {
94        self.status |= AccountStatus::Cold;
95    }
96
97    /// Marks the account as warm and return true if it was previously cold.
98    pub fn mark_warm(&mut self) -> bool {
99        if self.status.contains(AccountStatus::Cold) {
100            self.status -= AccountStatus::Cold;
101            true
102        } else {
103            false
104        }
105    }
106
107    /// Is account loaded as not existing from database.
108    ///
109    /// This is needed for pre spurious dragon hardforks where
110    /// existing and empty were two separate states.
111    pub fn is_loaded_as_not_existing(&self) -> bool {
112        self.status.contains(AccountStatus::LoadedAsNotExisting)
113    }
114
115    /// Is account newly created in this transaction.
116    pub fn is_created(&self) -> bool {
117        self.status.contains(AccountStatus::Created)
118    }
119
120    /// Is account empty, check if nonce and balance are zero and code is empty.
121    pub fn is_empty(&self) -> bool {
122        self.info.is_empty()
123    }
124
125    /// Returns an iterator over the storage slots that have been changed.
126    ///
127    /// See also [EvmStorageSlot::is_changed].
128    pub fn changed_storage_slots(&self) -> impl Iterator<Item = (&U256, &EvmStorageSlot)> {
129        self.storage.iter().filter(|(_, slot)| slot.is_changed())
130    }
131}
132
133impl From<AccountInfo> for Account {
134    fn from(info: AccountInfo) -> Self {
135        Self {
136            info,
137            storage: HashMap::default(),
138            status: AccountStatus::Loaded,
139        }
140    }
141}
142
143// The `bitflags!` macro generates `struct`s that manage a set of flags.
144bitflags! {
145    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
146    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
147    #[cfg_attr(feature = "serde", serde(transparent))]
148    pub struct AccountStatus: u8 {
149        /// When account is loaded but not touched or interacted with.
150        /// This is the default state.
151        const Loaded = 0b00000000;
152        /// When account is newly created we will not access database
153        /// to fetch storage values
154        const Created = 0b00000001;
155        /// If account is marked for self destruction.
156        const SelfDestructed = 0b00000010;
157        /// Only when account is marked as touched we will save it to database.
158        const Touched = 0b00000100;
159        /// used only for pre spurious dragon hardforks where existing and empty were two separate states.
160        /// it became same state after EIP-161: State trie clearing
161        const LoadedAsNotExisting = 0b0001000;
162        /// used to mark account as cold
163        const Cold = 0b0010000;
164    }
165}
166
167impl Default for AccountStatus {
168    fn default() -> Self {
169        Self::Loaded
170    }
171}
172
173/// This type keeps track of the current value of a storage slot.
174#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
175#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
176pub struct EvmStorageSlot {
177    /// Original value of the storage slot
178    pub original_value: U256,
179    /// Present value of the storage slot
180    pub present_value: U256,
181    /// Represents if the storage slot is cold
182    pub is_cold: bool,
183}
184
185impl EvmStorageSlot {
186    /// Creates a new _unchanged_ `EvmStorageSlot` for the given value.
187    pub fn new(original: U256) -> Self {
188        Self {
189            original_value: original,
190            present_value: original,
191            is_cold: false,
192        }
193    }
194
195    /// Creates a new _changed_ `EvmStorageSlot`.
196    pub fn new_changed(original_value: U256, present_value: U256) -> Self {
197        Self {
198            original_value,
199            present_value,
200            is_cold: false,
201        }
202    }
203    /// Returns true if the present value differs from the original value.
204    pub fn is_changed(&self) -> bool {
205        self.original_value != self.present_value
206    }
207
208    /// Returns the original value of the storage slot.
209    pub fn original_value(&self) -> U256 {
210        self.original_value
211    }
212
213    /// Returns the current value of the storage slot.
214    pub fn present_value(&self) -> U256 {
215        self.present_value
216    }
217
218    /// Marks the storage slot as cold.
219    pub fn mark_cold(&mut self) {
220        self.is_cold = true;
221    }
222
223    /// Marks the storage slot as warm and returns a bool indicating if it was previously cold.
224    pub fn mark_warm(&mut self) -> bool {
225        core::mem::replace(&mut self.is_cold, false)
226    }
227}
228
229#[cfg(test)]
230mod tests {
231    use crate::Account;
232    use primitives::{KECCAK_EMPTY, U256};
233
234    #[test]
235    fn account_is_empty_balance() {
236        let mut account = Account::default();
237        assert!(account.is_empty());
238
239        account.info.balance = U256::from(1);
240        assert!(!account.is_empty());
241
242        account.info.balance = U256::ZERO;
243        assert!(account.is_empty());
244    }
245
246    #[test]
247    fn account_is_empty_nonce() {
248        let mut account = Account::default();
249        assert!(account.is_empty());
250
251        account.info.nonce = 1;
252        assert!(!account.is_empty());
253
254        account.info.nonce = 0;
255        assert!(account.is_empty());
256    }
257
258    #[test]
259    fn account_is_empty_code_hash() {
260        let mut account = Account::default();
261        assert!(account.is_empty());
262
263        account.info.code_hash = [1; 32].into();
264        assert!(!account.is_empty());
265
266        account.info.code_hash = [0; 32].into();
267        assert!(account.is_empty());
268
269        account.info.code_hash = KECCAK_EMPTY;
270        assert!(account.is_empty());
271    }
272
273    #[test]
274    fn account_state() {
275        let mut account = Account::default();
276
277        assert!(!account.is_touched());
278        assert!(!account.is_selfdestructed());
279
280        account.mark_touch();
281        assert!(account.is_touched());
282        assert!(!account.is_selfdestructed());
283
284        account.mark_selfdestruct();
285        assert!(account.is_touched());
286        assert!(account.is_selfdestructed());
287
288        account.unmark_selfdestruct();
289        assert!(account.is_touched());
290        assert!(!account.is_selfdestructed());
291    }
292
293    #[test]
294    fn account_is_cold() {
295        let mut account = Account::default();
296
297        // Account is not cold by default
298        assert!(!account.status.contains(crate::AccountStatus::Cold));
299
300        // When marking warm account as warm again, it should return false
301        assert!(!account.mark_warm());
302
303        // Mark account as cold
304        account.mark_cold();
305
306        // Account is cold
307        assert!(account.status.contains(crate::AccountStatus::Cold));
308
309        // When marking cold account as warm, it should return true
310        assert!(account.mark_warm());
311    }
312}