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