Skip to main content

anvil/eth/backend/
executor.rs

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/// Represents an executed transaction (transacted on the DB)
49#[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
60// == impl ExecutedTransaction ==
61
62impl ExecutedTransaction {
63    /// Creates the receipt for the transaction
64    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        // successful return see [Return]
69        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/// Represents the outcome of mining a new block
93#[derive(Clone, Debug)]
94pub struct ExecutedTransactions {
95    /// The block created after executing the `included` transactions
96    pub block: BlockInfo,
97    /// All transactions included in the block
98    pub included: Vec<Arc<PoolTransaction>>,
99    /// All transactions that were invalid at the point of their execution and were not included in
100    /// the block
101    pub invalid: Vec<Arc<PoolTransaction>>,
102}
103
104/// An executor for a series of transactions
105pub struct TransactionExecutor<'a, Db: ?Sized, V: TransactionValidator> {
106    /// where to insert the transactions
107    pub db: &'a mut Db,
108    /// type used to validate before inclusion
109    pub validator: &'a V,
110    /// all pending transactions
111    pub pending: std::vec::IntoIter<Arc<PoolTransaction>>,
112    pub block_env: BlockEnv,
113    /// The configuration environment and spec id
114    pub cfg_env: CfgEnv,
115    pub parent_hash: B256,
116    /// Cumulative gas used by all executed transactions
117    pub gas_used: u64,
118    /// Cumulative blob gas used by all executed transactions
119    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    /// Recorder used for decoding traces, used together with print_traces
126    pub call_trace_decoder: Arc<CallTraceDecoder>,
127    /// Precompiles to inject to the EVM.
128    pub precompile_factory: Option<Arc<dyn PrecompileFactory>>,
129    pub blob_params: BlobParams,
130}
131
132impl<DB: Db + ?Sized, V: TransactionValidator> TransactionExecutor<'_, DB, V> {
133    /// Executes all transactions and puts them in a new block with the provided `timestamp`
134    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                    // Note: this is only possible in forking mode, if for example a rpc request
182                    // failed
183                    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/// Represents the result of a single transaction execution attempt
272#[derive(Debug)]
273pub enum TransactionExecutionOutcome {
274    /// Transaction successfully executed
275    Executed(ExecutedTransaction),
276    /// Invalid transaction not executed
277    Invalid(Arc<PoolTransaction>, InvalidTransactionError),
278    /// Execution skipped because could exceed gas limit
279    Exhausted(Arc<PoolTransaction>),
280    /// Execution skipped because it exceeded the blob gas limit
281    BlobGasExhausted(Arc<PoolTransaction>),
282    /// When an error occurred during execution
283    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        // check that we comply with the block's gas limit, if not disabled
299        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        // check that we comply with the block's blob gas limit
306        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        // validate before executing
314        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            // transact and commit the transaction
349            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                        // This will correspond to prevrandao not set, and it should never happen.
367                        // If it does, it's a bug.
368                        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            // this currently useful for debugging estimations
393            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        // Track the total gas used for total gas per block checks
399        self.gas_used = self.gas_used.saturating_add(gas_used);
400
401        // Track the total blob gas used for total blob gas per blob checks
402        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
422/// Inserts all logs into the bloom
423fn 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
432/// Creates a database with given database and inspector, optionally enabling odyssey features.
433pub 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                // Converting SpecId into OpSpecId
448                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
504/// Creates a new EVM with the given inspector and wraps the database in a `WrapDatabaseRef`.
505pub 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}