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