1#![doc(
4 html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png",
5 html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256",
6 issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
7)]
8#![cfg_attr(not(test), warn(unused_crate_dependencies))]
9#![cfg_attr(docsrs, feature(doc_cfg))]
10#![cfg_attr(not(feature = "std"), no_std)]
11
12extern crate alloc;
13
14use alloc::{borrow::Cow, sync::Arc};
15use alloy_consensus::{BlockHeader, Header};
16use alloy_eips::{eip4895::Withdrawal, eip7840::BlobParams, merge::SLOT_DURATION_SECS};
17use alloy_primitives::{Address, Bytes, B256};
18use convert::{block_env_with_blob_params, spec_id};
19use core::{any::TypeId, convert::Infallible, fmt::Debug};
20use reth_chainspec::{ChainSpec, EthChainSpec, EthereumHardforks, MAINNET};
21use reth_ethereum_forks::Hardforks;
22use reth_ethereum_primitives::{Block, EthPrimitives};
23use reth_evm::{ConfigureEvm, EvmEnv, EvmEnvFor, NextBlockEnvAttributes, SenderRecoveryCache};
24use reth_primitives_traits::{SealedBlock, SealedHeader};
25
26#[cfg(feature = "std")]
27use alloy_consensus::Transaction;
28#[cfg(feature = "std")]
29use alloy_eips::Decodable2718;
30#[cfg(feature = "std")]
31use alloy_rpc_types_engine::ExecutionData;
32#[cfg(feature = "std")]
33use convert::payload_block_env;
34#[cfg(feature = "std")]
35use evm2::evm::{Database, Db};
36#[cfg(feature = "std")]
37use reth_ethereum_primitives::TransactionSigned;
38#[cfg(feature = "std")]
39use reth_evm::{
40 precompile_cache::PrecompileCacheMap, ConfigureEngineEvm, EvmFor, ExecutableTxIterator,
41 ExecutionCtxFor, SpecFor,
42};
43#[cfg(feature = "std")]
44use reth_primitives_traits::SignedTransaction;
45#[cfg(feature = "std")]
46use reth_storage_errors::any::AnyError;
47
48#[cfg(feature = "jit")]
49use core::any::Any;
50
51pub use convert::spec_id_by_timestamp_and_block_number;
52
53pub struct EthEvmEnv<T = evm2::BaseEvmTypes>
55where
56 T: evm2::EvmTypesHost,
57{
58 pub spec: T::SpecId,
60 pub version: evm2::Version,
62 pub block: evm2::env::BlockEnv<T>,
64}
65
66impl<T: evm2::EvmTypesHost> Clone for EthEvmEnv<T> {
67 fn clone(&self) -> Self {
68 Self { spec: self.spec, version: self.version, block: self.block }
69 }
70}
71
72impl<T: evm2::EvmTypesHost> Debug for EthEvmEnv<T>
73where
74 T::SpecId: Debug,
75 T::BlockEnvExt: Debug,
76{
77 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
78 f.debug_struct("EthEvmEnv")
79 .field("spec", &self.spec)
80 .field("version", &self.version)
81 .field("block", &self.block)
82 .finish()
83 }
84}
85
86impl<T: evm2::EvmTypesHost> EthEvmEnv<T> {
87 pub fn new(spec: T::SpecId, block: evm2::env::BlockEnv<T>, chain_id: u64) -> Self {
89 let mut version = evm2::Version::new(spec.into());
90 version.chain_id = chain_id;
91 Self { spec, version, block }
92 }
93
94 pub const fn new_with_version(
96 spec: T::SpecId,
97 block: evm2::env::BlockEnv<T>,
98 version: evm2::Version,
99 ) -> Self {
100 Self { spec, version, block }
101 }
102}
103
104impl<T> Default for EthEvmEnv<T>
105where
106 T: evm2::EvmTypesHost,
107 T::SpecId: Default,
108{
109 fn default() -> Self {
110 let spec = T::SpecId::default();
111 Self { spec, version: evm2::Version::new(spec.into()), block: Default::default() }
112 }
113}
114
115impl<T: evm2::EvmTypesHost> AsRef<evm2::env::BlockEnv<T>> for EthEvmEnv<T> {
116 fn as_ref(&self) -> &evm2::env::BlockEnv<T> {
117 &self.block
118 }
119}
120
121impl<T: evm2::EvmTypes> EvmEnv for EthEvmEnv<T>
122where
123 T::SpecId: Debug + Send + Sync,
124 T::BlockEnvExt: Send + Sync,
125{
126 type EvmTypes = T;
127
128 fn spec_id(&self) -> T::SpecId {
129 self.spec
130 }
131
132 fn chain_id(&self) -> u64 {
133 self.version.chain_id
134 }
135
136 fn block_env(&self) -> &evm2::env::BlockEnv<T> {
137 &self.block
138 }
139
140 fn block_env_mut(&mut self) -> &mut evm2::env::BlockEnv<T> {
141 &mut self.block
142 }
143
144 fn version(&self) -> &evm2::Version {
145 &self.version
146 }
147
148 fn version_mut(&mut self) -> &mut evm2::Version {
149 &mut self.version
150 }
151
152 fn block_base_fee(&self) -> u64 {
153 self.block.basefee.saturating_to()
154 }
155
156 fn block_blob_base_fee(&self) -> u64 {
157 self.block.blob_basefee.saturating_to()
158 }
159
160 fn transaction_validation_limits(&self) -> reth_evm::EvmTransactionValidationLimits {
161 let tx_gas_limit_cap = match self.version.tx_gas_caps().0 {
163 u64::MAX => 0,
164 cap => cap,
165 };
166
167 reth_evm::EvmTransactionValidationLimits {
168 max_initcode_size: self.version.max_initcode_size,
169 tx_gas_limit_cap,
170 }
171 }
172
173 fn transaction_validation_gas_rules(&self) -> reth_evm::EvmTransactionValidationGasRules {
174 reth_evm::EvmTransactionValidationGasRules { version: self.version }
175 }
176
177 fn uses_separate_block_gas(&self) -> bool {
178 self.version.feature(evm2::EvmFeatures::EIP8037)
179 }
180
181 fn regular_gas_limit_cap(&self) -> u64 {
182 self.version.tx_gas_limit_cap
183 }
184
185 fn with_nonce_check_disabled(mut self) -> Self {
186 self.version.features.remove(evm2::EvmFeatures::NONCE_CHECK);
187 self
188 }
189
190 fn with_balance_check_disabled(mut self) -> Self {
191 self.version.features.remove(evm2::EvmFeatures::BALANCE_CHECK);
192 self
193 }
194}
195
196#[derive(Debug, Clone)]
198pub struct EthBlockExecutionCtx<'a> {
199 pub tx_count_hint: Option<usize>,
201 pub parent_hash: B256,
203 pub parent_beacon_block_root: Option<B256>,
205 pub ommers: &'a [Header],
207 pub withdrawals: Option<Cow<'a, [Withdrawal]>>,
209 pub extra_data: Bytes,
211 pub slot_number: Option<u64>,
213}
214
215#[doc(hidden)]
217pub mod execute {
218 use crate::EthEvmConfig;
219
220 #[deprecated(note = "Use `EthEvmConfig` instead")]
221 pub type EthExecutorProvider = EthEvmConfig;
222}
223
224mod build;
225pub use build::EthBlockAssembler;
226
227mod executor;
228pub use executor::{
229 EthBigBlockExecutor, EthBigBlockPlan, EthBigBlockSegment, EthBlockExecutor,
230 EthTransactionResultWithState,
231};
232
233pub mod factory;
235#[cfg(not(feature = "jit"))]
236pub use factory::RethEvmFactory;
237#[cfg(feature = "jit")]
238pub use factory::{
239 maybe_run_jit_helper, CompilationEvent, CompilationKind, CompileTimings, JitBackend,
240 JitMetrics, JitMode, RethEvmFactory, RuntimeConfig, RuntimeStatsSnapshot, RuntimeTuning,
241};
242pub use factory::{EthBigBlockExecutorFactory, EthBlockExecutorFactory, EvmFactory};
243
244mod convert;
245pub use convert::{recover_authorizations, ExecutableRecoveredTx};
246
247mod execution;
248
249#[cfg(test)]
250mod dao_fork;
251
252mod receipt;
253pub use receipt::RethReceiptBuilder;
254
255#[cfg(feature = "test-utils")]
256mod test_utils;
257#[cfg(feature = "test-utils")]
258pub use test_utils::*;
259
260#[derive(Debug, Clone)]
262pub struct EthEvmConfig<C = ChainSpec, F = RethEvmFactory>
263where
264 F: EvmFactory,
265{
266 pub executor_factory: EthBlockExecutorFactory<RethReceiptBuilder, C, F>,
268 pub block_assembler: EthBlockAssembler<C>,
270 pub sender_recovery_cache: Option<SenderRecoveryCache>,
272}
273
274impl EthEvmConfig {
275 pub fn mainnet() -> Self {
277 Self::ethereum(MAINNET.clone())
278 }
279}
280
281impl<ChainSpec> EthEvmConfig<ChainSpec> {
282 pub fn new(chain_spec: Arc<ChainSpec>) -> Self {
284 Self::ethereum(chain_spec)
285 }
286
287 pub fn ethereum(chain_spec: Arc<ChainSpec>) -> Self {
289 Self::new_with_evm_factory(chain_spec, RethEvmFactory::default())
290 }
291}
292
293impl<ChainSpec, F: EvmFactory> EthEvmConfig<ChainSpec, F> {
294 pub fn new_with_evm_factory(chain_spec: Arc<ChainSpec>, evm_factory: F) -> Self {
296 Self {
297 block_assembler: EthBlockAssembler::new(chain_spec.clone()),
298 sender_recovery_cache: None,
299 executor_factory: EthBlockExecutorFactory::new_with_evm_factory(
300 chain_spec,
301 evm_factory,
302 ),
303 }
304 }
305
306 pub const fn chain_spec(&self) -> &Arc<ChainSpec> {
308 self.executor_factory.chain_spec()
309 }
310
311 fn evm_env_from_base_spec(
312 &self,
313 spec: evm2::SpecId,
314 block: evm2::env::BlockEnv,
315 blob_params: Option<BlobParams>,
316 ) -> EthEvmEnv<F::Types>
317 where
318 ChainSpec: EthChainSpec,
319 {
320 let factory = self.executor_factory.evm_factory();
321 let mut version = factory.version(spec, self.chain_spec().chain_id());
322 if let Some(blob_params) = blob_params {
323 version.max_blobs_per_tx = blob_params.max_blobs_per_tx as usize;
324 }
325 EthEvmEnv::new_with_version(factory.spec_id(spec), factory.block_env(block), version)
326 }
327
328 pub fn with_sender_recovery_cache(mut self, cache: SenderRecoveryCache) -> Self {
330 self.sender_recovery_cache = Some(cache);
331 self
332 }
333}
334
335impl<ChainSpec, EvmF> ConfigureEvm for EthEvmConfig<ChainSpec, EvmF>
336where
337 ChainSpec: EthChainSpec<Header = Header> + EthereumHardforks + Hardforks + 'static,
338 EvmF: EvmFactory,
339 <EvmF::Types as evm2::EvmTypesHost>::Tx: alloy_eips::eip2718::Typed2718
340 + reth_evm::FromTxWithEncoded<reth_ethereum_primitives::TransactionSigned>
341 + reth_evm::TransactionEnvMut,
342{
343 type Primitives = EthPrimitives;
344 type Error = Infallible;
345 type NextBlockEnvCtx = NextBlockEnvAttributes;
346 type BlockExecutorFactory = EthBlockExecutorFactory<RethReceiptBuilder, ChainSpec, EvmF>;
347 #[cfg(feature = "std")]
348 type BlockAssembler = EthBlockAssembler<ChainSpec>;
349
350 #[cfg(feature = "std")]
351 fn block_executor_factory(&self) -> &Self::BlockExecutorFactory {
352 &self.executor_factory
353 }
354
355 #[cfg(feature = "std")]
356 fn block_assembler(&self) -> &Self::BlockAssembler {
357 &self.block_assembler
358 }
359
360 fn with_jit_support_enabled(self, enabled: bool) -> Self
361 where
362 Self: Sized,
363 {
364 #[cfg(feature = "jit")]
365 {
366 let mut this = self;
367 if let Some(factory) = (this.executor_factory.evm_factory_mut() as &mut dyn Any)
368 .downcast_mut::<factory::RethEvmFactory>()
369 {
370 factory.set_jit_support(enabled);
371 }
372 this
373 }
374
375 #[cfg(not(feature = "jit"))]
376 {
377 let _ = enabled;
378 self
379 }
380 }
381
382 #[cfg(feature = "std")]
383 fn evm_with_precompile_cache<'a, DB>(
384 &self,
385 db: DB,
386 evm_env: EvmEnvFor<Self>,
387 cache_map: PrecompileCacheMap<SpecFor<Self>>,
388 metrics_enabled: bool,
389 ) -> EvmFor<'a, Self>
390 where
391 DB: Database + 'a,
392 {
393 self.executor_factory.build_evm_with_precompile_cache(
394 Db::new(db),
395 evm_env,
396 cache_map,
397 metrics_enabled,
398 )
399 }
400
401 fn jit_backend(&self) -> Option<&dyn reth_evm::JitBackend> {
402 #[cfg(feature = "jit")]
403 if let Some(factory) = (self.executor_factory.evm_factory() as &dyn Any)
404 .downcast_ref::<factory::RethEvmFactory>()
405 {
406 return Some(factory);
407 }
408
409 None
410 }
411
412 fn evm_env(&self, header: &Header) -> Result<EvmEnvFor<Self>, Self::Error> {
413 let spec = spec_id(self.chain_spec().as_ref(), header);
414 let blob_params = self.chain_spec().as_ref().blob_params_at_timestamp(header.timestamp);
415 Ok(self.evm_env_from_base_spec(
416 spec,
417 block_env_with_blob_params(header, blob_params),
418 blob_params,
419 ))
420 }
421
422 fn next_evm_env(
423 &self,
424 parent: &Header,
425 attributes: &NextBlockEnvAttributes,
426 ) -> Result<EvmEnvFor<Self>, Self::Error> {
427 let base_fee = self
428 .chain_spec()
429 .as_ref()
430 .next_block_base_fee(parent, attributes.timestamp)
431 .unwrap_or_default();
432 let blob_params = self.chain_spec().as_ref().blob_params_at_timestamp(attributes.timestamp);
433 let header = Header {
434 beneficiary: attributes.suggested_fee_recipient,
435 timestamp: attributes.timestamp,
436 number: parent.number + 1,
437 gas_limit: attributes.gas_limit,
438 base_fee_per_gas: Some(base_fee),
439 mix_hash: attributes.prev_randao,
440 slot_number: attributes.slot_number,
441 excess_blob_gas: parent
442 .maybe_next_block_excess_blob_gas(blob_params)
443 .or_else(|| blob_params.map(|_| 0)),
444 ..Default::default()
445 };
446
447 let spec = spec_id(self.chain_spec().as_ref(), &header);
448 Ok(self.evm_env_from_base_spec(
449 spec,
450 block_env_with_blob_params(&header, blob_params),
451 blob_params,
452 ))
453 }
454
455 fn context_for_block<'a>(
456 &self,
457 block: &'a SealedBlock<Block>,
458 ) -> Result<EthBlockExecutionCtx<'a>, Self::Error>
459 where
460 Self: 'a,
461 {
462 Ok(EthBlockExecutionCtx {
463 tx_count_hint: Some(block.transaction_count()),
464 parent_hash: block.header().parent_hash,
465 parent_beacon_block_root: block.header().parent_beacon_block_root,
466 ommers: &block.body().ommers,
467 withdrawals: block.body().withdrawals.as_ref().map(|w| Cow::Borrowed(w.as_slice())),
468 extra_data: block.header().extra_data.clone(),
469 slot_number: block.header().slot_number,
470 })
471 }
472
473 fn context_for_next_block(
474 &self,
475 parent: &SealedHeader,
476 attributes: Self::NextBlockEnvCtx,
477 ) -> Result<EthBlockExecutionCtx<'_>, Self::Error> {
478 Ok(EthBlockExecutionCtx {
479 tx_count_hint: None,
480 parent_hash: parent.hash(),
481 parent_beacon_block_root: attributes.parent_beacon_block_root,
482 ommers: &[],
483 withdrawals: attributes.withdrawals.map(|w| Cow::Owned(w.into_inner())),
484 extra_data: attributes.extra_data,
485 slot_number: attributes.slot_number,
486 })
487 }
488
489 fn reuses_prewarm_results(&self) -> bool {
490 let factory = TypeId::of::<EvmF>();
492 factory == TypeId::of::<factory::RethEvmFactory>()
493 }
494
495 #[cfg(feature = "std")]
496 fn prewarm_tx_result<'a>(
497 &self,
498 tx: &TransactionSigned,
499 result: reth_evm::TxResultWithStateFor<Self>,
500 ) -> Option<reth_evm::TxExecutionResultFor<'a, Self>>
501 where
502 Self: 'a,
503 {
504 Some(EthTransactionResultWithState::new(
505 result,
506 tx.tx_type(),
507 tx.blob_gas_used().unwrap_or_default(),
508 ))
509 }
510
511 fn speculative_next_evm_env(
512 &self,
513 parent: &SealedHeader,
514 beneficiary: Address,
515 ) -> Option<EvmEnvFor<Self>> {
516 let Ok(evm_env) = self.next_evm_env(
517 parent,
518 &NextBlockEnvAttributes {
519 timestamp: parent.timestamp + SLOT_DURATION_SECS,
520 suggested_fee_recipient: beneficiary,
521 prev_randao: B256::ZERO,
522 gas_limit: parent.gas_limit,
523 parent_beacon_block_root: None,
524 withdrawals: None,
525 extra_data: Bytes::new(),
526 slot_number: None,
527 },
528 );
529 Some(evm_env)
530 }
531}
532
533#[cfg(feature = "std")]
534impl<ChainSpec, EvmF> ConfigureEngineEvm<ExecutionData> for EthEvmConfig<ChainSpec, EvmF>
535where
536 ChainSpec: EthChainSpec<Header = Header> + EthereumHardforks + Hardforks + 'static,
537 EvmF: EvmFactory,
538 EvmF::Types: evm2::EvmTypes<Tx = evm2::ethereum::TxEnvelope>,
539{
540 fn evm_env_for_payload(&self, payload: &ExecutionData) -> Result<EvmEnvFor<Self>, Self::Error> {
541 let spec = spec_id_by_timestamp_and_block_number(
542 self.chain_spec().as_ref(),
543 payload.payload.timestamp(),
544 payload.payload.block_number(),
545 );
546 let blob_params =
547 self.chain_spec().as_ref().blob_params_at_timestamp(payload.payload.timestamp());
548 Ok(self.evm_env_from_base_spec(
549 spec,
550 payload_block_env(payload, spec, blob_params),
551 blob_params,
552 ))
553 }
554
555 fn context_for_payload<'a>(
556 &self,
557 payload: &'a ExecutionData,
558 ) -> Result<ExecutionCtxFor<'a, Self>, Self::Error> {
559 Ok(EthBlockExecutionCtx {
560 tx_count_hint: Some(payload.payload.transactions().len()),
561 parent_hash: payload.parent_hash(),
562 parent_beacon_block_root: payload.sidecar.parent_beacon_block_root(),
563 ommers: &[],
564 withdrawals: payload.payload.withdrawals().map(|w| Cow::Borrowed(w.as_slice())),
565 extra_data: payload.payload.as_v1().extra_data.clone(),
566 slot_number: payload.payload.as_v4().map(|v4| v4.slot_number),
567 })
568 }
569
570 fn tx_iterator_for_payload(
571 &self,
572 payload: &ExecutionData,
573 ) -> Result<impl ExecutableTxIterator<Self>, Self::Error> {
574 let txs = payload.payload.transactions().clone();
575 let sender_recovery_cache = self.sender_recovery_cache.clone();
576 let convert = move |tx: Bytes| {
577 let tx = TransactionSigned::decode_2718_exact(tx.as_ref()).map_err(AnyError::new)?;
578 let signer = if let Some(cache) = &sender_recovery_cache {
579 cache.recover(&tx)
580 } else {
581 tx.try_recover()
582 }
583 .map_err(AnyError::new)?;
584 Ok::<_, AnyError>(ExecutableRecoveredTx::new(tx.with_signer(signer)))
585 };
586
587 Ok((txs, convert))
588 }
589}
590
591#[cfg(test)]
592mod tests {
593 use super::*;
594 use alloy_eips::eip7825::MAX_TX_GAS_LIMIT_OSAKA;
595 use alloy_genesis::Genesis;
596 use alloy_primitives::U256;
597 use reth_chainspec::{Chain, ChainSpec};
598
599 #[test]
600 fn test_evm_env_for_header_uses_chain_blob_params() {
601 for update_fraction in [BlobParams::cancun().update_fraction, u128::from(u64::MAX) + 1] {
602 let mut chain_spec = ChainSpec::builder()
603 .chain(Chain::mainnet())
604 .genesis(Genesis::default())
605 .cancun_activated()
606 .build();
607 chain_spec.blob_params.cancun.update_fraction = update_fraction;
608 let blob_params = chain_spec.blob_params_at_timestamp(1).expect("cancun blob params");
609 let excess_blob_gas = 1_000_000;
610 let header = Header {
611 timestamp: 1,
612 excess_blob_gas: Some(excess_blob_gas),
613 ..Default::default()
614 };
615
616 let env = EthEvmConfig::new(Arc::new(chain_spec)).evm_env(&header).unwrap().block;
617
618 assert_eq!(env.blob_basefee, U256::from(blob_params.calc_blob_fee(excess_blob_gas)));
619 }
620 }
621
622 #[test]
623 fn test_transaction_validation_limits_tx_gas_limit_cap() {
624 let cap = |spec| {
625 EthEvmEnv::<evm2::BaseEvmTypes>::new(spec, Default::default(), 1)
626 .transaction_validation_limits()
627 .tx_gas_limit_cap
628 };
629
630 assert_eq!(cap(evm2::SpecId::PRAGUE), 0);
631 assert_eq!(cap(evm2::SpecId::OSAKA), MAX_TX_GAS_LIMIT_OSAKA);
632 assert_eq!(cap(evm2::SpecId::AMSTERDAM), evm2::constants::MAX_TX_TOTAL_GAS_LIMIT_AMSTERDAM);
633 }
634
635 #[derive(Debug, Clone)]
636 struct CustomFactory;
637
638 impl EvmFactory for CustomFactory {
639 type Types = evm2::BaseEvmTypes;
640 type SpecId = evm2::SpecId;
641
642 fn spec_id(&self, spec: evm2::SpecId) -> Self::SpecId {
643 spec
644 }
645
646 fn tx_registry(
647 &self,
648 spec: Self::SpecId,
649 ) -> evm2::registry::TxRegistry<Self::Types, evm2::TxResult<Self::Types>> {
650 evm2::ethereum::ethereum_tx_registry(spec)
651 }
652 }
653
654 #[test]
655 fn test_reuses_prewarm_results_only_for_known_factories() {
656 assert!(EthEvmConfig::mainnet().reuses_prewarm_results());
657 assert!(EthEvmConfig::new_with_evm_factory(
658 MAINNET.clone(),
659 factory::RethEvmFactory::default()
660 )
661 .reuses_prewarm_results());
662
663 assert!(!EthEvmConfig::new_with_evm_factory(MAINNET.clone(), CustomFactory)
664 .reuses_prewarm_results());
665 }
666
667 #[cfg(feature = "jit")]
668 #[test]
669 fn test_jit_support_updates_reth_factory() {
670 let evm_config =
671 EthEvmConfig::new_with_evm_factory(MAINNET.clone(), RethEvmFactory::disabled());
672
673 assert!(evm_config.jit_backend().is_some());
674 assert!(!evm_config.executor_factory.evm_factory().jit_support_enabled());
675
676 let jit_evm_config = evm_config.clone().with_jit_support();
677 assert!(jit_evm_config.executor_factory.evm_factory().jit_support_enabled());
678 assert!(!evm_config.executor_factory.evm_factory().jit_support_enabled());
679
680 let jit_evm_config = jit_evm_config.with_jit_support_enabled(false);
681 assert!(!jit_evm_config.executor_factory.evm_factory().jit_support_enabled());
682 }
683
684 #[cfg(feature = "jit")]
685 #[test]
686 fn test_jit_support_ignores_plain_factory() {
687 let evm_config = EthEvmConfig::new_with_evm_factory(MAINNET.clone(), CustomFactory);
688
689 assert!(evm_config.jit_backend().is_none());
690
691 let evm_config = evm_config.with_jit_support();
692 assert!(evm_config.jit_backend().is_none());
693 }
694}