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