anvil/eth/backend/
executor.rs

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/// Represents an executed transaction (transacted on the DB)
54#[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
65// == impl ExecutedTransaction ==
66
67impl ExecutedTransaction {
68    /// Creates the receipt for the transaction
69    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        // successful return see [Return]
74        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/// Represents the outcome of mining a new block
98#[derive(Clone, Debug)]
99pub struct ExecutedTransactions {
100    /// The block created after executing the `included` transactions
101    pub block: BlockInfo,
102    /// All transactions included in the block
103    pub included: Vec<Arc<PoolTransaction>>,
104    /// All transactions that were invalid at the point of their execution and were not included in
105    /// the block
106    pub invalid: Vec<Arc<PoolTransaction>>,
107}
108
109/// An executor for a series of transactions
110pub struct TransactionExecutor<'a, Db: ?Sized, V: TransactionValidator> {
111    /// where to insert the transactions
112    pub db: &'a mut Db,
113    /// type used to validate before inclusion
114    pub validator: &'a V,
115    /// all pending transactions
116    pub pending: std::vec::IntoIter<Arc<PoolTransaction>>,
117    pub block_env: BlockEnv,
118    /// The configuration environment and spec id
119    pub cfg_env: CfgEnv,
120    pub parent_hash: B256,
121    /// Cumulative gas used by all executed transactions
122    pub gas_used: u64,
123    /// Cumulative blob gas used by all executed transactions
124    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    /// Recorder used for decoding traces, used together with print_traces
130    pub call_trace_decoder: Arc<CallTraceDecoder>,
131    /// Precompiles to inject to the EVM.
132    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    /// Executes all transactions and puts them in a new block with the provided `timestamp`
139    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                    // Note: this is only possible in forking mode, if for example a rpc request
191                    // failed
192                    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/// Represents the result of a single transaction execution attempt
281#[derive(Debug)]
282pub enum TransactionExecutionOutcome {
283    /// Transaction successfully executed
284    Executed(ExecutedTransaction),
285    /// Invalid transaction not executed
286    Invalid(Arc<PoolTransaction>, InvalidTransactionError),
287    /// Execution skipped because could exceed block gas limit
288    BlockGasExhausted(Arc<PoolTransaction>),
289    /// Execution skipped because it exceeded the blob gas limit
290    BlobGasExhausted(Arc<PoolTransaction>),
291    /// Execution skipped because it exceeded the transaction gas limit
292    TransactionGasExhausted(Arc<PoolTransaction>),
293    /// When an error occurred during execution
294    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        // check that we comply with the block's gas limit, if not disabled
310        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        // check that we comply with the transaction's gas limit as imposed by Osaka (EIP-7825)
318        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        // check that we comply with the block's blob gas limit
326        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        // validate before executing
334        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            // transact and commit the transaction
377            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                        // This will correspond to prevrandao not set, and it should never happen.
395                        // If it does, it's a bug.
396                        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            // this currently useful for debugging estimations
421            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        // Track the total gas used for total gas per block checks
427        self.gas_used = self.gas_used.saturating_add(gas_used);
428
429        // Track the total blob gas used for total blob gas per blob checks
430        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
450/// Inserts all logs into the bloom
451fn 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
460/// Creates a database with given database and inspector.
461pub 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                // Converting SpecId into OpSpecId
476                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
532/// Creates a new EVM with the given inspector and wraps the database in a `WrapDatabaseRef`.
533pub 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}