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/SeismicSystems/seismic-reth/issues/"
13)]
14#![cfg_attr(not(test), warn(unused_crate_dependencies))]
15#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
16#![cfg_attr(not(feature = "std"), no_std)]
17
18extern crate alloc;
19
20use core::{convert::Infallible, net::IpAddr};
21
22use alloc::{sync::Arc, vec::Vec};
23use alloy_consensus::{transaction::TxSeismicElements, Header, TxSeismic, Typed2718};
24use alloy_primitives::{Address, Bytes, TxHash, TxKind, U256};
25use reth_chainspec::{ChainSpec, Head};
26use reth_enclave::{EnclaveClient, EnclaveError, SchnorrkelKeypair, SyncEnclaveApiClient};
27use reth_evm::{ConfigureEvm, ConfigureEvmEnv, NextBlockEnvAttributes};
28use reth_primitives::{transaction::FillTxEnv, Transaction, TransactionSigned};
29use reth_tracing::tracing::debug;
30use revm_primitives::{
31    AnalysisKind, BlobExcessGasAndPrice, BlockEnv, CfgEnv, CfgEnvWithHandlerCfg, EVMError,
32    EVMResultGeneric, Env, SpecId, TxEnv,
33};
34
35mod config;
36use alloy_eips::eip1559::INITIAL_BASE_FEE;
37pub use config::{revm_spec, revm_spec_by_timestamp_after_merge};
38use reth_ethereum_forks::EthereumHardfork;
39use secp256k1::PublicKey;
40
41pub mod execute;
42
43/// Ethereum DAO hardfork state change data.
44pub mod dao_fork;
45
46/// [EIP-6110](https://eips.ethereum.org/EIPS/eip-6110) handling.
47pub mod eip6110;
48
49/// Ethereum-related EVM configuration.
50#[derive(Debug, Clone)]
51pub struct EthEvmConfig {
52    chain_spec: Arc<ChainSpec>,
53    enclave_client: EnclaveClient,
54}
55
56impl EthEvmConfig {
57    /// Creates a new Ethereum EVM configuration with the given chain spec.
58    pub fn new(chain_spec: Arc<ChainSpec>) -> Self {
59        Self { chain_spec, enclave_client: EnclaveClient::default() }
60    }
61
62    /// Creates a new Ethereum EVM configuration with the given chain spec.
63    pub fn new_with_enclave_client(
64        chain_spec: Arc<ChainSpec>,
65        enclave_client: EnclaveClient,
66    ) -> Self {
67        Self { chain_spec, enclave_client }
68    }
69
70    /// Creates a new Ethereum EVM configuration with the given chain spec and enclave address and
71    /// port.
72    pub fn new_with_enclave_addr_port(
73        chain_spec: Arc<ChainSpec>,
74        enclave_addr: IpAddr,
75        enclave_port: u16,
76        enclave_server_timeout: u64,
77    ) -> Self {
78        debug!(target: "reth::evm", ?enclave_addr, ?enclave_port, "Creating new enclave client");
79
80        let enclave_client = EnclaveClient::builder()
81            .addr(enclave_addr.to_string())
82            .port(enclave_port)
83            .timeout(std::time::Duration::from_secs(enclave_server_timeout))
84            .build();
85        Self::new_with_enclave_client(chain_spec, enclave_client)
86    }
87
88    /// Returns the chain spec associated with this configuration.
89    pub const fn chain_spec(&self) -> &Arc<ChainSpec> {
90        &self.chain_spec
91    }
92
93    /// Returns the public key from the signature.
94    pub fn recover_pubkey(&self, tx: &TransactionSigned) -> Option<PublicKey> {
95        let signature_hash = tx.signature_hash();
96
97        tx.signature.recover_from_prehash(&signature_hash).ok().and_then(|verifying_key| {
98            let pbk_bytes = verifying_key.to_encoded_point(false);
99            PublicKey::from_slice(pbk_bytes.as_bytes()).ok()
100        })
101    }
102}
103
104impl ConfigureEvmEnv for EthEvmConfig {
105    type Header = Header;
106    type Transaction = TransactionSigned;
107    type Error = Infallible;
108
109    fn encrypt(
110        &self,
111        data: &Bytes,
112        seismic_elements: &TxSeismicElements,
113    ) -> EVMResultGeneric<Bytes, EnclaveError> {
114        Ok(seismic_elements
115            .server_encrypt(&self.enclave_client, &data)
116            .map_err(|_| EVMError::Database(EnclaveError::EncryptionError))?)
117    }
118    fn decrypt(
119        &self,
120        data: &Bytes,
121        seismic_elements: &TxSeismicElements,
122    ) -> EVMResultGeneric<Bytes, EnclaveError> {
123        Ok(seismic_elements
124            .server_decrypt(&self.enclave_client, &data)
125            .map_err(|_| EVMError::Database(EnclaveError::DecryptionError))?)
126    }
127
128    /// Get current eph_rng_keypair
129    fn get_eph_rng_keypair(&self) -> EVMResultGeneric<SchnorrkelKeypair, EnclaveError> {
130        Ok(self.enclave_client.get_eph_rng_keypair().map_err(|e| {
131            EVMError::Database(EnclaveError::EphRngKeypairGenerationError(e.to_string()))
132        })?)
133    }
134
135    /// seismic feature decrypt the transaction
136    fn fill_seismic_tx_env(
137        &self,
138        tx_env: &mut TxEnv,
139        tx: &TxSeismic,
140        sender: Address,
141        tx_hash: TxHash,
142    ) -> EVMResultGeneric<(), EnclaveError> {
143        let enclave_decryption = self.decrypt(&tx.input, &tx.seismic_elements)?;
144
145        let data = Bytes::from(enclave_decryption.clone());
146
147        tx_env.caller = sender;
148        tx_env.gas_limit = tx.gas_limit;
149        tx_env.gas_price = U256::from(tx.gas_price);
150        tx_env.gas_priority_fee = None;
151        tx_env.transact_to = tx.to;
152        tx_env.value = tx.value;
153        tx_env.data = data;
154        tx_env.chain_id = Some(tx.chain_id);
155        tx_env.nonce = Some(tx.nonce);
156        tx_env.access_list.clear();
157        tx_env.blob_hashes.clear();
158        tx_env.max_fee_per_blob_gas.take();
159        tx_env.authorization_list = None;
160        tx_env.tx_hash = tx_hash;
161        tx_env.tx_type = Some(tx.ty() as isize);
162        Ok(())
163    }
164
165    fn fill_tx_env(
166        &self,
167        tx_env: &mut TxEnv,
168        transaction: &TransactionSigned,
169        sender: Address,
170    ) -> EVMResultGeneric<(), EnclaveError> {
171        match &transaction.transaction {
172            Transaction::Seismic(tx) => {
173                self.fill_seismic_tx_env(tx_env, tx, sender, transaction.hash())?;
174                Ok(())
175            }
176            _ => Ok(transaction.fill_tx_env(tx_env, sender)),
177        }
178    }
179
180    fn fill_tx_env_system_contract_call(
181        &self,
182        env: &mut Env,
183        caller: Address,
184        contract: Address,
185        data: Bytes,
186    ) {
187        #[allow(clippy::needless_update)] // side-effect of optimism fields
188        let tx = TxEnv {
189            caller,
190            transact_to: TxKind::Call(contract),
191            // Explicitly set nonce to None so revm does not do any nonce checks
192            nonce: None,
193            gas_limit: 30_000_000,
194            value: U256::ZERO,
195            data,
196            // Setting the gas price to zero enforces that no value is transferred as part of the
197            // call, and that the call will not count against the block's gas limit
198            gas_price: U256::ZERO,
199            // The chain ID check is not relevant here and is disabled if set to None
200            chain_id: None,
201            // Setting the gas priority fee to None ensures the effective gas price is derived from
202            // the `gas_price` field, which we need to be zero
203            gas_priority_fee: None,
204            access_list: Vec::new(),
205            // blob fields can be None for this tx
206            blob_hashes: Vec::new(),
207            max_fee_per_blob_gas: None,
208            // TODO remove this once this crate is no longer built with optimism
209            ..Default::default()
210        };
211        env.tx = tx;
212
213        // ensure the block gas limit is >= the tx
214        env.block.gas_limit = U256::from(env.tx.gas_limit);
215
216        // disable the base fee check for this call by setting the base fee to zero
217        env.block.basefee = U256::ZERO;
218    }
219
220    fn fill_cfg_env(
221        &self,
222        cfg_env: &mut CfgEnvWithHandlerCfg,
223        header: &Header,
224        total_difficulty: U256,
225    ) {
226        let spec_id = config::revm_spec(
227            self.chain_spec(),
228            &Head {
229                number: header.number,
230                timestamp: header.timestamp,
231                difficulty: header.difficulty,
232                total_difficulty,
233                hash: Default::default(),
234            },
235        );
236
237        cfg_env.chain_id = self.chain_spec.chain().id();
238        cfg_env.perf_analyse_created_bytecodes = AnalysisKind::Analyse;
239
240        cfg_env.handler_cfg.spec_id = spec_id;
241    }
242
243    fn next_cfg_and_block_env(
244        &self,
245        parent: &Self::Header,
246        attributes: NextBlockEnvAttributes,
247    ) -> Result<(CfgEnvWithHandlerCfg, BlockEnv), Self::Error> {
248        // configure evm env based on parent block
249        let cfg = CfgEnv::default().with_chain_id(self.chain_spec.chain().id());
250
251        // ensure we're not missing any timestamp based hardforks
252        let spec_id = revm_spec_by_timestamp_after_merge(&self.chain_spec, attributes.timestamp);
253
254        // if the parent block did not have excess blob gas (i.e. it was pre-cancun), but it is
255        // cancun now, we need to set the excess blob gas to the default value(0)
256        let blob_excess_gas_and_price = parent
257            .next_block_excess_blob_gas()
258            .or_else(|| (spec_id == SpecId::CANCUN).then_some(0))
259            .map(BlobExcessGasAndPrice::new);
260
261        let mut basefee = parent.next_block_base_fee(
262            self.chain_spec.base_fee_params_at_timestamp(attributes.timestamp),
263        );
264
265        let mut gas_limit = U256::from(parent.gas_limit);
266
267        // If we are on the London fork boundary, we need to multiply the parent's gas limit by the
268        // elasticity multiplier to get the new gas limit.
269        if self.chain_spec.fork(EthereumHardfork::London).transitions_at_block(parent.number + 1) {
270            let elasticity_multiplier = self
271                .chain_spec
272                .base_fee_params_at_timestamp(attributes.timestamp)
273                .elasticity_multiplier;
274
275            // multiply the gas limit by the elasticity multiplier
276            gas_limit *= U256::from(elasticity_multiplier);
277
278            // set the base fee to the initial base fee from the EIP-1559 spec
279            basefee = Some(INITIAL_BASE_FEE)
280        }
281
282        let block_env = BlockEnv {
283            number: U256::from(parent.number + 1),
284            coinbase: attributes.suggested_fee_recipient,
285            timestamp: U256::from(attributes.timestamp),
286            difficulty: U256::ZERO,
287            prevrandao: Some(attributes.prev_randao),
288            gas_limit,
289            // calculate basefee based on parent block's gas usage
290            basefee: basefee.map(U256::from).unwrap_or_default(),
291            // calculate excess gas based on parent block's blob gas usage
292            blob_excess_gas_and_price,
293        };
294
295        Ok((CfgEnvWithHandlerCfg::new_with_spec_id(cfg, spec_id), block_env))
296    }
297}
298
299impl ConfigureEvm for EthEvmConfig {
300    type DefaultExternalContext<'a> = ();
301
302    fn default_external_context<'a>(&self) -> Self::DefaultExternalContext<'a> {}
303}
304
305#[cfg(test)]
306mod tests {
307    use super::*;
308    use alloy_consensus::{constants::KECCAK_EMPTY, Header};
309    use alloy_genesis::Genesis;
310    use alloy_primitives::{B256, U256};
311    use reth_chainspec::{Chain, ChainSpec, MAINNET};
312    use reth_enclave::start_mock_enclave_server_random_port;
313    use reth_evm::execute::ProviderError;
314    use reth_revm::{
315        db::{CacheDB, EmptyDBTyped},
316        handler::register::EvmHandler,
317        inspectors::NoOpInspector,
318        precompile::u64_to_address,
319        primitives::{BlockEnv, CfgEnv, SpecId},
320        Evm, Handler, JournaledState,
321    };
322    use revm_primitives::{EnvWithHandlerCfg, HandlerCfg};
323    use std::collections::HashSet;
324
325    #[test]
326    fn test_fill_cfg_and_block_env() {
327        // Create a default header
328        let header = Header::default();
329
330        // Build the ChainSpec for Ethereum mainnet, activating London, Paris, and Shanghai
331        // hardforks
332        let chain_spec = ChainSpec::builder()
333            .chain(Chain::mainnet())
334            .genesis(Genesis::default())
335            .london_activated()
336            .paris_activated()
337            .shanghai_activated()
338            .build();
339
340        // Define the total difficulty as zero (default)
341        let total_difficulty = U256::ZERO;
342
343        // Use the `EthEvmConfig` to fill the `cfg_env` and `block_env` based on the ChainSpec,
344        // Header, and total difficulty
345        let (cfg_env, _) = EthEvmConfig::new(Arc::new(chain_spec.clone()))
346            .cfg_and_block_env(&header, total_difficulty);
347
348        // Assert that the chain ID in the `cfg_env` is correctly set to the chain ID of the
349        // ChainSpec
350        assert_eq!(cfg_env.chain_id, chain_spec.chain().id());
351    }
352
353    #[test]
354    #[allow(clippy::needless_update)]
355    fn test_evm_configure() {
356        // Create a default `EthEvmConfig`
357        let evm_config = EthEvmConfig::new(MAINNET.clone());
358
359        // Initialize an empty database wrapped in CacheDB
360        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
361
362        // Create an EVM instance using the configuration and the database
363        let evm = evm_config.evm(db);
364
365        // Check that the EVM environment is initialized with default values
366        assert_eq!(evm.context.evm.inner.env, Box::default());
367
368        // Latest spec ID and no warm preloaded addresses
369        assert_eq!(
370            evm.context.evm.inner.journaled_state,
371            JournaledState::new(SpecId::LATEST, HashSet::default())
372        );
373
374        // Ensure that the accounts database is empty
375        assert!(evm.context.evm.inner.db.accounts.is_empty());
376
377        // Ensure that the block hashes database is empty
378        assert!(evm.context.evm.inner.db.block_hashes.is_empty());
379
380        // Verify that there are two default contracts in the contracts database
381        assert_eq!(evm.context.evm.inner.db.contracts.len(), 2);
382        assert!(evm.context.evm.inner.db.contracts.contains_key(&KECCAK_EMPTY));
383        assert!(evm.context.evm.inner.db.contracts.contains_key(&B256::ZERO));
384
385        // Ensure that the logs database is empty
386        assert!(evm.context.evm.inner.db.logs.is_empty());
387
388        // No Optimism
389        assert_eq!(evm.handler.cfg, HandlerCfg { spec_id: SpecId::LATEST, ..Default::default() });
390    }
391
392    #[test]
393    #[allow(clippy::needless_update)]
394    fn test_evm_with_env_default_spec() {
395        let evm_config = EthEvmConfig::new(MAINNET.clone());
396
397        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
398
399        let env_with_handler = EnvWithHandlerCfg::default();
400
401        let evm = evm_config.evm_with_env(db, env_with_handler.clone());
402
403        // Check that the EVM environment
404        assert_eq!(evm.context.evm.env, env_with_handler.env);
405
406        // Default spec ID
407        assert_eq!(evm.handler.spec_id(), SpecId::LATEST);
408
409        // No Optimism
410        assert_eq!(evm.handler.cfg, HandlerCfg { spec_id: SpecId::LATEST, ..Default::default() });
411    }
412
413    #[test]
414    #[allow(clippy::needless_update)]
415    fn test_evm_with_env_custom_cfg() {
416        let evm_config = EthEvmConfig::new(MAINNET.clone());
417
418        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
419
420        // Create a custom configuration environment with a chain ID of 111
421        let cfg = CfgEnv::default().with_chain_id(111);
422
423        let env_with_handler = EnvWithHandlerCfg {
424            env: Box::new(Env {
425                cfg: cfg.clone(),
426                block: BlockEnv::default(),
427                tx: TxEnv::default(),
428            }),
429            handler_cfg: Default::default(),
430        };
431
432        let evm = evm_config.evm_with_env(db, env_with_handler);
433
434        // Check that the EVM environment is initialized with the custom environment
435        assert_eq!(evm.context.evm.inner.env.cfg, cfg);
436
437        // Default spec ID
438        assert_eq!(evm.handler.spec_id(), SpecId::LATEST);
439
440        // No Optimism
441        assert_eq!(evm.handler.cfg, HandlerCfg { spec_id: SpecId::LATEST, ..Default::default() });
442    }
443
444    #[test]
445    #[allow(clippy::needless_update)]
446    fn test_evm_with_env_custom_block_and_tx() {
447        // start server
448
449        let evm_config = EthEvmConfig::new(MAINNET.clone());
450
451        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
452
453        // Create customs block and tx env
454        let block = BlockEnv {
455            basefee: U256::from(1000),
456            gas_limit: U256::from(10_000_000),
457            number: U256::from(42),
458            ..Default::default()
459        };
460        let tx = TxEnv { gas_limit: 5_000_000, gas_price: U256::from(50), ..Default::default() };
461
462        let env_with_handler = EnvWithHandlerCfg {
463            env: Box::new(Env { cfg: CfgEnv::default(), block, tx }),
464            handler_cfg: Default::default(),
465        };
466
467        let evm = evm_config.evm_with_env(db, env_with_handler.clone());
468
469        // Verify that the block and transaction environments are set correctly
470        assert_eq!(evm.context.evm.env.block, env_with_handler.env.block);
471        assert_eq!(evm.context.evm.env.tx, env_with_handler.env.tx);
472
473        // Default spec ID
474        assert_eq!(evm.handler.spec_id(), SpecId::LATEST);
475
476        // No Optimism
477        assert_eq!(evm.handler.cfg, HandlerCfg { spec_id: SpecId::LATEST, ..Default::default() });
478    }
479
480    #[test]
481    #[allow(clippy::needless_update)]
482    fn test_evm_with_spec_id() {
483        let evm_config = EthEvmConfig::new(MAINNET.clone());
484
485        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
486
487        let handler_cfg = HandlerCfg { spec_id: SpecId::CONSTANTINOPLE, ..Default::default() };
488
489        let env_with_handler = EnvWithHandlerCfg { env: Box::new(Env::default()), handler_cfg };
490
491        let evm = evm_config.evm_with_env(db, env_with_handler);
492
493        // Check that the spec ID is setup properly
494        assert_eq!(evm.handler.spec_id(), SpecId::CONSTANTINOPLE);
495
496        // No Optimism
497        assert_eq!(
498            evm.handler.cfg,
499            HandlerCfg { spec_id: SpecId::CONSTANTINOPLE, ..Default::default() }
500        );
501    }
502
503    #[test]
504    #[allow(clippy::needless_update)]
505    fn test_evm_with_inspector() {
506        let evm_config = EthEvmConfig::new(MAINNET.clone());
507
508        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
509
510        // No operation inspector
511        let noop = NoOpInspector;
512
513        let evm = evm_config.evm_with_inspector(db, noop);
514
515        // Check that the inspector is set correctly
516        assert_eq!(evm.context.external, noop);
517
518        // Check that the EVM environment is initialized with default values
519        assert_eq!(evm.context.evm.inner.env, Box::default());
520
521        // Latest spec ID and no warm preloaded addresses
522        assert_eq!(
523            evm.context.evm.inner.journaled_state,
524            JournaledState::new(SpecId::LATEST, HashSet::default())
525        );
526
527        // Ensure that the accounts database is empty
528        assert!(evm.context.evm.inner.db.accounts.is_empty());
529
530        // Ensure that the block hashes database is empty
531        assert!(evm.context.evm.inner.db.block_hashes.is_empty());
532
533        // Verify that there are two default contracts in the contracts database
534        assert_eq!(evm.context.evm.inner.db.contracts.len(), 2);
535        assert!(evm.context.evm.inner.db.contracts.contains_key(&KECCAK_EMPTY));
536        assert!(evm.context.evm.inner.db.contracts.contains_key(&B256::ZERO));
537
538        // Ensure that the logs database is empty
539        assert!(evm.context.evm.inner.db.logs.is_empty());
540
541        // No Optimism
542        assert_eq!(evm.handler.cfg, HandlerCfg { spec_id: SpecId::LATEST, ..Default::default() });
543    }
544
545    #[test]
546    #[allow(clippy::needless_update)]
547    fn test_evm_with_env_and_default_inspector() {
548        let evm_config = EthEvmConfig::new(MAINNET.clone());
549        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
550
551        let env_with_handler = EnvWithHandlerCfg::default();
552
553        let evm =
554            evm_config.evm_with_env_and_inspector(db, env_with_handler.clone(), NoOpInspector);
555
556        // Check that the EVM environment is set to default values
557        assert_eq!(evm.context.evm.env, env_with_handler.env);
558        assert_eq!(evm.context.external, NoOpInspector);
559        assert_eq!(evm.handler.spec_id(), SpecId::LATEST);
560
561        // No Optimism
562        assert_eq!(evm.handler.cfg, HandlerCfg { spec_id: SpecId::LATEST, ..Default::default() });
563    }
564
565    #[test]
566    #[allow(clippy::needless_update)]
567    fn test_evm_with_env_inspector_and_custom_cfg() {
568        let evm_config = EthEvmConfig::new(MAINNET.clone());
569        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
570
571        let cfg = CfgEnv::default().with_chain_id(111);
572        let block = BlockEnv::default();
573        let tx = TxEnv::default();
574        let env_with_handler = EnvWithHandlerCfg {
575            env: Box::new(Env { cfg: cfg.clone(), block, tx }),
576            handler_cfg: Default::default(),
577        };
578
579        let evm = evm_config.evm_with_env_and_inspector(db, env_with_handler, NoOpInspector);
580
581        // Check that the EVM environment is set with custom configuration
582        assert_eq!(evm.context.evm.env.cfg, cfg);
583        assert_eq!(evm.context.external, NoOpInspector);
584        assert_eq!(evm.handler.spec_id(), SpecId::LATEST);
585
586        // No Optimism
587        assert_eq!(evm.handler.cfg, HandlerCfg { spec_id: SpecId::LATEST, ..Default::default() });
588    }
589
590    #[test]
591    #[allow(clippy::needless_update)]
592    fn test_evm_with_env_inspector_and_custom_block_tx() {
593        let evm_config = EthEvmConfig::new(MAINNET.clone());
594        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
595
596        // Create custom block and tx environment
597        let block = BlockEnv {
598            basefee: U256::from(1000),
599            gas_limit: U256::from(10_000_000),
600            number: U256::from(42),
601            ..Default::default()
602        };
603        let tx = TxEnv { gas_limit: 5_000_000, gas_price: U256::from(50), ..Default::default() };
604        let env_with_handler = EnvWithHandlerCfg {
605            env: Box::new(Env { cfg: CfgEnv::default(), block, tx }),
606            handler_cfg: Default::default(),
607        };
608
609        let evm =
610            evm_config.evm_with_env_and_inspector(db, env_with_handler.clone(), NoOpInspector);
611
612        // Verify that the block and transaction environments are set correctly
613        assert_eq!(evm.context.evm.env.block, env_with_handler.env.block);
614        assert_eq!(evm.context.evm.env.tx, env_with_handler.env.tx);
615        assert_eq!(evm.context.external, NoOpInspector);
616        assert_eq!(evm.handler.spec_id(), SpecId::LATEST);
617
618        // No Optimism
619        assert_eq!(evm.handler.cfg, HandlerCfg { spec_id: SpecId::LATEST, ..Default::default() });
620    }
621
622    #[test]
623    #[allow(clippy::needless_update)]
624    fn test_evm_with_env_inspector_and_spec_id() {
625        let evm_config = EthEvmConfig::new(MAINNET.clone());
626        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
627
628        let handler_cfg = HandlerCfg { spec_id: SpecId::CONSTANTINOPLE, ..Default::default() };
629        let env_with_handler = EnvWithHandlerCfg { env: Box::new(Env::default()), handler_cfg };
630
631        let evm =
632            evm_config.evm_with_env_and_inspector(db, env_with_handler.clone(), NoOpInspector);
633
634        // Check that the spec ID is set properly
635        assert_eq!(evm.handler.spec_id(), SpecId::CONSTANTINOPLE);
636        assert_eq!(evm.context.evm.env, env_with_handler.env);
637        assert_eq!(evm.context.external, NoOpInspector);
638
639        // No Optimism
640        assert_eq!(
641            evm.handler.cfg,
642            HandlerCfg { spec_id: SpecId::CONSTANTINOPLE, ..Default::default() }
643        );
644    }
645
646    #[tokio::test(flavor = "multi_thread")]
647    #[allow(clippy::needless_update)]
648    async fn test_evm_with_spec_id_seismic() {
649        // expect a call to hit the mock enclave server
650        let enclave_client = start_mock_enclave_server_random_port().await;
651
652        let evm_config = EthEvmConfig::new_with_enclave_client(MAINNET.clone(), enclave_client);
653
654        let db = CacheDB::<EmptyDBTyped<Infallible>>::default();
655
656        let handler_cfg = HandlerCfg { spec_id: SpecId::MERCURY, ..Default::default() };
657
658        let env_with_handler = EnvWithHandlerCfg { env: Box::new(Env::default()), handler_cfg };
659
660        let evm = evm_config.evm_with_env(db.clone(), env_with_handler);
661
662        // Check that the spec ID is setup properly
663        assert_eq!(evm.handler.spec_id(), SpecId::MERCURY);
664        assert!(evm.handler.is_seismic());
665
666        // Check that standard way of generating evm works
667        type DB = CacheDB<EmptyDBTyped<Infallible>>;
668        type EXT = ();
669
670        let seismic_handler: Handler<'_, reth_revm::Context<EXT, DB>, EXT, DB> =
671            EvmHandler::seismic_with_spec(SpecId::MERCURY);
672        let seismic_evm = Evm::builder().with_db(db).with_handler(seismic_handler).build();
673
674        let precompile_addresses =
675            [u64_to_address(101), u64_to_address(102), u64_to_address(103), u64_to_address(104)];
676
677        let precompiles = seismic_evm.handler.pre_execution().load_precompiles();
678
679        for &addr in &precompile_addresses {
680            let is_contained = precompiles.contains(&addr);
681            assert!(
682                is_contained,
683                "Expected Precompile at address for standard evm generation {addr:?}"
684            );
685        }
686        assert_eq!(evm.handler.spec_id(), seismic_evm.handler.spec_id());
687        assert_eq!(evm.handler.is_seismic(), seismic_evm.handler.is_seismic());
688
689        //Check that RETH way of generating evm works
690        let precompiles = evm.handler.pre_execution().load_precompiles();
691
692        for &addr in &precompile_addresses {
693            let is_contained = precompiles.contains(&addr);
694            assert!(
695                is_contained,
696                "Expected Precompile at address for RETH evm generation {addr:?}"
697            );
698        }
699    }
700}