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