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::{bal::DecodedBal, compute_block_access_list_hash_with_buf, 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), with its RLP bytes and
318    /// hash.
319    pub block_access_list: Option<DecodedBal>,
320}
321
322/// A type that knows how to execute and build a block.
323///
324/// It wraps an inner [`BlockExecutor`] and provides a way to execute transactions and
325/// construct a block.
326///
327/// This is a helper to erase `BasicBlockBuilder` type.
328pub trait BlockBuilder {
329    /// The primitive types used by the inner [`BlockExecutor`].
330    type Primitives: NodePrimitives;
331    /// Inner [`BlockExecutor`].
332    type Executor: BlockExecutor<
333        Transaction = TxTy<Self::Primitives>,
334        Receipt = ReceiptTy<Self::Primitives>,
335    >;
336
337    /// Invokes [`BlockExecutor::apply_pre_execution_changes`].
338    fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError>;
339
340    /// Invokes [`BlockExecutor::execute_transaction_with_commit_condition`] and saves the
341    /// transaction in internal state only if the transaction was committed.
342    fn execute_transaction_with_commit_condition(
343        &mut self,
344        tx: impl ExecutorTx<Self::Executor>,
345        f: impl FnOnce(&<Self::Executor as BlockExecutor>::Result) -> CommitChanges,
346    ) -> Result<Option<GasOutput>, BlockExecutionError>;
347
348    /// Invokes [`BlockExecutor::execute_transaction_with_result_closure`] and saves the
349    /// transaction in internal state.
350    fn execute_transaction_with_result_closure(
351        &mut self,
352        tx: impl ExecutorTx<Self::Executor>,
353        f: impl FnOnce(&<Self::Executor as BlockExecutor>::Result),
354    ) -> Result<GasOutput, BlockExecutionError> {
355        self.execute_transaction_with_commit_condition(tx, |res| {
356            f(res);
357            CommitChanges::Yes
358        })
359        .map(Option::unwrap_or_default)
360    }
361
362    /// Invokes [`BlockExecutor::execute_transaction`] and saves the transaction in
363    /// internal state.
364    fn execute_transaction(
365        &mut self,
366        tx: impl ExecutorTx<Self::Executor>,
367    ) -> Result<GasOutput, BlockExecutionError> {
368        self.execute_transaction_with_result_closure(tx, |_| ())
369    }
370
371    /// Completes the block building process and returns the [`BlockBuilderOutcome`].
372    ///
373    /// When `state_root_precomputed` is `None`, the state root is computed internally via
374    /// `state_root_with_updates()`. When `Some`, the provided root and trie updates are used
375    /// directly, skipping the expensive computation (e.g. when using the sparse trie pipeline).
376    fn finish(
377        self,
378        state_provider: impl StateProvider,
379        state_root_precomputed: Option<(B256, TrieUpdates)>,
380    ) -> Result<BlockBuilderOutcome<Self::Primitives>, BlockExecutionError>;
381
382    /// Provides mutable access to the inner [`BlockExecutor`].
383    fn executor_mut(&mut self) -> &mut Self::Executor;
384
385    /// Provides access to the inner [`BlockExecutor`].
386    fn executor(&self) -> &Self::Executor;
387
388    /// Helper to access inner [`BlockExecutor::Evm`] mutably.
389    fn evm_mut(&mut self) -> &mut <Self::Executor as BlockExecutor>::Evm {
390        self.executor_mut().evm_mut()
391    }
392
393    /// Helper to access inner [`BlockExecutor::Evm`].
394    fn evm(&self) -> &<Self::Executor as BlockExecutor>::Evm {
395        self.executor().evm()
396    }
397
398    /// Consumes the type and returns the underlying [`BlockExecutor`].
399    fn into_executor(self) -> Self::Executor;
400}
401
402/// A type that constructs a block from transactions and execution results.
403#[derive(Debug)]
404pub struct BasicBlockBuilder<'a, F, Executor, Builder, N: NodePrimitives>
405where
406    F: BlockExecutorFactory,
407{
408    /// The block executor used to execute transactions.
409    pub executor: Executor,
410    /// The transactions executed in this block.
411    pub transactions: Vec<Recovered<TxTy<N>>>,
412    /// The parent block execution context.
413    pub ctx: F::ExecutionCtx<'a>,
414    /// The sealed parent block header.
415    pub parent: &'a SealedHeader<HeaderTy<N>>,
416    /// The assembler used to build the block.
417    pub assembler: Builder,
418}
419
420/// Conversions for executable transactions.
421pub trait ExecutorTx<Executor: BlockExecutor> {
422    /// Converts the transaction into a tuple of [`TxEnvFor`] and [`Recovered`].
423    fn into_parts(self) -> (<Executor::Evm as Evm>::Tx, Recovered<Executor::Transaction>);
424}
425
426impl<Executor: BlockExecutor> ExecutorTx<Executor>
427    for WithEncoded<Recovered<Executor::Transaction>>
428{
429    fn into_parts(self) -> (<Executor::Evm as Evm>::Tx, Recovered<Executor::Transaction>) {
430        (self.to_tx_env(), self.1)
431    }
432}
433
434impl<Executor: BlockExecutor> ExecutorTx<Executor> for Recovered<Executor::Transaction> {
435    fn into_parts(self) -> (<Executor::Evm as Evm>::Tx, Self) {
436        (self.to_tx_env(), self)
437    }
438}
439
440impl<Executor: BlockExecutor> ExecutorTx<Executor>
441    for (<Executor::Evm as Evm>::Tx, Recovered<Executor::Transaction>)
442{
443    fn into_parts(self) -> (<Executor::Evm as Evm>::Tx, Recovered<Executor::Transaction>) {
444        self
445    }
446}
447
448impl<Executor> ExecutorTx<Executor>
449    for WithTxEnv<<Executor::Evm as Evm>::Tx, Recovered<Executor::Transaction>>
450where
451    Executor: BlockExecutor<Transaction: Clone>,
452{
453    fn into_parts(self) -> (<Executor::Evm as Evm>::Tx, Recovered<Executor::Transaction>) {
454        (self.tx_env, Arc::unwrap_or_clone(self.tx))
455    }
456}
457
458impl<'a, F, DB, Executor, Builder, N> BlockBuilder
459    for BasicBlockBuilder<'a, F, Executor, Builder, N>
460where
461    F: BlockExecutorFactory<Transaction = N::SignedTx, Receipt = N::Receipt>,
462    Executor: BlockExecutor<
463        Evm: Evm<
464            Spec = <F::EvmFactory as EvmFactory>::Spec,
465            HaltReason = <F::EvmFactory as EvmFactory>::HaltReason,
466            BlockEnv = <F::EvmFactory as EvmFactory>::BlockEnv,
467            DB = &'a mut State<DB>,
468        >,
469        Transaction = N::SignedTx,
470        Receipt = N::Receipt,
471    >,
472    DB: Database + 'a,
473    Builder: BlockAssembler<F, Block = N::Block>,
474    N: NodePrimitives,
475{
476    type Primitives = N;
477    type Executor = Executor;
478
479    fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError> {
480        self.executor.apply_pre_execution_changes()?;
481        self.executor.evm_mut().db_mut().bump_bal_index();
482
483        Ok(())
484    }
485
486    fn execute_transaction_with_commit_condition(
487        &mut self,
488        tx: impl ExecutorTx<Self::Executor>,
489        f: impl FnOnce(&<Self::Executor as BlockExecutor>::Result) -> CommitChanges,
490    ) -> Result<Option<GasOutput>, BlockExecutionError> {
491        let (tx_env, tx) = tx.into_parts();
492        if let Some(gas_used) =
493            self.executor.execute_transaction_with_commit_condition((tx_env, &tx), f)?
494        {
495            self.transactions.push(tx);
496            self.executor.evm_mut().db_mut().bump_bal_index();
497            Ok(Some(gas_used))
498        } else {
499            Ok(None)
500        }
501    }
502
503    fn finish(
504        self,
505        state: impl StateProvider,
506        state_root_precomputed: Option<(B256, TrieUpdates)>,
507    ) -> Result<BlockBuilderOutcome<N>, BlockExecutionError> {
508        let (evm, result) = self.executor.finish()?;
509        let (db, evm_env) = evm.finish();
510
511        // merge all transitions into bundle state
512        db.merge_transitions(BundleRetention::Reverts);
513
514        // Encode the built BAL once and keep the bytes, so callers don't re-encode it.
515        let block_access_list = db.take_built_alloy_bal().map(|bal| {
516            let mut raw = Vec::new();
517            let hash = compute_block_access_list_hash_with_buf(&bal, &mut raw);
518            DecodedBal::new_unchecked(bal.into(), raw.into(), hash)
519        });
520        let block_access_list_hash = block_access_list.as_ref().map(DecodedBal::hash);
521
522        let hashed_state =
523            state.hashed_post_state(&db.bundle_state).map_err(BlockExecutionError::other)?;
524        let (state_root, trie_updates) = match state_root_precomputed {
525            Some(precomputed) => precomputed,
526            None => state
527                .state_root_with_updates(hashed_state.clone())
528                .map_err(BlockExecutionError::other)?,
529        };
530
531        let (transactions, senders) =
532            self.transactions.into_iter().map(|tx| tx.into_parts()).unzip();
533
534        let block = self.assembler.assemble_block(BlockAssemblerInput {
535            evm_env,
536            execution_ctx: self.ctx,
537            parent: self.parent,
538            transactions,
539            output: &result,
540            bundle_state: &db.bundle_state,
541            state_provider: &state,
542            state_root,
543            block_access_list_hash,
544        })?;
545
546        let block = RecoveredBlock::new_unhashed(block, senders);
547
548        Ok(BlockBuilderOutcome {
549            execution_result: result,
550            hashed_state,
551            trie_updates,
552            block,
553            block_access_list,
554        })
555    }
556
557    fn executor_mut(&mut self) -> &mut Self::Executor {
558        &mut self.executor
559    }
560
561    fn executor(&self) -> &Self::Executor {
562        &self.executor
563    }
564
565    fn into_executor(self) -> Self::Executor {
566        self.executor
567    }
568}
569
570/// A generic block executor that uses a [`BlockExecutor`] to
571/// execute blocks.
572#[expect(missing_debug_implementations)]
573pub struct BasicBlockExecutor<F, DB> {
574    /// Block execution strategy.
575    pub(crate) strategy_factory: F,
576    /// Database.
577    pub(crate) db: State<DB>,
578}
579
580impl<F, DB: Database> BasicBlockExecutor<F, DB> {
581    /// Creates a new `BasicBlockExecutor` with the given strategy.
582    pub fn new(strategy_factory: F, db: DB) -> Self {
583        let db = State::builder().with_database(db).with_bundle_update().build();
584        Self { strategy_factory, db }
585    }
586}
587
588impl<F, DB> Executor<DB> for BasicBlockExecutor<F, DB>
589where
590    F: ConfigureEvm,
591    DB: Database,
592{
593    type Primitives = F::Primitives;
594    type Error = BlockExecutionError;
595
596    fn execute_one(
597        &mut self,
598        block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
599    ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
600    {
601        let mut executor = self
602            .strategy_factory
603            .executor_for_block(&mut self.db, block)
604            .map_err(BlockExecutionError::other)?;
605
606        let has_bal = block.header().block_access_list_hash().is_some();
607
608        if has_bal {
609            executor.evm_mut().db_mut().bal_state.bal_builder = Some(Bal::new());
610        } else {
611            executor.evm_mut().db_mut().bal_state.bal_builder = None;
612        }
613
614        executor.apply_pre_execution_changes()?;
615
616        if has_bal {
617            executor.evm_mut().db_mut().bump_bal_index();
618        }
619
620        for tx in block.transactions_recovered() {
621            executor.execute_transaction(tx)?;
622            if has_bal {
623                executor.evm_mut().db_mut().bump_bal_index();
624            }
625        }
626
627        let result = executor.apply_post_execution_changes()?;
628
629        self.db.merge_transitions(BundleRetention::Reverts);
630
631        Ok(result)
632    }
633
634    fn execute_one_with_state_hook<H>(
635        &mut self,
636        block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
637        state_hook: H,
638    ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
639    where
640        H: OnStateHook + 'static,
641    {
642        let mut executor = self
643            .strategy_factory
644            .executor_for_block(&mut self.db, block)
645            .map_err(BlockExecutionError::other)?;
646
647        executor.evm_mut().db_mut().set_state_hook(Some(Box::new(state_hook)));
648
649        let result = executor.execute_block(block.transactions_recovered());
650
651        self.db.set_state_hook(None);
652        self.db.merge_transitions(BundleRetention::Reverts);
653
654        result
655    }
656
657    fn into_state(self) -> State<DB> {
658        self.db
659    }
660
661    fn size_hint(&self) -> usize {
662        self.db.bundle_size_hint()
663    }
664
665    fn take_bal(&mut self) -> Option<BlockAccessList> {
666        self.db.take_built_alloy_bal()
667    }
668}
669
670/// A helper trait marking a 'static type that can be converted into an [`ExecutableTxParts`] for
671/// block executor.
672pub trait ExecutableTxFor<Evm: ConfigureEvm>:
673    ExecutableTxParts<TxEnvFor<Evm>, TxTy<Evm::Primitives>> + RecoveredTx<TxTy<Evm::Primitives>>
674{
675}
676
677impl<T, Evm: ConfigureEvm> ExecutableTxFor<Evm> for T where
678    T: ExecutableTxParts<TxEnvFor<Evm>, TxTy<Evm::Primitives>> + RecoveredTx<TxTy<Evm::Primitives>>
679{
680}
681
682/// A transaction stored together with its `TxEnv`.
683///
684/// See also [`ExecutableTxParts`] for types that can be split into a transaction environment and
685/// recovered transaction.
686#[derive(Debug)]
687pub struct WithTxEnv<TxEnv, T> {
688    /// The transaction environment for EVM.
689    pub tx_env: TxEnv,
690    /// The recovered transaction.
691    pub tx: Arc<T>,
692}
693
694impl<TxEnv, T> WithTxEnv<TxEnv, T> {
695    /// Creates a transaction/environment pair from a type that can be split with
696    /// [`ExecutableTxParts::into_parts`].
697    pub fn new<Tx, InnerTx>(tx: Tx) -> Self
698    where
699        Tx: ExecutableTxParts<TxEnv, InnerTx, Recovered = T>,
700    {
701        let (tx_env, tx) = tx.into_parts();
702        Self { tx_env, tx: Arc::new(tx) }
703    }
704}
705
706impl<TxEnv: Clone, T> Clone for WithTxEnv<TxEnv, T> {
707    fn clone(&self) -> Self {
708        Self { tx_env: self.tx_env.clone(), tx: self.tx.clone() }
709    }
710}
711
712impl<TxEnv, Tx, T: RecoveredTx<Tx>> RecoveredTx<Tx> for WithTxEnv<TxEnv, T> {
713    fn tx(&self) -> &Tx {
714        self.tx.tx()
715    }
716
717    fn signer(&self) -> &Address {
718        self.tx.signer()
719    }
720}
721
722impl<TxEnv, T: RecoveredTx<Tx>, Tx> ExecutableTxParts<TxEnv, Tx> for WithTxEnv<TxEnv, T> {
723    type Recovered = Arc<T>;
724
725    fn into_parts(self) -> (TxEnv, Self::Recovered) {
726        (self.tx_env, self.tx)
727    }
728}
729
730#[cfg(test)]
731mod tests {
732
733    use super::*;
734    use core::marker::PhantomData;
735    use reth_ethereum_primitives::EthPrimitives;
736    use revm::database::InMemoryDB;
737
738    #[derive(Clone, Debug, Default)]
739    struct TestExecutorProvider;
740
741    impl TestExecutorProvider {
742        fn executor<DB>(&self, _db: DB) -> TestExecutor<DB>
743        where
744            DB: Database,
745        {
746            TestExecutor(PhantomData)
747        }
748    }
749
750    struct TestExecutor<DB>(PhantomData<DB>);
751
752    impl<DB: Database> Executor<DB> for TestExecutor<DB> {
753        type Primitives = EthPrimitives;
754        type Error = BlockExecutionError;
755
756        fn execute_one(
757            &mut self,
758            _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
759        ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
760        {
761            Err(BlockExecutionError::msg("execution unavailable for tests"))
762        }
763
764        fn execute_one_with_state_hook<F>(
765            &mut self,
766            _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
767            _state_hook: F,
768        ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
769        where
770            F: OnStateHook + 'static,
771        {
772            Err(BlockExecutionError::msg("execution unavailable for tests"))
773        }
774
775        fn into_state(self) -> State<DB> {
776            unreachable!()
777        }
778
779        fn size_hint(&self) -> usize {
780            0
781        }
782
783        fn take_bal(&mut self) -> Option<BlockAccessList> {
784            None
785        }
786    }
787
788    #[test]
789    fn test_provider() {
790        let provider = TestExecutorProvider;
791        let db = InMemoryDB::default();
792        let executor = provider.executor(db);
793        let _ = executor.execute(&Default::default());
794    }
795}