revm_context/journal/
entry.rs

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