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