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