Skip to main content

reth_evm_ethereum/
lib.rs

1//! EVM config for vanilla ethereum.
2//!
3//! # Revm features
4//!
5//! This crate does __not__ enforce specific revm features such as `blst` or `c-kzg`, which are
6//! critical for revm's evm internals, it is the responsibility of the implementer to ensure the
7//! proper features are selected.
8
9#![doc(
10    html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png",
11    html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256",
12    issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
13)]
14#![cfg_attr(not(test), warn(unused_crate_dependencies))]
15#![cfg_attr(docsrs, feature(doc_cfg))]
16#![cfg_attr(not(feature = "std"), no_std)]
17
18extern crate alloc;
19
20use alloc::{borrow::Cow, sync::Arc};
21use alloy_consensus::{Header, Transaction};
22use alloy_eips::merge::SLOT_DURATION_SECS;
23use alloy_evm::{
24    eth::{EthBlockExecutionCtx, EthBlockExecutorFactory, EthTxResult},
25    EthEvmFactory, FromRecoveredTx, FromTxWithEncoded,
26};
27use alloy_primitives::{Address, B256};
28#[cfg(feature = "jit")]
29use core::any::Any;
30use core::{any::TypeId, convert::Infallible, fmt::Debug};
31use reth_chainspec::{ChainSpec, EthChainSpec, MAINNET};
32use reth_ethereum_primitives::{Block, EthPrimitives, TransactionSigned};
33use reth_evm::{
34    eth::NextEvmEnvAttributes, precompiles::PrecompilesMap, ConfigureEvm, EvmEnv, EvmFactory,
35    HaltReasonFor, JitBackend, NextBlockEnvAttributes, SenderRecoveryCache, TransactionEnvMut,
36    TxExecutionResultFor,
37};
38use reth_primitives_traits::{SealedBlock, SealedHeader};
39use revm::{
40    context::{result::ResultAndState, BlockEnv},
41    primitives::hardfork::SpecId,
42};
43
44#[cfg(feature = "std")]
45use reth_evm::{ConfigureEngineEvm, ExecutableTxIterator};
46#[allow(unused_imports)]
47use {
48    alloy_eips::Decodable2718,
49    alloy_primitives::{Bytes, U256},
50    alloy_rpc_types_engine::ExecutionData,
51    reth_chainspec::EthereumHardforks,
52    reth_evm::{EvmEnvFor, ExecutionCtxFor},
53    reth_primitives_traits::{constants::MAX_TX_GAS_LIMIT_OSAKA, SignedTransaction, TxTy},
54    reth_storage_errors::any::AnyError,
55    revm::context::CfgEnv,
56    revm::context_interface::block::BlobExcessGasAndPrice,
57};
58
59pub use alloy_evm::EthEvm;
60
61mod config;
62use alloy_evm::eth::spec::EthExecutorSpec;
63pub use config::{revm_spec, revm_spec_by_timestamp_and_block_number};
64use reth_ethereum_forks::Hardforks;
65
66/// Helper type with backwards compatible methods to obtain Ethereum executor
67/// providers.
68#[doc(hidden)]
69pub mod execute {
70    use crate::EthEvmConfig;
71
72    #[deprecated(note = "Use `EthEvmConfig` instead")]
73    pub type EthExecutorProvider = EthEvmConfig;
74}
75
76mod build;
77pub use build::EthBlockAssembler;
78
79mod receipt;
80pub use receipt::RethReceiptBuilder;
81
82#[cfg(feature = "test-utils")]
83mod test_utils;
84#[cfg(feature = "test-utils")]
85pub use test_utils::*;
86
87pub mod factory;
88
89/// Ethereum-related EVM configuration.
90#[derive(Debug, Clone)]
91pub struct EthEvmConfig<C = ChainSpec, EvmFactory = EthEvmFactory> {
92    /// Inner [`EthBlockExecutorFactory`].
93    pub executor_factory: EthBlockExecutorFactory<RethReceiptBuilder, Arc<C>, EvmFactory>,
94    /// Ethereum block assembler.
95    pub block_assembler: EthBlockAssembler<C>,
96    /// Cache of recovered transaction senders, if enabled.
97    pub sender_recovery_cache: Option<SenderRecoveryCache>,
98}
99
100impl EthEvmConfig {
101    /// Creates a new Ethereum EVM configuration for the ethereum mainnet.
102    pub fn mainnet() -> Self {
103        Self::ethereum(MAINNET.clone())
104    }
105}
106
107impl<ChainSpec> EthEvmConfig<ChainSpec> {
108    /// Creates a new Ethereum EVM configuration with the given chain spec.
109    pub fn new(chain_spec: Arc<ChainSpec>) -> Self {
110        Self::ethereum(chain_spec)
111    }
112
113    /// Creates a new Ethereum EVM configuration.
114    pub fn ethereum(chain_spec: Arc<ChainSpec>) -> Self {
115        Self::new_with_evm_factory(chain_spec, EthEvmFactory::default())
116    }
117}
118
119impl<ChainSpec, EvmFactory> EthEvmConfig<ChainSpec, EvmFactory> {
120    /// Creates a new Ethereum EVM configuration with the given chain spec and EVM factory.
121    pub fn new_with_evm_factory(chain_spec: Arc<ChainSpec>, evm_factory: EvmFactory) -> Self {
122        Self {
123            block_assembler: EthBlockAssembler::new(chain_spec.clone()),
124            sender_recovery_cache: None,
125            executor_factory: EthBlockExecutorFactory::new(
126                RethReceiptBuilder::default(),
127                chain_spec,
128                evm_factory,
129            ),
130        }
131    }
132
133    /// Returns the chain spec associated with this configuration.
134    pub const fn chain_spec(&self) -> &Arc<ChainSpec> {
135        self.executor_factory.spec()
136    }
137
138    /// Uses the provided sender recovery cache.
139    pub fn with_sender_recovery_cache(mut self, cache: SenderRecoveryCache) -> Self {
140        self.sender_recovery_cache = Some(cache);
141        self
142    }
143}
144
145impl<ChainSpec, EvmF> ConfigureEvm for EthEvmConfig<ChainSpec, EvmF>
146where
147    ChainSpec: EthExecutorSpec + EthChainSpec<Header = Header> + Hardforks + 'static,
148    EvmF: EvmFactory<
149            Tx: TransactionEnvMut
150                    + FromRecoveredTx<TransactionSigned>
151                    + FromTxWithEncoded<TransactionSigned>,
152            Spec = SpecId,
153            BlockEnv = BlockEnv,
154            Precompiles = PrecompilesMap,
155        > + Clone
156        + Debug
157        + Send
158        + Sync
159        + Unpin
160        + 'static,
161{
162    type Primitives = EthPrimitives;
163    type Error = Infallible;
164    type NextBlockEnvCtx = NextBlockEnvAttributes;
165    type BlockExecutorFactory = EthBlockExecutorFactory<RethReceiptBuilder, Arc<ChainSpec>, EvmF>;
166    type BlockAssembler = EthBlockAssembler<ChainSpec>;
167
168    fn block_executor_factory(&self) -> &Self::BlockExecutorFactory {
169        &self.executor_factory
170    }
171
172    fn block_assembler(&self) -> &Self::BlockAssembler {
173        &self.block_assembler
174    }
175
176    fn with_jit_support_enabled(self, enabled: bool) -> Self
177    where
178        Self: Sized,
179    {
180        #[cfg(feature = "jit")]
181        {
182            let mut this = self;
183            let mut evm_factory = this.executor_factory.evm_factory().clone();
184            if let Some(factory) =
185                (&mut evm_factory as &mut dyn Any).downcast_mut::<factory::RethEvmFactory>()
186            {
187                factory.set_jit_support(enabled);
188            }
189            this.executor_factory = EthBlockExecutorFactory::new(
190                *this.executor_factory.receipt_builder(),
191                this.executor_factory.spec().clone(),
192                evm_factory,
193            );
194            this
195        }
196
197        #[cfg(not(feature = "jit"))]
198        {
199            let _ = enabled;
200            self
201        }
202    }
203
204    fn jit_backend(&self) -> Option<&dyn JitBackend> {
205        #[cfg(feature = "jit")]
206        if let Some(factory) = (self.executor_factory.evm_factory() as &dyn Any)
207            .downcast_ref::<factory::RethEvmFactory>()
208        {
209            return Some(factory);
210        }
211
212        None
213    }
214
215    fn evm_env(&self, header: &Header) -> Result<EvmEnv<SpecId>, Self::Error> {
216        Ok(EvmEnv::for_eth_block(
217            header,
218            self.chain_spec(),
219            self.chain_spec().chain().id(),
220            self.chain_spec().blob_params_at_timestamp(header.timestamp),
221        ))
222    }
223
224    fn next_evm_env(
225        &self,
226        parent: &Header,
227        attributes: &NextBlockEnvAttributes,
228    ) -> Result<EvmEnv, Self::Error> {
229        Ok(EvmEnv::for_eth_next_block(
230            parent,
231            NextEvmEnvAttributes {
232                timestamp: attributes.timestamp,
233                suggested_fee_recipient: attributes.suggested_fee_recipient,
234                prev_randao: attributes.prev_randao,
235                gas_limit: attributes.gas_limit,
236                slot_number: attributes.slot_number,
237            },
238            self.chain_spec().next_block_base_fee(parent, attributes.timestamp).unwrap_or_default(),
239            self.chain_spec(),
240            self.chain_spec().chain().id(),
241            self.chain_spec().blob_params_at_timestamp(attributes.timestamp),
242        ))
243    }
244
245    fn context_for_block<'a>(
246        &self,
247        block: &'a SealedBlock<Block>,
248    ) -> Result<EthBlockExecutionCtx<'a>, Self::Error> {
249        Ok(EthBlockExecutionCtx {
250            tx_count_hint: Some(block.transaction_count()),
251            parent_hash: block.header().parent_hash,
252            parent_beacon_block_root: block.header().parent_beacon_block_root,
253            ommers: &block.body().ommers,
254            withdrawals: block.body().withdrawals.as_ref().map(|w| Cow::Borrowed(w.as_slice())),
255            extra_data: block.header().extra_data.clone(),
256            slot_number: block.header().slot_number,
257        })
258    }
259
260    fn context_for_next_block(
261        &self,
262        parent: &SealedHeader,
263        attributes: Self::NextBlockEnvCtx,
264    ) -> Result<EthBlockExecutionCtx<'_>, Self::Error> {
265        Ok(EthBlockExecutionCtx {
266            tx_count_hint: None,
267            parent_hash: parent.hash(),
268            parent_beacon_block_root: attributes.parent_beacon_block_root,
269            ommers: &[],
270            withdrawals: attributes.withdrawals.map(|w| Cow::Owned(w.into_inner())),
271            extra_data: attributes.extra_data,
272            slot_number: attributes.slot_number,
273        })
274    }
275
276    fn reuses_prewarm_results(&self) -> bool {
277        // Custom factories may change transaction validation or read state outside the database.
278        let factory = TypeId::of::<EvmF>();
279        factory == TypeId::of::<EthEvmFactory>() ||
280            factory == TypeId::of::<factory::RethEvmFactory>()
281    }
282
283    fn prewarm_tx_result(
284        &self,
285        tx: &TransactionSigned,
286        result: ResultAndState<HaltReasonFor<Self>>,
287    ) -> Option<TxExecutionResultFor<Self>> {
288        Some(EthTxResult {
289            result,
290            blob_gas_used: tx.blob_gas_used().unwrap_or_default(),
291            tx_type: tx.tx_type(),
292        })
293    }
294
295    fn speculative_next_evm_env(
296        &self,
297        parent: &SealedHeader,
298        beneficiary: Address,
299    ) -> Option<EvmEnv> {
300        let Ok(evm_env) = self.next_evm_env(
301            parent,
302            &NextBlockEnvAttributes {
303                timestamp: parent.timestamp + SLOT_DURATION_SECS,
304                suggested_fee_recipient: beneficiary,
305                prev_randao: B256::ZERO,
306                gas_limit: parent.gas_limit,
307                parent_beacon_block_root: None,
308                withdrawals: None,
309                extra_data: Bytes::new(),
310                slot_number: None,
311            },
312        );
313        Some(evm_env)
314    }
315}
316
317#[cfg(feature = "std")]
318impl<ChainSpec, EvmF> ConfigureEngineEvm<ExecutionData> for EthEvmConfig<ChainSpec, EvmF>
319where
320    ChainSpec: EthExecutorSpec + EthChainSpec<Header = Header> + Hardforks + 'static,
321    EvmF: EvmFactory<
322            Tx: TransactionEnvMut
323                    + FromRecoveredTx<TransactionSigned>
324                    + FromTxWithEncoded<TransactionSigned>,
325            Spec = SpecId,
326            BlockEnv = BlockEnv,
327            Precompiles = PrecompilesMap,
328        > + Clone
329        + Debug
330        + Send
331        + Sync
332        + Unpin
333        + 'static,
334{
335    fn evm_env_for_payload(&self, payload: &ExecutionData) -> Result<EvmEnvFor<Self>, Self::Error> {
336        let timestamp = payload.payload.timestamp();
337        let block_number = payload.payload.block_number();
338
339        let blob_params = self.chain_spec().blob_params_at_timestamp(timestamp);
340        let spec =
341            revm_spec_by_timestamp_and_block_number(self.chain_spec(), timestamp, block_number);
342
343        // configure evm env based on parent block
344        let mut cfg_env = CfgEnv::new()
345            .with_chain_id(self.chain_spec().chain().id())
346            .with_spec_and_mainnet_gas_params(spec);
347
348        if let Some(blob_params) = &blob_params {
349            cfg_env.set_max_blobs_per_tx(blob_params.max_blobs_per_tx);
350        }
351
352        if self.chain_spec().is_osaka_active_at_timestamp(timestamp) {
353            cfg_env.tx_gas_limit_cap = Some(MAX_TX_GAS_LIMIT_OSAKA);
354        }
355
356        // derive the EIP-4844 blob fees from the header's `excess_blob_gas` and the current
357        // blobparams
358        let blob_excess_gas_and_price =
359            payload.payload.excess_blob_gas().zip(blob_params).map(|(excess_blob_gas, params)| {
360                let blob_gasprice = params.calc_blob_fee(excess_blob_gas);
361                BlobExcessGasAndPrice { excess_blob_gas, blob_gasprice }
362            });
363
364        let block_env = BlockEnv {
365            number: U256::from(block_number),
366            beneficiary: payload.payload.fee_recipient(),
367            timestamp: U256::from(timestamp),
368            difficulty: if spec >= SpecId::MERGE {
369                U256::ZERO
370            } else {
371                payload.payload.as_v1().prev_randao.into()
372            },
373            prevrandao: (spec >= SpecId::MERGE).then(|| payload.payload.as_v1().prev_randao),
374            gas_limit: payload.payload.gas_limit(),
375            basefee: payload.payload.saturated_base_fee_per_gas(),
376            blob_excess_gas_and_price,
377            slot_num: payload.payload.as_v4().map(|v4| v4.slot_number).unwrap_or_default(),
378        };
379
380        Ok(EvmEnv { cfg_env, block_env })
381    }
382
383    fn context_for_payload<'a>(
384        &self,
385        payload: &'a ExecutionData,
386    ) -> Result<ExecutionCtxFor<'a, Self>, Self::Error> {
387        Ok(EthBlockExecutionCtx {
388            tx_count_hint: Some(payload.payload.transactions().len()),
389            parent_hash: payload.parent_hash(),
390            parent_beacon_block_root: payload.sidecar.parent_beacon_block_root(),
391            ommers: &[],
392            withdrawals: payload.payload.withdrawals().map(|w| Cow::Borrowed(w.as_slice())),
393            extra_data: payload.payload.as_v1().extra_data.clone(),
394            slot_number: payload.payload.as_v4().map(|v4| v4.slot_number),
395        })
396    }
397
398    fn tx_iterator_for_payload(
399        &self,
400        payload: &ExecutionData,
401    ) -> Result<impl ExecutableTxIterator<Self>, Self::Error> {
402        let txs = payload.payload.transactions().clone();
403        let sender_recovery_cache = self.sender_recovery_cache.clone();
404        let convert = move |tx: Bytes| {
405            let tx =
406                TxTy::<Self::Primitives>::decode_2718_exact(tx.as_ref()).map_err(AnyError::new)?;
407            let signer = if let Some(cache) = &sender_recovery_cache {
408                cache.recover(&tx)
409            } else {
410                tx.try_recover()
411            }
412            .map_err(AnyError::new)?;
413            Ok::<_, AnyError>(tx.with_signer(signer))
414        };
415
416        Ok((txs, convert))
417    }
418}
419
420#[cfg(test)]
421mod tests {
422    use super::*;
423    use alloy_consensus::Header;
424    use alloy_evm::{
425        eth::{EthEvm, EthEvmContext},
426        Database,
427    };
428    use alloy_genesis::Genesis;
429    use reth_chainspec::{Chain, ChainSpec};
430    use reth_evm::{execute::ProviderError, EvmEnv};
431    use revm::{
432        context::{
433            result::{EVMError, HaltReason},
434            BlockEnv, CfgEnv, TxEnv,
435        },
436        database::CacheDB,
437        database_interface::{DBErrorMarker, EmptyDBTyped},
438        inspector::NoOpInspector,
439        Inspector,
440    };
441
442    #[test]
443    fn test_fill_cfg_and_block_env() {
444        // Create a default header
445        let header = Header::default();
446
447        // Build the ChainSpec for Ethereum mainnet, activating London, Paris, and Shanghai
448        // hardforks
449        let chain_spec = ChainSpec::builder()
450            .chain(Chain::mainnet())
451            .genesis(Genesis::default())
452            .london_activated()
453            .paris_activated()
454            .shanghai_activated()
455            .build();
456
457        // Use the `EthEvmConfig` to fill the `cfg_env` and `block_env` based on the ChainSpec,
458        // Header, and total difficulty
459        let EvmEnv { cfg_env, .. } =
460            EthEvmConfig::new(Arc::new(chain_spec.clone())).evm_env(&header).unwrap();
461
462        // Assert that the chain ID in the `cfg_env` is correctly set to the chain ID of the
463        // ChainSpec
464        assert_eq!(cfg_env.chain_id, chain_spec.chain().id());
465    }
466
467    #[test]
468    fn test_evm_with_env_default_spec() {
469        let evm_config = EthEvmConfig::mainnet();
470
471        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
472
473        let evm_env = EvmEnv::default();
474
475        let evm = evm_config.evm_with_env(db, evm_env.clone());
476
477        // Check that the EVM environment
478        assert_eq!(evm.block, evm_env.block_env);
479        assert_eq!(evm.cfg, evm_env.cfg_env);
480    }
481
482    #[test]
483    fn test_evm_with_env_custom_cfg() {
484        let evm_config = EthEvmConfig::mainnet();
485
486        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
487
488        // Create a custom configuration environment with a chain ID of 111
489        let cfg = CfgEnv::default().with_chain_id(111);
490
491        let evm_env = EvmEnv { cfg_env: cfg.clone(), ..Default::default() };
492
493        let evm = evm_config.evm_with_env(db, evm_env);
494
495        // Check that the EVM environment is initialized with the custom environment
496        assert_eq!(evm.cfg, cfg);
497    }
498
499    #[test]
500    fn test_evm_with_env_custom_block_and_tx() {
501        let evm_config = EthEvmConfig::mainnet();
502
503        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
504
505        // Create customs block and tx env
506        let block = BlockEnv {
507            basefee: 1000,
508            gas_limit: 10_000_000,
509            number: U256::from(42),
510            ..Default::default()
511        };
512
513        let evm_env = EvmEnv { block_env: block, ..Default::default() };
514
515        let evm = evm_config.evm_with_env(db, evm_env.clone());
516
517        // Verify that the block and transaction environments are set correctly
518        assert_eq!(evm.block, evm_env.block_env);
519
520        // Default spec ID
521        assert_eq!(evm.cfg.spec, SpecId::default());
522    }
523
524    #[test]
525    fn test_evm_with_spec_id() {
526        let evm_config = EthEvmConfig::mainnet();
527
528        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
529
530        let evm_env = EvmEnv {
531            cfg_env: CfgEnv::new().with_spec_and_mainnet_gas_params(SpecId::PETERSBURG),
532            ..Default::default()
533        };
534
535        let evm = evm_config.evm_with_env(db, evm_env);
536
537        // Check that the spec ID is setup properly
538        assert_eq!(evm.cfg.spec, SpecId::PETERSBURG);
539    }
540
541    #[test]
542    fn test_evm_with_env_and_default_inspector() {
543        let evm_config = EthEvmConfig::mainnet();
544        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
545
546        let evm_env = EvmEnv::default();
547
548        let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
549
550        // Check that the EVM environment is set to default values
551        assert_eq!(evm.block, evm_env.block_env);
552        assert_eq!(evm.cfg, evm_env.cfg_env);
553    }
554
555    #[test]
556    fn test_evm_with_env_inspector_and_custom_cfg() {
557        let evm_config = EthEvmConfig::mainnet();
558        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
559
560        let cfg_env = CfgEnv::default().with_chain_id(111);
561        let block = BlockEnv::default();
562        let evm_env = EvmEnv { cfg_env: cfg_env.clone(), block_env: block };
563
564        let evm = evm_config.evm_with_env_and_inspector(db, evm_env, NoOpInspector {});
565
566        // Check that the EVM environment is set with custom configuration
567        assert_eq!(evm.cfg, cfg_env);
568        assert_eq!(evm.cfg.spec, SpecId::default());
569    }
570
571    #[test]
572    fn test_evm_with_env_inspector_and_custom_block_tx() {
573        let evm_config = EthEvmConfig::mainnet();
574        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
575
576        // Create custom block and tx environment
577        let block = BlockEnv {
578            basefee: 1000,
579            gas_limit: 10_000_000,
580            number: U256::from(42),
581            ..Default::default()
582        };
583        let evm_env = EvmEnv { block_env: block, ..Default::default() };
584
585        let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
586
587        // Verify that the block and transaction environments are set correctly
588        assert_eq!(evm.block, evm_env.block_env);
589        assert_eq!(evm.cfg.spec, SpecId::default());
590    }
591
592    #[test]
593    fn test_evm_with_env_inspector_and_spec_id() {
594        let evm_config = EthEvmConfig::mainnet();
595        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
596
597        let evm_env = EvmEnv {
598            cfg_env: CfgEnv::new().with_spec_and_mainnet_gas_params(SpecId::PETERSBURG),
599            ..Default::default()
600        };
601
602        let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
603
604        // Check that the spec ID is set properly
605        assert_eq!(evm.block, evm_env.block_env);
606        assert_eq!(evm.cfg, evm_env.cfg_env);
607        assert_eq!(evm.tx, Default::default());
608    }
609
610    #[test]
611    fn test_reuses_prewarm_results_only_for_known_factories() {
612        assert!(EthEvmConfig::mainnet().reuses_prewarm_results());
613        assert!(EthEvmConfig::new_with_evm_factory(
614            MAINNET.clone(),
615            factory::RethEvmFactory::default()
616        )
617        .reuses_prewarm_results());
618
619        #[derive(Debug, Clone)]
620        struct CustomFactory(EthEvmFactory);
621
622        impl EvmFactory for CustomFactory {
623            type Evm<DB: Database, I: Inspector<Self::Context<DB>>> =
624                EthEvm<DB, I, Self::Precompiles>;
625            type Context<DB: Database> = EthEvmContext<DB>;
626            type Tx = TxEnv;
627            type Error<DBError: DBErrorMarker> = EVMError<DBError>;
628            type HaltReason = HaltReason;
629            type Spec = SpecId;
630            type BlockEnv = BlockEnv;
631            type Precompiles = PrecompilesMap;
632
633            fn create_evm<DB: Database>(
634                &self,
635                db: DB,
636                input: EvmEnv,
637            ) -> Self::Evm<DB, NoOpInspector> {
638                self.0.create_evm(db, input)
639            }
640
641            fn create_evm_with_inspector<DB: Database, I: Inspector<Self::Context<DB>>>(
642                &self,
643                db: DB,
644                input: EvmEnv,
645                inspector: I,
646            ) -> Self::Evm<DB, I> {
647                self.0.create_evm_with_inspector(db, input, inspector)
648            }
649        }
650
651        assert!(!EthEvmConfig::new_with_evm_factory(
652            MAINNET.clone(),
653            CustomFactory(EthEvmFactory::default())
654        )
655        .reuses_prewarm_results());
656    }
657
658    #[cfg(feature = "jit")]
659    #[test]
660    fn test_jit_support_downcast_updates_reth_factory() {
661        let evm_config = EthEvmConfig::new_with_evm_factory(
662            MAINNET.clone(),
663            factory::RethEvmFactory::disabled(),
664        );
665
666        assert!(evm_config.jit_backend().is_some());
667        assert!(!evm_config.executor_factory.evm_factory().jit_support_enabled());
668
669        let evm_config = evm_config.with_jit_support();
670        assert!(evm_config.executor_factory.evm_factory().jit_support_enabled());
671
672        let evm_config = evm_config.with_jit_support_enabled(false);
673        assert!(!evm_config.executor_factory.evm_factory().jit_support_enabled());
674    }
675
676    #[cfg(feature = "jit")]
677    #[test]
678    fn test_jit_support_downcast_ignores_plain_factory() {
679        let evm_config = EthEvmConfig::mainnet();
680
681        assert!(evm_config.jit_backend().is_none());
682
683        let evm_config = evm_config.with_jit_support();
684        assert!(evm_config.jit_backend().is_none());
685    }
686}