1use crate::{
2 PrecompileFactory,
3 eth::{
4 backend::{
5 cheats::{CheatEcrecover, CheatsManager},
6 db::Db,
7 env::Env,
8 mem::op_haltreason_to_instruction_result,
9 validate::TransactionValidator,
10 },
11 error::InvalidTransactionError,
12 pool::transactions::PoolTransaction,
13 },
14 mem::inspector::AnvilInspector,
15};
16use alloy_consensus::{
17 Header, Receipt, ReceiptWithBloom, Transaction, constants::EMPTY_WITHDRAWALS,
18 proofs::calculate_receipt_root, transaction::Either,
19};
20use alloy_eips::{
21 eip7685::EMPTY_REQUESTS_HASH,
22 eip7702::{RecoveredAuthority, RecoveredAuthorization},
23 eip7840::BlobParams,
24};
25use alloy_evm::{
26 EthEvmFactory, Evm, EvmEnv, EvmFactory, FromRecoveredTx,
27 eth::EthEvmContext,
28 precompiles::{DynPrecompile, Precompile, PrecompilesMap},
29};
30use alloy_op_evm::OpEvmFactory;
31use alloy_primitives::{B256, Bloom, BloomInput, Log};
32use anvil_core::eth::{
33 block::{BlockInfo, create_block},
34 transaction::{PendingTransaction, TransactionInfo},
35};
36use foundry_evm::{
37 backend::DatabaseError,
38 core::{either_evm::EitherEvm, precompiles::EC_RECOVER},
39 traces::{CallTraceDecoder, CallTraceNode},
40};
41use foundry_evm_networks::NetworkConfigs;
42use foundry_primitives::{FoundryReceiptEnvelope, FoundryTxEnvelope};
43use op_revm::{OpContext, OpTransaction};
44use revm::{
45 Database, Inspector,
46 context::{Block as RevmBlock, Cfg, TxEnv},
47 context_interface::result::{EVMError, ExecutionResult, Output},
48 interpreter::InstructionResult,
49 primitives::hardfork::SpecId,
50};
51use std::{fmt::Debug, sync::Arc};
52
53#[derive(Debug)]
55pub struct ExecutedTransaction {
56 transaction: Arc<PoolTransaction>,
57 exit_reason: InstructionResult,
58 out: Option<Output>,
59 gas_used: u64,
60 logs: Vec<Log>,
61 traces: Vec<CallTraceNode>,
62 nonce: u64,
63}
64
65impl ExecutedTransaction {
68 fn create_receipt(&self, cumulative_gas_used: &mut u64) -> FoundryReceiptEnvelope {
70 let logs = self.logs.clone();
71 *cumulative_gas_used = cumulative_gas_used.saturating_add(self.gas_used);
72
73 let status_code = u8::from(self.exit_reason.is_ok());
75 let receipt_with_bloom: ReceiptWithBloom = Receipt {
76 status: (status_code == 1).into(),
77 cumulative_gas_used: *cumulative_gas_used,
78 logs,
79 }
80 .into();
81
82 match self.transaction.pending_transaction.transaction.as_ref() {
83 FoundryTxEnvelope::Legacy(_) => FoundryReceiptEnvelope::Legacy(receipt_with_bloom),
84 FoundryTxEnvelope::Eip2930(_) => FoundryReceiptEnvelope::Eip2930(receipt_with_bloom),
85 FoundryTxEnvelope::Eip1559(_) => FoundryReceiptEnvelope::Eip1559(receipt_with_bloom),
86 FoundryTxEnvelope::Eip4844(_) => FoundryReceiptEnvelope::Eip4844(receipt_with_bloom),
87 FoundryTxEnvelope::Eip7702(_) => FoundryReceiptEnvelope::Eip7702(receipt_with_bloom),
88 FoundryTxEnvelope::Deposit(_tx) => {
89 FoundryReceiptEnvelope::Deposit(op_alloy_consensus::OpDepositReceiptWithBloom {
90 receipt: op_alloy_consensus::OpDepositReceipt {
91 inner: receipt_with_bloom.receipt,
92 deposit_nonce: Some(0),
93 deposit_receipt_version: Some(1),
94 },
95 logs_bloom: receipt_with_bloom.logs_bloom,
96 })
97 }
98 FoundryTxEnvelope::Tempo(_) => todo!(),
100 }
101 }
102}
103
104#[derive(Clone, Debug)]
106pub struct ExecutedTransactions {
107 pub block: BlockInfo,
109 pub included: Vec<Arc<PoolTransaction>>,
111 pub invalid: Vec<Arc<PoolTransaction>>,
114}
115
116pub struct TransactionExecutor<'a, Db: ?Sized, V: TransactionValidator> {
118 pub db: &'a mut Db,
120 pub validator: &'a V,
122 pub pending: std::vec::IntoIter<Arc<PoolTransaction>>,
124 pub evm_env: EvmEnv,
125 pub parent_hash: B256,
126 pub gas_used: u64,
128 pub blob_gas_used: u64,
130 pub enable_steps_tracing: bool,
131 pub networks: NetworkConfigs,
132 pub print_logs: bool,
133 pub print_traces: bool,
134 pub call_trace_decoder: Arc<CallTraceDecoder>,
136 pub precompile_factory: Option<Arc<dyn PrecompileFactory>>,
138 pub blob_params: BlobParams,
139 pub cheats: CheatsManager,
140}
141
142impl<DB: Db + ?Sized, V: TransactionValidator> TransactionExecutor<'_, DB, V> {
143 pub fn execute(mut self) -> ExecutedTransactions {
145 let mut transactions = Vec::new();
146 let mut transaction_infos = Vec::new();
147 let mut receipts = Vec::new();
148 let mut bloom = Bloom::default();
149 let mut cumulative_gas_used = 0u64;
150 let mut invalid = Vec::new();
151 let mut included = Vec::new();
152 let gas_limit = self.evm_env.block_env().gas_limit;
153 let parent_hash = self.parent_hash;
154 let block_number = self.evm_env.block_env().number;
155 let difficulty = self.evm_env.block_env().difficulty;
156 let mix_hash = self.evm_env.block_env().prevrandao;
157 let beneficiary = self.evm_env.block_env().beneficiary;
158 let timestamp = self.evm_env.block_env().timestamp;
159 let base_fee = if self.evm_env.cfg_env().spec.is_enabled_in(SpecId::LONDON) {
160 Some(self.evm_env.block_env().basefee)
161 } else {
162 None
163 };
164
165 let is_shanghai = self.evm_env.cfg_env().spec >= SpecId::SHANGHAI;
166 let is_cancun = self.evm_env.cfg_env().spec >= SpecId::CANCUN;
167 let is_prague = self.evm_env.cfg_env().spec >= SpecId::PRAGUE;
168 let excess_blob_gas =
169 if is_cancun { self.evm_env.block_env().blob_excess_gas() } else { None };
170 let mut cumulative_blob_gas_used = if is_cancun { Some(0u64) } else { None };
171
172 for tx in self.into_iter() {
173 let tx = match tx {
174 TransactionExecutionOutcome::Executed(tx) => {
175 included.push(tx.transaction.clone());
176 tx
177 }
178 TransactionExecutionOutcome::BlockGasExhausted(tx) => {
179 trace!(target: "backend", tx_gas_limit = %tx.pending_transaction.transaction.gas_limit(), ?tx, "block gas limit exhausting, skipping transaction");
180 continue;
181 }
182 TransactionExecutionOutcome::BlobGasExhausted(tx) => {
183 trace!(target: "backend", blob_gas = %tx.pending_transaction.transaction.blob_gas_used().unwrap_or_default(), ?tx, "block blob gas limit exhausting, skipping transaction");
184 continue;
185 }
186 TransactionExecutionOutcome::TransactionGasExhausted(tx) => {
187 trace!(target: "backend", tx_gas_limit = %tx.pending_transaction.transaction.gas_limit(), ?tx, "transaction gas limit exhausting, skipping transaction");
188 continue;
189 }
190 TransactionExecutionOutcome::Invalid(tx, _) => {
191 trace!(target: "backend", ?tx, "skipping invalid transaction");
192 invalid.push(tx);
193 continue;
194 }
195 TransactionExecutionOutcome::DatabaseError(_, err) => {
196 trace!(target: "backend", ?err, "Failed to execute transaction due to database error");
199 continue;
200 }
201 };
202 if is_cancun {
203 let tx_blob_gas =
204 tx.transaction.pending_transaction.transaction.blob_gas_used().unwrap_or(0);
205 cumulative_blob_gas_used =
206 Some(cumulative_blob_gas_used.unwrap_or(0u64).saturating_add(tx_blob_gas));
207 }
208 let receipt = tx.create_receipt(&mut cumulative_gas_used);
209
210 let ExecutedTransaction { transaction, logs, out, traces, exit_reason: exit, .. } = tx;
211 build_logs_bloom(&logs, &mut bloom);
212
213 let sender = *transaction.pending_transaction.sender();
216 let contract_address = if transaction.pending_transaction.transaction.to().is_none() {
217 let addr = sender.create(tx.nonce);
218 trace!(target: "backend", "Contract creation tx: computed address {:?}", addr);
219 Some(addr)
220 } else {
221 None
222 };
223
224 let transaction_index = transaction_infos.len() as u64;
225 let info = TransactionInfo {
226 transaction_hash: transaction.hash(),
227 transaction_index,
228 from: *transaction.pending_transaction.sender(),
229 to: transaction.pending_transaction.transaction.to(),
230 contract_address,
231 traces,
232 exit,
233 out: out.map(Output::into_data),
234 nonce: tx.nonce,
235 gas_used: tx.gas_used,
236 };
237
238 transaction_infos.push(info);
239 receipts.push(receipt);
240 transactions.push(transaction.pending_transaction.transaction.clone());
241 }
242
243 let receipts_root = calculate_receipt_root(&receipts);
244
245 let header = Header {
246 parent_hash,
247 ommers_hash: Default::default(),
248 beneficiary,
249 state_root: self.db.maybe_state_root().unwrap_or_default(),
250 transactions_root: Default::default(), receipts_root,
252 logs_bloom: bloom,
253 difficulty,
254 number: block_number.saturating_to(),
255 gas_limit,
256 gas_used: cumulative_gas_used,
257 timestamp: timestamp.saturating_to(),
258 extra_data: Default::default(),
259 mix_hash: mix_hash.unwrap_or_default(),
260 nonce: Default::default(),
261 base_fee_per_gas: base_fee,
262 parent_beacon_block_root: is_cancun.then_some(Default::default()),
263 blob_gas_used: cumulative_blob_gas_used,
264 excess_blob_gas,
265 withdrawals_root: is_shanghai.then_some(EMPTY_WITHDRAWALS),
266 requests_hash: is_prague.then_some(EMPTY_REQUESTS_HASH),
267 };
268
269 let block = create_block(header, transactions);
270 let block = BlockInfo { block, transactions: transaction_infos, receipts };
271 ExecutedTransactions { block, included, invalid }
272 }
273
274 fn env_for(&self, tx: &PendingTransaction) -> Env {
275 let mut tx_env: OpTransaction<TxEnv> =
276 FromRecoveredTx::from_recovered_tx(tx.transaction.as_ref(), *tx.sender());
277
278 if let FoundryTxEnvelope::Eip7702(tx_7702) = tx.transaction.as_ref()
279 && self.cheats.has_recover_overrides()
280 {
281 let cheated_auths = tx_7702
283 .tx()
284 .authorization_list
285 .iter()
286 .zip(tx_env.base.authorization_list)
287 .map(|(signed_auth, either_auth)| {
288 either_auth.right_and_then(|recovered_auth| {
289 if recovered_auth.authority().is_none()
290 && let Ok(signature) = signed_auth.signature()
291 && let Some(override_addr) =
292 self.cheats.get_recover_override(&signature.as_bytes().into())
293 {
294 Either::Right(RecoveredAuthorization::new_unchecked(
295 recovered_auth.into_parts().0,
296 RecoveredAuthority::Valid(override_addr),
297 ))
298 } else {
299 Either::Right(recovered_auth)
300 }
301 })
302 })
303 .collect();
304 tx_env.base.authorization_list = cheated_auths;
305 }
306
307 if self.networks.is_optimism() {
308 tx_env.enveloped_tx = Some(alloy_rlp::encode(tx.transaction.as_ref()).into());
309 }
310
311 Env::new(self.evm_env.clone(), tx_env, self.networks)
312 }
313}
314
315#[derive(Debug)]
317pub enum TransactionExecutionOutcome {
318 Executed(ExecutedTransaction),
320 Invalid(Arc<PoolTransaction>, InvalidTransactionError),
322 BlockGasExhausted(Arc<PoolTransaction>),
324 BlobGasExhausted(Arc<PoolTransaction>),
326 TransactionGasExhausted(Arc<PoolTransaction>),
328 DatabaseError(Arc<PoolTransaction>, DatabaseError),
330}
331
332impl<DB: Db + ?Sized, V: TransactionValidator> Iterator for &mut TransactionExecutor<'_, DB, V> {
333 type Item = TransactionExecutionOutcome;
334
335 fn next(&mut self) -> Option<Self::Item> {
336 let transaction = self.pending.next()?;
337 let sender = *transaction.pending_transaction.sender();
338 let account = match self.db.basic(sender).map(|acc| acc.unwrap_or_default()) {
339 Ok(account) => account,
340 Err(err) => return Some(TransactionExecutionOutcome::DatabaseError(transaction, err)),
341 };
342 let env = self.env_for(&transaction.pending_transaction);
343
344 let max_block_gas = self.gas_used.saturating_add(env.tx.base.gas_limit);
346 if !env.evm_env.cfg_env.disable_block_gas_limit
347 && max_block_gas > env.evm_env.block_env.gas_limit
348 {
349 return Some(TransactionExecutionOutcome::BlockGasExhausted(transaction));
350 }
351
352 if env.evm_env.cfg_env.tx_gas_limit_cap.is_none()
354 && transaction.pending_transaction.transaction.gas_limit()
355 > env.evm_env.cfg_env().tx_gas_limit_cap()
356 {
357 return Some(TransactionExecutionOutcome::TransactionGasExhausted(transaction));
358 }
359
360 let max_blob_gas = self.blob_gas_used.saturating_add(
362 transaction.pending_transaction.transaction.blob_gas_used().unwrap_or(0),
363 );
364 if max_blob_gas > self.blob_params.max_blob_gas_per_block() {
365 return Some(TransactionExecutionOutcome::BlobGasExhausted(transaction));
366 }
367
368 if let Err(err) = self.validator.validate_pool_transaction_for(
370 &transaction.pending_transaction,
371 &account,
372 &env,
373 ) {
374 warn!(target: "backend", "Skipping invalid tx execution [{:?}] {}", transaction.hash(), err);
375 return Some(TransactionExecutionOutcome::Invalid(transaction, err));
376 }
377
378 let nonce = account.nonce;
379
380 let mut inspector = AnvilInspector::default().with_tracing();
381 if self.enable_steps_tracing {
382 inspector = inspector.with_steps_tracing();
383 }
384 if self.print_logs {
385 inspector = inspector.with_log_collector();
386 }
387 if self.print_traces {
388 inspector = inspector.with_trace_printer();
389 }
390
391 let exec_result = {
392 let mut evm = new_evm_with_inspector(&mut *self.db, &env, &mut inspector);
393 self.networks.inject_precompiles(evm.precompiles_mut());
394
395 if let Some(factory) = &self.precompile_factory {
396 evm.precompiles_mut().extend_precompiles(factory.precompiles());
397 }
398
399 let cheats = Arc::new(self.cheats.clone());
400 if cheats.has_recover_overrides() {
401 let cheat_ecrecover = CheatEcrecover::new(Arc::clone(&cheats));
402 evm.precompiles_mut().apply_precompile(&EC_RECOVER, move |_| {
403 Some(DynPrecompile::new_stateful(
404 cheat_ecrecover.precompile_id().clone(),
405 move |input| cheat_ecrecover.call(input),
406 ))
407 });
408 }
409
410 trace!(target: "backend", "[{:?}] executing", transaction.hash());
411 match evm.transact_commit(env.tx) {
413 Ok(exec_result) => exec_result,
414 Err(err) => {
415 warn!(target: "backend", "[{:?}] failed to execute: {:?}", transaction.hash(), err);
416 match err {
417 EVMError::Database(err) => {
418 return Some(TransactionExecutionOutcome::DatabaseError(
419 transaction,
420 err,
421 ));
422 }
423 EVMError::Transaction(err) => {
424 return Some(TransactionExecutionOutcome::Invalid(
425 transaction,
426 err.into(),
427 ));
428 }
429 e => panic!("failed to execute transaction: {e}"),
432 }
433 }
434 }
435 };
436
437 if self.print_traces {
438 inspector.print_traces(self.call_trace_decoder.clone());
439 }
440 inspector.print_logs();
441
442 let (exit_reason, gas_used, out, logs) = match exec_result {
443 ExecutionResult::Success { reason, gas_used, logs, output, .. } => {
444 (reason.into(), gas_used, Some(output), Some(logs))
445 }
446 ExecutionResult::Revert { gas_used, output } => {
447 (InstructionResult::Revert, gas_used, Some(Output::Call(output)), None)
448 }
449 ExecutionResult::Halt { reason, gas_used } => {
450 (op_haltreason_to_instruction_result(reason), gas_used, None, None)
451 }
452 };
453
454 if exit_reason == InstructionResult::OutOfGas {
455 warn!(target: "backend", "[{:?}] executed with out of gas", transaction.hash())
457 }
458
459 trace!(target: "backend", ?exit_reason, ?gas_used, "[{:?}] executed with out={:?}", transaction.hash(), out);
460
461 self.gas_used = self.gas_used.saturating_add(gas_used);
463
464 if let Some(blob_gas) = transaction.pending_transaction.transaction.blob_gas_used() {
466 self.blob_gas_used = self.blob_gas_used.saturating_add(blob_gas);
467 }
468
469 trace!(target: "backend::executor", "transacted [{:?}], result: {:?} gas {}", transaction.hash(), exit_reason, gas_used);
470
471 let tx = ExecutedTransaction {
472 transaction,
473 exit_reason,
474 out,
475 gas_used,
476 logs: logs.unwrap_or_default(),
477 traces: inspector.tracer.map(|t| t.into_traces().into_nodes()).unwrap_or_default(),
478 nonce,
479 };
480
481 Some(TransactionExecutionOutcome::Executed(tx))
482 }
483}
484
485fn build_logs_bloom(logs: &[Log], bloom: &mut Bloom) {
487 for log in logs {
488 bloom.accrue(BloomInput::Raw(&log.address[..]));
489 for topic in log.topics() {
490 bloom.accrue(BloomInput::Raw(&topic[..]));
491 }
492 }
493}
494
495pub fn new_evm_with_inspector<DB, I>(
497 db: DB,
498 env: &Env,
499 inspector: I,
500) -> EitherEvm<DB, I, PrecompilesMap>
501where
502 DB: Database<Error = DatabaseError> + Debug,
503 I: Inspector<EthEvmContext<DB>> + Inspector<OpContext<DB>>,
504{
505 if env.networks.is_optimism() {
506 let evm_env = EvmEnv::new(
507 env.evm_env
508 .cfg_env
509 .clone()
510 .with_spec_and_mainnet_gas_params(op_revm::OpSpecId::ISTHMUS),
511 env.evm_env.block_env.clone(),
512 );
513 EitherEvm::Op(OpEvmFactory::default().create_evm_with_inspector(db, evm_env, inspector))
514 } else {
515 EitherEvm::Eth(EthEvmFactory::default().create_evm_with_inspector(
516 db,
517 env.evm_env.clone(),
518 inspector,
519 ))
520 }
521}