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, TransactionEnvMut,
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: TransactionEnvMut
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                slot_number: attributes.slot_number,
179            },
180            self.chain_spec().next_block_base_fee(parent, attributes.timestamp).unwrap_or_default(),
181            self.chain_spec(),
182            self.chain_spec().chain().id(),
183            self.chain_spec().blob_params_at_timestamp(attributes.timestamp),
184        ))
185    }
186
187    fn context_for_block<'a>(
188        &self,
189        block: &'a SealedBlock<Block>,
190    ) -> Result<EthBlockExecutionCtx<'a>, Self::Error> {
191        Ok(EthBlockExecutionCtx {
192            tx_count_hint: Some(block.transaction_count()),
193            parent_hash: block.header().parent_hash,
194            parent_beacon_block_root: block.header().parent_beacon_block_root,
195            ommers: &block.body().ommers,
196            withdrawals: block.body().withdrawals.as_ref().map(|w| Cow::Borrowed(w.as_slice())),
197            extra_data: block.header().extra_data.clone(),
198            slot_number: block.header().slot_number,
199        })
200    }
201
202    fn context_for_next_block(
203        &self,
204        parent: &SealedHeader,
205        attributes: Self::NextBlockEnvCtx,
206    ) -> Result<EthBlockExecutionCtx<'_>, Self::Error> {
207        Ok(EthBlockExecutionCtx {
208            tx_count_hint: None,
209            parent_hash: parent.hash(),
210            parent_beacon_block_root: attributes.parent_beacon_block_root,
211            ommers: &[],
212            withdrawals: attributes.withdrawals.map(|w| Cow::Owned(w.into_inner())),
213            extra_data: attributes.extra_data,
214            slot_number: attributes.slot_number,
215        })
216    }
217}
218
219#[cfg(feature = "std")]
220impl<ChainSpec, EvmF> ConfigureEngineEvm<ExecutionData> for EthEvmConfig<ChainSpec, EvmF>
221where
222    ChainSpec: EthExecutorSpec + EthChainSpec<Header = Header> + Hardforks + 'static,
223    EvmF: EvmFactory<
224            Tx: TransactionEnvMut
225                    + FromRecoveredTx<TransactionSigned>
226                    + FromTxWithEncoded<TransactionSigned>,
227            Spec = SpecId,
228            BlockEnv = BlockEnv,
229            Precompiles = PrecompilesMap,
230        > + Clone
231        + Debug
232        + Send
233        + Sync
234        + Unpin
235        + 'static,
236{
237    fn evm_env_for_payload(&self, payload: &ExecutionData) -> Result<EvmEnvFor<Self>, Self::Error> {
238        let timestamp = payload.payload.timestamp();
239        let block_number = payload.payload.block_number();
240
241        let blob_params = self.chain_spec().blob_params_at_timestamp(timestamp);
242        let spec =
243            revm_spec_by_timestamp_and_block_number(self.chain_spec(), timestamp, block_number);
244
245        // configure evm env based on parent block
246        let mut cfg_env = CfgEnv::new()
247            .with_chain_id(self.chain_spec().chain().id())
248            .with_spec_and_mainnet_gas_params(spec);
249
250        if let Some(blob_params) = &blob_params {
251            cfg_env.set_max_blobs_per_tx(blob_params.max_blobs_per_tx);
252        }
253
254        if self.chain_spec().is_osaka_active_at_timestamp(timestamp) {
255            cfg_env.tx_gas_limit_cap = Some(MAX_TX_GAS_LIMIT_OSAKA);
256        }
257
258        // derive the EIP-4844 blob fees from the header's `excess_blob_gas` and the current
259        // blobparams
260        let blob_excess_gas_and_price =
261            payload.payload.excess_blob_gas().zip(blob_params).map(|(excess_blob_gas, params)| {
262                let blob_gasprice = params.calc_blob_fee(excess_blob_gas);
263                BlobExcessGasAndPrice { excess_blob_gas, blob_gasprice }
264            });
265
266        let block_env = BlockEnv {
267            number: U256::from(block_number),
268            beneficiary: payload.payload.fee_recipient(),
269            timestamp: U256::from(timestamp),
270            difficulty: if spec >= SpecId::MERGE {
271                U256::ZERO
272            } else {
273                payload.payload.as_v1().prev_randao.into()
274            },
275            prevrandao: (spec >= SpecId::MERGE).then(|| payload.payload.as_v1().prev_randao),
276            gas_limit: payload.payload.gas_limit(),
277            basefee: payload.payload.saturated_base_fee_per_gas(),
278            blob_excess_gas_and_price,
279            slot_num: payload.payload.as_v4().map(|v4| v4.slot_number).unwrap_or_default(),
280        };
281
282        Ok(EvmEnv { cfg_env, block_env })
283    }
284
285    fn context_for_payload<'a>(
286        &self,
287        payload: &'a ExecutionData,
288    ) -> Result<ExecutionCtxFor<'a, Self>, Self::Error> {
289        Ok(EthBlockExecutionCtx {
290            tx_count_hint: Some(payload.payload.transactions().len()),
291            parent_hash: payload.parent_hash(),
292            parent_beacon_block_root: payload.sidecar.parent_beacon_block_root(),
293            ommers: &[],
294            withdrawals: payload.payload.withdrawals().map(|w| Cow::Borrowed(w.as_slice())),
295            extra_data: payload.payload.as_v1().extra_data.clone(),
296            slot_number: payload.payload.as_v4().map(|v4| v4.slot_number),
297        })
298    }
299
300    fn tx_iterator_for_payload(
301        &self,
302        payload: &ExecutionData,
303    ) -> Result<impl ExecutableTxIterator<Self>, Self::Error> {
304        let txs = payload.payload.transactions().clone();
305        let convert = |tx: Bytes| {
306            let tx =
307                TxTy::<Self::Primitives>::decode_2718_exact(tx.as_ref()).map_err(AnyError::new)?;
308            let signer = tx.try_recover().map_err(AnyError::new)?;
309            Ok::<_, AnyError>(tx.with_signer(signer))
310        };
311
312        Ok((txs, convert))
313    }
314}
315
316#[cfg(test)]
317mod tests {
318    use super::*;
319    use alloy_consensus::Header;
320    use alloy_genesis::Genesis;
321    use reth_chainspec::{Chain, ChainSpec};
322    use reth_evm::{execute::ProviderError, EvmEnv};
323    use revm::{
324        context::{BlockEnv, CfgEnv},
325        database::CacheDB,
326        database_interface::EmptyDBTyped,
327        inspector::NoOpInspector,
328    };
329
330    #[test]
331    fn test_fill_cfg_and_block_env() {
332        // Create a default header
333        let header = Header::default();
334
335        // Build the ChainSpec for Ethereum mainnet, activating London, Paris, and Shanghai
336        // hardforks
337        let chain_spec = ChainSpec::builder()
338            .chain(Chain::mainnet())
339            .genesis(Genesis::default())
340            .london_activated()
341            .paris_activated()
342            .shanghai_activated()
343            .build();
344
345        // Use the `EthEvmConfig` to fill the `cfg_env` and `block_env` based on the ChainSpec,
346        // Header, and total difficulty
347        let EvmEnv { cfg_env, .. } =
348            EthEvmConfig::new(Arc::new(chain_spec.clone())).evm_env(&header).unwrap();
349
350        // Assert that the chain ID in the `cfg_env` is correctly set to the chain ID of the
351        // ChainSpec
352        assert_eq!(cfg_env.chain_id, chain_spec.chain().id());
353    }
354
355    #[test]
356    fn test_evm_with_env_default_spec() {
357        let evm_config = EthEvmConfig::mainnet();
358
359        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
360
361        let evm_env = EvmEnv::default();
362
363        let evm = evm_config.evm_with_env(db, evm_env.clone());
364
365        // Check that the EVM environment
366        assert_eq!(evm.block, evm_env.block_env);
367        assert_eq!(evm.cfg, evm_env.cfg_env);
368    }
369
370    #[test]
371    fn test_evm_with_env_custom_cfg() {
372        let evm_config = EthEvmConfig::mainnet();
373
374        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
375
376        // Create a custom configuration environment with a chain ID of 111
377        let cfg = CfgEnv::default().with_chain_id(111);
378
379        let evm_env = EvmEnv { cfg_env: cfg.clone(), ..Default::default() };
380
381        let evm = evm_config.evm_with_env(db, evm_env);
382
383        // Check that the EVM environment is initialized with the custom environment
384        assert_eq!(evm.cfg, cfg);
385    }
386
387    #[test]
388    fn test_evm_with_env_custom_block_and_tx() {
389        let evm_config = EthEvmConfig::mainnet();
390
391        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
392
393        // Create customs block and tx env
394        let block = BlockEnv {
395            basefee: 1000,
396            gas_limit: 10_000_000,
397            number: U256::from(42),
398            ..Default::default()
399        };
400
401        let evm_env = EvmEnv { block_env: block, ..Default::default() };
402
403        let evm = evm_config.evm_with_env(db, evm_env.clone());
404
405        // Verify that the block and transaction environments are set correctly
406        assert_eq!(evm.block, evm_env.block_env);
407
408        // Default spec ID
409        assert_eq!(evm.cfg.spec, SpecId::default());
410    }
411
412    #[test]
413    fn test_evm_with_spec_id() {
414        let evm_config = EthEvmConfig::mainnet();
415
416        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
417
418        let evm_env = EvmEnv {
419            cfg_env: CfgEnv::new().with_spec_and_mainnet_gas_params(SpecId::CONSTANTINOPLE),
420            ..Default::default()
421        };
422
423        let evm = evm_config.evm_with_env(db, evm_env);
424
425        // Check that the spec ID is setup properly
426        assert_eq!(evm.cfg.spec, SpecId::CONSTANTINOPLE);
427    }
428
429    #[test]
430    fn test_evm_with_env_and_default_inspector() {
431        let evm_config = EthEvmConfig::mainnet();
432        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
433
434        let evm_env = EvmEnv::default();
435
436        let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
437
438        // Check that the EVM environment is set to default values
439        assert_eq!(evm.block, evm_env.block_env);
440        assert_eq!(evm.cfg, evm_env.cfg_env);
441    }
442
443    #[test]
444    fn test_evm_with_env_inspector_and_custom_cfg() {
445        let evm_config = EthEvmConfig::mainnet();
446        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
447
448        let cfg_env = CfgEnv::default().with_chain_id(111);
449        let block = BlockEnv::default();
450        let evm_env = EvmEnv { cfg_env: cfg_env.clone(), block_env: block };
451
452        let evm = evm_config.evm_with_env_and_inspector(db, evm_env, NoOpInspector {});
453
454        // Check that the EVM environment is set with custom configuration
455        assert_eq!(evm.cfg, cfg_env);
456        assert_eq!(evm.cfg.spec, SpecId::default());
457    }
458
459    #[test]
460    fn test_evm_with_env_inspector_and_custom_block_tx() {
461        let evm_config = EthEvmConfig::mainnet();
462        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
463
464        // Create custom block and tx environment
465        let block = BlockEnv {
466            basefee: 1000,
467            gas_limit: 10_000_000,
468            number: U256::from(42),
469            ..Default::default()
470        };
471        let evm_env = EvmEnv { block_env: block, ..Default::default() };
472
473        let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
474
475        // Verify that the block and transaction environments are set correctly
476        assert_eq!(evm.block, evm_env.block_env);
477        assert_eq!(evm.cfg.spec, SpecId::default());
478    }
479
480    #[test]
481    fn test_evm_with_env_inspector_and_spec_id() {
482        let evm_config = EthEvmConfig::mainnet();
483        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
484
485        let evm_env = EvmEnv {
486            cfg_env: CfgEnv::new().with_spec_and_mainnet_gas_params(SpecId::CONSTANTINOPLE),
487            ..Default::default()
488        };
489
490        let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
491
492        // Check that the spec ID is set properly
493        assert_eq!(evm.block, evm_env.block_env);
494        assert_eq!(evm.cfg, evm_env.cfg_env);
495        assert_eq!(evm.tx, Default::default());
496    }
497}