reth_evm_ethereum/
test_utils.rs

1use crate::EthEvmConfig;
2use alloc::{boxed::Box, sync::Arc, vec::Vec};
3use alloy_consensus::Header;
4use alloy_eips::eip7685::Requests;
5use alloy_evm::precompiles::PrecompilesMap;
6use parking_lot::Mutex;
7use reth_ethereum_primitives::{Receipt, TransactionSigned};
8use reth_evm::{
9    block::{
10        BlockExecutionError, BlockExecutor, BlockExecutorFactory, BlockExecutorFor, CommitChanges,
11    },
12    eth::{EthBlockExecutionCtx, EthEvmContext},
13    ConfigureEvm, Database, EthEvm, EthEvmFactory, Evm, EvmEnvFor, EvmFactory,
14};
15use reth_execution_types::{BlockExecutionResult, ExecutionOutcome};
16use reth_primitives_traits::{BlockTy, SealedBlock, SealedHeader};
17use revm::{
18    context::result::{ExecutionResult, HaltReason},
19    database::State,
20    Inspector,
21};
22
23/// A helper type alias for mocked block executor provider.
24pub type MockExecutorProvider = MockEvmConfig;
25
26/// A block executor provider that returns mocked execution results.
27#[derive(Clone, Debug)]
28pub struct MockEvmConfig {
29    inner: EthEvmConfig,
30    exec_results: Arc<Mutex<Vec<ExecutionOutcome>>>,
31}
32
33impl Default for MockEvmConfig {
34    fn default() -> Self {
35        Self { inner: EthEvmConfig::mainnet(), exec_results: Default::default() }
36    }
37}
38
39impl MockEvmConfig {
40    /// Extend the mocked execution results
41    pub fn extend(&self, results: impl IntoIterator<Item = impl Into<ExecutionOutcome>>) {
42        self.exec_results.lock().extend(results.into_iter().map(Into::into));
43    }
44}
45
46impl BlockExecutorFactory for MockEvmConfig {
47    type EvmFactory = EthEvmFactory;
48    type ExecutionCtx<'a> = EthBlockExecutionCtx<'a>;
49    type Receipt = Receipt;
50    type Transaction = TransactionSigned;
51
52    fn evm_factory(&self) -> &Self::EvmFactory {
53        self.inner.evm_factory()
54    }
55
56    fn create_executor<'a, DB, I>(
57        &'a self,
58        evm: EthEvm<&'a mut State<DB>, I, PrecompilesMap>,
59        _ctx: Self::ExecutionCtx<'a>,
60    ) -> impl BlockExecutorFor<'a, Self, DB, I>
61    where
62        DB: Database + 'a,
63        I: Inspector<<Self::EvmFactory as EvmFactory>::Context<&'a mut State<DB>>> + 'a,
64    {
65        MockExecutor { result: self.exec_results.lock().pop().unwrap(), evm, hook: None }
66    }
67}
68
69/// Mock executor that returns a fixed execution result.
70#[derive(derive_more::Debug)]
71pub struct MockExecutor<'a, DB: Database, I> {
72    result: ExecutionOutcome,
73    evm: EthEvm<&'a mut State<DB>, I, PrecompilesMap>,
74    #[debug(skip)]
75    hook: Option<Box<dyn reth_evm::OnStateHook>>,
76}
77
78impl<'a, DB: Database, I: Inspector<EthEvmContext<&'a mut State<DB>>>> BlockExecutor
79    for MockExecutor<'a, DB, I>
80{
81    type Evm = EthEvm<&'a mut State<DB>, I, PrecompilesMap>;
82    type Transaction = TransactionSigned;
83    type Receipt = Receipt;
84
85    fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError> {
86        Ok(())
87    }
88
89    fn execute_transaction_with_commit_condition(
90        &mut self,
91        _tx: impl alloy_evm::block::ExecutableTx<Self>,
92        _f: impl FnOnce(&ExecutionResult<HaltReason>) -> CommitChanges,
93    ) -> Result<Option<u64>, BlockExecutionError> {
94        Ok(Some(0))
95    }
96
97    fn finish(
98        self,
99    ) -> Result<(Self::Evm, BlockExecutionResult<Self::Receipt>), BlockExecutionError> {
100        let Self { result, mut evm, .. } = self;
101        let ExecutionOutcome { bundle, receipts, requests, first_block: _ } = result;
102        let result = BlockExecutionResult {
103            receipts: receipts.into_iter().flatten().collect(),
104            requests: requests.into_iter().fold(Requests::default(), |mut reqs, req| {
105                reqs.extend(req);
106                reqs
107            }),
108            gas_used: 0,
109        };
110
111        evm.db_mut().bundle_state = bundle;
112
113        Ok((evm, result))
114    }
115
116    fn set_state_hook(&mut self, hook: Option<Box<dyn reth_evm::OnStateHook>>) {
117        self.hook = hook;
118    }
119
120    fn evm(&self) -> &Self::Evm {
121        &self.evm
122    }
123
124    fn evm_mut(&mut self) -> &mut Self::Evm {
125        &mut self.evm
126    }
127}
128
129impl ConfigureEvm for MockEvmConfig {
130    type BlockAssembler = <EthEvmConfig as ConfigureEvm>::BlockAssembler;
131    type BlockExecutorFactory = Self;
132    type Error = <EthEvmConfig as ConfigureEvm>::Error;
133    type NextBlockEnvCtx = <EthEvmConfig as ConfigureEvm>::NextBlockEnvCtx;
134    type Primitives = <EthEvmConfig as ConfigureEvm>::Primitives;
135
136    fn block_executor_factory(&self) -> &Self::BlockExecutorFactory {
137        self
138    }
139
140    fn block_assembler(&self) -> &Self::BlockAssembler {
141        self.inner.block_assembler()
142    }
143
144    fn evm_env(&self, header: &Header) -> EvmEnvFor<Self> {
145        self.inner.evm_env(header)
146    }
147
148    fn next_evm_env(
149        &self,
150        parent: &Header,
151        attributes: &Self::NextBlockEnvCtx,
152    ) -> Result<EvmEnvFor<Self>, Self::Error> {
153        self.inner.next_evm_env(parent, attributes)
154    }
155
156    fn context_for_block<'a>(
157        &self,
158        block: &'a SealedBlock<BlockTy<Self::Primitives>>,
159    ) -> reth_evm::ExecutionCtxFor<'a, Self> {
160        self.inner.context_for_block(block)
161    }
162
163    fn context_for_next_block(
164        &self,
165        parent: &SealedHeader,
166        attributes: Self::NextBlockEnvCtx,
167    ) -> reth_evm::ExecutionCtxFor<'_, Self> {
168        self.inner.context_for_next_block(parent, attributes)
169    }
170}