revm_handler/
pre_execution.rs1use crate::{EvmTr, PrecompileProvider};
6use bytecode::Bytecode;
7use context::journaled_state::account::JournaledAccountTr;
8use context_interface::{
9 journaled_state::JournalTr,
10 result::InvalidTransaction,
11 transaction::{AccessListItemTr, AuthorizationTr, Transaction, TransactionType},
12 Block, Cfg, ContextTr, Database,
13};
14use core::cmp::Ordering;
15use primitives::{eip7702, hardfork::SpecId, Address, HashMap, HashSet, StorageKey, U256};
16use state::AccountInfo;
17
18pub fn load_accounts<
20 EVM: EvmTr<Precompiles: PrecompileProvider<EVM::Context>>,
21 ERROR: From<<<EVM::Context as ContextTr>::Db as Database>::Error>,
22>(
23 evm: &mut EVM,
24) -> Result<(), ERROR> {
25 let (context, precompiles) = evm.ctx_precompiles();
26
27 let gen_spec = context.cfg().spec();
28 let spec = gen_spec.clone().into();
29 context.journal_mut().set_spec_id(spec);
31 let precompiles_changed = precompiles.set_spec(gen_spec);
32 let empty_warmed_precompiles = context.journal_mut().precompile_addresses().is_empty();
33
34 if precompiles_changed || empty_warmed_precompiles {
35 context
38 .journal_mut()
39 .warm_precompiles(precompiles.warm_addresses().collect());
40 }
41
42 if spec.is_enabled_in(SpecId::SHANGHAI) {
45 let coinbase = context.block().beneficiary();
46 context.journal_mut().warm_coinbase_account(coinbase);
47 }
48
49 let (tx, journal) = context.tx_journal_mut();
51 if tx.tx_type() != TransactionType::Legacy {
53 if let Some(access_list) = tx.access_list() {
54 let mut map: HashMap<Address, HashSet<StorageKey>> = HashMap::default();
55 for item in access_list {
56 map.entry(*item.address())
57 .or_default()
58 .extend(item.storage_slots().map(|key| U256::from_be_bytes(key.0)));
59 }
60 journal.warm_access_list(map);
61 }
62 }
63
64 Ok(())
65}
66
67#[inline]
69pub fn validate_account_nonce_and_code_with_components(
70 caller_info: &AccountInfo,
71 tx: impl Transaction,
72 cfg: impl Cfg,
73) -> Result<(), InvalidTransaction> {
74 validate_account_nonce_and_code(
75 caller_info,
76 tx.nonce(),
77 cfg.is_eip3607_disabled(),
78 cfg.is_nonce_check_disabled(),
79 )
80}
81
82#[inline]
84pub fn validate_account_nonce_and_code(
85 caller_info: &AccountInfo,
86 tx_nonce: u64,
87 is_eip3607_disabled: bool,
88 is_nonce_check_disabled: bool,
89) -> Result<(), InvalidTransaction> {
90 if !is_eip3607_disabled {
94 let bytecode = match caller_info.code.as_ref() {
95 Some(code) => code,
96 None => &Bytecode::default(),
97 };
98 if !bytecode.is_empty() && !bytecode.is_eip7702() {
101 return Err(InvalidTransaction::RejectCallerWithCode);
102 }
103 }
104
105 if !is_nonce_check_disabled {
107 let tx = tx_nonce;
108 let state = caller_info.nonce;
109 match tx.cmp(&state) {
110 Ordering::Greater => {
111 return Err(InvalidTransaction::NonceTooHigh { tx, state });
112 }
113 Ordering::Less => {
114 return Err(InvalidTransaction::NonceTooLow { tx, state });
115 }
116 _ => {}
117 }
118 }
119 Ok(())
120}
121
122#[inline]
126pub fn calculate_caller_fee(
127 balance: U256,
128 tx: impl Transaction,
129 block: impl Block,
130 cfg: impl Cfg,
131) -> Result<U256, InvalidTransaction> {
132 let basefee = block.basefee() as u128;
133 let blob_price = block.blob_gasprice().unwrap_or_default();
134 let is_balance_check_disabled = cfg.is_balance_check_disabled();
135
136 if !is_balance_check_disabled {
137 tx.ensure_enough_balance(balance)?;
138 }
139
140 let effective_balance_spending = tx
141 .effective_balance_spending(basefee, blob_price)
142 .expect("effective balance is always smaller than max balance so it can't overflow");
143
144 let gas_balance_spending = effective_balance_spending - tx.value();
145
146 let mut new_balance = balance.saturating_sub(gas_balance_spending);
148
149 if is_balance_check_disabled {
150 new_balance = new_balance.max(tx.value());
152 }
153
154 Ok(new_balance)
155}
156
157#[inline]
159pub fn validate_against_state_and_deduct_caller<
160 CTX: ContextTr,
161 ERROR: From<InvalidTransaction> + From<<CTX::Db as Database>::Error>,
162>(
163 context: &mut CTX,
164) -> Result<(), ERROR> {
165 let (block, tx, cfg, journal, _, _) = context.all_mut();
166
167 let mut caller = journal.load_account_with_code_mut(tx.caller())?.data;
169
170 validate_account_nonce_and_code_with_components(&caller.account().info, tx, cfg)?;
171
172 let new_balance = calculate_caller_fee(*caller.balance(), tx, block, cfg)?;
173
174 caller.set_balance(new_balance);
175 if tx.kind().is_call() {
176 caller.bump_nonce();
177 }
178 Ok(())
179}
180
181#[inline]
188pub fn apply_eip7702_auth_list<
189 CTX: ContextTr,
190 ERROR: From<InvalidTransaction> + From<<CTX::Db as Database>::Error>,
191>(
192 context: &mut CTX,
193) -> Result<u64, ERROR> {
194 let chain_id = context.cfg().chain_id();
195 let (tx, journal) = context.tx_journal_mut();
196
197 if tx.tx_type() != TransactionType::Eip7702 {
199 return Ok(0);
200 }
201 apply_auth_list(chain_id, tx.authorization_list(), journal)
202}
203
204#[inline]
208pub fn apply_auth_list<
209 JOURNAL: JournalTr,
210 ERROR: From<InvalidTransaction> + From<<JOURNAL::Database as Database>::Error>,
211>(
212 chain_id: u64,
213 auth_list: impl Iterator<Item = impl AuthorizationTr>,
214 journal: &mut JOURNAL,
215) -> Result<u64, ERROR> {
216 let mut refunded_accounts = 0;
217 for authorization in auth_list {
218 let auth_chain_id = authorization.chain_id();
220 if !auth_chain_id.is_zero() && auth_chain_id != U256::from(chain_id) {
221 continue;
222 }
223
224 if authorization.nonce() == u64::MAX {
226 continue;
227 }
228
229 let Some(authority) = authorization.authority() else {
232 continue;
233 };
234
235 let mut authority_acc = journal.load_account_with_code_mut(authority)?;
238 let authority_acc_info = &authority_acc.account().info;
239
240 if let Some(bytecode) = &authority_acc_info.code {
242 if !bytecode.is_empty() && !bytecode.is_eip7702() {
244 continue;
245 }
246 }
247
248 if authorization.nonce() != authority_acc_info.nonce {
250 continue;
251 }
252
253 if !(authority_acc_info.is_empty()
255 && authority_acc
256 .account()
257 .is_loaded_as_not_existing_not_touched())
258 {
259 refunded_accounts += 1;
260 }
261
262 authority_acc.delegate(authorization.address());
267 }
268
269 let refunded_gas =
270 refunded_accounts * (eip7702::PER_EMPTY_ACCOUNT_COST - eip7702::PER_AUTH_BASE_COST);
271
272 Ok(refunded_gas)
273}