1pub use env::NextEvmEnvAttributes;
4
5#[cfg(feature = "op")]
6pub(crate) use env::EvmEnvInput;
7
8use crate::{env::EvmEnv, evm::EvmFactory, precompiles::PrecompilesMap, Database, Evm};
9use alloy_primitives::{Address, Bytes};
10use core::{
11 fmt::Debug,
12 ops::{Deref, DerefMut},
13};
14use revm::{
15 context::{BlockEnv, CfgEnv, Evm as RevmEvm, TxEnv},
16 context_interface::result::{EVMError, HaltReason, ResultAndState},
17 handler::{instructions::EthInstructions, EthFrame, EthPrecompiles, PrecompileProvider},
18 inspector::NoOpInspector,
19 interpreter::{interpreter::EthInterpreter, InterpreterResult},
20 precompile::{PrecompileSpecId, Precompiles},
21 primitives::hardfork::SpecId,
22 Context, ExecuteEvm, InspectEvm, Inspector, MainBuilder, MainContext, SystemCallEvm,
23};
24
25mod block;
26pub use block::*;
27
28pub mod dao_fork;
29pub mod eip6110;
30pub mod receipt_builder;
31pub mod spec;
32
33mod env;
34pub(crate) mod spec_id;
35
36pub type EthEvmContext<DB> = Context<BlockEnv, TxEnv, CfgEnv, DB>;
38
39#[derive(Debug)]
41pub struct EthEvmBuilder<DB: Database, I = NoOpInspector> {
42 db: DB,
43 block_env: BlockEnv,
44 cfg_env: CfgEnv,
45 inspector: I,
46 inspect: bool,
47 precompiles: Option<PrecompilesMap>,
48}
49
50impl<DB: Database> EthEvmBuilder<DB, NoOpInspector> {
51 pub const fn new(db: DB, env: EvmEnv) -> Self {
53 Self {
54 db,
55 block_env: env.block_env,
56 cfg_env: env.cfg_env,
57 inspector: NoOpInspector {},
58 inspect: false,
59 precompiles: None,
60 }
61 }
62}
63
64impl<DB: Database, I> EthEvmBuilder<DB, I> {
65 pub fn inspector<J>(self, inspector: J) -> EthEvmBuilder<DB, J> {
67 EthEvmBuilder {
68 db: self.db,
69 block_env: self.block_env,
70 cfg_env: self.cfg_env,
71 inspector,
72 inspect: self.inspect,
73 precompiles: self.precompiles,
74 }
75 }
76
77 pub fn activate_inspector<J>(self, inspector: J) -> EthEvmBuilder<DB, J> {
79 self.inspector(inspector).inspect()
80 }
81
82 pub const fn set_inspect(mut self, inspect: bool) -> Self {
84 self.inspect = inspect;
85 self
86 }
87
88 pub const fn inspect(self) -> Self {
90 self.set_inspect(true)
91 }
92
93 pub fn precompiles(mut self, precompiles: PrecompilesMap) -> Self {
96 self.precompiles = Some(precompiles);
97 self
98 }
99
100 pub fn build(self) -> EthEvm<DB, I, PrecompilesMap>
102 where
103 I: Inspector<EthEvmContext<DB>>,
104 {
105 let precompiles = match self.precompiles {
106 Some(p) => p,
107 None => PrecompilesMap::from_static(Precompiles::new(PrecompileSpecId::from_spec_id(
108 self.cfg_env.spec,
109 ))),
110 };
111
112 let inner = Context::mainnet()
113 .with_block(self.block_env)
114 .with_cfg(self.cfg_env)
115 .with_db(self.db)
116 .build_mainnet_with_inspector(self.inspector)
117 .with_precompiles(precompiles);
118
119 EthEvm { inner, inspect: self.inspect }
120 }
121}
122
123#[expect(missing_debug_implementations)]
129pub struct EthEvm<DB: Database, I, PRECOMPILE = EthPrecompiles> {
130 inner: RevmEvm<
131 EthEvmContext<DB>,
132 I,
133 EthInstructions<EthInterpreter, EthEvmContext<DB>>,
134 PRECOMPILE,
135 EthFrame,
136 >,
137 inspect: bool,
138}
139
140impl<DB: Database, I, PRECOMPILE> EthEvm<DB, I, PRECOMPILE> {
141 pub const fn new(
146 evm: RevmEvm<
147 EthEvmContext<DB>,
148 I,
149 EthInstructions<EthInterpreter, EthEvmContext<DB>>,
150 PRECOMPILE,
151 EthFrame,
152 >,
153 inspect: bool,
154 ) -> Self {
155 Self { inner: evm, inspect }
156 }
157
158 pub fn into_inner(
160 self,
161 ) -> RevmEvm<
162 EthEvmContext<DB>,
163 I,
164 EthInstructions<EthInterpreter, EthEvmContext<DB>>,
165 PRECOMPILE,
166 EthFrame,
167 > {
168 self.inner
169 }
170
171 pub const fn ctx(&self) -> &EthEvmContext<DB> {
173 &self.inner.ctx
174 }
175
176 pub const fn ctx_mut(&mut self) -> &mut EthEvmContext<DB> {
178 &mut self.inner.ctx
179 }
180}
181
182impl<DB: Database, I, PRECOMPILE> Deref for EthEvm<DB, I, PRECOMPILE> {
183 type Target = EthEvmContext<DB>;
184
185 #[inline]
186 fn deref(&self) -> &Self::Target {
187 self.ctx()
188 }
189}
190
191impl<DB: Database, I, PRECOMPILE> DerefMut for EthEvm<DB, I, PRECOMPILE> {
192 #[inline]
193 fn deref_mut(&mut self) -> &mut Self::Target {
194 self.ctx_mut()
195 }
196}
197
198impl<DB, I, PRECOMPILE> Evm for EthEvm<DB, I, PRECOMPILE>
199where
200 DB: Database,
201 I: Inspector<EthEvmContext<DB>>,
202 PRECOMPILE: PrecompileProvider<EthEvmContext<DB>, Output = InterpreterResult>,
203{
204 type DB = DB;
205 type Tx = TxEnv;
206 type Error = EVMError<DB::Error>;
207 type HaltReason = HaltReason;
208 type Spec = SpecId;
209 type BlockEnv = BlockEnv;
210 type Precompiles = PRECOMPILE;
211 type Inspector = I;
212
213 fn block(&self) -> &BlockEnv {
214 &self.block
215 }
216
217 fn chain_id(&self) -> u64 {
218 self.cfg.chain_id
219 }
220
221 fn transact_raw(
222 &mut self,
223 tx: Self::Tx,
224 ) -> Result<ResultAndState<Self::HaltReason>, Self::Error> {
225 if self.inspect {
226 self.inner.inspect_tx(tx)
227 } else {
228 self.inner.transact(tx)
229 }
230 }
231
232 fn transact_system_call(
233 &mut self,
234 caller: Address,
235 contract: Address,
236 data: Bytes,
237 ) -> Result<ResultAndState<Self::HaltReason>, Self::Error> {
238 self.inner.system_call_with_caller(caller, contract, data)
239 }
240
241 fn finish(self) -> (Self::DB, EvmEnv<Self::Spec>) {
242 let Context { block: block_env, cfg: cfg_env, journaled_state, .. } = self.inner.ctx;
243
244 (journaled_state.database, EvmEnv { block_env, cfg_env })
245 }
246
247 fn set_inspector_enabled(&mut self, enabled: bool) {
248 self.inspect = enabled;
249 }
250
251 fn components(&self) -> (&Self::DB, &Self::Inspector, &Self::Precompiles) {
252 (&self.inner.ctx.journaled_state.database, &self.inner.inspector, &self.inner.precompiles)
253 }
254
255 fn components_mut(&mut self) -> (&mut Self::DB, &mut Self::Inspector, &mut Self::Precompiles) {
256 (
257 &mut self.inner.ctx.journaled_state.database,
258 &mut self.inner.inspector,
259 &mut self.inner.precompiles,
260 )
261 }
262}
263
264#[derive(Debug, Default, Clone, Copy)]
266#[non_exhaustive]
267pub struct EthEvmFactory;
268
269impl EvmFactory for EthEvmFactory {
270 type Evm<DB: Database, I: Inspector<EthEvmContext<DB>>> = EthEvm<DB, I, Self::Precompiles>;
271 type Context<DB: Database> = Context<BlockEnv, TxEnv, CfgEnv, DB>;
272 type Tx = TxEnv;
273 type Error<DBError: core::error::Error + Send + Sync + 'static> = EVMError<DBError>;
274 type HaltReason = HaltReason;
275 type Spec = SpecId;
276 type BlockEnv = BlockEnv;
277 type Precompiles = PrecompilesMap;
278
279 fn create_evm<DB: Database>(&self, db: DB, input: EvmEnv) -> Self::Evm<DB, NoOpInspector> {
280 EthEvmBuilder::new(db, input).build()
281 }
282
283 fn create_evm_with_inspector<DB: Database, I: Inspector<Self::Context<DB>>>(
284 &self,
285 db: DB,
286 input: EvmEnv,
287 inspector: I,
288 ) -> Self::Evm<DB, I> {
289 EthEvmBuilder::new(db, input).activate_inspector(inspector).build()
290 }
291}
292
293#[cfg(test)]
294mod tests {
295 use super::*;
296 use alloy_primitives::address;
297 use revm::{database_interface::EmptyDB, primitives::hardfork::SpecId};
298
299 #[test]
300 fn test_precompiles_with_correct_spec() {
301 let specs_to_test = [
303 (
305 address!("0x0000000000000000000000000000000000000005"),
306 SpecId::FRONTIER, SpecId::BYZANTIUM, "MODEXP",
309 ),
310 (
312 address!("0x0000000000000000000000000000000000000009"),
313 SpecId::BYZANTIUM, SpecId::ISTANBUL, "BLAKE2F",
316 ),
317 ];
318
319 for (precompile_addr, early_spec, later_spec, name) in specs_to_test {
320 let mut early_cfg_env = CfgEnv::default();
321 early_cfg_env.spec = early_spec;
322 early_cfg_env.chain_id = 1;
323
324 let early_env = EvmEnv { block_env: BlockEnv::default(), cfg_env: early_cfg_env };
325 let factory = EthEvmFactory;
326 let mut early_evm = factory.create_evm(EmptyDB::default(), early_env);
327
328 assert!(
330 early_evm.precompiles_mut().get(&precompile_addr).is_none(),
331 "{name} precompile at {precompile_addr:?} should NOT be available for early spec {early_spec:?}"
332 );
333
334 let mut later_cfg_env = CfgEnv::default();
335 later_cfg_env.spec = later_spec;
336 later_cfg_env.chain_id = 1;
337
338 let later_env = EvmEnv { block_env: BlockEnv::default(), cfg_env: later_cfg_env };
339 let mut later_evm = factory.create_evm(EmptyDB::default(), later_env);
340
341 assert!(
343 later_evm.precompiles_mut().get(&precompile_addr).is_some(),
344 "{name} precompile at {precompile_addr:?} should be available for later spec {later_spec:?}"
345 );
346 }
347 }
348}