Skip to main content

reth_evm/
execute.rs

1//! Traits for execution.
2
3use crate::{ConfigureEvm, Database, OnStateHook, TxEnvFor};
4use alloc::{boxed::Box, sync::Arc, vec::Vec};
5use alloy_consensus::{BlockHeader, Header};
6use alloy_eip7928::{compute_block_access_list_hash, BlockAccessList};
7use alloy_eips::eip2718::WithEncoded;
8pub use alloy_evm::block::{BlockExecutor, BlockExecutorFactory, GasOutput};
9use alloy_evm::{
10    block::{CommitChanges, ExecutableTxParts},
11    Evm, EvmEnv, EvmFactory, RecoveredTx, ToTxEnv,
12};
13use alloy_primitives::{Address, B256};
14pub use reth_execution_errors::{
15    BlockExecutionError, BlockValidationError, InternalBlockExecutionError,
16};
17use reth_execution_types::BlockExecutionResult;
18pub use reth_execution_types::{BlockExecutionOutput, ExecutionOutcome};
19use reth_primitives_traits::{
20    Block, HeaderTy, NodePrimitives, ReceiptTy, Recovered, RecoveredBlock, SealedHeader, TxTy,
21};
22use reth_storage_api::StateProvider;
23pub use reth_storage_errors::provider::ProviderError;
24use reth_trie_common::{updates::TrieUpdates, HashedPostState};
25use revm::{
26    database::{states::bundle_state::BundleRetention, BundleState, State},
27    state::bal::Bal,
28};
29
30/// A type that knows how to execute a block. It is assumed to operate on a
31/// [`crate::Evm`] internally and use [`State`] as database.
32pub trait Executor<DB: Database>: Sized {
33    /// The primitive types used by the executor.
34    type Primitives: NodePrimitives;
35    /// The error type returned by the executor.
36    type Error;
37
38    /// Executes a single block and returns [`BlockExecutionResult`], without the state changes.
39    fn execute_one(
40        &mut self,
41        block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
42    ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>;
43
44    /// Executes the EVM with the given input and accepts a state hook closure that is invoked with
45    /// the EVM state after execution.
46    fn execute_one_with_state_hook<F>(
47        &mut self,
48        block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
49        state_hook: F,
50    ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
51    where
52        F: OnStateHook + 'static;
53
54    /// Consumes the type and executes the block.
55    ///
56    /// # Note
57    /// Execution happens without any validation of the output.
58    ///
59    /// # Returns
60    /// The output of the block execution.
61    fn execute(
62        mut self,
63        block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
64    ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
65    {
66        let result = self.execute_one(block)?;
67        let mut state = self.into_state();
68        Ok(BlockExecutionOutput { state: state.take_bundle(), result })
69    }
70
71    /// Executes multiple inputs in the batch, and returns an aggregated [`ExecutionOutcome`].
72    fn execute_batch<'a, I>(
73        mut self,
74        blocks: I,
75    ) -> Result<ExecutionOutcome<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
76    where
77        I: IntoIterator<Item = &'a RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>>,
78    {
79        let blocks_iter = blocks.into_iter();
80        let capacity = blocks_iter.size_hint().0;
81        let mut results = Vec::with_capacity(capacity);
82        let mut first_block = None;
83        for block in blocks_iter {
84            if first_block.is_none() {
85                first_block = Some(block.header().number());
86            }
87            results.push(self.execute_one(block)?);
88        }
89
90        Ok(ExecutionOutcome::from_blocks(
91            first_block.unwrap_or_default(),
92            self.into_state().take_bundle(),
93            results,
94        ))
95    }
96
97    /// Executes the EVM with the given input and accepts a state closure that is invoked with
98    /// the EVM state after execution.
99    fn execute_with_state_closure<F>(
100        mut self,
101        block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
102        mut f: F,
103    ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
104    where
105        F: FnMut(&State<DB>),
106    {
107        let result = self.execute_one(block)?;
108        let mut state = self.into_state();
109        f(&state);
110        Ok(BlockExecutionOutput { state: state.take_bundle(), result })
111    }
112
113    /// Executes the EVM with the given input and accepts a state closure that is always invoked
114    /// with the EVM state after execution, even after failure.
115    fn execute_with_state_closure_always<F>(
116        mut self,
117        block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
118        mut f: F,
119    ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
120    where
121        F: FnMut(&State<DB>),
122    {
123        let result = self.execute_one(block);
124        let mut state = self.into_state();
125        f(&state);
126
127        Ok(BlockExecutionOutput { state: state.take_bundle(), result: result? })
128    }
129
130    /// Executes the EVM with the given input and accepts a state hook closure that is invoked with
131    /// the EVM state after execution.
132    fn execute_with_state_hook<F>(
133        mut self,
134        block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
135        state_hook: F,
136    ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
137    where
138        F: OnStateHook + 'static,
139    {
140        let result = self.execute_one_with_state_hook(block, state_hook)?;
141        let mut state = self.into_state();
142        Ok(BlockExecutionOutput { state: state.take_bundle(), result })
143    }
144
145    /// Consumes the executor and returns the [`State`] containing all state changes.
146    fn into_state(self) -> State<DB>;
147
148    /// The size hint of the batch's tracked state size.
149    ///
150    /// This is used to optimize DB commits depending on the size of the state.
151    fn size_hint(&self) -> usize;
152
153    /// Takes built [`BlockAccessList`] from executor.
154    fn take_bal(&mut self) -> Option<BlockAccessList>;
155}
156
157/// Input for block building. Consumed by [`BlockAssembler`].
158///
159/// This struct contains all the data needed by the [`BlockAssembler`] to create
160/// a complete block after transaction execution.
161///
162/// # Fields Overview
163///
164/// - `evm_env`: The EVM configuration used during execution (spec ID, block env, etc.)
165/// - `execution_ctx`: Additional context like withdrawals and ommers
166/// - `parent`: The parent block header this block builds on
167/// - `transactions`: All transactions that were successfully executed
168/// - `output`: Execution results including receipts and gas used
169/// - `bundle_state`: Accumulated state changes from all transactions
170/// - `state_provider`: Access to the current state for additional lookups
171/// - `state_root`: The calculated state root after all changes
172/// - `block_access_list_hash`: Block access list hash (EIP-7928, Amsterdam)
173///
174/// # Usage
175///
176/// This is typically created internally by [`BlockBuilder::finish`] after all
177/// transactions have been executed:
178///
179/// ```rust,ignore
180/// let input = BlockAssemblerInput {
181///     evm_env: builder.evm_env(),
182///     execution_ctx: builder.context(),
183///     parent: &parent_header,
184///     transactions: executed_transactions,
185///     output: &execution_result,
186///     bundle_state: &state_changes,
187///     state_provider: &state,
188///     state_root: calculated_root,
189///     block_access_list_hash: Some(calculated_bal_hash),
190/// };
191///
192/// let block = assembler.assemble_block(input)?;
193/// ```
194#[derive(derive_more::Debug)]
195#[non_exhaustive]
196pub struct BlockAssemblerInput<'a, 'b, F: BlockExecutorFactory, H = Header> {
197    /// Configuration of EVM used when executing the block.
198    ///
199    /// Contains context relevant to EVM such as [`revm::context::BlockEnv`].
200    pub evm_env:
201        EvmEnv<<F::EvmFactory as EvmFactory>::Spec, <F::EvmFactory as EvmFactory>::BlockEnv>,
202    /// [`BlockExecutorFactory::ExecutionCtx`] used to execute the block.
203    pub execution_ctx: F::ExecutionCtx<'a>,
204    /// Parent block header.
205    pub parent: &'a SealedHeader<H>,
206    /// Transactions that were executed in this block.
207    pub transactions: Vec<F::Transaction>,
208    /// Output of block execution.
209    pub output: &'b BlockExecutionResult<F::Receipt>,
210    /// [`BundleState`] after the block execution.
211    pub bundle_state: &'a BundleState,
212    /// Provider with access to state.
213    #[debug(skip)]
214    pub state_provider: &'b dyn StateProvider,
215    /// State root for this block.
216    pub state_root: B256,
217    /// Block access list hash (EIP-7928, Amsterdam).
218    pub block_access_list_hash: Option<B256>,
219}
220
221impl<'a, 'b, F: BlockExecutorFactory, H> BlockAssemblerInput<'a, 'b, F, H> {
222    /// Creates a new [`BlockAssemblerInput`].
223    #[expect(clippy::too_many_arguments)]
224    pub fn new(
225        evm_env: EvmEnv<
226            <F::EvmFactory as EvmFactory>::Spec,
227            <F::EvmFactory as EvmFactory>::BlockEnv,
228        >,
229        execution_ctx: F::ExecutionCtx<'a>,
230        parent: &'a SealedHeader<H>,
231        transactions: Vec<F::Transaction>,
232        output: &'b BlockExecutionResult<F::Receipt>,
233        bundle_state: &'a BundleState,
234        state_provider: &'b dyn StateProvider,
235        state_root: B256,
236        block_access_list_hash: Option<B256>,
237    ) -> Self {
238        Self {
239            evm_env,
240            execution_ctx,
241            parent,
242            transactions,
243            output,
244            bundle_state,
245            state_provider,
246            state_root,
247            block_access_list_hash,
248        }
249    }
250}
251
252/// A type that knows how to assemble a block from execution results.
253///
254/// The [`BlockAssembler`] is the final step in block production. After transactions
255/// have been executed by the [`BlockExecutor`], the assembler takes all the execution
256/// outputs and creates a properly formatted block.
257///
258/// # Responsibilities
259///
260/// The assembler is responsible for:
261/// - Setting the correct block header fields (gas used, receipts root, logs bloom, etc.)
262/// - Including the executed transactions in the correct order
263/// - Setting the state root from the post-execution state
264/// - Applying any chain-specific rules or adjustments
265///
266/// # Example Flow
267///
268/// ```rust,ignore
269/// // 1. Execute transactions and get results
270/// let execution_result = block_executor.finish()?;
271///
272/// // 2. Calculate state root from changes
273/// let state_root = state_provider.state_root(&bundle_state)?;
274///
275/// // 3. Assemble the final block
276/// let block = assembler.assemble_block(BlockAssemblerInput {
277///     evm_env,           // Environment used during execution
278///     execution_ctx,     // Context like withdrawals, ommers
279///     parent,            // Parent block header
280///     transactions,      // Executed transactions
281///     output,            // Execution results (receipts, gas)
282///     bundle_state,      // All state changes
283///     state_provider,    // For additional lookups if needed
284///     state_root,        // Computed state root
285/// })?;
286/// ```
287///
288/// # Relationship with Block Building
289///
290/// The assembler works together with:
291/// - `NextBlockEnvAttributes`: Provides the configuration for the new block
292/// - [`BlockExecutor`]: Executes transactions and produces results
293/// - [`BlockBuilder`]: Orchestrates the entire process and calls the assembler
294#[auto_impl::auto_impl(&, Arc)]
295pub trait BlockAssembler<F: BlockExecutorFactory> {
296    /// The block type produced by the assembler.
297    type Block: Block;
298
299    /// Builds a block. see [`BlockAssemblerInput`] documentation for more details.
300    fn assemble_block(
301        &self,
302        input: BlockAssemblerInput<'_, '_, F, <Self::Block as Block>::Header>,
303    ) -> Result<Self::Block, BlockExecutionError>;
304}
305
306/// Output of block building.
307#[derive(Debug, Clone)]
308pub struct BlockBuilderOutcome<N: NodePrimitives> {
309    /// Result of block execution.
310    pub execution_result: BlockExecutionResult<N::Receipt>,
311    /// Hashed state after execution.
312    pub hashed_state: HashedPostState,
313    /// Trie updates collected during state root calculation.
314    pub trie_updates: TrieUpdates,
315    /// The built block.
316    pub block: RecoveredBlock<N::Block>,
317    /// Block access list built during execution (EIP-7928, Amsterdam).
318    pub block_access_list: Option<BlockAccessList>,
319}
320
321/// A type that knows how to execute and build a block.
322///
323/// It wraps an inner [`BlockExecutor`] and provides a way to execute transactions and
324/// construct a block.
325///
326/// This is a helper to erase `BasicBlockBuilder` type.
327pub trait BlockBuilder {
328    /// The primitive types used by the inner [`BlockExecutor`].
329    type Primitives: NodePrimitives;
330    /// Inner [`BlockExecutor`].
331    type Executor: BlockExecutor<
332        Transaction = TxTy<Self::Primitives>,
333        Receipt = ReceiptTy<Self::Primitives>,
334    >;
335
336    /// Invokes [`BlockExecutor::apply_pre_execution_changes`].
337    fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError>;
338
339    /// Invokes [`BlockExecutor::execute_transaction_with_commit_condition`] and saves the
340    /// transaction in internal state only if the transaction was committed.
341    fn execute_transaction_with_commit_condition(
342        &mut self,
343        tx: impl ExecutorTx<Self::Executor>,
344        f: impl FnOnce(&<Self::Executor as BlockExecutor>::Result) -> CommitChanges,
345    ) -> Result<Option<GasOutput>, BlockExecutionError>;
346
347    /// Invokes [`BlockExecutor::execute_transaction_with_result_closure`] and saves the
348    /// transaction in internal state.
349    fn execute_transaction_with_result_closure(
350        &mut self,
351        tx: impl ExecutorTx<Self::Executor>,
352        f: impl FnOnce(&<Self::Executor as BlockExecutor>::Result),
353    ) -> Result<GasOutput, BlockExecutionError> {
354        self.execute_transaction_with_commit_condition(tx, |res| {
355            f(res);
356            CommitChanges::Yes
357        })
358        .map(Option::unwrap_or_default)
359    }
360
361    /// Invokes [`BlockExecutor::execute_transaction`] and saves the transaction in
362    /// internal state.
363    fn execute_transaction(
364        &mut self,
365        tx: impl ExecutorTx<Self::Executor>,
366    ) -> Result<GasOutput, BlockExecutionError> {
367        self.execute_transaction_with_result_closure(tx, |_| ())
368    }
369
370    /// Completes the block building process and returns the [`BlockBuilderOutcome`].
371    ///
372    /// When `state_root_precomputed` is `None`, the state root is computed internally via
373    /// `state_root_with_updates()`. When `Some`, the provided root and trie updates are used
374    /// directly, skipping the expensive computation (e.g. when using the sparse trie pipeline).
375    fn finish(
376        self,
377        state_provider: impl StateProvider,
378        state_root_precomputed: Option<(B256, TrieUpdates)>,
379    ) -> Result<BlockBuilderOutcome<Self::Primitives>, BlockExecutionError>;
380
381    /// Provides mutable access to the inner [`BlockExecutor`].
382    fn executor_mut(&mut self) -> &mut Self::Executor;
383
384    /// Provides access to the inner [`BlockExecutor`].
385    fn executor(&self) -> &Self::Executor;
386
387    /// Helper to access inner [`BlockExecutor::Evm`] mutably.
388    fn evm_mut(&mut self) -> &mut <Self::Executor as BlockExecutor>::Evm {
389        self.executor_mut().evm_mut()
390    }
391
392    /// Helper to access inner [`BlockExecutor::Evm`].
393    fn evm(&self) -> &<Self::Executor as BlockExecutor>::Evm {
394        self.executor().evm()
395    }
396
397    /// Consumes the type and returns the underlying [`BlockExecutor`].
398    fn into_executor(self) -> Self::Executor;
399}
400
401/// A type that constructs a block from transactions and execution results.
402#[derive(Debug)]
403pub struct BasicBlockBuilder<'a, F, Executor, Builder, N: NodePrimitives>
404where
405    F: BlockExecutorFactory,
406{
407    /// The block executor used to execute transactions.
408    pub executor: Executor,
409    /// The transactions executed in this block.
410    pub transactions: Vec<Recovered<TxTy<N>>>,
411    /// The parent block execution context.
412    pub ctx: F::ExecutionCtx<'a>,
413    /// The sealed parent block header.
414    pub parent: &'a SealedHeader<HeaderTy<N>>,
415    /// The assembler used to build the block.
416    pub assembler: Builder,
417}
418
419/// Conversions for executable transactions.
420pub trait ExecutorTx<Executor: BlockExecutor> {
421    /// Converts the transaction into a tuple of [`TxEnvFor`] and [`Recovered`].
422    fn into_parts(self) -> (<Executor::Evm as Evm>::Tx, Recovered<Executor::Transaction>);
423}
424
425impl<Executor: BlockExecutor> ExecutorTx<Executor>
426    for WithEncoded<Recovered<Executor::Transaction>>
427{
428    fn into_parts(self) -> (<Executor::Evm as Evm>::Tx, Recovered<Executor::Transaction>) {
429        (self.to_tx_env(), self.1)
430    }
431}
432
433impl<Executor: BlockExecutor> ExecutorTx<Executor> for Recovered<Executor::Transaction> {
434    fn into_parts(self) -> (<Executor::Evm as Evm>::Tx, Self) {
435        (self.to_tx_env(), self)
436    }
437}
438
439impl<Executor: BlockExecutor> ExecutorTx<Executor>
440    for (<Executor::Evm as Evm>::Tx, Recovered<Executor::Transaction>)
441{
442    fn into_parts(self) -> (<Executor::Evm as Evm>::Tx, Recovered<Executor::Transaction>) {
443        self
444    }
445}
446
447impl<Executor> ExecutorTx<Executor>
448    for WithTxEnv<<Executor::Evm as Evm>::Tx, Recovered<Executor::Transaction>>
449where
450    Executor: BlockExecutor<Transaction: Clone>,
451{
452    fn into_parts(self) -> (<Executor::Evm as Evm>::Tx, Recovered<Executor::Transaction>) {
453        (self.tx_env, Arc::unwrap_or_clone(self.tx))
454    }
455}
456
457impl<'a, F, DB, Executor, Builder, N> BlockBuilder
458    for BasicBlockBuilder<'a, F, Executor, Builder, N>
459where
460    F: BlockExecutorFactory<Transaction = N::SignedTx, Receipt = N::Receipt>,
461    Executor: BlockExecutor<
462        Evm: Evm<
463            Spec = <F::EvmFactory as EvmFactory>::Spec,
464            HaltReason = <F::EvmFactory as EvmFactory>::HaltReason,
465            BlockEnv = <F::EvmFactory as EvmFactory>::BlockEnv,
466            DB = &'a mut State<DB>,
467        >,
468        Transaction = N::SignedTx,
469        Receipt = N::Receipt,
470    >,
471    DB: Database + 'a,
472    Builder: BlockAssembler<F, Block = N::Block>,
473    N: NodePrimitives,
474{
475    type Primitives = N;
476    type Executor = Executor;
477
478    fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError> {
479        self.executor.apply_pre_execution_changes()?;
480        self.executor.evm_mut().db_mut().bump_bal_index();
481
482        Ok(())
483    }
484
485    fn execute_transaction_with_commit_condition(
486        &mut self,
487        tx: impl ExecutorTx<Self::Executor>,
488        f: impl FnOnce(&<Self::Executor as BlockExecutor>::Result) -> CommitChanges,
489    ) -> Result<Option<GasOutput>, BlockExecutionError> {
490        let (tx_env, tx) = tx.into_parts();
491        if let Some(gas_used) =
492            self.executor.execute_transaction_with_commit_condition((tx_env, &tx), f)?
493        {
494            self.transactions.push(tx);
495            self.executor.evm_mut().db_mut().bump_bal_index();
496            Ok(Some(gas_used))
497        } else {
498            Ok(None)
499        }
500    }
501
502    fn finish(
503        self,
504        state: impl StateProvider,
505        state_root_precomputed: Option<(B256, TrieUpdates)>,
506    ) -> Result<BlockBuilderOutcome<N>, BlockExecutionError> {
507        let (evm, result) = self.executor.finish()?;
508        let (db, evm_env) = evm.finish();
509
510        // merge all transitions into bundle state
511        db.merge_transitions(BundleRetention::Reverts);
512
513        let block_access_list = db.take_built_alloy_bal();
514        let block_access_list_hash =
515            block_access_list.as_ref().map(|bal| compute_block_access_list_hash(bal.as_slice()));
516
517        let hashed_state =
518            state.hashed_post_state(&db.bundle_state).map_err(BlockExecutionError::other)?;
519        let (state_root, trie_updates) = match state_root_precomputed {
520            Some(precomputed) => precomputed,
521            None => state
522                .state_root_with_updates(hashed_state.clone())
523                .map_err(BlockExecutionError::other)?,
524        };
525
526        let (transactions, senders) =
527            self.transactions.into_iter().map(|tx| tx.into_parts()).unzip();
528
529        let block = self.assembler.assemble_block(BlockAssemblerInput {
530            evm_env,
531            execution_ctx: self.ctx,
532            parent: self.parent,
533            transactions,
534            output: &result,
535            bundle_state: &db.bundle_state,
536            state_provider: &state,
537            state_root,
538            block_access_list_hash,
539        })?;
540
541        let block = RecoveredBlock::new_unhashed(block, senders);
542
543        Ok(BlockBuilderOutcome {
544            execution_result: result,
545            hashed_state,
546            trie_updates,
547            block,
548            block_access_list,
549        })
550    }
551
552    fn executor_mut(&mut self) -> &mut Self::Executor {
553        &mut self.executor
554    }
555
556    fn executor(&self) -> &Self::Executor {
557        &self.executor
558    }
559
560    fn into_executor(self) -> Self::Executor {
561        self.executor
562    }
563}
564
565/// A generic block executor that uses a [`BlockExecutor`] to
566/// execute blocks.
567#[expect(missing_debug_implementations)]
568pub struct BasicBlockExecutor<F, DB> {
569    /// Block execution strategy.
570    pub(crate) strategy_factory: F,
571    /// Database.
572    pub(crate) db: State<DB>,
573}
574
575impl<F, DB: Database> BasicBlockExecutor<F, DB> {
576    /// Creates a new `BasicBlockExecutor` with the given strategy.
577    pub fn new(strategy_factory: F, db: DB) -> Self {
578        let db = State::builder().with_database(db).with_bundle_update().build();
579        Self { strategy_factory, db }
580    }
581}
582
583impl<F, DB> Executor<DB> for BasicBlockExecutor<F, DB>
584where
585    F: ConfigureEvm,
586    DB: Database,
587{
588    type Primitives = F::Primitives;
589    type Error = BlockExecutionError;
590
591    fn execute_one(
592        &mut self,
593        block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
594    ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
595    {
596        let mut executor = self
597            .strategy_factory
598            .executor_for_block(&mut self.db, block)
599            .map_err(BlockExecutionError::other)?;
600
601        let has_bal = block.header().block_access_list_hash().is_some();
602
603        if has_bal {
604            executor.evm_mut().db_mut().bal_state.bal_builder = Some(Bal::new());
605        } else {
606            executor.evm_mut().db_mut().bal_state.bal_builder = None;
607        }
608
609        executor.apply_pre_execution_changes()?;
610
611        if has_bal {
612            executor.evm_mut().db_mut().bump_bal_index();
613        }
614
615        for tx in block.transactions_recovered() {
616            executor.execute_transaction(tx)?;
617            if has_bal {
618                executor.evm_mut().db_mut().bump_bal_index();
619            }
620        }
621
622        let result = executor.apply_post_execution_changes()?;
623
624        self.db.merge_transitions(BundleRetention::Reverts);
625
626        Ok(result)
627    }
628
629    fn execute_one_with_state_hook<H>(
630        &mut self,
631        block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
632        state_hook: H,
633    ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
634    where
635        H: OnStateHook + 'static,
636    {
637        let mut executor = self
638            .strategy_factory
639            .executor_for_block(&mut self.db, block)
640            .map_err(BlockExecutionError::other)?;
641
642        executor.evm_mut().db_mut().set_state_hook(Some(Box::new(state_hook)));
643
644        let result = executor.execute_block(block.transactions_recovered());
645
646        self.db.set_state_hook(None);
647        self.db.merge_transitions(BundleRetention::Reverts);
648
649        result
650    }
651
652    fn into_state(self) -> State<DB> {
653        self.db
654    }
655
656    fn size_hint(&self) -> usize {
657        self.db.bundle_state.size_hint()
658    }
659
660    fn take_bal(&mut self) -> Option<BlockAccessList> {
661        self.db.take_built_alloy_bal()
662    }
663}
664
665/// A helper trait marking a 'static type that can be converted into an [`ExecutableTxParts`] for
666/// block executor.
667pub trait ExecutableTxFor<Evm: ConfigureEvm>:
668    ExecutableTxParts<TxEnvFor<Evm>, TxTy<Evm::Primitives>> + RecoveredTx<TxTy<Evm::Primitives>>
669{
670}
671
672impl<T, Evm: ConfigureEvm> ExecutableTxFor<Evm> for T where
673    T: ExecutableTxParts<TxEnvFor<Evm>, TxTy<Evm::Primitives>> + RecoveredTx<TxTy<Evm::Primitives>>
674{
675}
676
677/// A transaction stored together with its `TxEnv`.
678///
679/// See also [`ExecutableTxParts`] for types that can be split into a transaction environment and
680/// recovered transaction.
681#[derive(Debug)]
682pub struct WithTxEnv<TxEnv, T> {
683    /// The transaction environment for EVM.
684    pub tx_env: TxEnv,
685    /// The recovered transaction.
686    pub tx: Arc<T>,
687}
688
689impl<TxEnv, T> WithTxEnv<TxEnv, T> {
690    /// Creates a transaction/environment pair from a type that can be split with
691    /// [`ExecutableTxParts::into_parts`].
692    pub fn new<Tx, InnerTx>(tx: Tx) -> Self
693    where
694        Tx: ExecutableTxParts<TxEnv, InnerTx, Recovered = T>,
695    {
696        let (tx_env, tx) = tx.into_parts();
697        Self { tx_env, tx: Arc::new(tx) }
698    }
699}
700
701impl<TxEnv: Clone, T> Clone for WithTxEnv<TxEnv, T> {
702    fn clone(&self) -> Self {
703        Self { tx_env: self.tx_env.clone(), tx: self.tx.clone() }
704    }
705}
706
707impl<TxEnv, Tx, T: RecoveredTx<Tx>> RecoveredTx<Tx> for WithTxEnv<TxEnv, T> {
708    fn tx(&self) -> &Tx {
709        self.tx.tx()
710    }
711
712    fn signer(&self) -> &Address {
713        self.tx.signer()
714    }
715}
716
717impl<TxEnv, T: RecoveredTx<Tx>, Tx> ExecutableTxParts<TxEnv, Tx> for WithTxEnv<TxEnv, T> {
718    type Recovered = Arc<T>;
719
720    fn into_parts(self) -> (TxEnv, Self::Recovered) {
721        (self.tx_env, self.tx)
722    }
723}
724
725#[cfg(test)]
726mod tests {
727    use super::*;
728    use core::marker::PhantomData;
729    use reth_ethereum_primitives::EthPrimitives;
730    use revm::database::{CacheDB, EmptyDB};
731
732    #[derive(Clone, Debug, Default)]
733    struct TestExecutorProvider;
734
735    impl TestExecutorProvider {
736        fn executor<DB>(&self, _db: DB) -> TestExecutor<DB>
737        where
738            DB: Database,
739        {
740            TestExecutor(PhantomData)
741        }
742    }
743
744    struct TestExecutor<DB>(PhantomData<DB>);
745
746    impl<DB: Database> Executor<DB> for TestExecutor<DB> {
747        type Primitives = EthPrimitives;
748        type Error = BlockExecutionError;
749
750        fn execute_one(
751            &mut self,
752            _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
753        ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
754        {
755            Err(BlockExecutionError::msg("execution unavailable for tests"))
756        }
757
758        fn execute_one_with_state_hook<F>(
759            &mut self,
760            _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
761            _state_hook: F,
762        ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
763        where
764            F: OnStateHook + 'static,
765        {
766            Err(BlockExecutionError::msg("execution unavailable for tests"))
767        }
768
769        fn into_state(self) -> State<DB> {
770            unreachable!()
771        }
772
773        fn size_hint(&self) -> usize {
774            0
775        }
776
777        fn take_bal(&mut self) -> Option<BlockAccessList> {
778            None
779        }
780    }
781
782    #[test]
783    fn test_provider() {
784        let provider = TestExecutorProvider;
785        let db = CacheDB::<EmptyDB>::default();
786        let executor = provider.executor(db);
787        let _ = executor.execute(&Default::default());
788    }
789}