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