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