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::StorageKey;
17use primitives::{eip7702, hardfork::SpecId, KECCAK_EMPTY, U256};
18use state::AccountInfo;
19use std::boxed::Box;
20
21pub 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 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 context
41 .journal_mut()
42 .warm_precompiles(precompiles.warm_addresses().collect());
43 }
44
45 if spec.is_enabled_in(SpecId::SHANGHAI) {
48 let coinbase = context.block().beneficiary();
49 context.journal_mut().warm_coinbase_account(coinbase);
50 }
51
52 let (tx, journal) = context.tx_journal_mut();
54 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#[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 if !is_eip3607_disabled {
87 let bytecode = match caller_info.code.as_ref() {
88 Some(code) => code,
89 None => &Bytecode::default(),
90 };
91 if !bytecode.is_empty() && !bytecode.is_eip7702() {
94 return Err(InvalidTransaction::RejectCallerWithCode);
95 }
96 }
97
98 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#[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 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 if tx.kind().is_call() {
143 caller_account.info.nonce = caller_account.info.nonce.saturating_add(1);
145 }
146
147 let max_balance_spending = tx.max_balance_spending()?;
148
149 let mut new_balance = caller_account.info.balance;
150
151 if is_balance_check_disabled {
154 new_balance = caller_account.info.balance.max(tx.value());
156 } else if max_balance_spending > caller_account.info.balance {
157 return Err(InvalidTransaction::LackOfFundForMaxFee {
158 fee: Box::new(max_balance_spending),
159 balance: Box::new(caller_account.info.balance),
160 }
161 .into());
162 } else {
163 let effective_balance_spending = tx
164 .effective_balance_spending(basefee, blob_price)
165 .expect("effective balance is always smaller than max balance so it can't overflow");
166
167 let gas_balance_spending = effective_balance_spending - tx.value();
169
170 new_balance = new_balance.saturating_sub(gas_balance_spending);
171 }
172
173 let old_balance = caller_account.info.balance;
174 caller_account.mark_touch();
176 caller_account.info.balance = new_balance;
177
178 journal.caller_accounting_journal_entry(tx.caller(), old_balance, tx.kind().is_call());
179 Ok(())
180}
181
182#[inline]
184pub fn apply_eip7702_auth_list<
185 CTX: ContextTr,
186 ERROR: From<InvalidTransaction> + From<<CTX::Db as Database>::Error>,
187>(
188 context: &mut CTX,
189) -> Result<u64, ERROR> {
190 let tx = context.tx();
191 if tx.tx_type() != TransactionType::Eip7702 {
193 return Ok(0);
194 }
195
196 let chain_id = context.cfg().chain_id();
197 let (tx, journal) = context.tx_journal_mut();
198
199 let mut refunded_accounts = 0;
200 for authorization in tx.authorization_list() {
201 let auth_chain_id = authorization.chain_id();
203 if !auth_chain_id.is_zero() && auth_chain_id != U256::from(chain_id) {
204 continue;
205 }
206
207 if authorization.nonce() == u64::MAX {
209 continue;
210 }
211
212 let Some(authority) = authorization.authority() else {
215 continue;
216 };
217
218 let mut authority_acc = journal.load_account_code(authority)?;
221
222 if let Some(bytecode) = &authority_acc.info.code {
224 if !bytecode.is_empty() && !bytecode.is_eip7702() {
226 continue;
227 }
228 }
229
230 if authorization.nonce() != authority_acc.info.nonce {
232 continue;
233 }
234
235 if !(authority_acc.is_empty() && authority_acc.is_loaded_as_not_existing_not_touched()) {
237 refunded_accounts += 1;
238 }
239
240 let address = authorization.address();
244 let (bytecode, hash) = if address.is_zero() {
245 (Bytecode::default(), KECCAK_EMPTY)
246 } else {
247 let bytecode = Bytecode::new_eip7702(address);
248 let hash = bytecode.hash_slow();
249 (bytecode, hash)
250 };
251 authority_acc.info.code_hash = hash;
252 authority_acc.info.code = Some(bytecode);
253
254 authority_acc.info.nonce = authority_acc.info.nonce.saturating_add(1);
256 authority_acc.mark_touch();
257 }
258
259 let refunded_gas =
260 refunded_accounts * (eip7702::PER_EMPTY_ACCOUNT_COST - eip7702::PER_AUTH_BASE_COST);
261
262 Ok(refunded_gas)
263}