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 bytecode::Bytecode;
6use context_interface::transaction::{AccessListTr, AuthorizationTr};
7use context_interface::ContextTr;
8use context_interface::{
9    journaled_state::Journal,
10    result::InvalidTransaction,
11    transaction::{Transaction, TransactionType},
12    Block, Cfg, Database,
13};
14use primitives::{BLOCKHASH_STORAGE_ADDRESS, KECCAK_EMPTY, U256};
15use specification::{eip7702, hardfork::SpecId};
16
17pub fn load_accounts<CTX: ContextTr, ERROR: From<<CTX::Db as Database>::Error>>(
18    context: &mut CTX,
19) -> Result<(), ERROR> {
20    let spec = context.cfg().spec().into();
21    // Set journaling state flag.
22    context.journal().set_spec_id(spec);
23
24    // Load coinbase
25    // EIP-3651: Warm COINBASE. Starts the `COINBASE` address warm
26    if spec.is_enabled_in(SpecId::SHANGHAI) {
27        let coinbase = context.block().beneficiary();
28        context.journal().warm_account(coinbase);
29    }
30
31    // Load blockhash storage address
32    // EIP-2935: Serve historical block hashes from state
33    if spec.is_enabled_in(SpecId::PRAGUE) {
34        context.journal().warm_account(BLOCKHASH_STORAGE_ADDRESS);
35    }
36
37    // Load access list
38    let (tx, journal) = context.tx_journal();
39    if let Some(access_list) = tx.access_list() {
40        for (address, storage) in access_list.access_list() {
41            journal.warm_account_and_storage(address, storage.map(|i| U256::from_be_bytes(i.0)))?;
42        }
43    }
44
45    Ok(())
46}
47
48#[inline]
49pub fn deduct_caller<CTX: ContextTr>(
50    context: &mut CTX,
51) -> Result<(), <CTX::Db as Database>::Error> {
52    let basefee = context.block().basefee();
53    let blob_price = context.block().blob_gasprice().unwrap_or_default();
54    let effective_gas_price = context.tx().effective_gas_price(basefee as u128);
55    // Subtract gas costs from the caller's account.
56    // We need to saturate the gas cost to prevent underflow in case that `disable_balance_check` is enabled.
57    let mut gas_cost = (context.tx().gas_limit() as u128).saturating_mul(effective_gas_price);
58
59    // EIP-4844
60    if context.tx().tx_type() == TransactionType::Eip4844 {
61        let blob_gas = context.tx().total_blob_gas() as u128;
62        gas_cost = gas_cost.saturating_add(blob_price.saturating_mul(blob_gas));
63    }
64
65    let is_call = context.tx().kind().is_call();
66    let caller = context.tx().caller();
67
68    // Load caller's account.
69    let caller_account = context.journal().load_account(caller)?.data;
70    // Set new caller account balance.
71    caller_account.info.balance = caller_account
72        .info
73        .balance
74        .saturating_sub(U256::from(gas_cost));
75
76    // Bump the nonce for calls. Nonce for CREATE will be bumped in `handle_create`.
77    if is_call {
78        // Nonce is already checked
79        caller_account.info.nonce = caller_account.info.nonce.saturating_add(1);
80    }
81
82    // Touch account so we know it is changed.
83    caller_account.mark_touch();
84    Ok(())
85}
86
87/// Apply EIP-7702 auth list and return number gas refund on already created accounts.
88#[inline]
89pub fn apply_eip7702_auth_list<
90    CTX: ContextTr,
91    ERROR: From<InvalidTransaction> + From<<CTX::Db as Database>::Error>,
92>(
93    context: &mut CTX,
94) -> Result<u64, ERROR> {
95    let spec = context.cfg().spec().into();
96    let tx = context.tx();
97    if !spec.is_enabled_in(SpecId::PRAGUE) {
98        return Ok(0);
99    }
100    // Return if there is no auth list.
101    if tx.tx_type() != TransactionType::Eip7702 {
102        return Ok(0);
103    }
104
105    let chain_id = context.cfg().chain_id();
106    let (tx, journal) = context.tx_journal();
107
108    let mut refunded_accounts = 0;
109    for authorization in tx.authorization_list() {
110        // 1. Verify the chain id is either 0 or the chain's current ID.
111        let auth_chain_id = authorization.chain_id();
112        if !auth_chain_id.is_zero() && auth_chain_id != U256::from(chain_id) {
113            continue;
114        }
115
116        // 2. Verify the `nonce` is less than `2**64 - 1`.
117        if authorization.nonce() == u64::MAX {
118            continue;
119        }
120
121        // recover authority and authorized addresses.
122        // 3. `authority = ecrecover(keccak(MAGIC || rlp([chain_id, address, nonce])), y_parity, r, s]`
123        let Some(authority) = authorization.authority() else {
124            continue;
125        };
126
127        // warm authority account and check nonce.
128        // 4. Add `authority` to `accessed_addresses` (as defined in [EIP-2929](./eip-2929.md).)
129        let mut authority_acc = journal.load_account_code(authority)?;
130
131        // 5. Verify the code of `authority` is either empty or already delegated.
132        if let Some(bytecode) = &authority_acc.info.code {
133            // if it is not empty and it is not eip7702
134            if !bytecode.is_empty() && !bytecode.is_eip7702() {
135                continue;
136            }
137        }
138
139        // 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`.
140        if authorization.nonce() != authority_acc.info.nonce {
141            continue;
142        }
143
144        // 7. Add `PER_EMPTY_ACCOUNT_COST - PER_AUTH_BASE_COST` gas to the global refund counter if `authority` exists in the trie.
145        if !authority_acc.is_empty() {
146            refunded_accounts += 1;
147        }
148
149        // 8. Set the code of `authority` to be `0xef0100 || address`. This is a delegation designation.
150        //  * As a special case, if `address` is `0x0000000000000000000000000000000000000000` do not write the designation.
151        //    Clear the accounts code and reset the account's code hash to the empty hash `0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470`.
152        let address = authorization.address();
153        let (bytecode, hash) = if address.is_zero() {
154            (Bytecode::default(), KECCAK_EMPTY)
155        } else {
156            let bytecode = Bytecode::new_eip7702(address);
157            let hash = bytecode.hash_slow();
158            (bytecode, hash)
159        };
160        authority_acc.info.code_hash = hash;
161        authority_acc.info.code = Some(bytecode);
162
163        // 9. Increase the nonce of `authority` by one.
164        authority_acc.info.nonce = authority_acc.info.nonce.saturating_add(1);
165        authority_acc.mark_touch();
166    }
167
168    let refunded_gas =
169        refunded_accounts * (eip7702::PER_EMPTY_ACCOUNT_COST - eip7702::PER_AUTH_BASE_COST);
170
171    Ok(refunded_gas)
172}