Skip to main content

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}