reth_evm/
execute.rs

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