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