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