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::hardfork::SpecId;
17use primitives::{HashMap, U256};
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    /// Sets account info and returns self for method chaining.
133    pub fn with_info(mut self, info: AccountInfo) -> Self {
134        self.info = info;
135        self
136    }
137
138    /// Populates storage from an iterator of storage slots and returns self for method chaining.
139    pub fn with_storage<I>(mut self, storage_iter: I) -> Self
140    where
141        I: Iterator<Item = (U256, EvmStorageSlot)>,
142    {
143        for (key, slot) in storage_iter {
144            self.storage.insert(key, slot);
145        }
146        self
147    }
148
149    /// Marks the account as self destructed and returns self for method chaining.
150    pub fn with_selfdestruct_mark(mut self) -> Self {
151        self.mark_selfdestruct();
152        self
153    }
154
155    /// Marks the account as touched and returns self for method chaining.
156    pub fn with_touched_mark(mut self) -> Self {
157        self.mark_touch();
158        self
159    }
160
161    /// Marks the account as newly created and returns self for method chaining.
162    pub fn with_created_mark(mut self) -> Self {
163        self.mark_created();
164        self
165    }
166
167    /// Marks the account as cold and returns self for method chaining.
168    pub fn with_cold_mark(mut self) -> Self {
169        self.mark_cold();
170        self
171    }
172
173    /// Marks the account as warm (not cold) and returns self for method chaining.
174    /// Also returns whether the account was previously cold.
175    pub fn with_warm_mark(mut self) -> (Self, bool) {
176        let was_cold = self.mark_warm();
177        (self, was_cold)
178    }
179
180    /// Variant of with_warm_mark that doesn't return the previous state.
181    pub fn with_warm(mut self) -> Self {
182        self.mark_warm();
183        self
184    }
185}
186
187impl From<AccountInfo> for Account {
188    fn from(info: AccountInfo) -> Self {
189        Self {
190            info,
191            storage: HashMap::default(),
192            status: AccountStatus::Loaded,
193        }
194    }
195}
196
197// The `bitflags!` macro generates `struct`s that manage a set of flags.
198bitflags! {
199    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
200    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
201    #[cfg_attr(feature = "serde", serde(transparent))]
202    pub struct AccountStatus: u8 {
203        /// When account is loaded but not touched or interacted with.
204        /// This is the default state.
205        const Loaded = 0b00000000;
206        /// When account is newly created we will not access database
207        /// to fetch storage values
208        const Created = 0b00000001;
209        /// If account is marked for self destruction.
210        const SelfDestructed = 0b00000010;
211        /// Only when account is marked as touched we will save it to database.
212        const Touched = 0b00000100;
213        /// used only for pre spurious dragon hardforks where existing and empty were two separate states.
214        /// it became same state after EIP-161: State trie clearing
215        const LoadedAsNotExisting = 0b0001000;
216        /// used to mark account as cold
217        const Cold = 0b0010000;
218    }
219}
220
221impl Default for AccountStatus {
222    fn default() -> Self {
223        Self::Loaded
224    }
225}
226
227/// This type keeps track of the current value of a storage slot.
228#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
229#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
230pub struct EvmStorageSlot {
231    /// Original value of the storage slot
232    pub original_value: U256,
233    /// Present value of the storage slot
234    pub present_value: U256,
235    /// Represents if the storage slot is cold
236    pub is_cold: bool,
237}
238
239impl EvmStorageSlot {
240    /// Creates a new _unchanged_ `EvmStorageSlot` for the given value.
241    pub fn new(original: U256) -> Self {
242        Self {
243            original_value: original,
244            present_value: original,
245            is_cold: false,
246        }
247    }
248
249    /// Creates a new _changed_ `EvmStorageSlot`.
250    pub fn new_changed(original_value: U256, present_value: U256) -> Self {
251        Self {
252            original_value,
253            present_value,
254            is_cold: false,
255        }
256    }
257    /// Returns true if the present value differs from the original value.
258    pub fn is_changed(&self) -> bool {
259        self.original_value != self.present_value
260    }
261
262    /// Returns the original value of the storage slot.
263    pub fn original_value(&self) -> U256 {
264        self.original_value
265    }
266
267    /// Returns the current value of the storage slot.
268    pub fn present_value(&self) -> U256 {
269        self.present_value
270    }
271
272    /// Marks the storage slot as cold.
273    pub fn mark_cold(&mut self) {
274        self.is_cold = true;
275    }
276
277    /// Marks the storage slot as warm and returns a bool indicating if it was previously cold.
278    pub fn mark_warm(&mut self) -> bool {
279        core::mem::replace(&mut self.is_cold, false)
280    }
281}
282
283#[cfg(test)]
284mod tests {
285    use super::*;
286    use crate::EvmStorageSlot;
287    use primitives::KECCAK_EMPTY;
288
289    #[test]
290    fn account_is_empty_balance() {
291        let mut account = Account::default();
292        assert!(account.is_empty());
293
294        account.info.balance = U256::from(1);
295        assert!(!account.is_empty());
296
297        account.info.balance = U256::ZERO;
298        assert!(account.is_empty());
299    }
300
301    #[test]
302    fn account_is_empty_nonce() {
303        let mut account = Account::default();
304        assert!(account.is_empty());
305
306        account.info.nonce = 1;
307        assert!(!account.is_empty());
308
309        account.info.nonce = 0;
310        assert!(account.is_empty());
311    }
312
313    #[test]
314    fn account_is_empty_code_hash() {
315        let mut account = Account::default();
316        assert!(account.is_empty());
317
318        account.info.code_hash = [1; 32].into();
319        assert!(!account.is_empty());
320
321        account.info.code_hash = [0; 32].into();
322        assert!(account.is_empty());
323
324        account.info.code_hash = KECCAK_EMPTY;
325        assert!(account.is_empty());
326    }
327
328    #[test]
329    fn account_state() {
330        let mut account = Account::default();
331
332        assert!(!account.is_touched());
333        assert!(!account.is_selfdestructed());
334
335        account.mark_touch();
336        assert!(account.is_touched());
337        assert!(!account.is_selfdestructed());
338
339        account.mark_selfdestruct();
340        assert!(account.is_touched());
341        assert!(account.is_selfdestructed());
342
343        account.unmark_selfdestruct();
344        assert!(account.is_touched());
345        assert!(!account.is_selfdestructed());
346    }
347
348    #[test]
349    fn account_is_cold() {
350        let mut account = Account::default();
351
352        // Account is not cold by default
353        assert!(!account.status.contains(crate::AccountStatus::Cold));
354
355        // When marking warm account as warm again, it should return false
356        assert!(!account.mark_warm());
357
358        // Mark account as cold
359        account.mark_cold();
360
361        // Account is cold
362        assert!(account.status.contains(crate::AccountStatus::Cold));
363
364        // When marking cold account as warm, it should return true
365        assert!(account.mark_warm());
366    }
367
368    #[test]
369    fn test_account_with_info() {
370        let info = AccountInfo::default();
371        let account = Account::default().with_info(info.clone());
372
373        assert_eq!(account.info, info);
374        assert_eq!(account.storage, HashMap::default());
375        assert_eq!(account.status, AccountStatus::Loaded);
376    }
377
378    #[test]
379    fn test_account_with_storage() {
380        let mut storage = HashMap::new();
381        let key1 = U256::from(1);
382        let key2 = U256::from(2);
383        let slot1 = EvmStorageSlot::new(U256::from(10));
384        let slot2 = EvmStorageSlot::new(U256::from(20));
385
386        storage.insert(key1, slot1.clone());
387        storage.insert(key2, slot2.clone());
388
389        let account = Account::default().with_storage(storage.clone().into_iter());
390
391        assert_eq!(account.storage.len(), 2);
392        assert_eq!(account.storage.get(&key1), Some(&slot1));
393        assert_eq!(account.storage.get(&key2), Some(&slot2));
394    }
395
396    #[test]
397    fn test_account_with_selfdestruct_mark() {
398        let account = Account::default().with_selfdestruct_mark();
399
400        assert!(account.is_selfdestructed());
401        assert!(!account.is_touched());
402        assert!(!account.is_created());
403    }
404
405    #[test]
406    fn test_account_with_touched_mark() {
407        let account = Account::default().with_touched_mark();
408
409        assert!(!account.is_selfdestructed());
410        assert!(account.is_touched());
411        assert!(!account.is_created());
412    }
413
414    #[test]
415    fn test_account_with_created_mark() {
416        let account = Account::default().with_created_mark();
417
418        assert!(!account.is_selfdestructed());
419        assert!(!account.is_touched());
420        assert!(account.is_created());
421    }
422
423    #[test]
424    fn test_account_with_cold_mark() {
425        let account = Account::default().with_cold_mark();
426
427        assert!(account.status.contains(AccountStatus::Cold));
428    }
429
430    #[test]
431    fn test_account_with_warm_mark() {
432        // Start with a cold account
433        let cold_account = Account::default().with_cold_mark();
434        assert!(cold_account.status.contains(AccountStatus::Cold));
435
436        // Use with_warm_mark to warm it
437        let (warm_account, was_cold) = cold_account.with_warm_mark();
438
439        // Check that it's now warm and previously was cold
440        assert!(!warm_account.status.contains(AccountStatus::Cold));
441        assert!(was_cold);
442
443        // Try with an already warm account
444        let (still_warm_account, was_cold) = warm_account.with_warm_mark();
445        assert!(!still_warm_account.status.contains(AccountStatus::Cold));
446        assert!(!was_cold);
447    }
448
449    #[test]
450    fn test_account_with_warm() {
451        // Start with a cold account
452        let cold_account = Account::default().with_cold_mark();
453        assert!(cold_account.status.contains(AccountStatus::Cold));
454
455        // Use with_warm to warm it
456        let warm_account = cold_account.with_warm();
457
458        // Check that it's now warm
459        assert!(!warm_account.status.contains(AccountStatus::Cold));
460    }
461
462    #[test]
463    fn test_account_builder_chaining() {
464        let info = AccountInfo {
465            nonce: 5,
466            ..AccountInfo::default()
467        };
468
469        let slot_key = U256::from(42);
470        let slot_value = EvmStorageSlot::new(U256::from(123));
471        let mut storage = HashMap::new();
472        storage.insert(slot_key, slot_value.clone());
473
474        // Chain multiple builder methods together
475        let account = Account::default()
476            .with_info(info.clone())
477            .with_storage(storage.into_iter())
478            .with_created_mark()
479            .with_touched_mark()
480            .with_cold_mark()
481            .with_warm();
482
483        // Verify all modifications were applied
484        assert_eq!(account.info, info);
485        assert_eq!(account.storage.get(&slot_key), Some(&slot_value));
486        assert!(account.is_created());
487        assert!(account.is_touched());
488        assert!(!account.status.contains(AccountStatus::Cold));
489    }
490}