Skip to main content

reth_evm/
execute.rs

1//! Traits for execution.
2
3use crate::{ConfigureEvm, Database, DynDatabase, EvmEnv, TxEnvFor};
4use alloc::{boxed::Box, sync::Arc, vec::Vec};
5use alloy_consensus::{
6    transaction::{Either, Recovered, TransactionEnvelope},
7    BlockHeader as _, Header, Transaction, TxEip2930, TxEip4844, TxLegacy,
8};
9use alloy_eip7928::{
10    bal::DecodedBal, compute_block_access_list_hash_with_buf, BlockAccessIndex, BlockAccessList,
11};
12use alloy_eips::{
13    eip2718::{Typed2718, WithEncoded},
14    eip2930::AccessList,
15};
16use alloy_primitives::{Address, B256};
17use core::fmt::Debug;
18use evm2::interpreter::{Interpreter, Message, MessageResult};
19use reth_execution_types::{BlockExecutionResult, StateUpdate};
20use reth_primitives_traits::{
21    Block, HeaderTy, NodePrimitives, ReceiptTy, RecoveredBlock, SealedHeader, SignedTransaction,
22    TxTy,
23};
24use reth_storage_api::StateProvider;
25use reth_trie_common::{bundle_state::BundleState, updates::TrieUpdates, HashedPostState};
26
27#[cfg(feature = "std")]
28use evm2::evm::{CacheDB, Db};
29#[cfg(feature = "std")]
30use reth_execution_types::BundleSource;
31#[cfg(feature = "std")]
32use reth_primitives_traits::BlockTy;
33
34pub use evm2::block::{
35    map_database_error, map_handler_error, BlockTransactionResult, CommitChanges, GasOutput,
36    ReceiptBuilderCtx,
37};
38pub use reth_execution_errors::{
39    BlockExecutionError, BlockValidationError, InternalBlockExecutionError, InvalidTxError,
40};
41pub use reth_execution_types::{BlockExecutionOutput, ExecutionOutcome};
42
43/// Builds chain-specific receipts from raw transaction execution results.
44#[auto_impl::auto_impl(&, Arc)]
45pub trait ReceiptBuilder {
46    /// Consensus transaction type accepted by the executor.
47    type Transaction: SignedTransaction + TransactionEnvelope<TxType: Send + 'static>;
48    /// Receipt produced by this builder.
49    type Receipt: reth_primitives_traits::Receipt;
50
51    /// Builds a receipt for the transaction execution result.
52    fn build_receipt<T: evm2::EvmTypes>(
53        &self,
54        ctx: ReceiptBuilderCtx<
55            <Self::Transaction as TransactionEnvelope>::TxType,
56            evm2::TxResult<T>,
57        >,
58    ) -> Self::Receipt;
59}
60
61/// A configured EVM instance.
62pub trait Evm {
63    /// Runtime EVM type family.
64    type EvmTypes: evm2::EvmTypes<Tx = Self::Transaction>;
65    /// Transaction environment consumed by this EVM.
66    type Transaction;
67
68    /// Executes a transaction without committing its state changes.
69    fn transact(
70        &mut self,
71        transaction: &Recovered<Self::Transaction>,
72    ) -> Result<evm2::TxResultWithState<Self::EvmTypes>, BlockExecutionError>;
73
74    /// Executes and accepts writes in the EVM's block-scoped state without detaching them.
75    fn transact_commit(
76        &mut self,
77        transaction: &Recovered<Self::Transaction>,
78    ) -> Result<evm2::TxResult<Self::EvmTypes>, BlockExecutionError>;
79
80    /// Executes a transaction and discards writes without materializing detached state.
81    fn transact_result(
82        &mut self,
83        transaction: &Recovered<Self::Transaction>,
84    ) -> Result<evm2::TxResult<Self::EvmTypes>, BlockExecutionError>;
85
86    /// Executes a transaction with an inspector without committing its state changes.
87    fn transact_with_inspector<I>(
88        &mut self,
89        transaction: &Recovered<Self::Transaction>,
90        inspector: I,
91    ) -> Result<(I, evm2::TxResultWithState<Self::EvmTypes>), BlockExecutionError>
92    where
93        I: evm2::Inspector<Self::EvmTypes> + 'static;
94
95    /// Sets the inspector used by subsequent transactions.
96    fn set_inspector<I>(&mut self, inspector: I)
97    where
98        I: evm2::Inspector<Self::EvmTypes> + 'static;
99
100    /// Runs `f` on the inspector set by [`Self::set_inspector`] if it has type `I`.
101    ///
102    /// # Safety
103    ///
104    /// `I` must have no lifetime parameters. The type check cannot tell an inspector that borrows
105    /// data from an `I` that borrows `'static` data, so `f` could then keep a `'static` reference
106    /// to borrowed data.
107    unsafe fn with_inspector<I, R>(&mut self, f: impl FnOnce(&mut I) -> R) -> Option<R>
108    where
109        I: evm2::Inspector<Self::EvmTypes> + 'static;
110
111    /// Removes and returns the active inspector, if any.
112    fn take_inspector(&mut self) -> Option<Box<dyn evm2::Inspector<Self::EvmTypes> + '_>>;
113
114    /// Returns the accepted state overlay, including earlier committed transactions.
115    fn state_db_mut(&mut self) -> &mut dyn DynDatabase;
116
117    /// Accepts detached changes after a tracing callback has consumed the pre-state.
118    fn commit_state(&mut self, state: &evm2::evm::PendingState);
119
120    /// Takes cached reads and accepted writes for a caller that continues using its database.
121    fn take_state_cache(&mut self) -> evm2::evm::Cache;
122
123    /// Returns active precompile addresses and identifiers.
124    fn precompile_ids(&self) -> Vec<(Address, evm2::precompiles::PrecompileId)>;
125
126    /// Returns whether an address is an active precompile.
127    fn has_precompile(&self, address: &Address) -> bool;
128
129    /// Returns account information visible through the accepted state overlay.
130    fn account_info(
131        &mut self,
132        address: &Address,
133    ) -> Result<Option<evm2::evm::AccountInfo>, BlockExecutionError>;
134
135    /// Applies precompile address moves to the active precompile set.
136    fn move_precompiles(
137        &mut self,
138        moves: impl IntoIterator<Item = (Address, Address)>,
139    ) -> Result<(), evm2::precompiles::MovePrecompileError>;
140
141    /// Executes a transaction, lets `observe` inspect the executed transaction and its state
142    /// changes, then discards its writes.
143    fn transact_and_discard<R>(
144        &mut self,
145        transaction: &Recovered<Self::Transaction>,
146        observe: impl FnOnce(&evm2::ExecutedTx<'_, '_, Self::EvmTypes>) -> R,
147    ) -> Result<R, BlockExecutionError>;
148}
149
150impl<'a, T: evm2::EvmTypes<Tx: Typed2718>> Evm for evm2::Evm<'a, T> {
151    type EvmTypes = T;
152    type Transaction = T::Tx;
153
154    fn transact(
155        &mut self,
156        transaction: &Recovered<Self::Transaction>,
157    ) -> Result<evm2::TxResultWithState<T>, BlockExecutionError> {
158        evm2::Evm::transact(self, transaction)
159            .map(|executed| executed.detach())
160            .map_err(map_handler_error)
161    }
162
163    fn transact_commit(
164        &mut self,
165        transaction: &Recovered<Self::Transaction>,
166    ) -> Result<evm2::TxResult<T>, BlockExecutionError> {
167        execute_result(self, transaction, true)
168    }
169
170    fn transact_result(
171        &mut self,
172        transaction: &Recovered<Self::Transaction>,
173    ) -> Result<evm2::TxResult<T>, BlockExecutionError> {
174        execute_result(self, transaction, false)
175    }
176
177    fn transact_with_inspector<I: evm2::Inspector<T> + 'a>(
178        &mut self,
179        transaction: &Recovered<Self::Transaction>,
180        inspector: I,
181    ) -> Result<(I, evm2::TxResultWithState<T>), BlockExecutionError> {
182        // TODO(dani): use either `&mut Inspector` or `clear_inspector_as`.
183        evm2::Evm::set_inspector(self, inspector);
184        let result = evm2::Evm::transact(self, transaction)
185            .map(|executed| executed.detach())
186            .map_err(map_handler_error);
187        let inspector = self.clear_inspector().expect("inspector was set before execution");
188        // SAFETY: the boxed inspector was created from `I` immediately above and was not replaced.
189        let inspector = unsafe { Box::from_raw(Box::into_raw(inspector).cast::<I>()) };
190        result.map(|result| (*inspector, result))
191    }
192
193    fn set_inspector<I: evm2::Inspector<T> + 'a>(&mut self, inspector: I) {
194        evm2::Evm::set_inspector(self, inspector);
195    }
196
197    unsafe fn with_inspector<I, R>(&mut self, f: impl FnOnce(&mut I) -> R) -> Option<R>
198    where
199        I: evm2::Inspector<T> + 'static,
200    {
201        let inspector: &mut (dyn evm2::evm::NonStaticAny + 'a) = &mut **self.inspector_mut()?;
202        if evm2::evm::NonStaticAny::type_id(inspector) != core::any::TypeId::of::<I>() {
203            return None
204        }
205        // SAFETY: the type ID check shows that the inspector is an `I`, and the caller guarantees
206        // that `I` has no lifetime parameters, so no lifetime is erased.
207        Some(f(unsafe { inspector.downcast_mut_unchecked::<I>() }))
208    }
209
210    fn take_inspector(&mut self) -> Option<Box<dyn evm2::Inspector<T> + '_>> {
211        self.clear_inspector()
212    }
213
214    fn state_db_mut(&mut self) -> &mut dyn DynDatabase {
215        self.overlay_db_mut()
216    }
217
218    fn commit_state(&mut self, state: &evm2::evm::PendingState) {
219        self.overlay_db_mut().commit_pending(state);
220    }
221
222    fn take_state_cache(&mut self) -> evm2::evm::Cache {
223        core::mem::take(&mut self.overlay_db_mut().cache)
224    }
225
226    fn precompile_ids(&self) -> Vec<(Address, evm2::precompiles::PrecompileId)> {
227        self.precompiles().precompile_ids()
228    }
229
230    fn has_precompile(&self, address: &Address) -> bool {
231        self.precompiles().contains(address)
232    }
233
234    fn account_info(
235        &mut self,
236        address: &Address,
237    ) -> Result<Option<evm2::evm::AccountInfo>, BlockExecutionError> {
238        self.state_mut().account_info_untracked(address).map_err(map_database_error)
239    }
240
241    fn move_precompiles(
242        &mut self,
243        moves: impl IntoIterator<Item = (Address, Address)>,
244    ) -> Result<(), evm2::precompiles::MovePrecompileError> {
245        self.precompiles_mut().move_precompiles(&moves.into_iter().collect::<Vec<_>>())
246    }
247
248    fn transact_and_discard<R>(
249        &mut self,
250        transaction: &Recovered<Self::Transaction>,
251        observe: impl FnOnce(&evm2::ExecutedTx<'_, '_, T>) -> R,
252    ) -> Result<R, BlockExecutionError> {
253        let executed = self.transact(transaction).map_err(map_handler_error)?;
254        let observed = observe(&executed);
255        drop(executed);
256        Ok(observed)
257    }
258}
259
260fn execute_result<T: evm2::EvmTypes<Tx: Typed2718>>(
261    evm: &mut evm2::Evm<'_, T>,
262    transaction: &Recovered<T::Tx>,
263    commit: bool,
264) -> Result<evm2::TxResult<T>, BlockExecutionError> {
265    let executed = evm2::Evm::transact(evm, transaction).map_err(map_handler_error)?;
266    Ok(if commit { executed.commit() } else { executed.discard() })
267}
268
269/// Inspector installed by [`BlockExecutor::inspect_transaction_without_commit`].
270///
271/// Only this module constructs it, and only around a `'static` inspector, which is what lets
272/// [`TracedInspector::downcast`] cast a matching inspector soundly: evm2's type IDs ignore
273/// lifetimes, so a downcast to an arbitrary inspector type could extend a borrow.
274#[derive(Debug)]
275struct TracedInspector<I>(I);
276
277impl<I: 'static> TracedInspector<I> {
278    /// Returns the wrapped inspector if `inspector` is a `TracedInspector<I>`.
279    fn downcast<T: evm2::EvmTypesHost>(inspector: Box<dyn evm2::Inspector<T> + '_>) -> Option<I> {
280        let erased: &dyn evm2::evm::NonStaticAny = &*inspector;
281        if erased.type_id() != core::any::TypeId::of::<Self>() {
282            return None
283        }
284        // SAFETY: the erased type IDs match, and only this module constructs a `TracedInspector`,
285        // always around a `'static` inspector, so the boxed value is a `TracedInspector<I>` and no
286        // lifetime is extended.
287        let traced = unsafe { Box::from_raw(Box::into_raw(inspector).cast::<Self>()) };
288        Some(traced.0)
289    }
290}
291
292impl<T: evm2::EvmTypesHost, I: evm2::Inspector<T>> evm2::Inspector<T> for TracedInspector<I> {
293    fn initialize_interp(&mut self, interp: &mut Interpreter<'_, '_, T>) {
294        self.0.initialize_interp(interp);
295    }
296
297    fn inspector_config(&self) -> evm2::InspectorConfig {
298        self.0.inspector_config()
299    }
300
301    fn step(&mut self, interp: &mut Interpreter<'_, '_, T>) {
302        self.0.step(interp);
303    }
304
305    fn step_end(&mut self, interp: &mut Interpreter<'_, '_, T>) {
306        self.0.step_end(interp);
307    }
308
309    fn log(&mut self, log: &alloy_primitives::Log, host: &mut T::Host<'_>) {
310        self.0.log(log, host);
311    }
312
313    fn call(
314        &mut self,
315        interp: &mut Interpreter<'_, '_, T>,
316        message: &mut Message<T>,
317    ) -> Option<MessageResult<T>> {
318        self.0.call(interp, message)
319    }
320
321    fn call_end(
322        &mut self,
323        interp: &mut Interpreter<'_, '_, T>,
324        message: &Message<T>,
325        result: &mut MessageResult<T>,
326    ) {
327        self.0.call_end(interp, message, result);
328    }
329
330    fn create(
331        &mut self,
332        interp: &mut Interpreter<'_, '_, T>,
333        message: &mut Message<T>,
334    ) -> Option<MessageResult<T>> {
335        self.0.create(interp, message)
336    }
337
338    fn create_end(
339        &mut self,
340        interp: &mut Interpreter<'_, '_, T>,
341        message: &Message<T>,
342        result: &mut MessageResult<T>,
343    ) {
344        self.0.create_end(interp, message, result);
345    }
346
347    fn selfdestruct(
348        &mut self,
349        contract: &Address,
350        target: &Address,
351        value: &alloy_primitives::U256,
352        host: &mut T::Host<'_>,
353    ) {
354        self.0.selfdestruct(contract, target, value, host);
355    }
356}
357
358/// A configured block executor.
359pub trait BlockExecutor: Sized {
360    /// Consensus transaction type executed by this executor.
361    type Transaction;
362    /// Receipt type produced by this executor.
363    type Receipt;
364    /// EVM instance used by this executor.
365    type Evm: Evm;
366    /// Owned transaction execution result and detached state changes.
367    type TransactionResultWithState: BlockTransactionResult<<Self::Evm as Evm>::EvmTypes> + Send;
368    /// EVM-native block access list used for indexed reads.
369    type BlockAccessList: Send + Sync;
370
371    /// Returns the underlying EVM.
372    fn evm(&self) -> &Self::Evm;
373
374    /// Returns the underlying EVM mutably.
375    fn evm_mut(&mut self) -> &mut Self::Evm;
376
377    /// Sets a hook for state updates emitted during block execution, including pre-execution
378    /// block changes and transaction changes.
379    ///
380    /// Returns `true` if the hook was installed.
381    fn set_state_hook(&mut self, _hook: impl FnMut(StateUpdate) + Send + 'static) -> bool {
382        false
383    }
384
385    /// Converts a canonical EIP-7928 block access list into the EVM-native representation.
386    fn convert_block_access_list(
387        block_access_list: &BlockAccessList,
388    ) -> Result<Self::BlockAccessList, BlockExecutionError>;
389
390    /// Attaches an EIP-7928 block access list for indexed state reads.
391    fn set_block_access_list(&mut self, block_access_list: Arc<Self::BlockAccessList>);
392
393    /// Sets the EIP-7928 block access index used by state reads and BAL construction.
394    fn set_block_access_index(&mut self, index: BlockAccessIndex);
395
396    /// Enables construction of an EIP-7928 block access list from committed state changes.
397    fn enable_block_access_list_builder(&mut self);
398
399    /// Takes the canonical EIP-7928 block access list built from committed state changes.
400    fn take_block_access_list(&mut self) -> Option<BlockAccessList>;
401
402    /// Applies pre-execution block changes.
403    fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError>;
404
405    /// Consumes the executor and returns the state accumulated so far without applying
406    /// post-execution block changes.
407    fn into_state(self) -> BundleState;
408
409    /// Executes a transaction, invokes `f` with the borrowed execution result and state changes,
410    /// and commits changes when `f` returns [`CommitChanges::Yes`].
411    ///
412    /// The state changes are borrowed before the transaction is committed or discarded, so `f`
413    /// can read what the transaction wrote (balances, storage slots) to decide whether to keep it.
414    fn execute_transaction_with_commit_condition(
415        &mut self,
416        transaction: impl ExecutorTx<Self>,
417        f: impl FnOnce(
418            &evm2::TxResult<<Self::Evm as Evm>::EvmTypes>,
419            evm2::TxStateChanges<'_>,
420        ) -> CommitChanges,
421    ) -> Result<Option<GasOutput>, BlockExecutionError> {
422        let output = self.execute_transaction_without_commit(transaction)?;
423        let result = output.result();
424        if f(&result.result, result.pending_state.changes()).should_commit() {
425            self.commit_transaction(output).map(Some)
426        } else {
427            Ok(None)
428        }
429    }
430
431    /// Validates transaction gas admission against the executor's current block or segment budget.
432    /// Speculative workers and ordered commits must perform the same check before accepting a
433    /// transaction's result or execution error.
434    fn validate_transaction_gas_limit(&mut self, gas_limit: u64)
435        -> Result<(), BlockExecutionError>;
436
437    /// Executes a transaction and detaches its state changes without committing them.
438    fn execute_transaction_without_commit(
439        &mut self,
440        transaction: impl ExecutorTx<Self>,
441    ) -> Result<Self::TransactionResultWithState, BlockExecutionError>;
442
443    /// Executes a transaction with `inspector` and detaches its state changes without committing
444    /// them.
445    ///
446    /// Returns the inspector together with the output, so a tracer observes the transaction exactly
447    /// as the executor runs it, including any changes the executor applies on top of the EVM
448    /// result. Replaces any inspector already set on the executor's EVM. Returns an error if the
449    /// executor removed or replaced the inspector while executing the transaction.
450    fn inspect_transaction_without_commit<I>(
451        &mut self,
452        transaction: impl ExecutorTx<Self>,
453        inspector: I,
454    ) -> Result<(I, Self::TransactionResultWithState), BlockExecutionError>
455    where
456        I: evm2::Inspector<<Self::Evm as Evm>::EvmTypes> + 'static,
457    {
458        self.evm_mut().set_inspector(TracedInspector(inspector));
459        let output = self.execute_transaction_without_commit(transaction);
460        let inspector =
461            self.evm_mut().take_inspector().and_then(TracedInspector::downcast).ok_or_else(|| {
462                BlockExecutionError::msg("block executor replaced the tracing inspector")
463            });
464        let (inspector, output) = (inspector?, output?);
465        Ok((inspector, output))
466    }
467
468    /// Commits detached transaction state and records its receipt and gas accounting.
469    fn commit_transaction(
470        &mut self,
471        output: Self::TransactionResultWithState,
472    ) -> Result<GasOutput, BlockExecutionError>;
473
474    /// Executes a transaction, invokes `f` with the borrowed execution result, and commits
475    /// changes.
476    fn execute_transaction_with_result_closure(
477        &mut self,
478        transaction: impl ExecutorTx<Self>,
479        f: impl FnOnce(&evm2::TxResult<<Self::Evm as Evm>::EvmTypes>),
480    ) -> Result<GasOutput, BlockExecutionError> {
481        self.execute_transaction_with_commit_condition(transaction, |result, _| {
482            f(result);
483            CommitChanges::Yes
484        })
485        .map(Option::unwrap_or_default)
486    }
487
488    /// Executes a transaction and commits changes.
489    fn execute_transaction(
490        &mut self,
491        transaction: impl ExecutorTx<Self>,
492    ) -> Result<GasOutput, BlockExecutionError> {
493        self.execute_transaction_with_result_closure(transaction, |_| {})
494    }
495
496    /// Returns receipts accumulated so far.
497    fn receipts(&self) -> &[Self::Receipt];
498
499    /// Finishes block execution and returns the output.
500    fn finish_with_block_access_list(
501        self,
502    ) -> Result<(BlockExecutionOutput<Self::Receipt>, Option<BlockAccessList>), BlockExecutionError>;
503
504    /// Finishes execution while retaining the prepared evm2 BAL for downstream consumers.
505    ///
506    /// Executors that build a native BAL can override this to avoid converting it back from the
507    /// canonical representation.
508    fn finish_with_prepared_block_access_list(
509        self,
510    ) -> Result<(BlockExecutionOutput<Self::Receipt>, Option<evm2::evm::Bal>), BlockExecutionError>
511    {
512        let (output, bal) = self.finish_with_block_access_list()?;
513        let bal = bal
514            .map(|bal| evm2::evm::Bal::try_from(bal.as_slice()))
515            .transpose()
516            .map_err(BlockExecutionError::other)?;
517        Ok((output, bal))
518    }
519
520    /// Finishes block execution and returns the output.
521    fn finish(self) -> Result<BlockExecutionOutput<Self::Receipt>, BlockExecutionError> {
522        self.finish_with_block_access_list().map(|(output, _)| output)
523    }
524}
525
526/// A type that creates configured block executors.
527pub trait BlockExecutorFactory {
528    /// Additional EVM factory configuration owned by this executor factory.
529    type EvmFactory;
530    /// Runtime EVM type family.
531    type EvmTypes: evm2::EvmTypes<
532        TxResultExt: Send + Sync,
533        SpecId: Eq + core::hash::Hash + Debug + Send + Sync + 'static,
534    >;
535    /// Consensus transaction type consumed by executors from this factory.
536    type Transaction: Debug + Clone + Send + Sync + 'static;
537    /// Receipt type produced by executors from this factory.
538    type Receipt;
539    /// EVM instance consumed by executors from this factory.
540    type Evm<'a>: Evm<
541        EvmTypes = Self::EvmTypes,
542        Transaction = <Self::EvmTypes as evm2::EvmTypesHost>::Tx,
543    >;
544    /// EVM environment consumed by this factory.
545    type EvmEnv: EvmEnv<EvmTypes = Self::EvmTypes>;
546    /// Execution context for a block or payload.
547    type ExecutionCtx<'a>: Debug + Clone + Send
548    where
549        Self: 'a;
550    /// Block executor returned by this factory.
551    type Executor<'a>: BlockExecutor<
552        Transaction = Self::Transaction,
553        Receipt = Self::Receipt,
554        Evm = Self::Evm<'a>,
555        BlockAccessList = evm2::evm::Bal,
556    >
557    where
558        Self: 'a;
559
560    /// Creates a configured block executor.
561    fn create_executor<'a>(
562        &'a self,
563        evm: Self::Evm<'a>,
564        ctx: Self::ExecutionCtx<'a>,
565    ) -> Self::Executor<'a>
566    where
567        Self: 'a;
568
569    /// Returns the configured EVM factory state.
570    fn evm_factory(&self) -> &Self::EvmFactory;
571
572    /// Creates an EVM instance with the configured execution environment.
573    fn evm_with_env<'a, DB>(&self, db: DB, evm_env: Self::EvmEnv) -> Self::Evm<'a>
574    where
575        DB: DynDatabase + 'a;
576
577    /// Creates an EVM instance with the configured execution environment over a typed database.
578    #[cfg(feature = "std")]
579    fn evm_with_database<'a, DB>(&self, db: DB, evm_env: Self::EvmEnv) -> Self::Evm<'a>
580    where
581        DB: Database + 'a,
582    {
583        self.evm_with_env(Db::new(db), evm_env)
584    }
585}
586
587/// Input for block assembly.
588#[expect(missing_debug_implementations)]
589#[non_exhaustive]
590pub struct BlockAssemblerInput<'a, 'b, F: BlockExecutorFactory + 'a, H = Header> {
591    /// EVM environment used for block execution.
592    pub evm_env: F::EvmEnv,
593    /// Execution context used for block execution.
594    pub execution_ctx: F::ExecutionCtx<'a>,
595    /// Parent block header.
596    pub parent: &'a SealedHeader<H>,
597    /// Transactions that were executed in this block.
598    pub transactions: Vec<F::Transaction>,
599    /// Output of block execution.
600    pub output: &'b BlockExecutionResult<F::Receipt>,
601    /// Execution state after block execution.
602    pub execution_state: &'b BundleState,
603    /// Provider with access to state.
604    pub state_provider: &'b dyn StateProvider,
605    /// State root for the assembled block.
606    pub state_root: B256,
607    /// Block access list hash.
608    pub block_access_list_hash: Option<B256>,
609}
610
611impl<'a, 'b, F: BlockExecutorFactory + 'a, H> BlockAssemblerInput<'a, 'b, F, H> {
612    /// Creates a new [`BlockAssemblerInput`].
613    #[expect(clippy::too_many_arguments)]
614    pub const fn new(
615        evm_env: F::EvmEnv,
616        execution_ctx: F::ExecutionCtx<'a>,
617        parent: &'a SealedHeader<H>,
618        transactions: Vec<F::Transaction>,
619        output: &'b BlockExecutionResult<F::Receipt>,
620        execution_state: &'b BundleState,
621        state_provider: &'b dyn StateProvider,
622        state_root: B256,
623        block_access_list_hash: Option<B256>,
624    ) -> Self {
625        Self {
626            evm_env,
627            execution_ctx,
628            parent,
629            transactions,
630            output,
631            execution_state,
632            state_provider,
633            state_root,
634            block_access_list_hash,
635        }
636    }
637}
638
639/// A type that assembles a block from execution output.
640#[auto_impl::auto_impl(&, Arc)]
641pub trait BlockAssembler<F: BlockExecutorFactory> {
642    /// The block type produced by the assembler.
643    type Block: Block;
644
645    /// Builds a block. see [`BlockAssemblerInput`] documentation for more details.
646    fn assemble_block(
647        &self,
648        input: BlockAssemblerInput<'_, '_, F, <Self::Block as Block>::Header>,
649    ) -> Result<Self::Block, BlockExecutionError>;
650}
651
652/// Output of block building.
653#[derive(Debug, Clone)]
654pub struct BlockBuilderOutcome<N: NodePrimitives> {
655    /// Result of block execution.
656    pub execution_result: BlockExecutionResult<N::Receipt>,
657    /// Changed state produced by block execution.
658    pub execution_state: BundleState,
659    /// Hashed state after execution.
660    pub hashed_state: HashedPostState,
661    /// Trie updates collected during state-root calculation.
662    pub trie_updates: TrieUpdates,
663    /// The built block.
664    pub block: RecoveredBlock<N::Block>,
665    /// Block access list built during execution (EIP-7928, Amsterdam).
666    pub block_access_list: Option<DecodedBal>,
667}
668
669/// A type that knows how to execute transactions and assemble a block.
670pub trait BlockBuilder: Sized {
671    /// The primitive types used by the inner [`BlockExecutor`].
672    type Primitives: NodePrimitives;
673    /// Inner block executor.
674    type Executor: BlockExecutor<
675        Transaction = TxTy<Self::Primitives>,
676        Receipt = ReceiptTy<Self::Primitives>,
677        Evm: Evm<Transaction: FromTxWithEncoded<TxTy<Self::Primitives>>>,
678    >;
679    /// EVM environment used for block execution.
680    type EvmEnv: EvmEnv;
681
682    /// Returns the EVM environment used for block execution.
683    fn evm_env(&self) -> &Self::EvmEnv;
684
685    /// Applies pre-execution block changes.
686    fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError>;
687
688    /// Executes a transaction, exposes its borrowed execution result and state changes to `f`,
689    /// and saves it for block assembly only if committed.
690    fn execute_transaction_with_commit_condition(
691        &mut self,
692        tx: impl ExecutorTx<Self::Executor>,
693        f: impl FnOnce(
694            &evm2::TxResult<<<Self::Executor as BlockExecutor>::Evm as Evm>::EvmTypes>,
695            evm2::TxStateChanges<'_>,
696        ) -> CommitChanges,
697    ) -> Result<Option<GasOutput>, BlockExecutionError>;
698
699    /// Executes a transaction, invokes `f` with the detached result and state changes, and saves
700    /// it for block assembly.
701    fn execute_transaction_with_result_closure(
702        &mut self,
703        tx: impl ExecutorTx<Self::Executor>,
704        f: impl FnOnce(&<Self::Executor as BlockExecutor>::TransactionResultWithState),
705    ) -> Result<GasOutput, BlockExecutionError>;
706
707    /// Executes a transaction and saves it for block assembly.
708    fn execute_transaction(
709        &mut self,
710        tx: impl ExecutorTx<Self::Executor>,
711    ) -> Result<GasOutput, BlockExecutionError> {
712        self.execute_transaction_with_commit_condition(tx, |_, _| CommitChanges::Yes)
713            .map(Option::unwrap_or_default)
714    }
715
716    /// Completes block building.
717    fn finish(
718        self,
719        state_provider: impl StateProvider,
720        state_root_precomputed: Option<(B256, TrieUpdates)>,
721    ) -> Result<BlockBuilderOutcome<Self::Primitives>, BlockExecutionError> {
722        self.finish_with_state_root(state_provider, move |_| Ok(state_root_precomputed))
723    }
724
725    /// Completes block building, resolving an optional state root after execution is finalized.
726    fn finish_with_state_root(
727        self,
728        state_provider: impl StateProvider,
729        state_root: impl FnOnce(
730            &BlockExecutionOutput<ReceiptTy<Self::Primitives>>,
731        ) -> Result<Option<(B256, TrieUpdates)>, BlockExecutionError>,
732    ) -> Result<BlockBuilderOutcome<Self::Primitives>, BlockExecutionError>;
733
734    /// Sets a hook for transaction state updates emitted while building a block.
735    ///
736    /// Returns `true` if the hook was installed.
737    fn set_state_hook(&mut self, _hook: impl FnMut(StateUpdate) + Send + 'static) -> bool {
738        false
739    }
740
741    /// Provides mutable access to the inner [`BlockExecutor`].
742    fn executor_mut(&mut self) -> &mut Self::Executor;
743
744    /// Provides access to the inner [`BlockExecutor`].
745    fn executor(&self) -> &Self::Executor;
746
747    /// Provides mutable access to the underlying EVM.
748    fn evm_mut(&mut self) -> &mut <Self::Executor as BlockExecutor>::Evm {
749        self.executor_mut().evm_mut()
750    }
751
752    /// Provides access to the underlying EVM.
753    fn evm(&self) -> &<Self::Executor as BlockExecutor>::Evm {
754        self.executor().evm()
755    }
756
757    /// Consumes the type and returns the underlying executor.
758    fn into_executor(self) -> Self::Executor;
759}
760
761/// A block builder backed by a configured [`BlockExecutor`].
762#[derive(Debug)]
763pub struct BasicBlockBuilder<'a, F, Executor, Assembler, N>
764where
765    F: BlockExecutorFactory + 'a,
766    N: NodePrimitives,
767{
768    /// The block executor used to execute transactions.
769    pub executor: Executor,
770    /// EVM environment used for execution.
771    pub evm_env: F::EvmEnv,
772    /// Transactions executed in this block.
773    pub transactions: Vec<Recovered<TxTy<N>>>,
774    /// Block execution context.
775    pub ctx: F::ExecutionCtx<'a>,
776    /// Parent block header.
777    pub parent: &'a SealedHeader<HeaderTy<N>>,
778    /// Block assembler.
779    pub assembler: &'a Assembler,
780}
781
782impl<'a, F, Assembler, N> BasicBlockBuilder<'a, F, F::Executor<'a>, Assembler, N>
783where
784    F: BlockExecutorFactory<Transaction = TxTy<N>, Receipt = ReceiptTy<N>> + 'a,
785    Assembler: BlockAssembler<F, Block = N::Block> + 'a,
786    N: NodePrimitives,
787{
788    /// Creates a block builder that accumulates bundle state in the execution output.
789    pub fn new(
790        executor_factory: &'a F,
791        assembler: &'a Assembler,
792        evm: F::Evm<'a>,
793        evm_env: F::EvmEnv,
794        parent: &'a SealedHeader<HeaderTy<N>>,
795        ctx: F::ExecutionCtx<'a>,
796    ) -> Self {
797        Self {
798            executor: executor_factory.create_executor(evm, ctx.clone()),
799            evm_env,
800            transactions: Vec::new(),
801            ctx,
802            parent,
803            assembler,
804        }
805    }
806}
807
808/// Executable transaction consumed by a block executor.
809pub trait ExecutorTx<Executor: BlockExecutor>:
810    ExecutableTxParts<Recovered<<Executor::Evm as Evm>::Transaction>, Executor::Transaction>
811{
812}
813
814impl<T, Executor> ExecutorTx<Executor> for T
815where
816    Executor: BlockExecutor,
817    T: ExecutableTxParts<Recovered<<Executor::Evm as Evm>::Transaction>, Executor::Transaction>,
818{
819}
820
821impl<'a, F, Executor, Assembler, N> BlockBuilder
822    for BasicBlockBuilder<'a, F, Executor, Assembler, N>
823where
824    F: BlockExecutorFactory<Transaction = TxTy<N>, Receipt = ReceiptTy<N>>,
825    Executor: BlockExecutor<
826        Transaction = TxTy<N>,
827        Receipt = ReceiptTy<N>,
828        Evm: Evm<EvmTypes = F::EvmTypes>,
829    >,
830    Assembler: BlockAssembler<F, Block = N::Block>,
831    N: NodePrimitives,
832    TxTy<N>: Clone,
833    <<Executor as BlockExecutor>::Evm as Evm>::Transaction: FromTxWithEncoded<TxTy<N>>,
834{
835    type Primitives = N;
836    type Executor = Executor;
837    type EvmEnv = F::EvmEnv;
838
839    fn evm_env(&self) -> &Self::EvmEnv {
840        &self.evm_env
841    }
842
843    fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError> {
844        self.executor.apply_pre_execution_changes()
845    }
846
847    fn execute_transaction_with_commit_condition(
848        &mut self,
849        tx: impl ExecutorTx<Self::Executor>,
850        f: impl FnOnce(
851            &evm2::TxResult<<<Self::Executor as BlockExecutor>::Evm as Evm>::EvmTypes>,
852            evm2::TxStateChanges<'_>,
853        ) -> CommitChanges,
854    ) -> Result<Option<GasOutput>, BlockExecutionError> {
855        let (tx_env, tx) = tx.into_parts();
856        if let Some(output) =
857            self.executor.execute_transaction_with_commit_condition((tx_env, &tx), f)?
858        {
859            self.transactions.push(Recovered::new_unchecked(tx.tx().clone(), *tx.signer()));
860            Ok(Some(output))
861        } else {
862            Ok(None)
863        }
864    }
865
866    fn execute_transaction_with_result_closure(
867        &mut self,
868        tx: impl ExecutorTx<Self::Executor>,
869        f: impl FnOnce(&<Self::Executor as BlockExecutor>::TransactionResultWithState),
870    ) -> Result<GasOutput, BlockExecutionError> {
871        let (tx_env, tx) = tx.into_parts();
872        let output = self.executor.execute_transaction_without_commit((tx_env, &tx))?;
873        f(&output);
874        let gas_output = self.executor.commit_transaction(output)?;
875        self.transactions.push(Recovered::new_unchecked(tx.tx().clone(), *tx.signer()));
876        Ok(gas_output)
877    }
878
879    fn finish_with_state_root(
880        self,
881        state_provider: impl StateProvider,
882        state_root: impl FnOnce(
883            &BlockExecutionOutput<ReceiptTy<Self::Primitives>>,
884        ) -> Result<Option<(B256, TrieUpdates)>, BlockExecutionError>,
885    ) -> Result<BlockBuilderOutcome<N>, BlockExecutionError> {
886        let Self { executor, evm_env, transactions, ctx, parent, assembler } = self;
887
888        let (output, block_access_list) = executor.finish_with_block_access_list()?;
889        let block_access_list = block_access_list.map(|bal| {
890            let mut raw = Vec::new();
891            let hash = compute_block_access_list_hash_with_buf(&bal, &mut raw);
892            DecodedBal::new_unchecked(bal.into(), raw.into(), hash)
893        });
894        let block_access_list_hash = block_access_list.as_ref().map(DecodedBal::hash);
895        let hashed_state =
896            state_provider.hashed_post_state(&output.state).map_err(BlockExecutionError::other)?;
897        let (state_root, trie_updates) = match state_root(&output)? {
898            Some(precomputed) => precomputed,
899            None => state_provider
900                .state_root_with_updates(hashed_state.clone())
901                .map_err(BlockExecutionError::other)?,
902        };
903        let (transactions, senders) = transactions.into_iter().map(|tx| tx.into_parts()).unzip();
904
905        let block = assembler.assemble_block(BlockAssemblerInput {
906            evm_env,
907            execution_ctx: ctx,
908            parent,
909            transactions,
910            output: &output.result,
911            execution_state: &output.state,
912            state_provider: &state_provider,
913            state_root,
914            block_access_list_hash,
915        })?;
916        let block = RecoveredBlock::new_unhashed(block, senders);
917
918        Ok(BlockBuilderOutcome {
919            execution_result: output.result,
920            execution_state: output.state,
921            hashed_state,
922            trie_updates,
923            block,
924            block_access_list,
925        })
926    }
927
928    fn set_state_hook(&mut self, hook: impl FnMut(StateUpdate) + Send + 'static) -> bool {
929        self.executor.set_state_hook(hook)
930    }
931
932    fn executor_mut(&mut self) -> &mut Self::Executor {
933        &mut self.executor
934    }
935
936    fn executor(&self) -> &Self::Executor {
937        &self.executor
938    }
939
940    fn into_executor(self) -> Self::Executor {
941        self.executor
942    }
943}
944
945/// A type that knows how to execute blocks over a database.
946pub trait Executor<DB: Database>: Sized {
947    /// The primitive types used by the executor.
948    type Primitives: NodePrimitives;
949    /// The error type returned by the executor.
950    type Error;
951
952    /// Executes a single block and returns [`BlockExecutionResult`], without the state changes.
953    fn execute_one(
954        &mut self,
955        block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
956    ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>;
957
958    /// Executes a single block and emits transaction state updates to the provided hook.
959    fn execute_one_with_state_hook<F>(
960        &mut self,
961        block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
962        state_hook: F,
963    ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
964    where
965        F: FnMut(StateUpdate) + Send + 'static;
966
967    /// Consumes the type and executes the block.
968    fn execute(
969        mut self,
970        block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
971    ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
972    {
973        let result = self.execute_one(block)?;
974        let state = self.into_state();
975        Ok(BlockExecutionOutput::new(result, state))
976    }
977
978    /// Consumes the type, executes the block, and emits transaction state updates to the provided
979    /// hook.
980    fn execute_with_state_hook<F>(
981        mut self,
982        block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
983        state_hook: F,
984    ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
985    where
986        F: FnMut(StateUpdate) + Send + 'static,
987    {
988        let result = self.execute_one_with_state_hook(block, state_hook)?;
989        let state = self.into_state();
990        Ok(BlockExecutionOutput::new(result, state))
991    }
992
993    /// Executes the block and invokes `f` with the accumulated execution state after execution.
994    fn execute_with_state_closure<F>(
995        mut self,
996        block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
997        mut f: F,
998    ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
999    where
1000        F: FnMut(&BundleState),
1001    {
1002        let result = self.execute_one(block)?;
1003        let state = self.into_state();
1004        f(&state);
1005
1006        Ok(BlockExecutionOutput::new(result, state))
1007    }
1008
1009    /// Executes the block and always invokes `f` with the accumulated execution state, even when
1010    /// execution fails.
1011    fn execute_with_state_closure_always<F>(
1012        mut self,
1013        block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
1014        mut f: F,
1015    ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
1016    where
1017        F: FnMut(&BundleState),
1018    {
1019        let result = self.execute_one(block);
1020        let state = self.into_state();
1021        f(&state);
1022
1023        Ok(BlockExecutionOutput::new(result?, state))
1024    }
1025
1026    /// Executes multiple inputs in the batch and returns an aggregated [`ExecutionOutcome`].
1027    fn execute_batch<'a, I>(
1028        mut self,
1029        blocks: I,
1030    ) -> Result<ExecutionOutcome<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
1031    where
1032        I: IntoIterator<Item = &'a RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>>,
1033    {
1034        let blocks_iter = blocks.into_iter();
1035        let capacity = blocks_iter.size_hint().0;
1036        let mut results = Vec::with_capacity(capacity);
1037        let mut first_block = None;
1038        for block in blocks_iter {
1039            if first_block.is_none() {
1040                first_block = Some(block.header().number());
1041            }
1042            results.push(self.execute_one(block)?);
1043        }
1044
1045        Ok(ExecutionOutcome::from_blocks(
1046            first_block.unwrap_or_default(),
1047            self.into_state(),
1048            results,
1049        ))
1050    }
1051
1052    /// The size hint of the batch's tracked state size.
1053    fn size_hint(&self) -> usize;
1054
1055    /// Converts the executor into its accumulated batch state.
1056    fn into_state(self) -> BundleState;
1057
1058    /// Takes the canonical block access list from executor.
1059    fn take_bal(&mut self) -> Option<BlockAccessList>;
1060}
1061
1062/// Generic block executor backed by a [`ConfigureEvm`] implementation.
1063#[cfg(feature = "std")]
1064#[expect(missing_debug_implementations)]
1065pub struct BasicBlockExecutor<Evm, DB: Database> {
1066    evm_config: Evm,
1067    batch_database: CacheDB<Db<DB>>,
1068    batch_state: BundleState,
1069    block_access_list: Option<BlockAccessList>,
1070}
1071
1072#[cfg(feature = "std")]
1073impl<Evm, DB: Database> BasicBlockExecutor<Evm, DB> {
1074    /// Creates a new generic block executor.
1075    pub fn new(evm_config: Evm, database: DB) -> Self {
1076        Self {
1077            evm_config,
1078            batch_database: CacheDB::new(Db::new(database)),
1079            batch_state: BundleState::default(),
1080            block_access_list: None,
1081        }
1082    }
1083}
1084
1085#[cfg(feature = "std")]
1086impl<Evm, DB> BasicBlockExecutor<Evm, DB>
1087where
1088    Evm: ConfigureEvm,
1089    DB: Database,
1090{
1091    fn merge_batch_output(
1092        mut batch_state: BundleState,
1093        output: BlockExecutionOutput<ReceiptTy<Evm::Primitives>>,
1094    ) -> BlockExecutionOutput<ReceiptTy<Evm::Primitives>> {
1095        if batch_state.reverts.is_empty() {
1096            return output
1097        }
1098
1099        let result = output.result;
1100        let state = output.state;
1101        batch_state.extend(state);
1102        BlockExecutionOutput::new(result, batch_state)
1103    }
1104
1105    #[expect(clippy::type_complexity)]
1106    fn execute_block_with_database(
1107        evm_config: &Evm,
1108        block: &RecoveredBlock<BlockTy<Evm::Primitives>>,
1109        database: impl DynDatabase,
1110    ) -> Result<
1111        (BlockExecutionOutput<ReceiptTy<Evm::Primitives>>, Option<BlockAccessList>),
1112        BlockExecutionError,
1113    > {
1114        Self::execute_block_with_database_and_state_hook(evm_config, block, database, None)
1115    }
1116
1117    #[expect(clippy::type_complexity)]
1118    fn execute_block_with_database_and_state_hook(
1119        evm_config: &Evm,
1120        block: &RecoveredBlock<BlockTy<Evm::Primitives>>,
1121        database: impl DynDatabase,
1122        state_hook: Option<Box<dyn FnMut(StateUpdate) + Send>>,
1123    ) -> Result<
1124        (BlockExecutionOutput<ReceiptTy<Evm::Primitives>>, Option<BlockAccessList>),
1125        BlockExecutionError,
1126    > {
1127        let evm_env = evm_config.evm_env(block.header()).map_err(BlockExecutionError::other)?;
1128        let evm = evm_config.block_executor_factory().evm_with_env(database, evm_env);
1129        let ctx = evm_config
1130            .context_for_block(block.sealed_block())
1131            .map_err(BlockExecutionError::other)?;
1132        let mut executor = evm_config.block_executor_factory().create_executor(evm, ctx);
1133        if block.header().block_access_list_hash().is_some() {
1134            executor.enable_block_access_list_builder();
1135        }
1136        if let Some(hook) = state_hook &&
1137            !executor.set_state_hook(hook)
1138        {
1139            return Err(BlockExecutionError::msg("block executor does not support state hooks"))
1140        }
1141
1142        executor.apply_pre_execution_changes()?;
1143        for transaction in block.transactions_recovered() {
1144            executor.execute_transaction(transaction)?;
1145        }
1146        let (output, block_access_list) = executor.finish_with_block_access_list()?;
1147        Ok((output, block_access_list))
1148    }
1149}
1150
1151#[cfg(feature = "std")]
1152impl<Evm, DB> Executor<DB> for BasicBlockExecutor<Evm, DB>
1153where
1154    Evm: ConfigureEvm,
1155    DB: Database,
1156{
1157    type Primitives = Evm::Primitives;
1158    type Error = BlockExecutionError;
1159
1160    fn execute_one(
1161        &mut self,
1162        block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
1163    ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
1164    {
1165        let (output, block_access_list) =
1166            Self::execute_block_with_database(&self.evm_config, block, &mut self.batch_database)?;
1167        self.block_access_list = block_access_list;
1168        self.batch_database.commit_source(&BundleSource(&output.state));
1169
1170        let block_state = output.state;
1171        self.batch_state.extend(block_state);
1172
1173        Ok(output.result)
1174    }
1175
1176    fn execute_one_with_state_hook<F>(
1177        &mut self,
1178        block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
1179        state_hook: F,
1180    ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
1181    where
1182        F: FnMut(StateUpdate) + Send + 'static,
1183    {
1184        let (output, block_access_list) = Self::execute_block_with_database_and_state_hook(
1185            &self.evm_config,
1186            block,
1187            &mut self.batch_database,
1188            Some(Box::new(state_hook)),
1189        )?;
1190        self.block_access_list = block_access_list;
1191        self.batch_database.commit_source(&BundleSource(&output.state));
1192
1193        let block_state = output.state;
1194        self.batch_state.extend(block_state);
1195
1196        Ok(output.result)
1197    }
1198
1199    fn execute(
1200        self,
1201        block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
1202    ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
1203    {
1204        let Self { evm_config, batch_database, batch_state, .. } = self;
1205        let (output, _) = Self::execute_block_with_database(&evm_config, block, batch_database)?;
1206        Ok(Self::merge_batch_output(batch_state, output))
1207    }
1208
1209    fn execute_with_state_hook<F>(
1210        self,
1211        block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
1212        state_hook: F,
1213    ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
1214    where
1215        F: FnMut(StateUpdate) + Send + 'static,
1216    {
1217        let Self { evm_config, batch_database, batch_state, .. } = self;
1218        let (output, _) = Self::execute_block_with_database_and_state_hook(
1219            &evm_config,
1220            block,
1221            batch_database,
1222            Some(Box::new(state_hook)),
1223        )?;
1224        Ok(Self::merge_batch_output(batch_state, output))
1225    }
1226
1227    fn size_hint(&self) -> usize {
1228        self.batch_state.size_hint()
1229    }
1230
1231    fn into_state(self) -> BundleState {
1232        self.batch_state
1233    }
1234
1235    fn take_bal(&mut self) -> Option<BlockAccessList> {
1236        self.block_access_list.take()
1237    }
1238}
1239
1240/// Executor returned by configurations that do not support block execution in the active build.
1241#[cfg(not(feature = "std"))]
1242pub(crate) struct UnsupportedExecutor<N> {
1243    _marker: core::marker::PhantomData<N>,
1244}
1245
1246#[cfg(not(feature = "std"))]
1247impl<N> Default for UnsupportedExecutor<N> {
1248    fn default() -> Self {
1249        Self { _marker: core::marker::PhantomData }
1250    }
1251}
1252
1253#[cfg(not(feature = "std"))]
1254impl<N, DB> Executor<DB> for UnsupportedExecutor<N>
1255where
1256    N: NodePrimitives,
1257    DB: Database,
1258{
1259    type Primitives = N;
1260    type Error = BlockExecutionError;
1261
1262    fn execute_one(
1263        &mut self,
1264        _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
1265    ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
1266    {
1267        Err(BlockExecutionError::msg("block execution is unsupported by this EVM configuration"))
1268    }
1269
1270    fn execute(
1271        self,
1272        _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
1273    ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
1274    {
1275        Err(BlockExecutionError::msg("block execution is unsupported by this EVM configuration"))
1276    }
1277
1278    fn execute_one_with_state_hook<F>(
1279        &mut self,
1280        _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
1281        _state_hook: F,
1282    ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
1283    where
1284        F: FnMut(StateUpdate) + Send + 'static,
1285    {
1286        Err(BlockExecutionError::msg("block execution is unsupported by this EVM configuration"))
1287    }
1288
1289    fn execute_with_state_hook<F>(
1290        self,
1291        _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
1292        _state_hook: F,
1293    ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
1294    where
1295        F: FnMut(StateUpdate) + Send + 'static,
1296    {
1297        Err(BlockExecutionError::msg("block execution is unsupported by this EVM configuration"))
1298    }
1299
1300    fn execute_batch<'a, I>(
1301        self,
1302        _blocks: I,
1303    ) -> Result<ExecutionOutcome<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
1304    where
1305        I: IntoIterator<Item = &'a RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>>,
1306    {
1307        Err(BlockExecutionError::msg("block execution is unsupported by this EVM configuration"))
1308    }
1309
1310    fn size_hint(&self) -> usize {
1311        0
1312    }
1313
1314    fn into_state(self) -> BundleState {
1315        BundleState::default()
1316    }
1317
1318    fn take_bal(&mut self) -> Option<BlockAccessList> {
1319        None
1320    }
1321}
1322
1323/// Helper trait to abstract over different [`Recovered<T>`] implementations.
1324///
1325/// Supports owned, borrowed, and shared transactions, including encoded and either wrappers.
1326#[auto_impl::auto_impl(&)]
1327pub trait RecoveredTx<T> {
1328    /// Returns the transaction.
1329    fn tx(&self) -> &T;
1330
1331    /// Returns the signer of the transaction.
1332    fn signer(&self) -> &Address;
1333}
1334
1335impl<T> RecoveredTx<T> for Recovered<&T> {
1336    fn tx(&self) -> &T {
1337        self.inner()
1338    }
1339
1340    fn signer(&self) -> &Address {
1341        self.signer_ref()
1342    }
1343}
1344
1345impl<T> RecoveredTx<T> for Recovered<Arc<T>> {
1346    fn tx(&self) -> &T {
1347        self.inner().as_ref()
1348    }
1349
1350    fn signer(&self) -> &Address {
1351        self.signer_ref()
1352    }
1353}
1354
1355impl<T> RecoveredTx<T> for Recovered<T> {
1356    fn tx(&self) -> &T {
1357        self.inner()
1358    }
1359
1360    fn signer(&self) -> &Address {
1361        self.signer_ref()
1362    }
1363}
1364
1365impl<Tx, T: RecoveredTx<Tx>> RecoveredTx<Tx> for WithEncoded<T> {
1366    fn tx(&self) -> &Tx {
1367        self.1.tx()
1368    }
1369
1370    fn signer(&self) -> &Address {
1371        self.1.signer()
1372    }
1373}
1374
1375impl<L, R, Tx> RecoveredTx<Tx> for Either<L, R>
1376where
1377    L: RecoveredTx<Tx>,
1378    R: RecoveredTx<Tx>,
1379{
1380    fn tx(&self) -> &Tx {
1381        match self {
1382            Self::Left(l) => l.tx(),
1383            Self::Right(r) => r.tx(),
1384        }
1385    }
1386
1387    fn signer(&self) -> &Address {
1388        match self {
1389            Self::Left(l) => l.signer(),
1390            Self::Right(r) => r.signer(),
1391        }
1392    }
1393}
1394
1395impl<Tx, T: RecoveredTx<Tx>> RecoveredTx<Tx> for Arc<T> {
1396    fn tx(&self) -> &Tx {
1397        (**self).tx()
1398    }
1399
1400    fn signer(&self) -> &Address {
1401        (**self).signer()
1402    }
1403}
1404
1405/// Helper trait to split an executable transaction into an EVM transaction environment and its
1406/// recovered transaction accessor.
1407pub trait ExecutableTxParts<TxEnv, T> {
1408    /// The recovered transaction accessor type.
1409    type Recovered: RecoveredTx<T>;
1410
1411    /// Converts the transaction into the executable transaction environment and recovered
1412    /// transaction accessor.
1413    fn into_parts(self) -> (TxEnv, Self::Recovered);
1414}
1415
1416/// Converts a recovered transaction into the configured transaction environment.
1417pub trait FromRecoveredTx<T> {
1418    /// Converts a recovered transaction into the configured transaction environment.
1419    fn from_recovered_tx(tx: Recovered<T>) -> Self;
1420}
1421
1422impl<T> FromRecoveredTx<T> for evm2::ethereum::TxEnvelope
1423where
1424    Self: From<T>,
1425{
1426    fn from_recovered_tx(tx: Recovered<T>) -> Self {
1427        tx.into_inner().into()
1428    }
1429}
1430
1431impl<T, TxEnv> FromRecoveredTx<T> for Recovered<TxEnv>
1432where
1433    TxEnv: FromRecoveredTx<T>,
1434{
1435    fn from_recovered_tx(tx: Recovered<T>) -> Self {
1436        let signer = tx.signer();
1437        Self::new_unchecked(TxEnv::from_recovered_tx(tx), signer)
1438    }
1439}
1440
1441/// Converts a recovered transaction with its encoded bytes into the configured transaction
1442/// environment.
1443pub trait FromTxWithEncoded<T>: FromRecoveredTx<T> {
1444    /// Converts a recovered transaction with its encoded bytes into the configured transaction
1445    /// environment.
1446    fn from_tx_with_encoded(tx: WithEncoded<Recovered<T>>) -> Self
1447    where
1448        Self: Sized,
1449    {
1450        Self::from_recovered_tx(tx.1)
1451    }
1452}
1453
1454impl<T> FromTxWithEncoded<T> for evm2::ethereum::TxEnvelope where Self: From<T> {}
1455
1456impl<T, TxEnv> FromTxWithEncoded<T> for Recovered<TxEnv>
1457where
1458    TxEnv: FromTxWithEncoded<T>,
1459{
1460    fn from_tx_with_encoded(tx: WithEncoded<Recovered<T>>) -> Self {
1461        let signer = tx.1.signer();
1462        Self::new_unchecked(TxEnv::from_tx_with_encoded(tx), signer)
1463    }
1464}
1465
1466/// Converts transaction wrappers into the configured transaction environment.
1467pub trait IntoTxEnv<TxEnv> {
1468    /// Converts this transaction wrapper into the configured transaction environment.
1469    fn into_tx_env(self) -> TxEnv;
1470}
1471
1472impl<T, TxEnv> IntoTxEnv<TxEnv> for Recovered<T>
1473where
1474    TxEnv: FromRecoveredTx<T>,
1475{
1476    fn into_tx_env(self) -> TxEnv {
1477        TxEnv::from_recovered_tx(self)
1478    }
1479}
1480
1481impl<T, TxEnv> IntoTxEnv<TxEnv> for WithEncoded<Recovered<T>>
1482where
1483    TxEnv: FromTxWithEncoded<T>,
1484{
1485    fn into_tx_env(self) -> TxEnv {
1486        TxEnv::from_tx_with_encoded(self)
1487    }
1488}
1489
1490/// Transaction environment that can be adjusted in place, e.g. by RPC simulations that re-execute
1491/// a prepared transaction with a different gas limit or access list.
1492///
1493/// Implementations update the transaction fields only: values derived when the environment was
1494/// created (sender, recovered authorities, or other cached metadata) are preserved.
1495pub trait TransactionEnvMut: Transaction + Clone {
1496    /// Sets the gas limit.
1497    fn set_gas_limit(&mut self, gas_limit: u64);
1498
1499    /// Sets the access list.
1500    fn set_access_list(&mut self, access_list: AccessList);
1501}
1502
1503impl TransactionEnvMut for evm2::ethereum::TxEnvelope {
1504    fn set_gas_limit(&mut self, gas_limit: u64) {
1505        match self {
1506            Self::Legacy(tx) => tx.gas_limit = gas_limit,
1507            Self::Eip2930(tx) => tx.gas_limit = gas_limit,
1508            Self::Eip1559(tx) => tx.gas_limit = gas_limit,
1509            Self::Eip4844(tx) => AsMut::<TxEip4844>::as_mut(tx).gas_limit = gas_limit,
1510            Self::Eip7702(tx) => tx.gas_limit = gas_limit,
1511        }
1512    }
1513
1514    fn set_access_list(&mut self, access_list: AccessList) {
1515        match self {
1516            // Legacy transactions cannot carry an access list, so upgrade them to EIP-2930 like an
1517            // RPC request with an access list would be.
1518            Self::Legacy(tx) => {
1519                let TxLegacy { chain_id, nonce, gas_price, gas_limit, to, value, input } =
1520                    core::mem::take(tx);
1521                *self = Self::Eip2930(TxEip2930 {
1522                    chain_id: chain_id.unwrap_or_default(),
1523                    nonce,
1524                    gas_price,
1525                    gas_limit,
1526                    to,
1527                    value,
1528                    access_list,
1529                    input,
1530                });
1531            }
1532            Self::Eip2930(tx) => tx.access_list = access_list,
1533            Self::Eip1559(tx) => tx.access_list = access_list,
1534            Self::Eip4844(tx) => AsMut::<TxEip4844>::as_mut(tx).access_list = access_list,
1535            Self::Eip7702(tx) => tx.access_list = access_list,
1536        }
1537    }
1538}
1539
1540impl<T: Clone, TxEnv> ExecutableTxParts<TxEnv, T> for Recovered<T>
1541where
1542    TxEnv: FromRecoveredTx<T>,
1543{
1544    type Recovered = Self;
1545
1546    fn into_parts(self) -> (TxEnv, Self) {
1547        (TxEnv::from_recovered_tx(self.clone()), self)
1548    }
1549}
1550
1551impl<T: Clone, TxEnv> ExecutableTxParts<TxEnv, T> for Recovered<&T>
1552where
1553    TxEnv: FromRecoveredTx<T>,
1554{
1555    type Recovered = Self;
1556
1557    fn into_parts(self) -> (TxEnv, Self) {
1558        (
1559            TxEnv::from_recovered_tx(Recovered::new_unchecked(
1560                (*self.inner()).clone(),
1561                *self.signer_ref(),
1562            )),
1563            self,
1564        )
1565    }
1566}
1567
1568impl<T: Clone, TxEnv> ExecutableTxParts<TxEnv, T> for WithEncoded<Recovered<T>>
1569where
1570    TxEnv: FromTxWithEncoded<T>,
1571{
1572    type Recovered = Self;
1573
1574    fn into_parts(self) -> (TxEnv, Self) {
1575        (TxEnv::from_tx_with_encoded(self.clone()), self)
1576    }
1577}
1578
1579impl<TxEnv, Tx, T> ExecutableTxParts<TxEnv, Tx> for (TxEnv, T)
1580where
1581    T: RecoveredTx<Tx>,
1582{
1583    type Recovered = T;
1584
1585    fn into_parts(self) -> (TxEnv, Self::Recovered) {
1586        self
1587    }
1588}
1589
1590impl<L, R, TxEnv, T> ExecutableTxParts<TxEnv, T> for Either<L, R>
1591where
1592    L: ExecutableTxParts<TxEnv, T>,
1593    R: ExecutableTxParts<TxEnv, T>,
1594{
1595    type Recovered = Either<L::Recovered, R::Recovered>;
1596
1597    fn into_parts(self) -> (TxEnv, Self::Recovered) {
1598        match self {
1599            Self::Left(l) => {
1600                let (tx_env, recovered) = l.into_parts();
1601                (tx_env, Either::Left(recovered))
1602            }
1603            Self::Right(r) => {
1604                let (tx_env, recovered) = r.into_parts();
1605                (tx_env, Either::Right(recovered))
1606            }
1607        }
1608    }
1609}
1610
1611/// A helper trait marking a type that can be converted into an [`ExecutableTxParts`] for block
1612/// executor.
1613pub trait ExecutableTxFor<Evm: ConfigureEvm>:
1614    ExecutableTxParts<TxEnvFor<Evm>, TxTy<Evm::Primitives>> + RecoveredTx<TxTy<Evm::Primitives>>
1615{
1616}
1617
1618impl<T, Evm: ConfigureEvm> ExecutableTxFor<Evm> for T where
1619    T: ExecutableTxParts<TxEnvFor<Evm>, TxTy<Evm::Primitives>> + RecoveredTx<TxTy<Evm::Primitives>>
1620{
1621}
1622
1623/// A transaction stored together with its `TxEnv`.
1624///
1625/// See also [`ExecutableTxParts`] for types that can be split into a transaction environment and
1626/// recovered transaction.
1627#[derive(Debug)]
1628pub struct WithTxEnv<TxEnv, T> {
1629    /// The transaction environment for EVM.
1630    pub tx_env: TxEnv,
1631    /// The recovered transaction.
1632    pub tx: Arc<T>,
1633}
1634
1635impl<TxEnv, T> WithTxEnv<TxEnv, T> {
1636    /// Creates a transaction/environment pair from a type that can be split with
1637    /// [`ExecutableTxParts::into_parts`].
1638    pub fn new<Tx, InnerTx>(tx: Tx) -> Self
1639    where
1640        Tx: ExecutableTxParts<TxEnv, InnerTx, Recovered = T>,
1641    {
1642        let (tx_env, tx) = tx.into_parts();
1643        Self { tx_env, tx: Arc::new(tx) }
1644    }
1645}
1646
1647impl<TxEnv: Clone, T> Clone for WithTxEnv<TxEnv, T> {
1648    fn clone(&self) -> Self {
1649        Self { tx_env: self.tx_env.clone(), tx: self.tx.clone() }
1650    }
1651}
1652
1653impl<TxEnv, Tx, T: RecoveredTx<Tx>> RecoveredTx<Tx> for WithTxEnv<TxEnv, T> {
1654    fn tx(&self) -> &Tx {
1655        self.tx.tx()
1656    }
1657
1658    fn signer(&self) -> &Address {
1659        self.tx.signer()
1660    }
1661}
1662
1663impl<TxEnv, T: RecoveredTx<Tx>, Tx> ExecutableTxParts<TxEnv, Tx> for WithTxEnv<TxEnv, T> {
1664    type Recovered = Arc<T>;
1665
1666    fn into_parts(self) -> (TxEnv, Self::Recovered) {
1667        (self.tx_env, self.tx)
1668    }
1669}
1670
1671#[cfg(test)]
1672mod tests {
1673    use super::*;
1674    use alloy_eips::eip2930::AccessListItem;
1675    use alloy_primitives::{Bytes, TxKind, U256};
1676
1677    #[test]
1678    fn traced_inspector_forwards_inspector_config() {
1679        struct NoSteps;
1680        impl<T: evm2::EvmTypesHost> evm2::Inspector<T> for NoSteps {
1681            fn inspector_config(&self) -> evm2::InspectorConfig {
1682                evm2::InspectorConfig::without_steps()
1683            }
1684        }
1685
1686        // a tracer that skips step hooks must not run every opcode through the inspected table
1687        assert_eq!(
1688            evm2::Inspector::<evm2::BaseEvmTypes>::inspector_config(&TracedInspector(NoSteps)),
1689            evm2::InspectorConfig::without_steps(),
1690        );
1691    }
1692
1693    #[test]
1694    fn transaction_env_mut_upgrades_legacy_with_access_list() {
1695        let mut tx = evm2::ethereum::TxEnvelope::Legacy(TxLegacy {
1696            chain_id: Some(1),
1697            nonce: 2,
1698            gas_price: 3,
1699            gas_limit: 4,
1700            to: TxKind::Call(Address::with_last_byte(5)),
1701            value: U256::from(6),
1702            input: Bytes::from_static(&[7]),
1703        });
1704        let access_list = AccessList(vec![AccessListItem {
1705            address: Address::with_last_byte(8),
1706            storage_keys: vec![B256::with_last_byte(9)],
1707        }]);
1708
1709        tx.set_gas_limit(10);
1710        tx.set_access_list(access_list.clone());
1711
1712        assert_eq!(
1713            tx,
1714            evm2::ethereum::TxEnvelope::Eip2930(TxEip2930 {
1715                chain_id: 1,
1716                nonce: 2,
1717                gas_price: 3,
1718                gas_limit: 10,
1719                to: TxKind::Call(Address::with_last_byte(5)),
1720                value: U256::from(6),
1721                access_list,
1722                input: Bytes::from_static(&[7]),
1723            })
1724        );
1725    }
1726
1727    #[test]
1728    fn recovered_tx_wrappers_preserve_transaction_and_signer() {
1729        let transaction = TxLegacy { nonce: 7, ..Default::default() };
1730        let signer = Address::with_last_byte(42);
1731        let recovered = Recovered::new_unchecked(transaction.clone(), signer);
1732
1733        let assert_parts = |tx: &dyn RecoveredTx<TxLegacy>| {
1734            assert_eq!(tx.tx(), &transaction);
1735            assert_eq!(tx.signer(), &signer);
1736        };
1737
1738        assert_parts(&recovered);
1739        assert_parts(&&recovered);
1740        assert_parts(&Recovered::new_unchecked(&transaction, signer));
1741        assert_parts(&Recovered::new_unchecked(Arc::new(transaction.clone()), signer));
1742        assert_parts(&Arc::new(recovered.clone()));
1743        assert_parts(&WithEncoded::new(Bytes::new(), recovered.clone()));
1744        assert_parts(&Either::<_, Recovered<TxLegacy>>::Left(recovered.clone()));
1745        assert_parts(&Either::<Recovered<TxLegacy>, _>::Right(recovered.clone()));
1746        assert_parts(&WithTxEnv { tx_env: (), tx: Arc::new(recovered) });
1747    }
1748}