revm_handler/pre_execution.rs
1//! Handles related to the main function of the EVM.
2//!
3//! They handle initial setup of the EVM, call loop and the final return of the EVM
4
5use crate::{EvmTr, PrecompileProvider};
6use bytecode::Bytecode;
7use context_interface::{
8 journaled_state::{account::JournaledAccountTr, JournalCheckpoint, JournalTr},
9 result::InvalidTransaction,
10 transaction::{AccessListItemTr, AuthorizationTr, Transaction, TransactionType},
11 Block, Cfg, ContextTr, Database,
12};
13use core::cmp::Ordering;
14use interpreter::GasTracker;
15use primitives::{hardfork::SpecId, Address, AddressMap, HashSet, StorageKey, TxKind, U256};
16use state::AccountInfo;
17
18/// Loads and warms accounts for execution, including precompiles and access list.
19pub fn load_accounts<
20 EVM: EvmTr<Precompiles: PrecompileProvider<EVM::Context>>,
21 ERROR: From<<<EVM::Context as ContextTr>::Db as Database>::Error>,
22>(
23 evm: &mut EVM,
24) -> Result<(), ERROR> {
25 let (context, precompiles) = evm.ctx_precompiles();
26
27 let gen_spec = context.cfg().spec();
28 let spec = gen_spec.clone().into();
29 // sets eth spec id in journal
30 context.journal_mut().set_spec_id(spec);
31 let precompiles_changed = precompiles.set_spec(gen_spec);
32 let empty_warmed_precompiles = context.journal_mut().precompile_addresses().is_empty();
33
34 if precompiles_changed || empty_warmed_precompiles {
35 // load new precompile addresses into journal.
36 // When precompiles addresses are changed we reset the warmed hashmap to those new addresses.
37 context
38 .journal_mut()
39 .warm_precompiles(precompiles.warm_addresses());
40 }
41
42 // Load coinbase
43 // EIP-3651: Warm COINBASE. Starts the `COINBASE` address warm
44 if spec.is_enabled_in(SpecId::SHANGHAI) {
45 let coinbase = context.block().beneficiary();
46 context.journal_mut().warm_coinbase_account(coinbase);
47 }
48
49 // Load access list
50 let (tx, journal) = context.tx_journal_mut();
51 // legacy is only tx type that does not have access list.
52 if tx.tx_type() != TransactionType::Legacy {
53 if let Some(access_list) = tx.access_list() {
54 let mut map: AddressMap<HashSet<StorageKey>> = AddressMap::default();
55 for item in access_list {
56 map.entry(*item.address())
57 .or_default()
58 .extend(item.storage_slots().map(|key| U256::from_be_bytes(key.0)));
59 }
60 journal.warm_access_list(map);
61 }
62 }
63
64 Ok(())
65}
66
67/// Validates caller account nonce and code according to EIP-3607.
68#[inline]
69pub fn validate_account_nonce_and_code_with_components(
70 caller_info: &AccountInfo,
71 tx: impl Transaction,
72 cfg: impl Cfg,
73) -> Result<(), InvalidTransaction> {
74 validate_account_nonce_and_code(
75 caller_info,
76 tx.nonce(),
77 cfg.is_eip3607_disabled(),
78 cfg.is_nonce_check_disabled(),
79 )
80}
81
82/// Validates caller account nonce and code according to EIP-3607.
83#[inline]
84pub fn validate_account_nonce_and_code(
85 caller_info: &AccountInfo,
86 tx_nonce: u64,
87 is_eip3607_disabled: bool,
88 is_nonce_check_disabled: bool,
89) -> Result<(), InvalidTransaction> {
90 // EIP-3607: Reject transactions from senders with deployed code
91 // This EIP is introduced after london but there was no collision in past
92 // so we can leave it enabled always
93 if !is_eip3607_disabled {
94 let bytecode = match caller_info.code.as_ref() {
95 Some(code) => code,
96 None => &Bytecode::default(),
97 };
98 // Allow EOAs whose code is a valid delegation designation,
99 // i.e. 0xef0100 || address, to continue to originate transactions.
100 if !bytecode.is_empty() && !bytecode.is_eip7702() {
101 return Err(InvalidTransaction::RejectCallerWithCode);
102 }
103 }
104
105 // Check that the transaction's nonce is correct
106 if !is_nonce_check_disabled {
107 let tx = tx_nonce;
108 let state = caller_info.nonce;
109 if tx == u64::MAX && state == u64::MAX {
110 return Err(InvalidTransaction::NonceOverflowInTransaction);
111 }
112 match tx.cmp(&state) {
113 Ordering::Greater => {
114 return Err(InvalidTransaction::NonceTooHigh { tx, state });
115 }
116 Ordering::Less => {
117 return Err(InvalidTransaction::NonceTooLow { tx, state });
118 }
119 _ => {}
120 }
121 }
122 Ok(())
123}
124
125/// Check maximum possible fee and deduct the effective fee.
126///
127/// Returns new balance.
128#[inline]
129pub fn calculate_caller_fee(
130 balance: U256,
131 tx: impl Transaction,
132 block: impl Block,
133 cfg: impl Cfg,
134) -> Result<U256, InvalidTransaction> {
135 // If fee charge is disabled, return the balance as-is without deducting fees.
136 // This is useful for `eth_call` and similar simulation scenarios.
137 if cfg.is_fee_charge_disabled() {
138 return Ok(balance);
139 }
140
141 let basefee = block.basefee() as u128;
142 let blob_price = block.blob_gasprice().unwrap_or_default();
143 let is_balance_check_disabled = cfg.is_balance_check_disabled();
144
145 if !is_balance_check_disabled {
146 tx.ensure_enough_balance(balance)?;
147 }
148
149 let effective_balance_spending = tx
150 .effective_balance_spending(basefee, blob_price)
151 .expect("effective balance is always smaller than max balance so it can't overflow");
152
153 let gas_balance_spending = effective_balance_spending - tx.value();
154
155 // new balance
156 let mut new_balance = balance.saturating_sub(gas_balance_spending);
157
158 if is_balance_check_disabled {
159 // Make sure the caller's balance is at least the value of the transaction.
160 new_balance = new_balance.max(tx.value());
161 }
162
163 Ok(new_balance)
164}
165
166/// Validates caller state and deducts transaction costs from the caller's balance.
167#[inline]
168pub fn validate_against_state_and_deduct_caller<
169 CTX: ContextTr,
170 ERROR: From<InvalidTransaction> + From<<CTX::Db as Database>::Error>,
171>(
172 context: &mut CTX,
173) -> Result<(), ERROR> {
174 let (block, tx, cfg, journal, _, _) = context.all_mut();
175
176 // Load caller's account.
177 let mut caller = journal.load_account_with_code_mut(tx.caller())?.data;
178
179 validate_account_nonce_and_code_with_components(&caller.account().info, tx, cfg)?;
180
181 let new_balance = calculate_caller_fee(*caller.balance(), tx, block, cfg)?;
182
183 caller.set_balance(new_balance);
184 if tx.kind().is_call() {
185 caller.bump_nonce();
186 }
187 Ok(())
188}
189
190/// Gas decisions made by the pre-execution phase, carried to the execution
191/// phase.
192#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
193pub struct PreExecutionOutput {
194 /// EIP-7702 regular gas refund for authorities that already existed.
195 pub eip7702_refund: u64,
196 /// Journal checkpoint opened by [`crate::Handler::pre_execution`] before
197 /// the authorization list is applied, spanning the EIP-2780 runtime gas
198 /// phase.
199 ///
200 /// It is left open because the runtime gas phase continues at first-frame
201 /// creation (`create_init_frame` charges the recipient and create-target
202 /// costs): [`crate::Handler::execution`] commits it once the first frame
203 /// is created, or reverts it — dropping the applied delegations — when
204 /// the frame-creation charges run out of gas. Pre-Amsterdam there are no
205 /// runtime charges, so the checkpoint is always committed.
206 pub checkpoint: JournalCheckpoint,
207}
208
209/// Apply EIP-7702 auth list and return number gas refund on already created accounts.
210///
211/// Note that this function will do nothing if the transaction type is not EIP-7702.
212/// If you need to apply auth list for other transaction types, use [`apply_auth_list`] function.
213///
214/// Internally uses [`apply_auth_list`] function.
215///
216/// Under EIP-2780 the authorization charges are instead metered on the
217/// transaction-level `gas` as the authorizations are applied
218/// ([`apply_auth_list_eip2780`]) and no refund is returned (the pessimistic
219/// intrinsic charge and its refund are replaced by conditional runtime
220/// charges). Charging as the authorizations are applied makes the phase stop
221/// at the first unaffordable charge: later authorities must not be loaded
222/// (observable through the EIP-7928 block access list).
223///
224/// Returns the EIP-7702 gas refund, or `None` when the authorization charges
225/// ran out of gas: the caller owns the runtime gas phase checkpoint and must
226/// revert it, dropping the applied delegations; the transaction stays valid
227/// but must be included as an out-of-gas halt without entering execution.
228///
229/// `init_and_floor_gas` is unused by this implementation — the EIP-2780
230/// charges are recorded on the transaction-level `gas` — and is kept in the
231/// signature so chain variants that meter the authorizations against the
232/// intrinsic/floor gas can reuse this entry point.
233#[inline]
234pub fn apply_eip7702_auth_list<
235 CTX: ContextTr,
236 ERROR: From<InvalidTransaction> + From<<CTX::Db as Database>::Error>,
237>(
238 context: &mut CTX,
239 gas: &mut GasTracker,
240) -> Result<Option<u64>, ERROR> {
241 // EIP-2780: state-dependent charges (authority creation, delegation bytes,
242 // delegation-target access, recipient new-account state gas) are charged at
243 // the runtime phase instead of pessimistically at the intrinsic phase.
244 if context.cfg().is_amsterdam_eip2780_enabled() {
245 if context.tx().tx_type() != TransactionType::Eip7702 {
246 return Ok(Some(0));
247 }
248 let chain_id = context.cfg().chain_id();
249 let is_eip8037 = context.cfg().is_amsterdam_eip8037_enabled();
250 let params = context.cfg().gas_params();
251 let account_write_cost = params.tx_account_write_cost();
252 let new_account_state_gas = if is_eip8037 {
253 params.new_account_state_gas()
254 } else {
255 0
256 };
257 let delegation_bytes_state_gas = if is_eip8037 {
258 params.tx_eip7702_state_gas_bytecode()
259 } else {
260 0
261 };
262 let (tx, journal) = context.tx_journal_mut();
263
264 // Accounts this transaction has already written (their `ACCOUNT_WRITE`
265 // is already paid): the sender's leaf is written at inclusion (priced
266 // into `TX_BASE`), and the recipient's when value is transferred
267 // (priced into `TX_VALUE_COST`).
268 let mut written_accounts: HashSet<Address> = HashSet::default();
269 written_accounts.insert(tx.caller());
270 if let TxKind::Call(target) = tx.kind() {
271 if !tx.value().is_zero() {
272 written_accounts.insert(target);
273 }
274 }
275 let oog = apply_auth_list_eip2780::<_, ERROR>(
276 chain_id,
277 tx.authorization_list(),
278 journal,
279 account_write_cost,
280 new_account_state_gas,
281 delegation_bytes_state_gas,
282 &mut written_accounts,
283 gas,
284 )?;
285 return Ok(if oog { None } else { Some(0) });
286 }
287
288 let chain_id = context.cfg().chain_id();
289 let (tx, journal) = context.tx_journal_mut();
290
291 // Return if not EIP-7702 transaction.
292 if tx.tx_type() != TransactionType::Eip7702 {
293 return Ok(Some(0));
294 }
295 let number_of_refunded_accounts =
296 apply_auth_list::<_, ERROR>(chain_id, tx.authorization_list(), journal)?;
297
298 let params = context.cfg().gas_params();
299
300 let regular_gas_refund = params
301 .tx_eip7702_auth_refund_regular()
302 .saturating_mul(number_of_refunded_accounts);
303
304 Ok(Some(regular_gas_refund))
305}
306
307/// Applies an EIP-7702 auth list under EIP-2780, recording the
308/// state-dependent runtime charges on the transaction-level `gas` instead of
309/// the pessimistic intrinsic-charge/refund bookkeeping of [`apply_auth_list`].
310///
311/// Rejected authorizations charge nothing here: the intrinsic
312/// `REGULAR_PER_AUTH_BASE_COST` already covers the work every authorization
313/// performs (calldata, recovery, authority access), so there is nothing to
314/// refund either.
315///
316/// `written_accounts` holds the accounts whose leaf write is already paid for
317/// (the sender, and the recipient of a value-bearing transaction); applying an
318/// authorization to any other authority pays `ACCOUNT_WRITE` on the first
319/// write to that authority within the transaction.
320///
321/// The charges are recorded on `gas` as the authorizations are applied, so
322/// the phase stops at the first unaffordable charge without loading the
323/// remaining authorities (observable through the EIP-7928 block access list).
324///
325/// Returns whether the authorization processing ran out of gas.
326#[inline]
327#[allow(clippy::too_many_arguments)]
328pub fn apply_auth_list_eip2780<
329 JOURNAL: JournalTr,
330 ERROR: From<InvalidTransaction> + From<<JOURNAL::Database as Database>::Error>,
331>(
332 chain_id: u64,
333 auth_list: impl Iterator<Item = impl AuthorizationTr>,
334 journal: &mut JOURNAL,
335 account_write_cost: u64,
336 new_account_state_gas: u64,
337 delegation_bytes_state_gas: u64,
338 written_accounts: &mut HashSet<Address>,
339 gas: &mut GasTracker,
340) -> Result<bool, ERROR> {
341 // EIP-8037 per-authority rules: each charge is applied at most once per
342 // authority. The new-account charges self-limit (after the first
343 // application the authority exists), the delegation-bytes charge is
344 // tracked explicitly to cover a set-clear-set sequence within one
345 // transaction.
346 let mut charged_delegation_bytes: HashSet<Address> = HashSet::default();
347
348 for authorization in auth_list {
349 // 1. Verify the chain id is either 0 or the chain's current ID.
350 let auth_chain_id = authorization.chain_id();
351 if !auth_chain_id.is_zero() && auth_chain_id != U256::from(chain_id) {
352 continue;
353 }
354
355 // 2. Verify the `nonce` is less than `2**64 - 1`.
356 if authorization.nonce() == u64::MAX {
357 continue;
358 }
359
360 // recover authority and authorized addresses.
361 // 3. `authority = ecrecover(keccak(MAGIC || rlp([chain_id, address, nonce])), y_parity, r, s]`
362 let Some(authority) = authorization.authority() else {
363 continue;
364 };
365
366 // warm authority account and check nonce.
367 // 4. Add `authority` to `accessed_addresses` (as defined in [EIP-2929](./eip-2929.md).)
368 let mut authority_acc = journal.load_account_with_code_mut(authority)?;
369 let authority_acc_info = &authority_acc.account().info;
370
371 // 5. Verify the code of `authority` is either empty or already delegated.
372 if let Some(bytecode) = &authority_acc_info.code {
373 // if it is not empty and it is not eip7702
374 if !bytecode.is_empty() && !bytecode.is_eip7702() {
375 continue;
376 }
377 }
378
379 // 6. Verify the nonce of `authority` is equal to `nonce`. In case `authority` does not exist in the trie, verify that `nonce` is equal to `0`.
380 if authorization.nonce() != authority_acc_info.nonce {
381 continue;
382 }
383
384 // Refund-relevant facts for this accepted authorization (mirrors
385 // execution-specs `set_delegation` / evm2 `apply_one_auth`).
386 // existed — the authority account already existed in state.
387 // delegated_now — its current code is a delegation (non-empty code
388 // is necessarily EIP-7702 here, having passed the
389 // validity check above).
390 // delegated_before_tx — its code at the start of the transaction was a
391 // delegation (may differ from `delegated_now` when
392 // an earlier authorization in this tx cleared it).
393 // Derived from the code hash because the original
394 // info carries no bytecode when the database serves
395 // code separately from the account; a non-empty
396 // hash is necessarily a delegation here, since code
397 // only changes within a transaction through earlier
398 // accepted authorizations, which keep it
399 // empty-or-delegation.
400 // clearing — this authorization clears the delegation.
401 let existed = !(authority_acc_info.is_empty()
402 && authority_acc
403 .account()
404 .is_loaded_as_not_existing_not_touched());
405 let delegated_now = !authority_acc_info.is_code_hash_empty_or_zero();
406 let delegated_before_tx = !authority_acc
407 .account()
408 .original_info()
409 .is_code_hash_empty_or_zero();
410 let clearing = authorization.address().is_zero();
411
412 // Non-existent authority: pay for the new account leaf's state bytes.
413 if !existed && !gas.record_state_cost(new_account_state_gas) {
414 return Ok(true);
415 }
416
417 // First write to the authority's leaf within the transaction pays
418 // `ACCOUNT_WRITE`, unless that write is already paid for (the sender at
419 // inclusion, the recipient of a value-bearing transaction, or a
420 // preceding valid authorization on the same authority).
421 if !written_accounts.contains(&authority) {
422 if !gas.record_regular_cost(account_write_cost) {
423 return Ok(true);
424 }
425 written_accounts.insert(authority);
426 }
427
428 // Net-new delegation bytes: the 23-byte delegation indicator written
429 // into a previously empty slot.
430 if !clearing
431 && !delegated_now
432 && !delegated_before_tx
433 && !charged_delegation_bytes.contains(&authority)
434 {
435 if !gas.record_state_cost(delegation_bytes_state_gas) {
436 return Ok(true);
437 }
438 charged_delegation_bytes.insert(authority);
439 }
440
441 // 8. Set the code of `authority` to be `0xef0100 || address`. This is a delegation designation.
442 // * As a special case, if `address` is `0x0000000000000000000000000000000000000000` do not write the designation.
443 // Clear the accounts code and reset the account's code hash to the empty hash `0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470`.
444 // 9. Increase the nonce of `authority` by one.
445 authority_acc.delegate(authorization.address());
446 }
447
448 Ok(false)
449}
450
451/// Apply EIP-7702 style auth list and return number gas refund on already created accounts.
452///
453/// It is more granular function from [`apply_eip7702_auth_list`] function as it takes only the list, journal and chain id.
454///
455/// The refund per existing account authorization is
456/// `PER_EMPTY_ACCOUNT_COST - PER_AUTH_BASE_COST` (25000 - 12500 = 12500), see
457/// [`GasParams::tx_eip7702_auth_refund_regular`](context_interface::cfg::gas_params::GasParams::tx_eip7702_auth_refund_regular).
458///
459/// Returns the number of refunded (already existing) accounts.
460#[inline]
461pub fn apply_auth_list<
462 JOURNAL: JournalTr,
463 ERROR: From<InvalidTransaction> + From<<JOURNAL::Database as Database>::Error>,
464>(
465 chain_id: u64,
466 auth_list: impl Iterator<Item = impl AuthorizationTr>,
467 journal: &mut JOURNAL,
468) -> Result<u64, ERROR> {
469 let mut refunded_accounts = 0;
470 for authorization in auth_list {
471 // 1. Verify the chain id is either 0 or the chain's current ID.
472 let auth_chain_id = authorization.chain_id();
473 if !auth_chain_id.is_zero() && auth_chain_id != U256::from(chain_id) {
474 continue;
475 }
476
477 // 2. Verify the `nonce` is less than `2**64 - 1`.
478 if authorization.nonce() == u64::MAX {
479 continue;
480 }
481
482 // recover authority and authorized addresses.
483 // 3. `authority = ecrecover(keccak(MAGIC || rlp([chain_id, address, nonce])), y_parity, r, s]`
484 let Some(authority) = authorization.authority() else {
485 continue;
486 };
487
488 // warm authority account and check nonce.
489 // 4. Add `authority` to `accessed_addresses` (as defined in [EIP-2929](./eip-2929.md).)
490 let mut authority_acc = journal.load_account_with_code_mut(authority)?;
491 let authority_acc_info = &authority_acc.account().info;
492
493 // 5. Verify the code of `authority` is either empty or already delegated.
494 if let Some(bytecode) = &authority_acc_info.code {
495 // if it is not empty and it is not eip7702
496 if !bytecode.is_empty() && !bytecode.is_eip7702() {
497 continue;
498 }
499 }
500
501 // 6. Verify the nonce of `authority` is equal to `nonce`. In case `authority` does not exist in the trie, verify that `nonce` is equal to `0`.
502 if authorization.nonce() != authority_acc_info.nonce {
503 continue;
504 }
505
506 // 7. Add `PER_EMPTY_ACCOUNT_COST - PER_AUTH_BASE_COST` gas to the global refund counter if `authority` exists in the trie.
507 let existed = !(authority_acc_info.is_empty()
508 && authority_acc
509 .account()
510 .is_loaded_as_not_existing_not_touched());
511 if existed {
512 refunded_accounts += 1;
513 }
514
515 // 8. Set the code of `authority` to be `0xef0100 || address`. This is a delegation designation.
516 // * As a special case, if `address` is `0x0000000000000000000000000000000000000000` do not write the designation.
517 // Clear the accounts code and reset the account's code hash to the empty hash `0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470`.
518 // 9. Increase the nonce of `authority` by one.
519 authority_acc.delegate(authorization.address());
520 }
521
522 Ok(refunded_accounts)
523}
524
525#[cfg(test)]
526mod tests {
527 use super::validate_account_nonce_and_code;
528 use context_interface::result::InvalidTransaction;
529 use state::AccountInfo;
530
531 #[test]
532 fn rejects_transactions_when_sender_nonce_is_max() {
533 let caller_info = AccountInfo {
534 nonce: u64::MAX,
535 ..AccountInfo::default()
536 };
537
538 let err = validate_account_nonce_and_code(&caller_info, u64::MAX, false, false)
539 .expect_err("nonce-max sender should be rejected before execution");
540
541 assert_eq!(err, InvalidTransaction::NonceOverflowInTransaction);
542 }
543
544 #[test]
545 fn allows_matching_non_max_nonce() {
546 let caller_info = AccountInfo {
547 nonce: 7,
548 ..AccountInfo::default()
549 };
550
551 assert!(validate_account_nonce_and_code(&caller_info, 7, false, false).is_ok());
552 }
553}