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