revm_primitives/
state.rs

1use crate::{Address, Bytecode, HashMap, SpecId, B256, KECCAK_EMPTY, U256};
2use bitflags::bitflags;
3use core::hash::{Hash, Hasher};
4
5/// EVM State is a mapping from addresses to accounts.
6pub type EvmState = HashMap<Address, Account>;
7
8/// Structure used for EIP-1153 transient storage.
9pub type TransientStorage = HashMap<(Address, U256), U256>;
10
11/// An account's Storage is a mapping from 256-bit integer keys to [EvmStorageSlot]s.
12pub type EvmStorage = HashMap<U256, EvmStorageSlot>;
13
14#[derive(Debug, Clone, PartialEq, Eq, Default)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
16pub struct Account {
17    /// Balance, nonce, and code.
18    pub info: AccountInfo,
19    /// Storage cache
20    pub storage: EvmStorage,
21    /// Account status flags.
22    pub status: AccountStatus,
23}
24
25// The `bitflags!` macro generates `struct`s that manage a set of flags.
26bitflags! {
27    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
28    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
29    #[cfg_attr(feature = "serde", serde(transparent))]
30    pub struct AccountStatus: u8 {
31        /// When account is loaded but not touched or interacted with.
32        /// This is the default state.
33        const Loaded = 0b00000000;
34        /// When account is newly created we will not access database
35        /// to fetch storage values
36        const Created = 0b00000001;
37        /// If account is marked for self destruction.
38        const SelfDestructed = 0b00000010;
39        /// Only when account is marked as touched we will save it to database.
40        const Touched = 0b00000100;
41        /// used only for pre spurious dragon hardforks where existing and empty were two separate states.
42        /// it became same state after EIP-161: State trie clearing
43        const LoadedAsNotExisting = 0b0001000;
44        /// used to mark account as cold
45        const Cold = 0b0010000;
46    }
47}
48
49impl Default for AccountStatus {
50    fn default() -> Self {
51        Self::Loaded
52    }
53}
54
55impl Account {
56    /// Create new account and mark it as non existing.
57    pub fn new_not_existing() -> Self {
58        Self {
59            info: AccountInfo::default(),
60            storage: HashMap::default(),
61            status: AccountStatus::LoadedAsNotExisting,
62        }
63    }
64
65    /// Check if account is empty and check if empty state before spurious dragon hardfork.
66    #[inline]
67    pub fn state_clear_aware_is_empty(&self, spec: SpecId) -> bool {
68        if SpecId::enabled(spec, SpecId::SPURIOUS_DRAGON) {
69            self.is_empty()
70        } else {
71            let loaded_not_existing = self.is_loaded_as_not_existing();
72            let is_not_touched = !self.is_touched();
73            loaded_not_existing && is_not_touched
74        }
75    }
76
77    /// Mark account as self destructed.
78    pub fn mark_selfdestruct(&mut self) {
79        self.status |= AccountStatus::SelfDestructed;
80    }
81
82    /// Unmark account as self destructed.
83    pub fn unmark_selfdestruct(&mut self) {
84        self.status -= AccountStatus::SelfDestructed;
85    }
86
87    /// Is account marked for self destruct.
88    pub fn is_selfdestructed(&self) -> bool {
89        self.status.contains(AccountStatus::SelfDestructed)
90    }
91
92    /// Mark account as touched
93    pub fn mark_touch(&mut self) {
94        self.status |= AccountStatus::Touched;
95    }
96
97    /// Unmark the touch flag.
98    pub fn unmark_touch(&mut self) {
99        self.status -= AccountStatus::Touched;
100    }
101
102    /// If account status is marked as touched.
103    pub fn is_touched(&self) -> bool {
104        self.status.contains(AccountStatus::Touched)
105    }
106
107    /// Mark account as newly created.
108    pub fn mark_created(&mut self) {
109        self.status |= AccountStatus::Created;
110    }
111
112    /// Unmark created flag.
113    pub fn unmark_created(&mut self) {
114        self.status -= AccountStatus::Created;
115    }
116
117    /// Mark account as cold.
118    pub fn mark_cold(&mut self) {
119        self.status |= AccountStatus::Cold;
120    }
121
122    /// Mark account as warm and return true if it was previously cold.
123    pub fn mark_warm(&mut self) -> bool {
124        if self.status.contains(AccountStatus::Cold) {
125            self.status -= AccountStatus::Cold;
126            true
127        } else {
128            false
129        }
130    }
131
132    /// Is account loaded as not existing from database
133    /// This is needed for pre spurious dragon hardforks where
134    /// existing and empty were two separate states.
135    pub fn is_loaded_as_not_existing(&self) -> bool {
136        self.status.contains(AccountStatus::LoadedAsNotExisting)
137    }
138
139    /// Is account newly created in this transaction.
140    pub fn is_created(&self) -> bool {
141        self.status.contains(AccountStatus::Created)
142    }
143
144    /// Is account empty, check if nonce and balance are zero and code is empty.
145    pub fn is_empty(&self) -> bool {
146        self.info.is_empty()
147    }
148
149    /// Returns an iterator over the storage slots that have been changed.
150    ///
151    /// See also [EvmStorageSlot::is_changed]
152    pub fn changed_storage_slots(&self) -> impl Iterator<Item = (&U256, &EvmStorageSlot)> {
153        self.storage.iter().filter(|(_, slot)| slot.is_changed())
154    }
155}
156
157impl From<AccountInfo> for Account {
158    fn from(info: AccountInfo) -> Self {
159        Self {
160            info,
161            storage: HashMap::default(),
162            status: AccountStatus::Loaded,
163        }
164    }
165}
166
167/// This type keeps track of the current value of a storage slot.
168#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
169#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
170pub struct EvmStorageSlot {
171    /// Original value of the storage slot.
172    pub original_value: U256,
173    /// Present value of the storage slot.
174    pub present_value: U256,
175    /// Represents if the storage slot is cold.
176    pub is_cold: bool,
177}
178
179impl EvmStorageSlot {
180    /// Creates a new _unchanged_ `EvmStorageSlot` for the given value.
181    pub fn new(original: U256) -> Self {
182        Self {
183            original_value: original,
184            present_value: original,
185            is_cold: false,
186        }
187    }
188
189    /// Creates a new _changed_ `EvmStorageSlot`.
190    pub fn new_changed(original_value: U256, present_value: U256) -> Self {
191        Self {
192            original_value,
193            present_value,
194            is_cold: false,
195        }
196    }
197    /// Returns true if the present value differs from the original value
198    pub fn is_changed(&self) -> bool {
199        self.original_value != self.present_value
200    }
201
202    /// Returns the original value of the storage slot.
203    pub fn original_value(&self) -> U256 {
204        self.original_value
205    }
206
207    /// Returns the current value of the storage slot.
208    pub fn present_value(&self) -> U256 {
209        self.present_value
210    }
211
212    /// Marks the storage slot as cold.
213    pub fn mark_cold(&mut self) {
214        self.is_cold = true;
215    }
216
217    /// Marks the storage slot as warm and returns a bool indicating if it was previously cold.
218    pub fn mark_warm(&mut self) -> bool {
219        core::mem::replace(&mut self.is_cold, false)
220    }
221}
222
223/// AccountInfo account information.
224#[derive(Clone, Debug, Eq)]
225#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
226pub struct AccountInfo {
227    /// Account balance.
228    pub balance: U256,
229    /// Account nonce.
230    pub nonce: u64,
231    /// code hash,
232    pub code_hash: B256,
233    /// code: if None, `code_by_hash` will be used to fetch it if code needs to be loaded from
234    /// inside `revm`.
235    pub code: Option<Bytecode>,
236}
237
238impl Default for AccountInfo {
239    fn default() -> Self {
240        Self {
241            balance: U256::ZERO,
242            code_hash: KECCAK_EMPTY,
243            code: Some(Bytecode::default()),
244            nonce: 0,
245        }
246    }
247}
248
249impl PartialEq for AccountInfo {
250    fn eq(&self, other: &Self) -> bool {
251        self.balance == other.balance
252            && self.nonce == other.nonce
253            && self.code_hash == other.code_hash
254    }
255}
256
257impl Hash for AccountInfo {
258    fn hash<H: Hasher>(&self, state: &mut H) {
259        self.balance.hash(state);
260        self.nonce.hash(state);
261        self.code_hash.hash(state);
262    }
263}
264
265impl AccountInfo {
266    pub fn new(balance: U256, nonce: u64, code_hash: B256, code: Bytecode) -> Self {
267        Self {
268            balance,
269            nonce,
270            code: Some(code),
271            code_hash,
272        }
273    }
274
275    /// Returns a copy of this account with the [`Bytecode`] removed. This is
276    /// useful when creating journals or snapshots of the state, where it is
277    /// desirable to store the code blobs elsewhere.
278    ///
279    /// ## Note
280    ///
281    /// This is distinct from [`AccountInfo::without_code`] in that it returns
282    /// a new `AccountInfo` instance with the code removed.
283    /// [`AccountInfo::without_code`] will modify and return the same instance.
284    pub fn copy_without_code(&self) -> Self {
285        Self {
286            balance: self.balance,
287            nonce: self.nonce,
288            code_hash: self.code_hash,
289            code: None,
290        }
291    }
292
293    /// Strip the [`Bytecode`] from this account and drop it. This is
294    /// useful when creating journals or snapshots of the state, where it is
295    /// desirable to store the code blobs elsewhere.
296    ///
297    /// ## Note
298    ///
299    /// This is distinct from [`AccountInfo::copy_without_code`] in that it
300    /// modifies the account in place. [`AccountInfo::copy_without_code`]
301    /// will copy the non-code fields and return a new `AccountInfo` instance.
302    pub fn without_code(mut self) -> Self {
303        self.take_bytecode();
304        self
305    }
306
307    /// Returns if an account is empty.
308    ///
309    /// An account is empty if the following conditions are met.
310    /// - code hash is zero or set to the Keccak256 hash of the empty string `""`
311    /// - balance is zero
312    /// - nonce is zero
313    pub fn is_empty(&self) -> bool {
314        let code_empty = self.is_empty_code_hash() || self.code_hash.is_zero();
315        code_empty && self.balance.is_zero() && self.nonce == 0
316    }
317
318    /// Returns `true` if the account is not empty.
319    pub fn exists(&self) -> bool {
320        !self.is_empty()
321    }
322
323    /// Returns `true` if account has no nonce and code.
324    pub fn has_no_code_and_nonce(&self) -> bool {
325        self.is_empty_code_hash() && self.nonce == 0
326    }
327
328    /// Return bytecode hash associated with this account.
329    /// If account does not have code, it returns `KECCAK_EMPTY` hash.
330    pub fn code_hash(&self) -> B256 {
331        self.code_hash
332    }
333
334    /// Returns true if the code hash is the Keccak256 hash of the empty string `""`.
335    #[inline]
336    pub fn is_empty_code_hash(&self) -> bool {
337        self.code_hash == KECCAK_EMPTY
338    }
339
340    /// Take bytecode from account. Code will be set to None.
341    pub fn take_bytecode(&mut self) -> Option<Bytecode> {
342        self.code.take()
343    }
344
345    pub fn from_balance(balance: U256) -> Self {
346        AccountInfo {
347            balance,
348            ..Default::default()
349        }
350    }
351
352    pub fn from_bytecode(bytecode: Bytecode) -> Self {
353        let hash = bytecode.hash_slow();
354
355        AccountInfo {
356            balance: U256::ZERO,
357            nonce: 1,
358            code: Some(bytecode),
359            code_hash: hash,
360        }
361    }
362}
363
364#[cfg(test)]
365mod tests {
366    use crate::{Account, KECCAK_EMPTY, U256};
367
368    #[test]
369    fn account_is_empty_balance() {
370        let mut account = Account::default();
371        assert!(account.is_empty());
372
373        account.info.balance = U256::from(1);
374        assert!(!account.is_empty());
375
376        account.info.balance = U256::ZERO;
377        assert!(account.is_empty());
378    }
379
380    #[test]
381    fn account_is_empty_nonce() {
382        let mut account = Account::default();
383        assert!(account.is_empty());
384
385        account.info.nonce = 1;
386        assert!(!account.is_empty());
387
388        account.info.nonce = 0;
389        assert!(account.is_empty());
390    }
391
392    #[test]
393    fn account_is_empty_code_hash() {
394        let mut account = Account::default();
395        assert!(account.is_empty());
396
397        account.info.code_hash = [1; 32].into();
398        assert!(!account.is_empty());
399
400        account.info.code_hash = [0; 32].into();
401        assert!(account.is_empty());
402
403        account.info.code_hash = KECCAK_EMPTY;
404        assert!(account.is_empty());
405    }
406
407    #[test]
408    fn account_state() {
409        let mut account = Account::default();
410
411        assert!(!account.is_touched());
412        assert!(!account.is_selfdestructed());
413
414        account.mark_touch();
415        assert!(account.is_touched());
416        assert!(!account.is_selfdestructed());
417
418        account.mark_selfdestruct();
419        assert!(account.is_touched());
420        assert!(account.is_selfdestructed());
421
422        account.unmark_selfdestruct();
423        assert!(account.is_touched());
424        assert!(!account.is_selfdestructed());
425    }
426
427    #[test]
428    fn account_is_cold() {
429        let mut account = Account::default();
430
431        // Account is not cold by default
432        assert!(!account.status.contains(crate::AccountStatus::Cold));
433
434        // When marking warm account as warm again, it should return false
435        assert!(!account.mark_warm());
436
437        // Mark account as cold
438        account.mark_cold();
439
440        // Account is cold
441        assert!(account.status.contains(crate::AccountStatus::Cold));
442
443        // When marking cold account as warm, it should return true
444        assert!(account.mark_warm());
445    }
446}