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