revm_context_interface/journaled_state/
entry.rs

1//! Contains the journal entry trait and implementations.
2//!
3//! Journal entries are used to track changes to the state and are used to revert it.
4//!
5//! They are created when there is change to the state from loading (making it warm), changes to the balance,
6//! or removal of the storage slot. Check [`JournalEntryTr`] for more details.
7
8use primitives::{Address, StorageKey, StorageValue, KECCAK_EMPTY, PRECOMPILE3, U256};
9use state::{EvmState, TransientStorage};
10
11/// Trait for tracking and reverting state changes in the EVM.
12/// Journal entry contains information about state changes that can be reverted.
13pub trait JournalEntryTr {
14    /// Creates a journal entry for when an account is accessed and marked as "warm" for gas metering
15    fn account_warmed(address: Address) -> Self;
16
17    /// Creates a journal entry for when an account is destroyed via SELFDESTRUCT
18    /// Records the target address that received the destroyed account's balance,
19    /// whether the account was already destroyed, and its balance before destruction
20    /// on revert, the balance is transferred back to the original account
21    fn account_destroyed(
22        address: Address,
23        target: Address,
24        destroyed_status: SelfdestructionRevertStatus,
25        had_balance: U256,
26    ) -> Self;
27
28    /// Creates a journal entry for when an account is "touched" - accessed in a way that may require saving it.
29    /// If account is empty and touch it will be removed from the state (EIP-161 state clear EIP)
30    fn account_touched(address: Address) -> Self;
31
32    /// Creates a journal entry for a balance transfer between accounts
33    fn balance_transfer(from: Address, to: Address, balance: U256) -> Self;
34
35    /// Creates a journal entry for when an account's balance is changed.
36    fn balance_changed(address: Address, old_balance: U256) -> Self;
37
38    /// Creates a journal entry for when an account's nonce is changed.
39    fn nonce_changed(address: Address, previous_nonce: u64) -> Self;
40
41    /// Creates a journal entry for when an account's nonce is bumped.
42    fn nonce_bumped(address: Address) -> Self;
43
44    /// Creates a journal entry for when a new account is created
45    fn account_created(address: Address, is_created_globally: bool) -> Self;
46
47    /// Creates a journal entry for when a storage slot is modified
48    /// Records the previous value for reverting
49    fn storage_changed(address: Address, key: StorageKey, had_value: StorageValue) -> Self;
50
51    /// Creates a journal entry for when a storage slot is accessed and marked as "warm" for gas metering
52    /// This is called with SLOAD opcode.
53    fn storage_warmed(address: Address, key: StorageKey) -> Self;
54
55    /// Creates a journal entry for when a transient storage slot is modified (EIP-1153)
56    /// Records the previous value for reverting
57    fn transient_storage_changed(
58        address: Address,
59        key: StorageKey,
60        had_value: StorageValue,
61    ) -> Self;
62
63    /// Creates a journal entry for when an account's code is modified
64    fn code_changed(address: Address) -> Self;
65
66    /// Reverts the state change recorded by this journal entry
67    ///
68    /// More information on what is reverted can be found in [`JournalEntry`] enum.
69    ///
70    /// If transient storage is not provided, revert on transient storage will not be performed.
71    /// This is used when we revert whole transaction and know that transient storage is empty.
72    ///
73    /// # Notes
74    ///
75    /// The spurious dragon flag is used to skip revertion 0x000..0003 precompile. This
76    /// Behaviour is special and it caused by bug in Geth and Parity that is explained in [PR#716](https://github.com/ethereum/EIPs/issues/716).
77    ///
78    /// From yellow paper:
79    /// ```text
80    /// K.1. Deletion of an Account Despite Out-of-gas. At block 2675119, in the transaction 0xcf416c536ec1a19ed1fb89e
81    /// 4ec7ffb3cf73aa413b3aa9b77d60e4fd81a4296ba, an account at address 0x03 was called and an out-of-gas occurred during
82    /// the call. Against the equation (209), this added 0x03 in the set of touched addresses, and this transaction turned σ[0x03]
83    /// into ∅.
84    /// ```
85    fn revert(
86        self,
87        state: &mut EvmState,
88        transient_storage: Option<&mut TransientStorage>,
89        is_spurious_dragon_enabled: bool,
90    );
91}
92
93/// Status of selfdestruction revert.
94///
95/// Global selfdestruction means that selfdestruct is called for first time in global scope.
96///
97/// Locally selfdesturction that selfdestruct is called for first time in one transaction scope.
98///
99/// Repeated selfdestruction means local selfdesturction was already called in one transaction scope.
100#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
101#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
102pub enum SelfdestructionRevertStatus {
103    /// Selfdestruct is called for first time in global scope.
104    GloballySelfdestroyed,
105    /// Selfdestruct is called for first time in one transaction scope.
106    LocallySelfdestroyed,
107    /// Selfdestruct is called again in one transaction scope.
108    RepeatedSelfdestruction,
109}
110
111/// Journal entries that are used to track changes to the state and are used to revert it.
112#[derive(Debug, Clone, PartialEq, Eq, Hash)]
113#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
114pub enum JournalEntry {
115    /// Used to mark account that is warm inside EVM in regard to EIP-2929 AccessList.
116    /// Action: We will add Account to state.
117    /// Revert: we will remove account from state.
118    AccountWarmed {
119        /// Address of warmed account.
120        address: Address,
121    },
122    /// Mark account to be destroyed and journal balance to be reverted
123    /// Action: Mark account and transfer the balance
124    /// Revert: Unmark the account and transfer balance back
125    AccountDestroyed {
126        /// Balance of account got transferred to target.
127        had_balance: U256,
128        /// Address of account to be destroyed.
129        address: Address,
130        /// Address of account that received the balance.
131        target: Address,
132        /// Status of selfdestruction revert.
133        destroyed_status: SelfdestructionRevertStatus,
134    },
135    /// Loading account does not mean that account will need to be added to MerkleTree (touched).
136    /// Only when account is called (to execute contract or transfer balance) only then account is made touched.
137    /// Action: Mark account touched
138    /// Revert: Unmark account touched
139    AccountTouched {
140        /// Address of account that is touched.
141        address: Address,
142    },
143    /// Balance changed
144    /// Action: Balance changed
145    /// Revert: Revert to previous balance
146    BalanceChange {
147        /// New balance of account.
148        old_balance: U256,
149        /// Address of account that had its balance changed.
150        address: Address,
151    },
152    /// Transfer balance between two accounts
153    /// Action: Transfer balance
154    /// Revert: Transfer balance back
155    BalanceTransfer {
156        /// Balance that is transferred.
157        balance: U256,
158        /// Address of account that sent the balance.
159        from: Address,
160        /// Address of account that received the balance.
161        to: Address,
162    },
163    /// Increment nonce
164    /// Action: Set nonce
165    /// Revert: Revert to previous nonce
166    NonceChange {
167        /// Address of account that had its nonce changed.
168        /// Nonce is incremented by one.
169        address: Address,
170        /// Previous nonce of account.
171        previous_nonce: u64,
172    },
173    /// Increment nonce
174    /// Action: Increment nonce by one
175    /// Revert: Decrement nonce by one
176    NonceBump {
177        /// Address of account that had its nonce changed.
178        /// Nonce is incremented by one.
179        address: Address,
180    },
181    /// Create account:
182    /// Actions: Mark account as created
183    /// Revert: Unmark account as created and reset nonce to zero.
184    AccountCreated {
185        /// Address of account that is created.
186        /// On revert, this account will be set to empty.
187        address: Address,
188        /// If account is created globally for first time.
189        is_created_globally: bool,
190    },
191    /// Entry used to track storage changes
192    /// Action: Storage change
193    /// Revert: Revert to previous value
194    StorageChanged {
195        /// Key of storage slot that is changed.
196        key: StorageKey,
197        /// Previous value of storage slot.
198        had_value: StorageValue,
199        /// Address of account that had its storage changed.
200        address: Address,
201    },
202    /// Entry used to track storage warming introduced by EIP-2929.
203    /// Action: Storage warmed
204    /// Revert: Revert to cold state
205    StorageWarmed {
206        /// Key of storage slot that is warmed.
207        key: StorageKey,
208        /// Address of account that had its storage warmed. By SLOAD or SSTORE opcode.
209        address: Address,
210    },
211    /// It is used to track an EIP-1153 transient storage change.
212    /// Action: Transient storage changed.
213    /// Revert: Revert to previous value.
214    TransientStorageChange {
215        /// Key of transient storage slot that is changed.
216        key: StorageKey,
217        /// Previous value of transient storage slot.
218        had_value: StorageValue,
219        /// Address of account that had its transient storage changed.
220        address: Address,
221    },
222    /// Code changed
223    /// Action: Account code changed
224    /// Revert: Revert to previous bytecode.
225    CodeChange {
226        /// Address of account that had its code changed.
227        address: Address,
228    },
229}
230impl JournalEntryTr for JournalEntry {
231    fn account_warmed(address: Address) -> Self {
232        JournalEntry::AccountWarmed { address }
233    }
234
235    fn account_destroyed(
236        address: Address,
237        target: Address,
238        destroyed_status: SelfdestructionRevertStatus,
239        had_balance: StorageValue,
240    ) -> Self {
241        JournalEntry::AccountDestroyed {
242            address,
243            target,
244            destroyed_status,
245            had_balance,
246        }
247    }
248
249    fn account_touched(address: Address) -> Self {
250        JournalEntry::AccountTouched { address }
251    }
252
253    fn balance_changed(address: Address, old_balance: U256) -> Self {
254        JournalEntry::BalanceChange {
255            address,
256            old_balance,
257        }
258    }
259
260    fn balance_transfer(from: Address, to: Address, balance: U256) -> Self {
261        JournalEntry::BalanceTransfer { from, to, balance }
262    }
263
264    fn account_created(address: Address, is_created_globally: bool) -> Self {
265        JournalEntry::AccountCreated {
266            address,
267            is_created_globally,
268        }
269    }
270
271    fn storage_changed(address: Address, key: StorageKey, had_value: StorageValue) -> Self {
272        JournalEntry::StorageChanged {
273            address,
274            key,
275            had_value,
276        }
277    }
278
279    fn nonce_changed(address: Address, previous_nonce: u64) -> Self {
280        JournalEntry::NonceChange {
281            address,
282            previous_nonce,
283        }
284    }
285
286    fn nonce_bumped(address: Address) -> Self {
287        JournalEntry::NonceBump { address }
288    }
289
290    fn storage_warmed(address: Address, key: StorageKey) -> Self {
291        JournalEntry::StorageWarmed { address, key }
292    }
293
294    fn transient_storage_changed(
295        address: Address,
296        key: StorageKey,
297        had_value: StorageValue,
298    ) -> Self {
299        JournalEntry::TransientStorageChange {
300            address,
301            key,
302            had_value,
303        }
304    }
305
306    fn code_changed(address: Address) -> Self {
307        JournalEntry::CodeChange { address }
308    }
309
310    fn revert(
311        self,
312        state: &mut EvmState,
313        transient_storage: Option<&mut TransientStorage>,
314        is_spurious_dragon_enabled: bool,
315    ) {
316        match self {
317            JournalEntry::AccountWarmed { address } => {
318                state.get_mut(&address).unwrap().mark_cold();
319            }
320            JournalEntry::AccountTouched { address } => {
321                if is_spurious_dragon_enabled && address == PRECOMPILE3 {
322                    return;
323                }
324                // remove touched status
325                state.get_mut(&address).unwrap().unmark_touch();
326            }
327            JournalEntry::AccountDestroyed {
328                address,
329                target,
330                destroyed_status,
331                had_balance,
332            } => {
333                let account = state.get_mut(&address).unwrap();
334                // set previous state of selfdestructed flag, as there could be multiple
335                // selfdestructs in one transaction.
336                match destroyed_status {
337                    SelfdestructionRevertStatus::GloballySelfdestroyed => {
338                        account.unmark_selfdestruct();
339                        account.unmark_selfdestructed_locally();
340                    }
341                    SelfdestructionRevertStatus::LocallySelfdestroyed => {
342                        account.unmark_selfdestructed_locally();
343                    }
344                    // do nothing on repeated selfdestruction
345                    SelfdestructionRevertStatus::RepeatedSelfdestruction => (),
346                }
347
348                account.info.balance += had_balance;
349
350                if address != target {
351                    let target = state.get_mut(&target).unwrap();
352                    target.info.balance -= had_balance;
353                }
354            }
355            JournalEntry::BalanceChange {
356                address,
357                old_balance,
358            } => {
359                let account = state.get_mut(&address).unwrap();
360                account.info.balance = old_balance;
361            }
362            JournalEntry::BalanceTransfer { from, to, balance } => {
363                // we don't need to check overflow and underflow when adding and subtracting the balance.
364                let from = state.get_mut(&from).unwrap();
365                from.info.balance += balance;
366                let to = state.get_mut(&to).unwrap();
367                to.info.balance -= balance;
368            }
369            JournalEntry::NonceChange {
370                address,
371                previous_nonce,
372            } => {
373                state.get_mut(&address).unwrap().info.nonce = previous_nonce;
374            }
375            JournalEntry::NonceBump { address } => {
376                let nonce = &mut state.get_mut(&address).unwrap().info.nonce;
377                *nonce = nonce.saturating_sub(1);
378            }
379            JournalEntry::AccountCreated {
380                address,
381                is_created_globally,
382            } => {
383                let account = &mut state.get_mut(&address).unwrap();
384                account.unmark_created_locally();
385                if is_created_globally {
386                    account.unmark_created();
387                }
388                // only account that have nonce == 0 can be created so it is safe to set it to 0.
389                account.info.nonce = 0;
390            }
391            JournalEntry::StorageWarmed { address, key } => {
392                state
393                    .get_mut(&address)
394                    .unwrap()
395                    .storage
396                    .get_mut(&key)
397                    .unwrap()
398                    .mark_cold();
399            }
400            JournalEntry::StorageChanged {
401                address,
402                key,
403                had_value,
404            } => {
405                state
406                    .get_mut(&address)
407                    .unwrap()
408                    .storage
409                    .get_mut(&key)
410                    .unwrap()
411                    .present_value = had_value;
412            }
413            JournalEntry::TransientStorageChange {
414                address,
415                key,
416                had_value,
417            } => {
418                let Some(transient_storage) = transient_storage else {
419                    return;
420                };
421                let tkey = (address, key);
422                if had_value.is_zero() {
423                    // if previous value is zero, remove it
424                    transient_storage.remove(&tkey);
425                } else {
426                    // if not zero, reinsert old value to transient storage.
427                    transient_storage.insert(tkey, had_value);
428                }
429            }
430            JournalEntry::CodeChange { address } => {
431                let acc = state.get_mut(&address).unwrap();
432                acc.info.code_hash = KECCAK_EMPTY;
433                acc.info.code = None;
434            }
435        }
436    }
437}