revm_context/journal/
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        was_destroyed: bool,
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 incremented.
39    fn nonce_changed(address: Address) -> Self;
40
41    /// Creates a journal entry for when a new account is created
42    fn account_created(address: Address) -> Self;
43
44    /// Creates a journal entry for when a storage slot is modified
45    /// Records the previous value for reverting
46    fn storage_changed(address: Address, key: StorageKey, had_value: StorageValue) -> Self;
47
48    /// Creates a journal entry for when a storage slot is accessed and marked as "warm" for gas metering
49    /// This is called with SLOAD opcode.
50    fn storage_warmed(address: Address, key: StorageKey) -> Self;
51
52    /// Creates a journal entry for when a transient storage slot is modified (EIP-1153)
53    /// Records the previous value for reverting
54    fn transient_storage_changed(
55        address: Address,
56        key: StorageKey,
57        had_value: StorageValue,
58    ) -> Self;
59
60    /// Creates a journal entry for when an account's code is modified
61    fn code_changed(address: Address) -> Self;
62
63    /// Reverts the state change recorded by this journal entry
64    ///
65    /// More information on what is reverted can be found in [`JournalEntry`] enum.
66    ///
67    /// If transient storage is not provided, revert on transient storage will not be performed.
68    /// This is used when we revert whole transaction and know that transient storage is empty.
69    ///
70    /// # Notes
71    ///
72    /// The spurious dragon flag is used to skip revertion 0x000..0003 precompile. This
73    /// 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).
74    ///
75    /// From yellow paper:
76    /// ```text
77    /// K.1. Deletion of an Account Despite Out-of-gas. At block 2675119, in the transaction 0xcf416c536ec1a19ed1fb89e
78    /// 4ec7ffb3cf73aa413b3aa9b77d60e4fd81a4296ba, an account at address 0x03 was called and an out-of-gas occurred during
79    /// the call. Against the equation (209), this added 0x03 in the set of touched addresses, and this transaction turned σ[0x03]
80    /// into ∅.
81    /// ```
82    fn revert(
83        self,
84        state: &mut EvmState,
85        transient_storage: Option<&mut TransientStorage>,
86        is_spurious_dragon_enabled: bool,
87    );
88}
89
90/// Journal entries that are used to track changes to the state and are used to revert it.
91#[derive(Debug, Clone, PartialEq, Eq, Hash)]
92#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
93pub enum JournalEntry {
94    /// Used to mark account that is warm inside EVM in regard to EIP-2929 AccessList.
95    /// Action: We will add Account to state.
96    /// Revert: we will remove account from state.
97    AccountWarmed {
98        /// Address of warmed account.
99        address: Address,
100    },
101    /// Mark account to be destroyed and journal balance to be reverted
102    /// Action: Mark account and transfer the balance
103    /// Revert: Unmark the account and transfer balance back
104    AccountDestroyed {
105        /// Balance of account got transferred to target.
106        had_balance: U256,
107        /// Address of account to be destroyed.
108        address: Address,
109        /// Address of account that received the balance.
110        target: Address,
111        /// Whether the account had already been destroyed before this journal entry.
112        was_destroyed: bool,
113    },
114    /// Loading account does not mean that account will need to be added to MerkleTree (touched).
115    /// Only when account is called (to execute contract or transfer balance) only then account is made touched.
116    /// Action: Mark account touched
117    /// Revert: Unmark account touched
118    AccountTouched {
119        /// Address of account that is touched.
120        address: Address,
121    },
122    /// Balance changed
123    /// Action: Balance changed
124    /// Revert: Revert to previous balance
125    BalanceChange {
126        /// New balance of account.
127        old_balance: U256,
128        /// Address of account that had its balance changed.
129        address: Address,
130    },
131    /// Transfer balance between two accounts
132    /// Action: Transfer balance
133    /// Revert: Transfer balance back
134    BalanceTransfer {
135        /// Balance that is transferred.
136        balance: U256,
137        /// Address of account that sent the balance.
138        from: Address,
139        /// Address of account that received the balance.
140        to: Address,
141    },
142    /// Increment nonce
143    /// Action: Increment nonce by one
144    /// Revert: Decrement nonce by one
145    NonceChange {
146        /// Address of account that had its nonce changed.
147        /// Nonce is incremented by one.
148        address: Address,
149    },
150    /// Create account:
151    /// Actions: Mark account as created
152    /// Revert: Unmark account as created and reset nonce to zero.
153    AccountCreated {
154        /// Address of account that is created.
155        /// On revert, this account will be set to empty.
156        address: Address,
157    },
158    /// Entry used to track storage changes
159    /// Action: Storage change
160    /// Revert: Revert to previous value
161    StorageChanged {
162        /// Key of storage slot that is changed.
163        key: StorageKey,
164        /// Previous value of storage slot.
165        had_value: StorageValue,
166        /// Address of account that had its storage changed.
167        address: Address,
168    },
169    /// Entry used to track storage warming introduced by EIP-2929.
170    /// Action: Storage warmed
171    /// Revert: Revert to cold state
172    StorageWarmed {
173        /// Key of storage slot that is warmed.
174        key: StorageKey,
175        /// Address of account that had its storage warmed. By SLOAD or SSTORE opcode.
176        address: Address,
177    },
178    /// It is used to track an EIP-1153 transient storage change.
179    /// Action: Transient storage changed.
180    /// Revert: Revert to previous value.
181    TransientStorageChange {
182        /// Key of transient storage slot that is changed.
183        key: StorageKey,
184        /// Previous value of transient storage slot.
185        had_value: StorageValue,
186        /// Address of account that had its transient storage changed.
187        address: Address,
188    },
189    /// Code changed
190    /// Action: Account code changed
191    /// Revert: Revert to previous bytecode.
192    CodeChange {
193        /// Address of account that had its code changed.
194        address: Address,
195    },
196}
197impl JournalEntryTr for JournalEntry {
198    fn account_warmed(address: Address) -> Self {
199        JournalEntry::AccountWarmed { address }
200    }
201
202    fn account_destroyed(
203        address: Address,
204        target: Address,
205        was_destroyed: bool, // if account had already been destroyed before this journal entry
206        had_balance: U256,
207    ) -> Self {
208        JournalEntry::AccountDestroyed {
209            address,
210            target,
211            was_destroyed,
212            had_balance,
213        }
214    }
215
216    fn account_touched(address: Address) -> Self {
217        JournalEntry::AccountTouched { address }
218    }
219
220    fn balance_changed(address: Address, old_balance: U256) -> Self {
221        JournalEntry::BalanceChange {
222            address,
223            old_balance,
224        }
225    }
226
227    fn balance_transfer(from: Address, to: Address, balance: U256) -> Self {
228        JournalEntry::BalanceTransfer { from, to, balance }
229    }
230
231    fn account_created(address: Address) -> Self {
232        JournalEntry::AccountCreated { address }
233    }
234
235    fn storage_changed(address: Address, key: StorageKey, had_value: StorageValue) -> Self {
236        JournalEntry::StorageChanged {
237            address,
238            key,
239            had_value,
240        }
241    }
242
243    fn nonce_changed(address: Address) -> Self {
244        JournalEntry::NonceChange { address }
245    }
246
247    fn storage_warmed(address: Address, key: StorageKey) -> Self {
248        JournalEntry::StorageWarmed { address, key }
249    }
250
251    fn transient_storage_changed(
252        address: Address,
253        key: StorageKey,
254        had_value: StorageValue,
255    ) -> Self {
256        JournalEntry::TransientStorageChange {
257            address,
258            key,
259            had_value,
260        }
261    }
262
263    fn code_changed(address: Address) -> Self {
264        JournalEntry::CodeChange { address }
265    }
266
267    fn revert(
268        self,
269        state: &mut EvmState,
270        transient_storage: Option<&mut TransientStorage>,
271        is_spurious_dragon_enabled: bool,
272    ) {
273        match self {
274            JournalEntry::AccountWarmed { address } => {
275                state.get_mut(&address).unwrap().mark_cold();
276            }
277            JournalEntry::AccountTouched { address } => {
278                if is_spurious_dragon_enabled && address == PRECOMPILE3 {
279                    return;
280                }
281                // remove touched status
282                state.get_mut(&address).unwrap().unmark_touch();
283            }
284            JournalEntry::AccountDestroyed {
285                address,
286                target,
287                was_destroyed,
288                had_balance,
289            } => {
290                let account = state.get_mut(&address).unwrap();
291                // set previous state of selfdestructed flag, as there could be multiple
292                // selfdestructs in one transaction.
293                if was_destroyed {
294                    // flag is still selfdestructed
295                    account.mark_selfdestruct();
296                } else {
297                    // flag that is not selfdestructed
298                    account.unmark_selfdestruct();
299                }
300                account.info.balance += had_balance;
301
302                if address != target {
303                    let target = state.get_mut(&target).unwrap();
304                    target.info.balance -= had_balance;
305                }
306            }
307            JournalEntry::BalanceChange {
308                address,
309                old_balance,
310            } => {
311                let account = state.get_mut(&address).unwrap();
312                account.info.balance = old_balance;
313            }
314            JournalEntry::BalanceTransfer { from, to, balance } => {
315                // we don't need to check overflow and underflow when adding and subtracting the balance.
316                let from = state.get_mut(&from).unwrap();
317                from.info.balance += balance;
318                let to = state.get_mut(&to).unwrap();
319                to.info.balance -= balance;
320            }
321            JournalEntry::NonceChange { address } => {
322                state.get_mut(&address).unwrap().info.nonce -= 1;
323            }
324            JournalEntry::AccountCreated { address } => {
325                let account = &mut state.get_mut(&address).unwrap();
326                account.unmark_created();
327                account.info.nonce = 0;
328            }
329            JournalEntry::StorageWarmed { address, key } => {
330                state
331                    .get_mut(&address)
332                    .unwrap()
333                    .storage
334                    .get_mut(&key)
335                    .unwrap()
336                    .mark_cold();
337            }
338            JournalEntry::StorageChanged {
339                address,
340                key,
341                had_value,
342            } => {
343                state
344                    .get_mut(&address)
345                    .unwrap()
346                    .storage
347                    .get_mut(&key)
348                    .unwrap()
349                    .present_value = had_value;
350            }
351            JournalEntry::TransientStorageChange {
352                address,
353                key,
354                had_value,
355            } => {
356                let Some(transient_storage) = transient_storage else {
357                    return;
358                };
359                let tkey = (address, key);
360                if had_value.is_zero() {
361                    // if previous value is zero, remove it
362                    transient_storage.remove(&tkey);
363                } else {
364                    // if not zero, reinsert old value to transient storage.
365                    transient_storage.insert(tkey, had_value);
366                }
367            }
368            JournalEntry::CodeChange { address } => {
369                let acc = state.get_mut(&address).unwrap();
370                acc.info.code_hash = KECCAK_EMPTY;
371                acc.info.code = None;
372            }
373        }
374    }
375}