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