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