Skip to main content

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