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::transaction::{AccessListItemTr, AuthorizationTr};
8use context_interface::ContextTr;
9use context_interface::{
10    journaled_state::JournalTr,
11    result::InvalidTransaction,
12    transaction::{Transaction, TransactionType},
13    Block, Cfg, Database,
14};
15use core::cmp::Ordering;
16use primitives::StorageKey;
17use primitives::{eip7702, hardfork::SpecId, KECCAK_EMPTY, U256};
18use state::AccountInfo;
19
20/// Loads and warms accounts for execution, including precompiles and access list.
21pub fn load_accounts<
22    EVM: EvmTr<Precompiles: PrecompileProvider<EVM::Context>>,
23    ERROR: From<<<EVM::Context as ContextTr>::Db as Database>::Error>,
24>(
25    evm: &mut EVM,
26) -> Result<(), ERROR> {
27    let (context, precompiles) = evm.ctx_precompiles();
28
29    let gen_spec = context.cfg().spec();
30    let spec = gen_spec.clone().into();
31    // sets eth spec id in journal
32    context.journal_mut().set_spec_id(spec);
33    let precompiles_changed = precompiles.set_spec(gen_spec);
34    let empty_warmed_precompiles = context.journal_mut().precompile_addresses().is_empty();
35
36    if precompiles_changed || empty_warmed_precompiles {
37        // load new precompile addresses into journal.
38        // When precompiles addresses are changed we reset the warmed hashmap to those new addresses.
39        context
40            .journal_mut()
41            .warm_precompiles(precompiles.warm_addresses().collect());
42    }
43
44    // Load coinbase
45    // EIP-3651: Warm COINBASE. Starts the `COINBASE` address warm
46    if spec.is_enabled_in(SpecId::SHANGHAI) {
47        let coinbase = context.block().beneficiary();
48        context.journal_mut().warm_coinbase_account(coinbase);
49    }
50
51    // Load access list
52    let (tx, journal) = context.tx_journal_mut();
53    // legacy is only tx type that does not have access list.
54    if tx.tx_type() != TransactionType::Legacy {
55        if let Some(access_list) = tx.access_list() {
56            for item in access_list {
57                journal.warm_account_and_storage(
58                    *item.address(),
59                    item.storage_slots().map(|i| StorageKey::from_be_bytes(i.0)),
60                )?;
61            }
62        }
63    }
64
65    Ok(())
66}
67
68/// Validates caller account nonce and code according to EIP-3607.
69#[inline]
70pub fn validate_account_nonce_and_code(
71    caller_info: &mut AccountInfo,
72    tx_nonce: u64,
73    is_eip3607_disabled: bool,
74    is_nonce_check_disabled: bool,
75) -> Result<(), InvalidTransaction> {
76    // EIP-3607: Reject transactions from senders with deployed code
77    // This EIP is introduced after london but there was no collision in past
78    // so we can leave it enabled always
79    if !is_eip3607_disabled {
80        let bytecode = match caller_info.code.as_ref() {
81            Some(code) => code,
82            None => &Bytecode::default(),
83        };
84        // Allow EOAs whose code is a valid delegation designation,
85        // i.e. 0xef0100 || address, to continue to originate transactions.
86        if !bytecode.is_empty() && !bytecode.is_eip7702() {
87            return Err(InvalidTransaction::RejectCallerWithCode);
88        }
89    }
90
91    // Check that the transaction's nonce is correct
92    if !is_nonce_check_disabled {
93        let tx = tx_nonce;
94        let state = caller_info.nonce;
95        match tx.cmp(&state) {
96            Ordering::Greater => {
97                return Err(InvalidTransaction::NonceTooHigh { tx, state });
98            }
99            Ordering::Less => {
100                return Err(InvalidTransaction::NonceTooLow { tx, state });
101            }
102            _ => {}
103        }
104    }
105    Ok(())
106}
107
108/// Check maximum possible fee and deduct the effective fee.
109///
110/// Returns new balance.
111#[inline]
112pub fn calculate_caller_fee(
113    balance: U256,
114    tx: impl Transaction,
115    block: impl Block,
116    cfg: impl Cfg,
117) -> Result<U256, InvalidTransaction> {
118    let basefee = block.basefee() as u128;
119    let blob_price = block.blob_gasprice().unwrap_or_default();
120    let is_balance_check_disabled = cfg.is_balance_check_disabled();
121
122    if !is_balance_check_disabled {
123        tx.ensure_enough_balance(balance)?;
124    }
125
126    let effective_balance_spending = tx
127        .effective_balance_spending(basefee, blob_price)
128        .expect("effective balance is always smaller than max balance so it can't overflow");
129
130    let gas_balance_spending = effective_balance_spending - tx.value();
131
132    // new balance
133    let mut new_balance = balance.saturating_sub(gas_balance_spending);
134
135    if is_balance_check_disabled {
136        // Make sure the caller's balance is at least the value of the transaction.
137        new_balance = new_balance.max(tx.value());
138    }
139
140    Ok(new_balance)
141}
142
143/// Validates caller state and deducts transaction costs from the caller's balance.
144#[inline]
145pub fn validate_against_state_and_deduct_caller<
146    CTX: ContextTr,
147    ERROR: From<InvalidTransaction> + From<<CTX::Db as Database>::Error>,
148>(
149    context: &mut CTX,
150) -> Result<(), ERROR> {
151    let (block, tx, cfg, journal, _, _) = context.all_mut();
152
153    // Load caller's account.
154    let caller_account = journal.load_account_code(tx.caller())?.data;
155
156    validate_account_nonce_and_code(
157        &mut caller_account.info,
158        tx.nonce(),
159        cfg.is_eip3607_disabled(),
160        cfg.is_nonce_check_disabled(),
161    )?;
162
163    let new_balance = calculate_caller_fee(caller_account.info.balance, tx, block, cfg)?;
164
165    let old_balance = caller_account.caller_initial_modification(new_balance, tx.kind().is_call());
166
167    journal.caller_accounting_journal_entry(tx.caller(), old_balance, tx.kind().is_call());
168    Ok(())
169}
170
171/// Apply EIP-7702 auth list and return number gas refund on already created accounts.
172#[inline]
173pub fn apply_eip7702_auth_list<
174    CTX: ContextTr,
175    ERROR: From<InvalidTransaction> + From<<CTX::Db as Database>::Error>,
176>(
177    context: &mut CTX,
178) -> Result<u64, ERROR> {
179    let tx = context.tx();
180    // Return if there is no auth list.
181    if tx.tx_type() != TransactionType::Eip7702 {
182        return Ok(0);
183    }
184
185    let chain_id = context.cfg().chain_id();
186    let (tx, journal) = context.tx_journal_mut();
187
188    let mut refunded_accounts = 0;
189    for authorization in tx.authorization_list() {
190        // 1. Verify the chain id is either 0 or the chain's current ID.
191        let auth_chain_id = authorization.chain_id();
192        if !auth_chain_id.is_zero() && auth_chain_id != U256::from(chain_id) {
193            continue;
194        }
195
196        // 2. Verify the `nonce` is less than `2**64 - 1`.
197        if authorization.nonce() == u64::MAX {
198            continue;
199        }
200
201        // recover authority and authorized addresses.
202        // 3. `authority = ecrecover(keccak(MAGIC || rlp([chain_id, address, nonce])), y_parity, r, s]`
203        let Some(authority) = authorization.authority() else {
204            continue;
205        };
206
207        // warm authority account and check nonce.
208        // 4. Add `authority` to `accessed_addresses` (as defined in [EIP-2929](./eip-2929.md).)
209        let mut authority_acc = journal.load_account_code(authority)?;
210
211        // 5. Verify the code of `authority` is either empty or already delegated.
212        if let Some(bytecode) = &authority_acc.info.code {
213            // if it is not empty and it is not eip7702
214            if !bytecode.is_empty() && !bytecode.is_eip7702() {
215                continue;
216            }
217        }
218
219        // 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`.
220        if authorization.nonce() != authority_acc.info.nonce {
221            continue;
222        }
223
224        // 7. Add `PER_EMPTY_ACCOUNT_COST - PER_AUTH_BASE_COST` gas to the global refund counter if `authority` exists in the trie.
225        if !(authority_acc.is_empty() && authority_acc.is_loaded_as_not_existing_not_touched()) {
226            refunded_accounts += 1;
227        }
228
229        // 8. Set the code of `authority` to be `0xef0100 || address`. This is a delegation designation.
230        //  * As a special case, if `address` is `0x0000000000000000000000000000000000000000` do not write the designation.
231        //    Clear the accounts code and reset the account's code hash to the empty hash `0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470`.
232        let address = authorization.address();
233        let (bytecode, hash) = if address.is_zero() {
234            (Bytecode::default(), KECCAK_EMPTY)
235        } else {
236            let bytecode = Bytecode::new_eip7702(address);
237            let hash = bytecode.hash_slow();
238            (bytecode, hash)
239        };
240        authority_acc.info.code_hash = hash;
241        authority_acc.info.code = Some(bytecode);
242
243        // 9. Increase the nonce of `authority` by one.
244        authority_acc.info.nonce = authority_acc.info.nonce.saturating_add(1);
245        authority_acc.mark_touch();
246    }
247
248    let refunded_gas =
249        refunded_accounts * (eip7702::PER_EMPTY_ACCOUNT_COST - eip7702::PER_AUTH_BASE_COST);
250
251    Ok(refunded_gas)
252}