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