revm_context_interface/journaled_state/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, B256, PRECOMPILE3, U256};
9use state::{Bytecode, 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 destroyed_status: SelfdestructionRevertStatus,
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 /// Records the previous extension so a reverted call restores it.
39 #[cfg(feature = "account-ext")]
40 fn extension_changed(address: Address, old_extension: state::AccountExtension) -> Self;
41
42 /// Creates a journal entry for when an account's nonce is changed.
43 fn nonce_changed(address: Address, previous_nonce: u64) -> Self;
44
45 /// Creates a journal entry for when an account's nonce is bumped.
46 fn nonce_bumped(address: Address) -> Self;
47
48 /// Creates a journal entry for when a new account is created
49 fn account_created(address: Address, is_created_globally: bool) -> Self;
50
51 /// Creates a journal entry for when a storage slot is modified
52 /// Records the previous value for reverting
53 fn storage_changed(address: Address, key: StorageKey, had_value: StorageValue) -> Self;
54
55 /// Creates a journal entry for when a storage slot is accessed and marked as "warm" for gas metering
56 /// This is called with SLOAD opcode.
57 fn storage_warmed(address: Address, key: StorageKey) -> Self;
58
59 /// Creates a journal entry for when a transient storage slot is modified (EIP-1153)
60 /// Records the previous value for reverting
61 fn transient_storage_changed(
62 address: Address,
63 key: StorageKey,
64 had_value: StorageValue,
65 ) -> Self;
66
67 /// Creates a journal entry for when an account's code is modified
68 ///
69 /// Records the previous code hash and bytecode for reverting: since
70 /// EIP-7702 the code of an already-delegated account can be changed (and
71 /// the change reverted), so the revert cannot assume the previous code was
72 /// empty.
73 fn code_changed(address: Address, had_code_hash: B256, had_code: Option<Bytecode>) -> Self;
74
75 /// Reverts the state change recorded by this journal entry
76 ///
77 /// More information on what is reverted can be found in [`JournalEntry`] enum.
78 ///
79 /// If transient storage is not provided, revert on transient storage will not be performed.
80 /// This is used when we revert whole transaction and know that transient storage is empty.
81 ///
82 /// # Notes
83 ///
84 /// The spurious dragon flag is used to skip revertion 0x000..0003 precompile. This
85 /// 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).
86 ///
87 /// From yellow paper:
88 /// ```text
89 /// K.1. Deletion of an Account Despite Out-of-gas. At block 2675119, in the transaction 0xcf416c536ec1a19ed1fb89e
90 /// 4ec7ffb3cf73aa413b3aa9b77d60e4fd81a4296ba, an account at address 0x03 was called and an out-of-gas occurred during
91 /// the call. Against the equation (209), this added 0x03 in the set of touched addresses, and this transaction turned σ[0x03]
92 /// into ∅.
93 /// ```
94 fn revert(
95 self,
96 state: &mut EvmState,
97 transient_storage: Option<&mut TransientStorage>,
98 is_spurious_dragon_enabled: bool,
99 );
100}
101
102/// Status of selfdestruction revert.
103///
104/// Global selfdestruction means that selfdestruct is called for first time in global scope.
105///
106/// Locally selfdesturction that selfdestruct is called for first time in one transaction scope.
107///
108/// Repeated selfdestruction means local selfdesturction was already called in one transaction scope.
109#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
110#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
111pub enum SelfdestructionRevertStatus {
112 /// Selfdestruct is called for first time in global scope.
113 GloballySelfdestroyed,
114 /// Selfdestruct is called for first time in one transaction scope.
115 LocallySelfdestroyed,
116 /// Selfdestruct is called again in one transaction scope.
117 RepeatedSelfdestruction,
118}
119
120/// Journal entries that are used to track changes to the state and are used to revert it.
121#[derive(Debug, Clone, PartialEq, Eq, Hash)]
122#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
123pub enum JournalEntry {
124 /// Restore an account's previous chain-specific payload.
125 #[cfg(feature = "account-ext")]
126 ExtensionChange {
127 /// Account whose extension changed.
128 address: Address,
129 /// Payload before the change.
130 #[cfg_attr(feature = "serde", serde(default))]
131 old_extension: state::AccountExtension,
132 },
133 /// Used to mark account that is warm inside EVM in regard to EIP-2929 AccessList.
134 /// Action: We will add Account to state.
135 /// Revert: we will remove account from state.
136 AccountWarmed {
137 /// Address of warmed account.
138 address: Address,
139 },
140 /// Mark account to be destroyed and journal balance to be reverted
141 /// Action: Mark account and transfer the balance
142 /// Revert: Unmark the account and transfer balance back
143 AccountDestroyed {
144 /// Balance of account got transferred to target.
145 had_balance: U256,
146 /// Address of account to be destroyed.
147 address: Address,
148 /// Address of account that received the balance.
149 target: Address,
150 /// Status of selfdestruction revert.
151 destroyed_status: SelfdestructionRevertStatus,
152 },
153 /// Loading account does not mean that account will need to be added to MerkleTree (touched).
154 /// Only when account is called (to execute contract or transfer balance) only then account is made touched.
155 /// Action: Mark account touched
156 /// Revert: Unmark account touched
157 AccountTouched {
158 /// Address of account that is touched.
159 address: Address,
160 },
161 /// Balance changed
162 /// Action: Balance changed
163 /// Revert: Revert to previous balance
164 BalanceChange {
165 /// New balance of account.
166 old_balance: U256,
167 /// Address of account that had its balance changed.
168 address: Address,
169 },
170 /// Transfer balance between two accounts
171 /// Action: Transfer balance
172 /// Revert: Transfer balance back
173 BalanceTransfer {
174 /// Balance that is transferred.
175 balance: U256,
176 /// Address of account that sent the balance.
177 from: Address,
178 /// Address of account that received the balance.
179 to: Address,
180 },
181 /// Increment nonce
182 /// Action: Set nonce
183 /// Revert: Revert to previous nonce
184 NonceChange {
185 /// Address of account that had its nonce changed.
186 /// Nonce is incremented by one.
187 address: Address,
188 /// Previous nonce of account.
189 previous_nonce: u64,
190 },
191 /// Increment nonce
192 /// Action: Increment nonce by one
193 /// Revert: Decrement nonce by one
194 NonceBump {
195 /// Address of account that had its nonce changed.
196 /// Nonce is incremented by one.
197 address: Address,
198 },
199 /// Create account:
200 /// Actions: Mark account as created
201 /// Revert: Unmark account as created and reset nonce to zero.
202 AccountCreated {
203 /// Address of account that is created.
204 /// On revert, this account will be set to empty.
205 address: Address,
206 /// If account is created globally for first time.
207 is_created_globally: bool,
208 },
209 /// Entry used to track storage changes
210 /// Action: Storage change
211 /// Revert: Revert to previous value
212 StorageChanged {
213 /// Key of storage slot that is changed.
214 key: StorageKey,
215 /// Previous value of storage slot.
216 had_value: StorageValue,
217 /// Address of account that had its storage changed.
218 address: Address,
219 },
220 /// Entry used to track storage warming introduced by EIP-2929.
221 /// Action: Storage warmed
222 /// Revert: Revert to cold state
223 StorageWarmed {
224 /// Key of storage slot that is warmed.
225 key: StorageKey,
226 /// Address of account that had its storage warmed. By SLOAD or SSTORE opcode.
227 address: Address,
228 },
229 /// It is used to track an EIP-1153 transient storage change.
230 /// Action: Transient storage changed.
231 /// Revert: Revert to previous value.
232 TransientStorageChange {
233 /// Key of transient storage slot that is changed.
234 key: StorageKey,
235 /// Previous value of transient storage slot.
236 had_value: StorageValue,
237 /// Address of account that had its transient storage changed.
238 address: Address,
239 },
240 /// Code changed
241 /// Action: Account code changed
242 /// Revert: Revert to previous bytecode.
243 CodeChange {
244 /// Address of account that had its code changed.
245 address: Address,
246 /// Previous code hash of the account.
247 had_code_hash: B256,
248 /// Previous bytecode of the account (`None` if it was not loaded).
249 had_code: Option<Bytecode>,
250 },
251}
252
253impl JournalEntryTr for JournalEntry {
254 #[cfg(feature = "account-ext")]
255 fn extension_changed(address: Address, old_extension: state::AccountExtension) -> Self {
256 Self::ExtensionChange {
257 address,
258 old_extension,
259 }
260 }
261 fn account_warmed(address: Address) -> Self {
262 JournalEntry::AccountWarmed { address }
263 }
264
265 fn account_destroyed(
266 address: Address,
267 target: Address,
268 destroyed_status: SelfdestructionRevertStatus,
269 had_balance: StorageValue,
270 ) -> Self {
271 JournalEntry::AccountDestroyed {
272 address,
273 target,
274 destroyed_status,
275 had_balance,
276 }
277 }
278
279 fn account_touched(address: Address) -> Self {
280 JournalEntry::AccountTouched { address }
281 }
282
283 fn balance_changed(address: Address, old_balance: U256) -> Self {
284 JournalEntry::BalanceChange {
285 address,
286 old_balance,
287 }
288 }
289
290 fn balance_transfer(from: Address, to: Address, balance: U256) -> Self {
291 JournalEntry::BalanceTransfer { from, to, balance }
292 }
293
294 fn account_created(address: Address, is_created_globally: bool) -> Self {
295 JournalEntry::AccountCreated {
296 address,
297 is_created_globally,
298 }
299 }
300
301 fn storage_changed(address: Address, key: StorageKey, had_value: StorageValue) -> Self {
302 JournalEntry::StorageChanged {
303 address,
304 key,
305 had_value,
306 }
307 }
308
309 fn nonce_changed(address: Address, previous_nonce: u64) -> Self {
310 JournalEntry::NonceChange {
311 address,
312 previous_nonce,
313 }
314 }
315
316 fn nonce_bumped(address: Address) -> Self {
317 JournalEntry::NonceBump { address }
318 }
319
320 fn storage_warmed(address: Address, key: StorageKey) -> Self {
321 JournalEntry::StorageWarmed { address, key }
322 }
323
324 fn transient_storage_changed(
325 address: Address,
326 key: StorageKey,
327 had_value: StorageValue,
328 ) -> Self {
329 JournalEntry::TransientStorageChange {
330 address,
331 key,
332 had_value,
333 }
334 }
335
336 fn code_changed(address: Address, had_code_hash: B256, had_code: Option<Bytecode>) -> Self {
337 JournalEntry::CodeChange {
338 address,
339 had_code_hash,
340 had_code,
341 }
342 }
343
344 fn revert(
345 self,
346 state: &mut EvmState,
347 transient_storage: Option<&mut TransientStorage>,
348 is_spurious_dragon_enabled: bool,
349 ) {
350 match self {
351 #[cfg(feature = "account-ext")]
352 Self::ExtensionChange {
353 address,
354 old_extension,
355 } => {
356 state.get_mut(&address).unwrap().info.extension = old_extension;
357 }
358 JournalEntry::AccountWarmed { address } => {
359 state.get_mut(&address).unwrap().mark_cold();
360 }
361 JournalEntry::AccountTouched { address } => {
362 if is_spurious_dragon_enabled && address == PRECOMPILE3 {
363 return;
364 }
365 // remove touched status
366 state.get_mut(&address).unwrap().unmark_touch();
367 }
368 JournalEntry::AccountDestroyed {
369 address,
370 target,
371 destroyed_status,
372 had_balance,
373 } => {
374 let account = state.get_mut(&address).unwrap();
375 // set previous state of selfdestructed flag, as there could be multiple
376 // selfdestructs in one transaction.
377 match destroyed_status {
378 SelfdestructionRevertStatus::GloballySelfdestroyed => {
379 account.unmark_selfdestruct();
380 account.unmark_selfdestructed_locally();
381 }
382 SelfdestructionRevertStatus::LocallySelfdestroyed => {
383 account.unmark_selfdestructed_locally();
384 }
385 // do nothing on repeated selfdestruction
386 SelfdestructionRevertStatus::RepeatedSelfdestruction => (),
387 }
388
389 account.info.balance += had_balance;
390
391 if address != target {
392 let target = state.get_mut(&target).unwrap();
393 target.info.balance -= had_balance;
394 }
395 }
396 JournalEntry::BalanceChange {
397 address,
398 old_balance,
399 } => {
400 let account = state.get_mut(&address).unwrap();
401 account.info.balance = old_balance;
402 }
403 JournalEntry::BalanceTransfer { from, to, balance } => {
404 // we don't need to check overflow and underflow when adding and subtracting the balance.
405 let from = state.get_mut(&from).unwrap();
406 from.info.balance += balance;
407 let to = state.get_mut(&to).unwrap();
408 to.info.balance -= balance;
409 }
410 JournalEntry::NonceChange {
411 address,
412 previous_nonce,
413 } => {
414 state.get_mut(&address).unwrap().info.nonce = previous_nonce;
415 }
416 JournalEntry::NonceBump { address } => {
417 let nonce = &mut state.get_mut(&address).unwrap().info.nonce;
418 *nonce = nonce.saturating_sub(1);
419 }
420 JournalEntry::AccountCreated {
421 address,
422 is_created_globally,
423 } => {
424 let account = &mut state.get_mut(&address).unwrap();
425 account.unmark_created_locally();
426 if is_created_globally {
427 account.unmark_created();
428 }
429 // only account that have nonce == 0 can be created so it is safe to set it to 0.
430 account.info.nonce = 0;
431 }
432 JournalEntry::StorageWarmed { address, key } => {
433 state
434 .get_mut(&address)
435 .unwrap()
436 .storage
437 .get_mut(&key)
438 .unwrap()
439 .mark_cold();
440 }
441 JournalEntry::StorageChanged {
442 address,
443 key,
444 had_value,
445 } => {
446 state
447 .get_mut(&address)
448 .unwrap()
449 .storage
450 .get_mut(&key)
451 .unwrap()
452 .present_value = had_value;
453 }
454 JournalEntry::TransientStorageChange {
455 address,
456 key,
457 had_value,
458 } => {
459 let Some(transient_storage) = transient_storage else {
460 return;
461 };
462 if had_value.is_zero() {
463 // if previous value is zero, remove it
464 transient_storage.remove_value(address, key);
465 } else {
466 // if not zero, reinsert old value to transient storage.
467 transient_storage.insert_value(address, key, had_value);
468 }
469 }
470 JournalEntry::CodeChange {
471 address,
472 had_code_hash,
473 had_code,
474 } => {
475 let acc = state.get_mut(&address).unwrap();
476 acc.info.code_hash = had_code_hash;
477 acc.info.code = had_code;
478 }
479 }
480 }
481}