1use crate::EthEvmConfig;
2use alloc::{boxed::Box, sync::Arc, vec, vec::Vec};
3use alloy_consensus::Header;
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},
15 ConfigureEngineEvm, ConfigureEvm, Database, EthEvm, EthEvmFactory, Evm, EvmEnvFor, EvmFactory,
16 ExecutableTxIterator, ExecutionCtxFor,
17};
18use reth_execution_types::{BlockExecutionResult, ExecutionOutcome};
19use reth_primitives_traits::{BlockTy, SealedBlock, SealedHeader};
20use revm::{
21 context::result::{ExecutionResult, Output, ResultAndState, SuccessReason},
22 database::State,
23 Inspector,
24};
25
26pub type MockExecutorProvider = MockEvmConfig;
28
29#[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 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 { result: self.exec_results.lock().pop().unwrap(), evm, hook: None }
69 }
70}
71
72#[derive(derive_more::Debug)]
74pub struct MockExecutor<'a, DB: Database, I> {
75 result: ExecutionOutcome,
76 evm: EthEvm<&'a mut State<DB>, I, PrecompilesMap>,
77 #[debug(skip)]
78 hook: Option<Box<dyn reth_evm::OnStateHook>>,
79}
80
81impl<'a, DB: Database, I: Inspector<EthEvmContext<&'a mut State<DB>>>> BlockExecutor
82 for MockExecutor<'a, DB, I>
83{
84 type Evm = EthEvm<&'a mut State<DB>, I, PrecompilesMap>;
85 type Transaction = TransactionSigned;
86 type Receipt = Receipt;
87
88 fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError> {
89 Ok(())
90 }
91
92 fn execute_transaction_without_commit(
93 &mut self,
94 _tx: impl ExecutableTx<Self>,
95 ) -> Result<ResultAndState<<Self::Evm as Evm>::HaltReason>, BlockExecutionError> {
96 Ok(ResultAndState::new(
97 ExecutionResult::Success {
98 reason: SuccessReason::Return,
99 gas_used: 0,
100 gas_refunded: 0,
101 logs: vec![],
102 output: Output::Call(Bytes::from(vec![])),
103 },
104 Default::default(),
105 ))
106 }
107
108 fn commit_transaction(
109 &mut self,
110 _output: ResultAndState<<Self::Evm as Evm>::HaltReason>,
111 _tx: impl ExecutableTx<Self>,
112 ) -> Result<u64, BlockExecutionError> {
113 Ok(0)
114 }
115
116 fn finish(
117 self,
118 ) -> Result<(Self::Evm, BlockExecutionResult<Self::Receipt>), BlockExecutionError> {
119 let Self { result, mut evm, .. } = self;
120 let ExecutionOutcome { bundle, receipts, requests, first_block: _ } = result;
121 let result = BlockExecutionResult {
122 receipts: receipts.into_iter().flatten().collect(),
123 requests: requests.into_iter().fold(Requests::default(), |mut reqs, req| {
124 reqs.extend(req);
125 reqs
126 }),
127 gas_used: 0,
128 blob_gas_used: 0,
129 };
130
131 evm.db_mut().bundle_state = bundle;
132
133 Ok((evm, result))
134 }
135
136 fn set_state_hook(&mut self, hook: Option<Box<dyn reth_evm::OnStateHook>>) {
137 self.hook = hook;
138 }
139
140 fn evm(&self) -> &Self::Evm {
141 &self.evm
142 }
143
144 fn evm_mut(&mut self) -> &mut Self::Evm {
145 &mut self.evm
146 }
147}
148
149impl ConfigureEvm for MockEvmConfig {
150 type BlockAssembler = <EthEvmConfig as ConfigureEvm>::BlockAssembler;
151 type BlockExecutorFactory = Self;
152 type Error = <EthEvmConfig as ConfigureEvm>::Error;
153 type NextBlockEnvCtx = <EthEvmConfig as ConfigureEvm>::NextBlockEnvCtx;
154 type Primitives = <EthEvmConfig as ConfigureEvm>::Primitives;
155
156 fn block_executor_factory(&self) -> &Self::BlockExecutorFactory {
157 self
158 }
159
160 fn block_assembler(&self) -> &Self::BlockAssembler {
161 self.inner.block_assembler()
162 }
163
164 fn evm_env(&self, header: &Header) -> Result<EvmEnvFor<Self>, Self::Error> {
165 self.inner.evm_env(header)
166 }
167
168 fn next_evm_env(
169 &self,
170 parent: &Header,
171 attributes: &Self::NextBlockEnvCtx,
172 ) -> Result<EvmEnvFor<Self>, Self::Error> {
173 self.inner.next_evm_env(parent, attributes)
174 }
175
176 fn context_for_block<'a>(
177 &self,
178 block: &'a SealedBlock<BlockTy<Self::Primitives>>,
179 ) -> Result<reth_evm::ExecutionCtxFor<'a, Self>, Self::Error> {
180 self.inner.context_for_block(block)
181 }
182
183 fn context_for_next_block(
184 &self,
185 parent: &SealedHeader,
186 attributes: Self::NextBlockEnvCtx,
187 ) -> Result<reth_evm::ExecutionCtxFor<'_, Self>, Self::Error> {
188 self.inner.context_for_next_block(parent, attributes)
189 }
190}
191
192impl ConfigureEngineEvm<ExecutionData> for MockEvmConfig {
193 fn evm_env_for_payload(&self, payload: &ExecutionData) -> Result<EvmEnvFor<Self>, Self::Error> {
194 self.inner.evm_env_for_payload(payload)
195 }
196
197 fn context_for_payload<'a>(
198 &self,
199 payload: &'a ExecutionData,
200 ) -> Result<ExecutionCtxFor<'a, Self>, Self::Error> {
201 self.inner.context_for_payload(payload)
202 }
203
204 fn tx_iterator_for_payload(
205 &self,
206 payload: &ExecutionData,
207 ) -> Result<impl ExecutableTxIterator<Self>, Self::Error> {
208 self.inner.tx_iterator_for_payload(payload)
209 }
210}