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