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};
26use core::{convert::Infallible, fmt::Debug};
27use reth_chainspec::{ChainSpec, EthChainSpec, MAINNET};
28use reth_ethereum_primitives::{Block, EthPrimitives, TransactionSigned};
29use reth_evm::{
30    eth::NextEvmEnvAttributes, precompiles::PrecompilesMap, ConfigureEvm, EvmEnv, EvmFactory,
31    NextBlockEnvAttributes, TransactionEnv,
32};
33use reth_primitives_traits::{SealedBlock, SealedHeader};
34use revm::{context::BlockEnv, primitives::hardfork::SpecId};
35
36#[cfg(feature = "std")]
37use reth_evm::{ConfigureEngineEvm, ExecutableTxIterator};
38#[allow(unused_imports)]
39use {
40    alloy_eips::Decodable2718,
41    alloy_primitives::{Bytes, U256},
42    alloy_rpc_types_engine::ExecutionData,
43    reth_chainspec::EthereumHardforks,
44    reth_evm::{EvmEnvFor, ExecutionCtxFor},
45    reth_primitives_traits::{constants::MAX_TX_GAS_LIMIT_OSAKA, SignedTransaction, TxTy},
46    reth_storage_errors::any::AnyError,
47    revm::context::CfgEnv,
48    revm::context_interface::block::BlobExcessGasAndPrice,
49};
50
51pub use alloy_evm::EthEvm;
52
53mod config;
54use alloy_evm::eth::spec::EthExecutorSpec;
55pub use config::{revm_spec, revm_spec_by_timestamp_and_block_number};
56use reth_ethereum_forks::Hardforks;
57
58/// Helper type with backwards compatible methods to obtain Ethereum executor
59/// providers.
60#[doc(hidden)]
61pub mod execute {
62    use crate::EthEvmConfig;
63
64    #[deprecated(note = "Use `EthEvmConfig` instead")]
65    pub type EthExecutorProvider = EthEvmConfig;
66}
67
68mod build;
69pub use build::EthBlockAssembler;
70
71mod receipt;
72pub use receipt::RethReceiptBuilder;
73
74#[cfg(feature = "test-utils")]
75mod test_utils;
76#[cfg(feature = "test-utils")]
77pub use test_utils::*;
78
79/// Ethereum-related EVM configuration.
80#[derive(Debug, Clone)]
81pub struct EthEvmConfig<C = ChainSpec, EvmFactory = EthEvmFactory> {
82    /// Inner [`EthBlockExecutorFactory`].
83    pub executor_factory: EthBlockExecutorFactory<RethReceiptBuilder, Arc<C>, EvmFactory>,
84    /// Ethereum block assembler.
85    pub block_assembler: EthBlockAssembler<C>,
86}
87
88impl EthEvmConfig {
89    /// Creates a new Ethereum EVM configuration for the ethereum mainnet.
90    pub fn mainnet() -> Self {
91        Self::ethereum(MAINNET.clone())
92    }
93}
94
95impl<ChainSpec> EthEvmConfig<ChainSpec> {
96    /// Creates a new Ethereum EVM configuration with the given chain spec.
97    pub fn new(chain_spec: Arc<ChainSpec>) -> Self {
98        Self::ethereum(chain_spec)
99    }
100
101    /// Creates a new Ethereum EVM configuration.
102    pub fn ethereum(chain_spec: Arc<ChainSpec>) -> Self {
103        Self::new_with_evm_factory(chain_spec, EthEvmFactory::default())
104    }
105}
106
107impl<ChainSpec, EvmFactory> EthEvmConfig<ChainSpec, EvmFactory> {
108    /// Creates a new Ethereum EVM configuration with the given chain spec and EVM factory.
109    pub fn new_with_evm_factory(chain_spec: Arc<ChainSpec>, evm_factory: EvmFactory) -> Self {
110        Self {
111            block_assembler: EthBlockAssembler::new(chain_spec.clone()),
112            executor_factory: EthBlockExecutorFactory::new(
113                RethReceiptBuilder::default(),
114                chain_spec,
115                evm_factory,
116            ),
117        }
118    }
119
120    /// Returns the chain spec associated with this configuration.
121    pub const fn chain_spec(&self) -> &Arc<ChainSpec> {
122        self.executor_factory.spec()
123    }
124}
125
126impl<ChainSpec, EvmF> ConfigureEvm for EthEvmConfig<ChainSpec, EvmF>
127where
128    ChainSpec: EthExecutorSpec + EthChainSpec<Header = Header> + Hardforks + 'static,
129    EvmF: EvmFactory<
130            Tx: TransactionEnv
131                    + FromRecoveredTx<TransactionSigned>
132                    + FromTxWithEncoded<TransactionSigned>,
133            Spec = SpecId,
134            BlockEnv = BlockEnv,
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) -> Result<EvmEnv<SpecId>, Self::Error> {
158        Ok(EvmEnv::for_eth_block(
159            header,
160            self.chain_spec(),
161            self.chain_spec().chain().id(),
162            self.chain_spec().blob_params_at_timestamp(header.timestamp),
163        ))
164    }
165
166    fn next_evm_env(
167        &self,
168        parent: &Header,
169        attributes: &NextBlockEnvAttributes,
170    ) -> Result<EvmEnv, Self::Error> {
171        Ok(EvmEnv::for_eth_next_block(
172            parent,
173            NextEvmEnvAttributes {
174                timestamp: attributes.timestamp,
175                suggested_fee_recipient: attributes.suggested_fee_recipient,
176                prev_randao: attributes.prev_randao,
177                gas_limit: attributes.gas_limit,
178            },
179            self.chain_spec().next_block_base_fee(parent, attributes.timestamp).unwrap_or_default(),
180            self.chain_spec(),
181            self.chain_spec().chain().id(),
182            self.chain_spec().blob_params_at_timestamp(attributes.timestamp),
183        ))
184    }
185
186    fn context_for_block<'a>(
187        &self,
188        block: &'a SealedBlock<Block>,
189    ) -> Result<EthBlockExecutionCtx<'a>, Self::Error> {
190        Ok(EthBlockExecutionCtx {
191            tx_count_hint: Some(block.transaction_count()),
192            parent_hash: block.header().parent_hash,
193            parent_beacon_block_root: block.header().parent_beacon_block_root,
194            ommers: &block.body().ommers,
195            withdrawals: block.body().withdrawals.as_ref().map(Cow::Borrowed),
196            extra_data: block.header().extra_data.clone(),
197        })
198    }
199
200    fn context_for_next_block(
201        &self,
202        parent: &SealedHeader,
203        attributes: Self::NextBlockEnvCtx,
204    ) -> Result<EthBlockExecutionCtx<'_>, Self::Error> {
205        Ok(EthBlockExecutionCtx {
206            tx_count_hint: None,
207            parent_hash: parent.hash(),
208            parent_beacon_block_root: attributes.parent_beacon_block_root,
209            ommers: &[],
210            withdrawals: attributes.withdrawals.map(Cow::Owned),
211            extra_data: attributes.extra_data,
212        })
213    }
214}
215
216#[cfg(feature = "std")]
217impl<ChainSpec, EvmF> ConfigureEngineEvm<ExecutionData> for EthEvmConfig<ChainSpec, EvmF>
218where
219    ChainSpec: EthExecutorSpec + EthChainSpec<Header = Header> + Hardforks + 'static,
220    EvmF: EvmFactory<
221            Tx: TransactionEnv
222                    + FromRecoveredTx<TransactionSigned>
223                    + FromTxWithEncoded<TransactionSigned>,
224            Spec = SpecId,
225            BlockEnv = BlockEnv,
226            Precompiles = PrecompilesMap,
227        > + Clone
228        + Debug
229        + Send
230        + Sync
231        + Unpin
232        + 'static,
233{
234    fn evm_env_for_payload(&self, payload: &ExecutionData) -> Result<EvmEnvFor<Self>, Self::Error> {
235        let timestamp = payload.payload.timestamp();
236        let block_number = payload.payload.block_number();
237
238        let blob_params = self.chain_spec().blob_params_at_timestamp(timestamp);
239        let spec =
240            revm_spec_by_timestamp_and_block_number(self.chain_spec(), timestamp, block_number);
241
242        // configure evm env based on parent block
243        let mut cfg_env = CfgEnv::new()
244            .with_chain_id(self.chain_spec().chain().id())
245            .with_spec_and_mainnet_gas_params(spec);
246
247        if let Some(blob_params) = &blob_params {
248            cfg_env.set_max_blobs_per_tx(blob_params.max_blobs_per_tx);
249        }
250
251        if self.chain_spec().is_osaka_active_at_timestamp(timestamp) {
252            cfg_env.tx_gas_limit_cap = Some(MAX_TX_GAS_LIMIT_OSAKA);
253        }
254
255        // derive the EIP-4844 blob fees from the header's `excess_blob_gas` and the current
256        // blobparams
257        let blob_excess_gas_and_price =
258            payload.payload.excess_blob_gas().zip(blob_params).map(|(excess_blob_gas, params)| {
259                let blob_gasprice = params.calc_blob_fee(excess_blob_gas);
260                BlobExcessGasAndPrice { excess_blob_gas, blob_gasprice }
261            });
262
263        let block_env = BlockEnv {
264            number: U256::from(block_number),
265            beneficiary: payload.payload.fee_recipient(),
266            timestamp: U256::from(timestamp),
267            difficulty: if spec >= SpecId::MERGE {
268                U256::ZERO
269            } else {
270                payload.payload.as_v1().prev_randao.into()
271            },
272            prevrandao: (spec >= SpecId::MERGE).then(|| payload.payload.as_v1().prev_randao),
273            gas_limit: payload.payload.gas_limit(),
274            basefee: payload.payload.saturated_base_fee_per_gas(),
275            blob_excess_gas_and_price,
276        };
277
278        Ok(EvmEnv { cfg_env, block_env })
279    }
280
281    fn context_for_payload<'a>(
282        &self,
283        payload: &'a ExecutionData,
284    ) -> Result<ExecutionCtxFor<'a, Self>, Self::Error> {
285        Ok(EthBlockExecutionCtx {
286            tx_count_hint: Some(payload.payload.transactions().len()),
287            parent_hash: payload.parent_hash(),
288            parent_beacon_block_root: payload.sidecar.parent_beacon_block_root(),
289            ommers: &[],
290            withdrawals: payload.payload.withdrawals().map(|w| Cow::Owned(w.clone().into())),
291            extra_data: payload.payload.as_v1().extra_data.clone(),
292        })
293    }
294
295    fn tx_iterator_for_payload(
296        &self,
297        payload: &ExecutionData,
298    ) -> Result<impl ExecutableTxIterator<Self>, Self::Error> {
299        let txs = payload.payload.transactions().clone();
300        let convert = |tx: Bytes| {
301            let tx =
302                TxTy::<Self::Primitives>::decode_2718_exact(tx.as_ref()).map_err(AnyError::new)?;
303            let signer = tx.try_recover().map_err(AnyError::new)?;
304            Ok::<_, AnyError>(tx.with_signer(signer))
305        };
306
307        Ok((txs, convert))
308    }
309}
310
311#[cfg(test)]
312mod tests {
313    use super::*;
314    use alloy_consensus::Header;
315    use alloy_genesis::Genesis;
316    use reth_chainspec::{Chain, ChainSpec};
317    use reth_evm::{execute::ProviderError, EvmEnv};
318    use revm::{
319        context::{BlockEnv, CfgEnv},
320        database::CacheDB,
321        database_interface::EmptyDBTyped,
322        inspector::NoOpInspector,
323    };
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        // Use the `EthEvmConfig` to fill the `cfg_env` and `block_env` based on the ChainSpec,
341        // Header, and total difficulty
342        let EvmEnv { cfg_env, .. } =
343            EthEvmConfig::new(Arc::new(chain_spec.clone())).evm_env(&header).unwrap();
344
345        // Assert that the chain ID in the `cfg_env` is correctly set to the chain ID of the
346        // ChainSpec
347        assert_eq!(cfg_env.chain_id, chain_spec.chain().id());
348    }
349
350    #[test]
351    fn test_evm_with_env_default_spec() {
352        let evm_config = EthEvmConfig::mainnet();
353
354        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
355
356        let evm_env = EvmEnv::default();
357
358        let evm = evm_config.evm_with_env(db, evm_env.clone());
359
360        // Check that the EVM environment
361        assert_eq!(evm.block, evm_env.block_env);
362        assert_eq!(evm.cfg, evm_env.cfg_env);
363    }
364
365    #[test]
366    fn test_evm_with_env_custom_cfg() {
367        let evm_config = EthEvmConfig::mainnet();
368
369        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
370
371        // Create a custom configuration environment with a chain ID of 111
372        let cfg = CfgEnv::default().with_chain_id(111);
373
374        let evm_env = EvmEnv { cfg_env: cfg.clone(), ..Default::default() };
375
376        let evm = evm_config.evm_with_env(db, evm_env);
377
378        // Check that the EVM environment is initialized with the custom environment
379        assert_eq!(evm.cfg, cfg);
380    }
381
382    #[test]
383    fn test_evm_with_env_custom_block_and_tx() {
384        let evm_config = EthEvmConfig::mainnet();
385
386        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
387
388        // Create customs block and tx env
389        let block = BlockEnv {
390            basefee: 1000,
391            gas_limit: 10_000_000,
392            number: U256::from(42),
393            ..Default::default()
394        };
395
396        let evm_env = EvmEnv { block_env: block, ..Default::default() };
397
398        let evm = evm_config.evm_with_env(db, evm_env.clone());
399
400        // Verify that the block and transaction environments are set correctly
401        assert_eq!(evm.block, evm_env.block_env);
402
403        // Default spec ID
404        assert_eq!(evm.cfg.spec, SpecId::default());
405    }
406
407    #[test]
408    fn test_evm_with_spec_id() {
409        let evm_config = EthEvmConfig::mainnet();
410
411        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
412
413        let evm_env = EvmEnv {
414            cfg_env: CfgEnv::new().with_spec_and_mainnet_gas_params(SpecId::CONSTANTINOPLE),
415            ..Default::default()
416        };
417
418        let evm = evm_config.evm_with_env(db, evm_env);
419
420        // Check that the spec ID is setup properly
421        assert_eq!(evm.cfg.spec, SpecId::CONSTANTINOPLE);
422    }
423
424    #[test]
425    fn test_evm_with_env_and_default_inspector() {
426        let evm_config = EthEvmConfig::mainnet();
427        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
428
429        let evm_env = EvmEnv::default();
430
431        let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
432
433        // Check that the EVM environment is set to default values
434        assert_eq!(evm.block, evm_env.block_env);
435        assert_eq!(evm.cfg, evm_env.cfg_env);
436    }
437
438    #[test]
439    fn test_evm_with_env_inspector_and_custom_cfg() {
440        let evm_config = EthEvmConfig::mainnet();
441        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
442
443        let cfg_env = CfgEnv::default().with_chain_id(111);
444        let block = BlockEnv::default();
445        let evm_env = EvmEnv { cfg_env: cfg_env.clone(), block_env: block };
446
447        let evm = evm_config.evm_with_env_and_inspector(db, evm_env, NoOpInspector {});
448
449        // Check that the EVM environment is set with custom configuration
450        assert_eq!(evm.cfg, cfg_env);
451        assert_eq!(evm.cfg.spec, SpecId::default());
452    }
453
454    #[test]
455    fn test_evm_with_env_inspector_and_custom_block_tx() {
456        let evm_config = EthEvmConfig::mainnet();
457        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
458
459        // Create custom block and tx environment
460        let block = BlockEnv {
461            basefee: 1000,
462            gas_limit: 10_000_000,
463            number: U256::from(42),
464            ..Default::default()
465        };
466        let evm_env = EvmEnv { block_env: block, ..Default::default() };
467
468        let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
469
470        // Verify that the block and transaction environments are set correctly
471        assert_eq!(evm.block, evm_env.block_env);
472        assert_eq!(evm.cfg.spec, SpecId::default());
473    }
474
475    #[test]
476    fn test_evm_with_env_inspector_and_spec_id() {
477        let evm_config = EthEvmConfig::mainnet();
478        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
479
480        let evm_env = EvmEnv {
481            cfg_env: CfgEnv::new().with_spec_and_mainnet_gas_params(SpecId::CONSTANTINOPLE),
482            ..Default::default()
483        };
484
485        let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
486
487        // Check that the spec ID is set properly
488        assert_eq!(evm.block, evm_env.block_env);
489        assert_eq!(evm.cfg, evm_env.cfg_env);
490        assert_eq!(evm.tx, Default::default());
491    }
492}