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 journal.warm_account_and_storage(
59 *item.address(),
60 item.storage_slots().map(|i| StorageKey::from_be_bytes(i.0)),
61 )?;
62 }
63 }
64 }
65
66 Ok(())
67}
68
69#[inline]
71pub fn validate_account_nonce_and_code(
72 caller_info: &mut AccountInfo,
73 tx_nonce: u64,
74 is_eip3607_disabled: bool,
75 is_nonce_check_disabled: bool,
76) -> Result<(), InvalidTransaction> {
77 if !is_eip3607_disabled {
81 let bytecode = match caller_info.code.as_ref() {
82 Some(code) => code,
83 None => &Bytecode::default(),
84 };
85 if !bytecode.is_empty() && !bytecode.is_eip7702() {
88 return Err(InvalidTransaction::RejectCallerWithCode);
89 }
90 }
91
92 if !is_nonce_check_disabled {
94 let tx = tx_nonce;
95 let state = caller_info.nonce;
96 match tx.cmp(&state) {
97 Ordering::Greater => {
98 return Err(InvalidTransaction::NonceTooHigh { tx, state });
99 }
100 Ordering::Less => {
101 return Err(InvalidTransaction::NonceTooLow { tx, state });
102 }
103 _ => {}
104 }
105 }
106 Ok(())
107}
108
109#[inline]
111pub fn validate_against_state_and_deduct_caller<
112 CTX: ContextTr,
113 ERROR: From<InvalidTransaction> + From<<CTX::Db as Database>::Error>,
114>(
115 context: &mut CTX,
116) -> Result<(), ERROR> {
117 let basefee = context.block().basefee() as u128;
118 let blob_price = context.block().blob_gasprice().unwrap_or_default();
119 let is_balance_check_disabled = context.cfg().is_balance_check_disabled();
120 let is_eip3607_disabled = context.cfg().is_eip3607_disabled();
121 let is_nonce_check_disabled = context.cfg().is_nonce_check_disabled();
122
123 let (tx, journal) = context.tx_journal_mut();
124
125 let caller_account = journal.load_account_code(tx.caller())?.data;
127
128 validate_account_nonce_and_code(
129 &mut caller_account.info,
130 tx.nonce(),
131 is_eip3607_disabled,
132 is_nonce_check_disabled,
133 )?;
134
135 let max_balance_spending = tx.max_balance_spending()?;
136
137 if max_balance_spending > caller_account.info.balance && !is_balance_check_disabled {
140 return Err(InvalidTransaction::LackOfFundForMaxFee {
141 fee: Box::new(max_balance_spending),
142 balance: Box::new(caller_account.info.balance),
143 }
144 .into());
145 }
146
147 let effective_balance_spending = tx
148 .effective_balance_spending(basefee, blob_price)
149 .expect("effective balance is always smaller than max balance so it can't overflow");
150
151 let gas_balance_spending = effective_balance_spending - tx.value();
153
154 let mut new_balance = caller_account
155 .info
156 .balance
157 .saturating_sub(gas_balance_spending);
158
159 if is_balance_check_disabled {
160 new_balance = new_balance.max(tx.value());
162 }
163
164 let old_balance = caller_account.info.balance;
165 caller_account.mark_touch();
167 caller_account.info.balance = new_balance;
168
169 if tx.kind().is_call() {
171 caller_account.info.nonce = caller_account.info.nonce.saturating_add(1);
173 }
174
175 journal.caller_accounting_journal_entry(tx.caller(), old_balance, tx.kind().is_call());
176 Ok(())
177}
178
179#[inline]
181pub fn apply_eip7702_auth_list<
182 CTX: ContextTr,
183 ERROR: From<InvalidTransaction> + From<<CTX::Db as Database>::Error>,
184>(
185 context: &mut CTX,
186) -> Result<u64, ERROR> {
187 let tx = context.tx();
188 if tx.tx_type() != TransactionType::Eip7702 {
190 return Ok(0);
191 }
192
193 let chain_id = context.cfg().chain_id();
194 let (tx, journal) = context.tx_journal_mut();
195
196 let mut refunded_accounts = 0;
197 for authorization in tx.authorization_list() {
198 let auth_chain_id = authorization.chain_id();
200 if !auth_chain_id.is_zero() && auth_chain_id != U256::from(chain_id) {
201 continue;
202 }
203
204 if authorization.nonce() == u64::MAX {
206 continue;
207 }
208
209 let Some(authority) = authorization.authority() else {
212 continue;
213 };
214
215 let mut authority_acc = journal.load_account_code(authority)?;
218
219 if let Some(bytecode) = &authority_acc.info.code {
221 if !bytecode.is_empty() && !bytecode.is_eip7702() {
223 continue;
224 }
225 }
226
227 if authorization.nonce() != authority_acc.info.nonce {
229 continue;
230 }
231
232 if !(authority_acc.is_empty() && authority_acc.is_loaded_as_not_existing_not_touched()) {
234 refunded_accounts += 1;
235 }
236
237 let address = authorization.address();
241 let (bytecode, hash) = if address.is_zero() {
242 (Bytecode::default(), KECCAK_EMPTY)
243 } else {
244 let bytecode = Bytecode::new_eip7702(address);
245 let hash = bytecode.hash_slow();
246 (bytecode, hash)
247 };
248 authority_acc.info.code_hash = hash;
249 authority_acc.info.code = Some(bytecode);
250
251 authority_acc.info.nonce = authority_acc.info.nonce.saturating_add(1);
253 authority_acc.mark_touch();
254 }
255
256 let refunded_gas =
257 refunded_accounts * (eip7702::PER_EMPTY_ACCOUNT_COST - eip7702::PER_AUTH_BASE_COST);
258
259 Ok(refunded_gas)
260}