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 inject_custom_precompiles,
15 mem::inspector::AnvilInspector,
16};
17use alloy_consensus::{
18 Receipt, ReceiptWithBloom, constants::EMPTY_WITHDRAWALS, proofs::calculate_receipt_root,
19};
20use alloy_eips::{eip7685::EMPTY_REQUESTS_HASH, eip7840::BlobParams};
21use alloy_evm::{
22 EthEvm, Evm,
23 eth::EthEvmContext,
24 precompiles::{DynPrecompile, Precompile, PrecompilesMap},
25};
26use alloy_op_evm::OpEvm;
27use alloy_primitives::{B256, Bloom, BloomInput, Log};
28use anvil_core::eth::{
29 block::{Block, BlockInfo, PartialHeader},
30 transaction::{
31 DepositReceipt, PendingTransaction, TransactionInfo, TypedReceipt, TypedTransaction,
32 },
33};
34use foundry_evm::{
35 backend::DatabaseError,
36 traces::{CallTraceDecoder, CallTraceNode},
37};
38use foundry_evm_core::{either_evm::EitherEvm, precompiles::EC_RECOVER};
39use foundry_evm_networks::NetworkConfigs;
40use op_revm::{L1BlockInfo, OpContext, precompiles::OpPrecompiles};
41use revm::{
42 Database, DatabaseRef, Inspector, Journal,
43 context::{Block as RevmBlock, BlockEnv, Cfg, CfgEnv, Evm as RevmEvm, JournalTr, LocalContext},
44 context_interface::result::{EVMError, ExecutionResult, Output},
45 database::WrapDatabaseRef,
46 handler::{EthPrecompiles, instructions::EthInstructions},
47 interpreter::InstructionResult,
48 precompile::{PrecompileSpecId, Precompiles},
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) -> TypedReceipt {
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 as u8 <= InstructionResult::SelfDestruct as u8);
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.transaction {
83 TypedTransaction::Legacy(_) => TypedReceipt::Legacy(receipt_with_bloom),
84 TypedTransaction::EIP2930(_) => TypedReceipt::EIP2930(receipt_with_bloom),
85 TypedTransaction::EIP1559(_) => TypedReceipt::EIP1559(receipt_with_bloom),
86 TypedTransaction::EIP4844(_) => TypedReceipt::EIP4844(receipt_with_bloom),
87 TypedTransaction::EIP7702(_) => TypedReceipt::EIP7702(receipt_with_bloom),
88 TypedTransaction::Deposit(_tx) => TypedReceipt::Deposit(DepositReceipt {
89 inner: receipt_with_bloom,
90 deposit_nonce: Some(0),
91 deposit_receipt_version: Some(1),
92 }),
93 }
94 }
95}
96
97#[derive(Clone, Debug)]
99pub struct ExecutedTransactions {
100 pub block: BlockInfo,
102 pub included: Vec<Arc<PoolTransaction>>,
104 pub invalid: Vec<Arc<PoolTransaction>>,
107}
108
109pub struct TransactionExecutor<'a, Db: ?Sized, V: TransactionValidator> {
111 pub db: &'a mut Db,
113 pub validator: &'a V,
115 pub pending: std::vec::IntoIter<Arc<PoolTransaction>>,
117 pub block_env: BlockEnv,
118 pub cfg_env: CfgEnv,
120 pub parent_hash: B256,
121 pub gas_used: u64,
123 pub blob_gas_used: u64,
125 pub enable_steps_tracing: bool,
126 pub networks: NetworkConfigs,
127 pub print_logs: bool,
128 pub print_traces: bool,
129 pub call_trace_decoder: Arc<CallTraceDecoder>,
131 pub precompile_factory: Option<Arc<dyn PrecompileFactory>>,
133 pub blob_params: BlobParams,
134 pub cheats: CheatsManager,
135}
136
137impl<DB: Db + ?Sized, V: TransactionValidator> TransactionExecutor<'_, DB, V> {
138 pub fn execute(mut self) -> ExecutedTransactions {
140 let mut transactions = Vec::new();
141 let mut transaction_infos = Vec::new();
142 let mut receipts = Vec::new();
143 let mut bloom = Bloom::default();
144 let mut cumulative_gas_used = 0u64;
145 let mut invalid = Vec::new();
146 let mut included = Vec::new();
147 let gas_limit = self.block_env.gas_limit;
148 let parent_hash = self.parent_hash;
149 let block_number = self.block_env.number;
150 let difficulty = self.block_env.difficulty;
151 let mix_hash = self.block_env.prevrandao;
152 let beneficiary = self.block_env.beneficiary;
153 let timestamp = self.block_env.timestamp;
154 let base_fee = if self.cfg_env.spec.is_enabled_in(SpecId::LONDON) {
155 Some(self.block_env.basefee)
156 } else {
157 None
158 };
159
160 let is_shanghai = self.cfg_env.spec >= SpecId::SHANGHAI;
161 let is_cancun = self.cfg_env.spec >= SpecId::CANCUN;
162 let is_prague = self.cfg_env.spec >= SpecId::PRAGUE;
163 let excess_blob_gas = if is_cancun { self.block_env.blob_excess_gas() } else { None };
164 let mut cumulative_blob_gas_used = if is_cancun { Some(0u64) } else { None };
165
166 for tx in self.into_iter() {
167 let tx = match tx {
168 TransactionExecutionOutcome::Executed(tx) => {
169 included.push(tx.transaction.clone());
170 tx
171 }
172 TransactionExecutionOutcome::BlockGasExhausted(tx) => {
173 trace!(target: "backend", tx_gas_limit = %tx.pending_transaction.transaction.gas_limit(), ?tx, "block gas limit exhausting, skipping transaction");
174 continue;
175 }
176 TransactionExecutionOutcome::BlobGasExhausted(tx) => {
177 trace!(target: "backend", blob_gas = %tx.pending_transaction.transaction.blob_gas().unwrap_or_default(), ?tx, "block blob gas limit exhausting, skipping transaction");
178 continue;
179 }
180 TransactionExecutionOutcome::TransactionGasExhausted(tx) => {
181 trace!(target: "backend", tx_gas_limit = %tx.pending_transaction.transaction.gas_limit(), ?tx, "transaction gas limit exhausting, skipping transaction");
182 continue;
183 }
184 TransactionExecutionOutcome::Invalid(tx, _) => {
185 trace!(target: "backend", ?tx, "skipping invalid transaction");
186 invalid.push(tx);
187 continue;
188 }
189 TransactionExecutionOutcome::DatabaseError(_, err) => {
190 trace!(target: "backend", ?err, "Failed to execute transaction due to database error");
193 continue;
194 }
195 };
196 if is_cancun {
197 let tx_blob_gas = tx
198 .transaction
199 .pending_transaction
200 .transaction
201 .transaction
202 .blob_gas()
203 .unwrap_or(0);
204 cumulative_blob_gas_used =
205 Some(cumulative_blob_gas_used.unwrap_or(0u64).saturating_add(tx_blob_gas));
206 }
207 let receipt = tx.create_receipt(&mut cumulative_gas_used);
208
209 let ExecutedTransaction { transaction, logs, out, traces, exit_reason: exit, .. } = tx;
210 build_logs_bloom(logs.clone(), &mut bloom);
211
212 let contract_address = out.as_ref().and_then(|out| {
213 if let Output::Create(_, contract_address) = out {
214 trace!(target: "backend", "New contract deployed: at {:?}", contract_address);
215 *contract_address
216 } else {
217 None
218 }
219 });
220
221 let transaction_index = transaction_infos.len() as u64;
222 let info = TransactionInfo {
223 transaction_hash: transaction.hash(),
224 transaction_index,
225 from: *transaction.pending_transaction.sender(),
226 to: transaction.pending_transaction.transaction.to(),
227 contract_address,
228 traces,
229 exit,
230 out: out.map(Output::into_data),
231 nonce: tx.nonce,
232 gas_used: tx.gas_used,
233 };
234
235 transaction_infos.push(info);
236 receipts.push(receipt);
237 transactions.push(transaction.pending_transaction.transaction.clone());
238 }
239
240 let receipts_root = calculate_receipt_root(&receipts);
241
242 let partial_header = PartialHeader {
243 parent_hash,
244 beneficiary,
245 state_root: self.db.maybe_state_root().unwrap_or_default(),
246 receipts_root,
247 logs_bloom: bloom,
248 difficulty,
249 number: block_number.saturating_to(),
250 gas_limit,
251 gas_used: cumulative_gas_used,
252 timestamp: timestamp.saturating_to(),
253 extra_data: Default::default(),
254 mix_hash: mix_hash.unwrap_or_default(),
255 nonce: Default::default(),
256 base_fee,
257 parent_beacon_block_root: is_cancun.then_some(Default::default()),
258 blob_gas_used: cumulative_blob_gas_used,
259 excess_blob_gas,
260 withdrawals_root: is_shanghai.then_some(EMPTY_WITHDRAWALS),
261 requests_hash: is_prague.then_some(EMPTY_REQUESTS_HASH),
262 };
263
264 let block = Block::new(partial_header, transactions.clone());
265 let block = BlockInfo { block, transactions: transaction_infos, receipts };
266 ExecutedTransactions { block, included, invalid }
267 }
268
269 fn env_for(&self, tx: &PendingTransaction) -> Env {
270 let mut tx_env = tx.to_revm_tx_env();
271
272 if self.networks.optimism {
273 tx_env.enveloped_tx = Some(alloy_rlp::encode(&tx.transaction.transaction).into());
274 }
275
276 Env::new(self.cfg_env.clone(), self.block_env.clone(), tx_env, self.networks)
277 }
278}
279
280#[derive(Debug)]
282pub enum TransactionExecutionOutcome {
283 Executed(ExecutedTransaction),
285 Invalid(Arc<PoolTransaction>, InvalidTransactionError),
287 BlockGasExhausted(Arc<PoolTransaction>),
289 BlobGasExhausted(Arc<PoolTransaction>),
291 TransactionGasExhausted(Arc<PoolTransaction>),
293 DatabaseError(Arc<PoolTransaction>, DatabaseError),
295}
296
297impl<DB: Db + ?Sized, V: TransactionValidator> Iterator for &mut TransactionExecutor<'_, DB, V> {
298 type Item = TransactionExecutionOutcome;
299
300 fn next(&mut self) -> Option<Self::Item> {
301 let transaction = self.pending.next()?;
302 let sender = *transaction.pending_transaction.sender();
303 let account = match self.db.basic(sender).map(|acc| acc.unwrap_or_default()) {
304 Ok(account) => account,
305 Err(err) => return Some(TransactionExecutionOutcome::DatabaseError(transaction, err)),
306 };
307 let env = self.env_for(&transaction.pending_transaction);
308
309 let max_block_gas = self.gas_used.saturating_add(env.tx.base.gas_limit);
311 if !env.evm_env.cfg_env.disable_block_gas_limit
312 && max_block_gas > env.evm_env.block_env.gas_limit
313 {
314 return Some(TransactionExecutionOutcome::BlockGasExhausted(transaction));
315 }
316
317 if env.evm_env.cfg_env.tx_gas_limit_cap.is_none()
319 && transaction.pending_transaction.transaction.gas_limit()
320 > env.evm_env.cfg_env().tx_gas_limit_cap()
321 {
322 return Some(TransactionExecutionOutcome::TransactionGasExhausted(transaction));
323 }
324
325 let max_blob_gas = self.blob_gas_used.saturating_add(
327 transaction.pending_transaction.transaction.transaction.blob_gas().unwrap_or(0),
328 );
329 if max_blob_gas > self.blob_params.max_blob_gas_per_block() {
330 return Some(TransactionExecutionOutcome::BlobGasExhausted(transaction));
331 }
332
333 if let Err(err) = self.validator.validate_pool_transaction_for(
335 &transaction.pending_transaction,
336 &account,
337 &env,
338 ) {
339 warn!(target: "backend", "Skipping invalid tx execution [{:?}] {}", transaction.hash(), err);
340 return Some(TransactionExecutionOutcome::Invalid(transaction, err));
341 }
342
343 let nonce = account.nonce;
344
345 let mut inspector = AnvilInspector::default().with_tracing();
346 if self.enable_steps_tracing {
347 inspector = inspector.with_steps_tracing();
348 }
349 if self.print_logs {
350 inspector = inspector.with_log_collector();
351 }
352 if self.print_traces {
353 inspector = inspector.with_trace_printer();
354 }
355
356 let exec_result = {
357 let mut evm = new_evm_with_inspector(&mut *self.db, &env, &mut inspector);
358 self.networks.inject_precompiles(evm.precompiles_mut());
359
360 if let Some(factory) = &self.precompile_factory {
361 inject_custom_precompiles(&mut evm, factory.precompiles());
362 }
363
364 let cheats = Arc::new(self.cheats.clone());
365 if cheats.has_recover_overrides() {
366 let cheat_ecrecover = CheatEcrecover::new(Arc::clone(&cheats));
367 evm.precompiles_mut().apply_precompile(&EC_RECOVER, move |_| {
368 Some(DynPrecompile::new_stateful(
369 cheat_ecrecover.precompile_id().clone(),
370 move |input| cheat_ecrecover.call(input),
371 ))
372 });
373 }
374
375 trace!(target: "backend", "[{:?}] executing", transaction.hash());
376 match evm.transact_commit(env.tx) {
378 Ok(exec_result) => exec_result,
379 Err(err) => {
380 warn!(target: "backend", "[{:?}] failed to execute: {:?}", transaction.hash(), err);
381 match err {
382 EVMError::Database(err) => {
383 return Some(TransactionExecutionOutcome::DatabaseError(
384 transaction,
385 err,
386 ));
387 }
388 EVMError::Transaction(err) => {
389 return Some(TransactionExecutionOutcome::Invalid(
390 transaction,
391 err.into(),
392 ));
393 }
394 e => panic!("failed to execute transaction: {e}"),
397 }
398 }
399 }
400 };
401
402 if self.print_traces {
403 inspector.print_traces(self.call_trace_decoder.clone());
404 }
405 inspector.print_logs();
406
407 let (exit_reason, gas_used, out, logs) = match exec_result {
408 ExecutionResult::Success { reason, gas_used, logs, output, .. } => {
409 (reason.into(), gas_used, Some(output), Some(logs))
410 }
411 ExecutionResult::Revert { gas_used, output } => {
412 (InstructionResult::Revert, gas_used, Some(Output::Call(output)), None)
413 }
414 ExecutionResult::Halt { reason, gas_used } => {
415 (op_haltreason_to_instruction_result(reason), gas_used, None, None)
416 }
417 };
418
419 if exit_reason == InstructionResult::OutOfGas {
420 warn!(target: "backend", "[{:?}] executed with out of gas", transaction.hash())
422 }
423
424 trace!(target: "backend", ?exit_reason, ?gas_used, "[{:?}] executed with out={:?}", transaction.hash(), out);
425
426 self.gas_used = self.gas_used.saturating_add(gas_used);
428
429 if let Some(blob_gas) = transaction.pending_transaction.transaction.transaction.blob_gas() {
431 self.blob_gas_used = self.blob_gas_used.saturating_add(blob_gas);
432 }
433
434 trace!(target: "backend::executor", "transacted [{:?}], result: {:?} gas {}", transaction.hash(), exit_reason, gas_used);
435
436 let tx = ExecutedTransaction {
437 transaction,
438 exit_reason,
439 out,
440 gas_used,
441 logs: logs.unwrap_or_default(),
442 traces: inspector.tracer.map(|t| t.into_traces().into_nodes()).unwrap_or_default(),
443 nonce,
444 };
445
446 Some(TransactionExecutionOutcome::Executed(tx))
447 }
448}
449
450fn build_logs_bloom(logs: Vec<Log>, bloom: &mut Bloom) {
452 for log in logs {
453 bloom.accrue(BloomInput::Raw(&log.address[..]));
454 for topic in log.topics() {
455 bloom.accrue(BloomInput::Raw(&topic[..]));
456 }
457 }
458}
459
460pub fn new_evm_with_inspector<DB, I>(
462 db: DB,
463 env: &Env,
464 inspector: I,
465) -> EitherEvm<DB, I, PrecompilesMap>
466where
467 DB: Database<Error = DatabaseError> + Debug,
468 I: Inspector<EthEvmContext<DB>> + Inspector<OpContext<DB>>,
469{
470 if env.networks.optimism {
471 let op_cfg = env.evm_env.cfg_env.clone().with_spec(op_revm::OpSpecId::ISTHMUS);
472 let op_context = OpContext {
473 journaled_state: {
474 let mut journal = Journal::new(db);
475 journal.set_spec_id(env.evm_env.cfg_env.spec);
477 journal
478 },
479 block: env.evm_env.block_env.clone(),
480 cfg: op_cfg.clone(),
481 tx: env.tx.clone(),
482 chain: L1BlockInfo::default(),
483 local: LocalContext::default(),
484 error: Ok(()),
485 };
486
487 let op_precompiles = OpPrecompiles::new_with_spec(op_cfg.spec).precompiles();
488 let op_evm = op_revm::OpEvm(RevmEvm::new_with_inspector(
489 op_context,
490 inspector,
491 EthInstructions::default(),
492 PrecompilesMap::from_static(op_precompiles),
493 ));
494
495 let op = OpEvm::new(op_evm, true);
496
497 EitherEvm::Op(op)
498 } else {
499 let spec = env.evm_env.cfg_env.spec;
500 let eth_context = EthEvmContext {
501 journaled_state: {
502 let mut journal = Journal::new(db);
503 journal.set_spec_id(spec);
504 journal
505 },
506 block: env.evm_env.block_env.clone(),
507 cfg: env.evm_env.cfg_env.clone(),
508 tx: env.tx.base.clone(),
509 chain: (),
510 local: LocalContext::default(),
511 error: Ok(()),
512 };
513
514 let eth_precompiles = EthPrecompiles {
515 precompiles: Precompiles::new(PrecompileSpecId::from_spec_id(spec)),
516 spec,
517 }
518 .precompiles;
519 let eth_evm = RevmEvm::new_with_inspector(
520 eth_context,
521 inspector,
522 EthInstructions::default(),
523 PrecompilesMap::from_static(eth_precompiles),
524 );
525
526 let eth = EthEvm::new(eth_evm, true);
527
528 EitherEvm::Eth(eth)
529 }
530}
531
532pub fn new_evm_with_inspector_ref<'db, DB, I>(
534 db: &'db DB,
535 env: &Env,
536 inspector: &'db mut I,
537) -> EitherEvm<WrapDatabaseRef<&'db DB>, &'db mut I, PrecompilesMap>
538where
539 DB: DatabaseRef<Error = DatabaseError> + Debug + 'db + ?Sized,
540 I: Inspector<EthEvmContext<WrapDatabaseRef<&'db DB>>>
541 + Inspector<OpContext<WrapDatabaseRef<&'db DB>>>,
542 WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
543{
544 new_evm_with_inspector(WrapDatabaseRef(db), env, inspector)
545}