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