reth_evm_ethereum/
test_utils.rs

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