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}