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    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    EthEvm, Evm, FromRecoveredTx,
27    eth::EthEvmContext,
28    precompiles::{DynPrecompile, Precompile, PrecompilesMap},
29};
30use alloy_op_evm::OpEvm;
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::{L1BlockInfo, OpContext, OpTransaction, precompiles::OpPrecompiles};
44use revm::{
45    Database, DatabaseRef, Inspector, Journal,
46    context::{
47        Block as RevmBlock, BlockEnv, Cfg, CfgEnv, Evm as RevmEvm, JournalTr, LocalContext, TxEnv,
48    },
49    context_interface::result::{EVMError, ExecutionResult, Output},
50    database::WrapDatabaseRef,
51    handler::{EthPrecompiles, instructions::EthInstructions},
52    interpreter::InstructionResult,
53    precompile::{PrecompileSpecId, Precompiles},
54    primitives::hardfork::SpecId,
55};
56use std::{fmt::Debug, sync::Arc};
57
58/// Represents an executed transaction (transacted on the DB)
59#[derive(Debug)]
60pub struct ExecutedTransaction {
61    transaction: Arc<PoolTransaction>,
62    exit_reason: InstructionResult,
63    out: Option<Output>,
64    gas_used: u64,
65    logs: Vec<Log>,
66    traces: Vec<CallTraceNode>,
67    nonce: u64,
68}
69
70// == impl ExecutedTransaction ==
71
72impl ExecutedTransaction {
73    /// Creates the receipt for the transaction
74    fn create_receipt(&self, cumulative_gas_used: &mut u64) -> FoundryReceiptEnvelope {
75        let logs = self.logs.clone();
76        *cumulative_gas_used = cumulative_gas_used.saturating_add(self.gas_used);
77
78        // successful return see [Return]
79        let status_code = u8::from(self.exit_reason as u8 <= InstructionResult::SelfDestruct as u8);
80        let receipt_with_bloom: ReceiptWithBloom = Receipt {
81            status: (status_code == 1).into(),
82            cumulative_gas_used: *cumulative_gas_used,
83            logs,
84        }
85        .into();
86
87        match self.transaction.pending_transaction.transaction.as_ref() {
88            FoundryTxEnvelope::Legacy(_) => FoundryReceiptEnvelope::Legacy(receipt_with_bloom),
89            FoundryTxEnvelope::Eip2930(_) => FoundryReceiptEnvelope::Eip2930(receipt_with_bloom),
90            FoundryTxEnvelope::Eip1559(_) => FoundryReceiptEnvelope::Eip1559(receipt_with_bloom),
91            FoundryTxEnvelope::Eip4844(_) => FoundryReceiptEnvelope::Eip4844(receipt_with_bloom),
92            FoundryTxEnvelope::Eip7702(_) => FoundryReceiptEnvelope::Eip7702(receipt_with_bloom),
93            FoundryTxEnvelope::Deposit(_tx) => {
94                FoundryReceiptEnvelope::Deposit(op_alloy_consensus::OpDepositReceiptWithBloom {
95                    receipt: op_alloy_consensus::OpDepositReceipt {
96                        inner: receipt_with_bloom.receipt,
97                        deposit_nonce: Some(0),
98                        deposit_receipt_version: Some(1),
99                    },
100                    logs_bloom: receipt_with_bloom.logs_bloom,
101                })
102            }
103        }
104    }
105}
106
107/// Represents the outcome of mining a new block
108#[derive(Clone, Debug)]
109pub struct ExecutedTransactions {
110    /// The block created after executing the `included` transactions
111    pub block: BlockInfo,
112    /// All transactions included in the block
113    pub included: Vec<Arc<PoolTransaction>>,
114    /// All transactions that were invalid at the point of their execution and were not included in
115    /// the block
116    pub invalid: Vec<Arc<PoolTransaction>>,
117}
118
119/// An executor for a series of transactions
120pub struct TransactionExecutor<'a, Db: ?Sized, V: TransactionValidator> {
121    /// where to insert the transactions
122    pub db: &'a mut Db,
123    /// type used to validate before inclusion
124    pub validator: &'a V,
125    /// all pending transactions
126    pub pending: std::vec::IntoIter<Arc<PoolTransaction>>,
127    pub block_env: BlockEnv,
128    /// The configuration environment and spec id
129    pub cfg_env: CfgEnv,
130    pub parent_hash: B256,
131    /// Cumulative gas used by all executed transactions
132    pub gas_used: u64,
133    /// Cumulative blob gas used by all executed transactions
134    pub blob_gas_used: u64,
135    pub enable_steps_tracing: bool,
136    pub networks: NetworkConfigs,
137    pub print_logs: bool,
138    pub print_traces: bool,
139    /// Recorder used for decoding traces, used together with print_traces
140    pub call_trace_decoder: Arc<CallTraceDecoder>,
141    /// Precompiles to inject to the EVM.
142    pub precompile_factory: Option<Arc<dyn PrecompileFactory>>,
143    pub blob_params: BlobParams,
144    pub cheats: CheatsManager,
145}
146
147impl<DB: Db + ?Sized, V: TransactionValidator> TransactionExecutor<'_, DB, V> {
148    /// Executes all transactions and puts them in a new block with the provided `timestamp`
149    pub fn execute(mut self) -> ExecutedTransactions {
150        let mut transactions = Vec::new();
151        let mut transaction_infos = Vec::new();
152        let mut receipts = Vec::new();
153        let mut bloom = Bloom::default();
154        let mut cumulative_gas_used = 0u64;
155        let mut invalid = Vec::new();
156        let mut included = Vec::new();
157        let gas_limit = self.block_env.gas_limit;
158        let parent_hash = self.parent_hash;
159        let block_number = self.block_env.number;
160        let difficulty = self.block_env.difficulty;
161        let mix_hash = self.block_env.prevrandao;
162        let beneficiary = self.block_env.beneficiary;
163        let timestamp = self.block_env.timestamp;
164        let base_fee = if self.cfg_env.spec.is_enabled_in(SpecId::LONDON) {
165            Some(self.block_env.basefee)
166        } else {
167            None
168        };
169
170        let is_shanghai = self.cfg_env.spec >= SpecId::SHANGHAI;
171        let is_cancun = self.cfg_env.spec >= SpecId::CANCUN;
172        let is_prague = self.cfg_env.spec >= SpecId::PRAGUE;
173        let excess_blob_gas = if is_cancun { self.block_env.blob_excess_gas() } else { None };
174        let mut cumulative_blob_gas_used = if is_cancun { Some(0u64) } else { None };
175
176        for tx in self.into_iter() {
177            let tx = match tx {
178                TransactionExecutionOutcome::Executed(tx) => {
179                    included.push(tx.transaction.clone());
180                    tx
181                }
182                TransactionExecutionOutcome::BlockGasExhausted(tx) => {
183                    trace!(target: "backend",  tx_gas_limit = %tx.pending_transaction.transaction.gas_limit(), ?tx,  "block gas limit exhausting, skipping transaction");
184                    continue;
185                }
186                TransactionExecutionOutcome::BlobGasExhausted(tx) => {
187                    trace!(target: "backend",  blob_gas = %tx.pending_transaction.transaction.blob_gas_used().unwrap_or_default(), ?tx,  "block blob gas limit exhausting, skipping transaction");
188                    continue;
189                }
190                TransactionExecutionOutcome::TransactionGasExhausted(tx) => {
191                    trace!(target: "backend",  tx_gas_limit = %tx.pending_transaction.transaction.gas_limit(), ?tx,  "transaction gas limit exhausting, skipping transaction");
192                    continue;
193                }
194                TransactionExecutionOutcome::Invalid(tx, _) => {
195                    trace!(target: "backend", ?tx,  "skipping invalid transaction");
196                    invalid.push(tx);
197                    continue;
198                }
199                TransactionExecutionOutcome::DatabaseError(_, err) => {
200                    // Note: this is only possible in forking mode, if for example a rpc request
201                    // failed
202                    trace!(target: "backend", ?err,  "Failed to execute transaction due to database error");
203                    continue;
204                }
205            };
206            if is_cancun {
207                let tx_blob_gas =
208                    tx.transaction.pending_transaction.transaction.blob_gas_used().unwrap_or(0);
209                cumulative_blob_gas_used =
210                    Some(cumulative_blob_gas_used.unwrap_or(0u64).saturating_add(tx_blob_gas));
211            }
212            let receipt = tx.create_receipt(&mut cumulative_gas_used);
213
214            let ExecutedTransaction { transaction, logs, out, traces, exit_reason: exit, .. } = tx;
215            build_logs_bloom(&logs, &mut bloom);
216
217            let contract_address = out.as_ref().and_then(|out| {
218                if let Output::Create(_, contract_address) = out {
219                    trace!(target: "backend", "New contract deployed: at {:?}", contract_address);
220                    *contract_address
221                } else {
222                    None
223                }
224            });
225
226            let transaction_index = transaction_infos.len() as u64;
227            let info = TransactionInfo {
228                transaction_hash: transaction.hash(),
229                transaction_index,
230                from: *transaction.pending_transaction.sender(),
231                to: transaction.pending_transaction.transaction.to(),
232                contract_address,
233                traces,
234                exit,
235                out: out.map(Output::into_data),
236                nonce: tx.nonce,
237                gas_used: tx.gas_used,
238            };
239
240            transaction_infos.push(info);
241            receipts.push(receipt);
242            transactions.push(transaction.pending_transaction.transaction.clone());
243        }
244
245        let receipts_root = calculate_receipt_root(&receipts);
246
247        let header = Header {
248            parent_hash,
249            ommers_hash: Default::default(),
250            beneficiary,
251            state_root: self.db.maybe_state_root().unwrap_or_default(),
252            transactions_root: Default::default(), // Will be computed by create_block
253            receipts_root,
254            logs_bloom: bloom,
255            difficulty,
256            number: block_number.saturating_to(),
257            gas_limit,
258            gas_used: cumulative_gas_used,
259            timestamp: timestamp.saturating_to(),
260            extra_data: Default::default(),
261            mix_hash: mix_hash.unwrap_or_default(),
262            nonce: Default::default(),
263            base_fee_per_gas: base_fee,
264            parent_beacon_block_root: is_cancun.then_some(Default::default()),
265            blob_gas_used: cumulative_blob_gas_used,
266            excess_blob_gas,
267            withdrawals_root: is_shanghai.then_some(EMPTY_WITHDRAWALS),
268            requests_hash: is_prague.then_some(EMPTY_REQUESTS_HASH),
269        };
270
271        let block = create_block(header, transactions);
272        let block = BlockInfo { block, transactions: transaction_infos, receipts };
273        ExecutedTransactions { block, included, invalid }
274    }
275
276    fn env_for(&self, tx: &PendingTransaction) -> Env {
277        let mut tx_env: OpTransaction<TxEnv> =
278            FromRecoveredTx::from_recovered_tx(tx.transaction.as_ref(), *tx.sender());
279
280        if let FoundryTxEnvelope::Eip7702(tx_7702) = tx.transaction.as_ref()
281            && self.cheats.has_recover_overrides()
282        {
283            // Override invalid recovered authorizations with signature overrides from cheat manager
284            let cheated_auths = tx_7702
285                .tx()
286                .authorization_list
287                .iter()
288                .zip(tx_env.base.authorization_list)
289                .map(|(signed_auth, either_auth)| {
290                    either_auth.right_and_then(|recovered_auth| {
291                        if recovered_auth.authority().is_none()
292                            && let Ok(signature) = signed_auth.signature()
293                            && let Some(override_addr) =
294                                self.cheats.get_recover_override(&signature.as_bytes().into())
295                        {
296                            Either::Right(RecoveredAuthorization::new_unchecked(
297                                recovered_auth.into_parts().0,
298                                RecoveredAuthority::Valid(override_addr),
299                            ))
300                        } else {
301                            Either::Right(recovered_auth)
302                        }
303                    })
304                })
305                .collect();
306            tx_env.base.authorization_list = cheated_auths;
307        }
308
309        if self.networks.is_optimism() {
310            tx_env.enveloped_tx = Some(alloy_rlp::encode(tx.transaction.as_ref()).into());
311        }
312
313        Env::new(self.cfg_env.clone(), self.block_env.clone(), tx_env, self.networks)
314    }
315}
316
317/// Represents the result of a single transaction execution attempt
318#[derive(Debug)]
319pub enum TransactionExecutionOutcome {
320    /// Transaction successfully executed
321    Executed(ExecutedTransaction),
322    /// Invalid transaction not executed
323    Invalid(Arc<PoolTransaction>, InvalidTransactionError),
324    /// Execution skipped because could exceed block gas limit
325    BlockGasExhausted(Arc<PoolTransaction>),
326    /// Execution skipped because it exceeded the blob gas limit
327    BlobGasExhausted(Arc<PoolTransaction>),
328    /// Execution skipped because it exceeded the transaction gas limit
329    TransactionGasExhausted(Arc<PoolTransaction>),
330    /// When an error occurred during execution
331    DatabaseError(Arc<PoolTransaction>, DatabaseError),
332}
333
334impl<DB: Db + ?Sized, V: TransactionValidator> Iterator for &mut TransactionExecutor<'_, DB, V> {
335    type Item = TransactionExecutionOutcome;
336
337    fn next(&mut self) -> Option<Self::Item> {
338        let transaction = self.pending.next()?;
339        let sender = *transaction.pending_transaction.sender();
340        let account = match self.db.basic(sender).map(|acc| acc.unwrap_or_default()) {
341            Ok(account) => account,
342            Err(err) => return Some(TransactionExecutionOutcome::DatabaseError(transaction, err)),
343        };
344        let env = self.env_for(&transaction.pending_transaction);
345
346        // check that we comply with the block's gas limit, if not disabled
347        let max_block_gas = self.gas_used.saturating_add(env.tx.base.gas_limit);
348        if !env.evm_env.cfg_env.disable_block_gas_limit
349            && max_block_gas > env.evm_env.block_env.gas_limit
350        {
351            return Some(TransactionExecutionOutcome::BlockGasExhausted(transaction));
352        }
353
354        // check that we comply with the transaction's gas limit as imposed by Osaka (EIP-7825)
355        if env.evm_env.cfg_env.tx_gas_limit_cap.is_none()
356            && transaction.pending_transaction.transaction.gas_limit()
357                > env.evm_env.cfg_env().tx_gas_limit_cap()
358        {
359            return Some(TransactionExecutionOutcome::TransactionGasExhausted(transaction));
360        }
361
362        // check that we comply with the block's blob gas limit
363        let max_blob_gas = self.blob_gas_used.saturating_add(
364            transaction.pending_transaction.transaction.blob_gas_used().unwrap_or(0),
365        );
366        if max_blob_gas > self.blob_params.max_blob_gas_per_block() {
367            return Some(TransactionExecutionOutcome::BlobGasExhausted(transaction));
368        }
369
370        // validate before executing
371        if let Err(err) = self.validator.validate_pool_transaction_for(
372            &transaction.pending_transaction,
373            &account,
374            &env,
375        ) {
376            warn!(target: "backend", "Skipping invalid tx execution [{:?}] {}", transaction.hash(), err);
377            return Some(TransactionExecutionOutcome::Invalid(transaction, err));
378        }
379
380        let nonce = account.nonce;
381
382        let mut inspector = AnvilInspector::default().with_tracing();
383        if self.enable_steps_tracing {
384            inspector = inspector.with_steps_tracing();
385        }
386        if self.print_logs {
387            inspector = inspector.with_log_collector();
388        }
389        if self.print_traces {
390            inspector = inspector.with_trace_printer();
391        }
392
393        let exec_result = {
394            let mut evm = new_evm_with_inspector(&mut *self.db, &env, &mut inspector);
395            self.networks.inject_precompiles(evm.precompiles_mut());
396
397            if let Some(factory) = &self.precompile_factory {
398                evm.precompiles_mut().extend_precompiles(factory.precompiles());
399            }
400
401            let cheats = Arc::new(self.cheats.clone());
402            if cheats.has_recover_overrides() {
403                let cheat_ecrecover = CheatEcrecover::new(Arc::clone(&cheats));
404                evm.precompiles_mut().apply_precompile(&EC_RECOVER, move |_| {
405                    Some(DynPrecompile::new_stateful(
406                        cheat_ecrecover.precompile_id().clone(),
407                        move |input| cheat_ecrecover.call(input),
408                    ))
409                });
410            }
411
412            trace!(target: "backend", "[{:?}] executing", transaction.hash());
413            // transact and commit the transaction
414            match evm.transact_commit(env.tx) {
415                Ok(exec_result) => exec_result,
416                Err(err) => {
417                    warn!(target: "backend", "[{:?}] failed to execute: {:?}", transaction.hash(), err);
418                    match err {
419                        EVMError::Database(err) => {
420                            return Some(TransactionExecutionOutcome::DatabaseError(
421                                transaction,
422                                err,
423                            ));
424                        }
425                        EVMError::Transaction(err) => {
426                            return Some(TransactionExecutionOutcome::Invalid(
427                                transaction,
428                                err.into(),
429                            ));
430                        }
431                        // This will correspond to prevrandao not set, and it should never happen.
432                        // If it does, it's a bug.
433                        e => panic!("failed to execute transaction: {e}"),
434                    }
435                }
436            }
437        };
438
439        if self.print_traces {
440            inspector.print_traces(self.call_trace_decoder.clone());
441        }
442        inspector.print_logs();
443
444        let (exit_reason, gas_used, out, logs) = match exec_result {
445            ExecutionResult::Success { reason, gas_used, logs, output, .. } => {
446                (reason.into(), gas_used, Some(output), Some(logs))
447            }
448            ExecutionResult::Revert { gas_used, output } => {
449                (InstructionResult::Revert, gas_used, Some(Output::Call(output)), None)
450            }
451            ExecutionResult::Halt { reason, gas_used } => {
452                (op_haltreason_to_instruction_result(reason), gas_used, None, None)
453            }
454        };
455
456        if exit_reason == InstructionResult::OutOfGas {
457            // this currently useful for debugging estimations
458            warn!(target: "backend", "[{:?}] executed with out of gas", transaction.hash())
459        }
460
461        trace!(target: "backend", ?exit_reason, ?gas_used, "[{:?}] executed with out={:?}", transaction.hash(), out);
462
463        // Track the total gas used for total gas per block checks
464        self.gas_used = self.gas_used.saturating_add(gas_used);
465
466        // Track the total blob gas used for total blob gas per blob checks
467        if let Some(blob_gas) = transaction.pending_transaction.transaction.blob_gas_used() {
468            self.blob_gas_used = self.blob_gas_used.saturating_add(blob_gas);
469        }
470
471        trace!(target: "backend::executor", "transacted [{:?}], result: {:?} gas {}", transaction.hash(), exit_reason, gas_used);
472
473        let tx = ExecutedTransaction {
474            transaction,
475            exit_reason,
476            out,
477            gas_used,
478            logs: logs.unwrap_or_default(),
479            traces: inspector.tracer.map(|t| t.into_traces().into_nodes()).unwrap_or_default(),
480            nonce,
481        };
482
483        Some(TransactionExecutionOutcome::Executed(tx))
484    }
485}
486
487/// Inserts all logs into the bloom
488fn build_logs_bloom(logs: &[Log], bloom: &mut Bloom) {
489    for log in logs {
490        bloom.accrue(BloomInput::Raw(&log.address[..]));
491        for topic in log.topics() {
492            bloom.accrue(BloomInput::Raw(&topic[..]));
493        }
494    }
495}
496
497/// Creates a database with given database and inspector.
498pub fn new_evm_with_inspector<DB, I>(
499    db: DB,
500    env: &Env,
501    inspector: I,
502) -> EitherEvm<DB, I, PrecompilesMap>
503where
504    DB: Database<Error = DatabaseError> + Debug,
505    I: Inspector<EthEvmContext<DB>> + Inspector<OpContext<DB>>,
506{
507    if env.networks.is_optimism() {
508        let op_cfg = env.evm_env.cfg_env.clone().with_spec(op_revm::OpSpecId::ISTHMUS);
509        let op_context = OpContext {
510            journaled_state: {
511                let mut journal = Journal::new(db);
512                // Converting SpecId into OpSpecId
513                journal.set_spec_id(env.evm_env.cfg_env.spec);
514                journal
515            },
516            block: env.evm_env.block_env.clone(),
517            cfg: op_cfg.clone(),
518            tx: env.tx.clone(),
519            chain: L1BlockInfo::default(),
520            local: LocalContext::default(),
521            error: Ok(()),
522        };
523
524        let op_precompiles = OpPrecompiles::new_with_spec(op_cfg.spec).precompiles();
525        let op_evm = op_revm::OpEvm(RevmEvm::new_with_inspector(
526            op_context,
527            inspector,
528            EthInstructions::default(),
529            PrecompilesMap::from_static(op_precompiles),
530        ));
531
532        let op = OpEvm::new(op_evm, true);
533
534        EitherEvm::Op(op)
535    } else {
536        let spec = env.evm_env.cfg_env.spec;
537        let eth_context = EthEvmContext {
538            journaled_state: {
539                let mut journal = Journal::new(db);
540                journal.set_spec_id(spec);
541                journal
542            },
543            block: env.evm_env.block_env.clone(),
544            cfg: env.evm_env.cfg_env.clone(),
545            tx: env.tx.base.clone(),
546            chain: (),
547            local: LocalContext::default(),
548            error: Ok(()),
549        };
550
551        let eth_precompiles = EthPrecompiles {
552            precompiles: Precompiles::new(PrecompileSpecId::from_spec_id(spec)),
553            spec,
554        }
555        .precompiles;
556        let eth_evm = RevmEvm::new_with_inspector(
557            eth_context,
558            inspector,
559            EthInstructions::default(),
560            PrecompilesMap::from_static(eth_precompiles),
561        );
562
563        let eth = EthEvm::new(eth_evm, true);
564
565        EitherEvm::Eth(eth)
566    }
567}
568
569/// Creates a new EVM with the given inspector and wraps the database in a `WrapDatabaseRef`.
570pub fn new_evm_with_inspector_ref<'db, DB, I>(
571    db: &'db DB,
572    env: &Env,
573    inspector: &'db mut I,
574) -> EitherEvm<WrapDatabaseRef<&'db DB>, &'db mut I, PrecompilesMap>
575where
576    DB: DatabaseRef<Error = DatabaseError> + Debug + 'db + ?Sized,
577    I: Inspector<EthEvmContext<WrapDatabaseRef<&'db DB>>>
578        + Inspector<OpContext<WrapDatabaseRef<&'db DB>>>,
579    WrapDatabaseRef<&'db DB>: Database<Error = DatabaseError>,
580{
581    new_evm_with_inspector(WrapDatabaseRef(db), env, inspector)
582}