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