revm_handler/
pre_execution.rs1use 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 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 context
39 .journal()
40 .warm_precompiles(precompiles.warm_addresses().collect());
41 }
42
43 if spec.is_enabled_in(SpecId::SHANGHAI) {
46 let coinbase = context.block().beneficiary();
47 context.journal().warm_account(coinbase);
48 }
49
50 let (tx, journal) = context.tx_journal();
52 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 if !is_eip3607_disabled {
85 let bytecode = match caller_info.code.as_ref() {
86 Some(code) => code,
87 None => &Bytecode::default(),
88 };
89 if !bytecode.is_empty() && !bytecode.is_eip7702() {
92 return Err(InvalidTransaction::RejectCallerWithCode);
93 }
94 }
95
96 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 if bump_nonce {
113 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 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 if is_balance_check_disabled {
151 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 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 caller_account.mark_touch();
175 Ok(())
176}
177
178#[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 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 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 if authorization.nonce() == u64::MAX {
205 continue;
206 }
207
208 let Some(authority) = authorization.authority() else {
211 continue;
212 };
213
214 let mut authority_acc = journal.load_account_code(authority)?;
217
218 if let Some(bytecode) = &authority_acc.info.code {
220 if !bytecode.is_empty() && !bytecode.is_eip7702() {
222 continue;
223 }
224 }
225
226 if authorization.nonce() != authority_acc.info.nonce {
228 continue;
229 }
230
231 if !authority_acc.is_empty() {
233 refunded_accounts += 1;
234 }
235
236 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 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}