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_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    eth::NextEvmEnvAttributes, precompiles::PrecompilesMap, ConfigureEngineEvm, ConfigureEvm,
35    EvmEnv, EvmEnvFor, EvmFactory, ExecutableTxIterator, ExecutionCtxFor, NextBlockEnvAttributes,
36    TransactionEnv,
37};
38use reth_primitives_traits::{
39    constants::MAX_TX_GAS_LIMIT_OSAKA, SealedBlock, SealedHeader, SignedTransaction, TxTy,
40};
41use reth_storage_errors::any::AnyError;
42use revm::{
43    context::{BlockEnv, CfgEnv},
44    context_interface::block::BlobExcessGasAndPrice,
45    primitives::hardfork::SpecId,
46};
47
48mod config;
49use alloy_evm::eth::spec::EthExecutorSpec;
50pub use config::{revm_spec, revm_spec_by_timestamp_and_block_number};
51use reth_ethereum_forks::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
121impl<ChainSpec, EvmF> ConfigureEvm for EthEvmConfig<ChainSpec, EvmF>
122where
123    ChainSpec: EthExecutorSpec + EthChainSpec<Header = Header> + Hardforks + 'static,
124    EvmF: EvmFactory<
125            Tx: TransactionEnv
126                    + FromRecoveredTx<TransactionSigned>
127                    + FromTxWithEncoded<TransactionSigned>,
128            Spec = SpecId,
129            BlockEnv = BlockEnv,
130            Precompiles = PrecompilesMap,
131        > + Clone
132        + Debug
133        + Send
134        + Sync
135        + Unpin
136        + 'static,
137{
138    type Primitives = EthPrimitives;
139    type Error = Infallible;
140    type NextBlockEnvCtx = NextBlockEnvAttributes;
141    type BlockExecutorFactory = EthBlockExecutorFactory<RethReceiptBuilder, Arc<ChainSpec>, EvmF>;
142    type BlockAssembler = EthBlockAssembler<ChainSpec>;
143
144    fn block_executor_factory(&self) -> &Self::BlockExecutorFactory {
145        &self.executor_factory
146    }
147
148    fn block_assembler(&self) -> &Self::BlockAssembler {
149        &self.block_assembler
150    }
151
152    fn evm_env(&self, header: &Header) -> Result<EvmEnv<SpecId>, Self::Error> {
153        Ok(EvmEnv::for_eth_block(
154            header,
155            self.chain_spec(),
156            self.chain_spec().chain().id(),
157            self.chain_spec().blob_params_at_timestamp(header.timestamp),
158        ))
159    }
160
161    fn next_evm_env(
162        &self,
163        parent: &Header,
164        attributes: &NextBlockEnvAttributes,
165    ) -> Result<EvmEnv, Self::Error> {
166        Ok(EvmEnv::for_eth_next_block(
167            parent,
168            NextEvmEnvAttributes {
169                timestamp: attributes.timestamp,
170                suggested_fee_recipient: attributes.suggested_fee_recipient,
171                prev_randao: attributes.prev_randao,
172                gas_limit: attributes.gas_limit,
173            },
174            self.chain_spec().next_block_base_fee(parent, attributes.timestamp).unwrap_or_default(),
175            self.chain_spec(),
176            self.chain_spec().chain().id(),
177            self.chain_spec().blob_params_at_timestamp(attributes.timestamp),
178        ))
179    }
180
181    fn context_for_block<'a>(
182        &self,
183        block: &'a SealedBlock<Block>,
184    ) -> Result<EthBlockExecutionCtx<'a>, Self::Error> {
185        Ok(EthBlockExecutionCtx {
186            parent_hash: block.header().parent_hash,
187            parent_beacon_block_root: block.header().parent_beacon_block_root,
188            ommers: &block.body().ommers,
189            withdrawals: block.body().withdrawals.as_ref().map(Cow::Borrowed),
190            extra_data: block.header().extra_data.clone(),
191        })
192    }
193
194    fn context_for_next_block(
195        &self,
196        parent: &SealedHeader,
197        attributes: Self::NextBlockEnvCtx,
198    ) -> Result<EthBlockExecutionCtx<'_>, Self::Error> {
199        Ok(EthBlockExecutionCtx {
200            parent_hash: parent.hash(),
201            parent_beacon_block_root: attributes.parent_beacon_block_root,
202            ommers: &[],
203            withdrawals: attributes.withdrawals.map(Cow::Owned),
204            extra_data: attributes.extra_data,
205        })
206    }
207}
208
209impl<ChainSpec, EvmF> ConfigureEngineEvm<ExecutionData> for EthEvmConfig<ChainSpec, EvmF>
210where
211    ChainSpec: EthExecutorSpec + EthChainSpec<Header = Header> + Hardforks + 'static,
212    EvmF: EvmFactory<
213            Tx: TransactionEnv
214                    + FromRecoveredTx<TransactionSigned>
215                    + FromTxWithEncoded<TransactionSigned>,
216            Spec = SpecId,
217            BlockEnv = BlockEnv,
218            Precompiles = PrecompilesMap,
219        > + Clone
220        + Debug
221        + Send
222        + Sync
223        + Unpin
224        + 'static,
225{
226    fn evm_env_for_payload(&self, payload: &ExecutionData) -> Result<EvmEnvFor<Self>, Self::Error> {
227        let timestamp = payload.payload.timestamp();
228        let block_number = payload.payload.block_number();
229
230        let blob_params = self.chain_spec().blob_params_at_timestamp(timestamp);
231        let spec =
232            revm_spec_by_timestamp_and_block_number(self.chain_spec(), timestamp, block_number);
233
234        // configure evm env based on parent block
235        let mut cfg_env =
236            CfgEnv::new().with_chain_id(self.chain_spec().chain().id()).with_spec(spec);
237
238        if let Some(blob_params) = &blob_params {
239            cfg_env.set_max_blobs_per_tx(blob_params.max_blobs_per_tx);
240        }
241
242        if self.chain_spec().is_osaka_active_at_timestamp(timestamp) {
243            cfg_env.tx_gas_limit_cap = Some(MAX_TX_GAS_LIMIT_OSAKA);
244        }
245
246        // derive the EIP-4844 blob fees from the header's `excess_blob_gas` and the current
247        // blobparams
248        let blob_excess_gas_and_price =
249            payload.payload.excess_blob_gas().zip(blob_params).map(|(excess_blob_gas, params)| {
250                let blob_gasprice = params.calc_blob_fee(excess_blob_gas);
251                BlobExcessGasAndPrice { excess_blob_gas, blob_gasprice }
252            });
253
254        let block_env = BlockEnv {
255            number: U256::from(block_number),
256            beneficiary: payload.payload.fee_recipient(),
257            timestamp: U256::from(timestamp),
258            difficulty: if spec >= SpecId::MERGE {
259                U256::ZERO
260            } else {
261                payload.payload.as_v1().prev_randao.into()
262            },
263            prevrandao: (spec >= SpecId::MERGE).then(|| payload.payload.as_v1().prev_randao),
264            gas_limit: payload.payload.gas_limit(),
265            basefee: payload.payload.saturated_base_fee_per_gas(),
266            blob_excess_gas_and_price,
267        };
268
269        Ok(EvmEnv { cfg_env, block_env })
270    }
271
272    fn context_for_payload<'a>(
273        &self,
274        payload: &'a ExecutionData,
275    ) -> Result<ExecutionCtxFor<'a, Self>, Self::Error> {
276        Ok(EthBlockExecutionCtx {
277            parent_hash: payload.parent_hash(),
278            parent_beacon_block_root: payload.sidecar.parent_beacon_block_root(),
279            ommers: &[],
280            withdrawals: payload.payload.withdrawals().map(|w| Cow::Owned(w.clone().into())),
281            extra_data: payload.payload.as_v1().extra_data.clone(),
282        })
283    }
284
285    fn tx_iterator_for_payload(
286        &self,
287        payload: &ExecutionData,
288    ) -> Result<impl ExecutableTxIterator<Self>, Self::Error> {
289        let txs = payload.payload.transactions().clone().into_iter();
290        let convert = |tx: Bytes| {
291            let tx =
292                TxTy::<Self::Primitives>::decode_2718_exact(tx.as_ref()).map_err(AnyError::new)?;
293            let signer = tx.try_recover().map_err(AnyError::new)?;
294            Ok::<_, AnyError>(tx.with_signer(signer))
295        };
296
297        Ok((txs, convert))
298    }
299}
300
301#[cfg(test)]
302mod tests {
303    use super::*;
304    use alloy_consensus::Header;
305    use alloy_genesis::Genesis;
306    use reth_chainspec::{Chain, ChainSpec};
307    use reth_evm::{execute::ProviderError, EvmEnv};
308    use revm::{
309        context::{BlockEnv, CfgEnv},
310        database::CacheDB,
311        database_interface::EmptyDBTyped,
312        inspector::NoOpInspector,
313    };
314
315    #[test]
316    fn test_fill_cfg_and_block_env() {
317        // Create a default header
318        let header = Header::default();
319
320        // Build the ChainSpec for Ethereum mainnet, activating London, Paris, and Shanghai
321        // hardforks
322        let chain_spec = ChainSpec::builder()
323            .chain(Chain::mainnet())
324            .genesis(Genesis::default())
325            .london_activated()
326            .paris_activated()
327            .shanghai_activated()
328            .build();
329
330        // Use the `EthEvmConfig` to fill the `cfg_env` and `block_env` based on the ChainSpec,
331        // Header, and total difficulty
332        let EvmEnv { cfg_env, .. } =
333            EthEvmConfig::new(Arc::new(chain_spec.clone())).evm_env(&header).unwrap();
334
335        // Assert that the chain ID in the `cfg_env` is correctly set to the chain ID of the
336        // ChainSpec
337        assert_eq!(cfg_env.chain_id, chain_spec.chain().id());
338    }
339
340    #[test]
341    fn test_evm_with_env_default_spec() {
342        let evm_config = EthEvmConfig::mainnet();
343
344        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
345
346        let evm_env = EvmEnv::default();
347
348        let evm = evm_config.evm_with_env(db, evm_env.clone());
349
350        // Check that the EVM environment
351        assert_eq!(evm.block, evm_env.block_env);
352        assert_eq!(evm.cfg, evm_env.cfg_env);
353    }
354
355    #[test]
356    fn test_evm_with_env_custom_cfg() {
357        let evm_config = EthEvmConfig::mainnet();
358
359        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
360
361        // Create a custom configuration environment with a chain ID of 111
362        let cfg = CfgEnv::default().with_chain_id(111);
363
364        let evm_env = EvmEnv { cfg_env: cfg.clone(), ..Default::default() };
365
366        let evm = evm_config.evm_with_env(db, evm_env);
367
368        // Check that the EVM environment is initialized with the custom environment
369        assert_eq!(evm.cfg, cfg);
370    }
371
372    #[test]
373    fn test_evm_with_env_custom_block_and_tx() {
374        let evm_config = EthEvmConfig::mainnet();
375
376        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
377
378        // Create customs block and tx env
379        let block = BlockEnv {
380            basefee: 1000,
381            gas_limit: 10_000_000,
382            number: U256::from(42),
383            ..Default::default()
384        };
385
386        let evm_env = EvmEnv { block_env: block, ..Default::default() };
387
388        let evm = evm_config.evm_with_env(db, evm_env.clone());
389
390        // Verify that the block and transaction environments are set correctly
391        assert_eq!(evm.block, evm_env.block_env);
392
393        // Default spec ID
394        assert_eq!(evm.cfg.spec, SpecId::default());
395    }
396
397    #[test]
398    fn test_evm_with_spec_id() {
399        let evm_config = EthEvmConfig::mainnet();
400
401        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
402
403        let evm_env = EvmEnv {
404            cfg_env: CfgEnv::new().with_spec(SpecId::CONSTANTINOPLE),
405            ..Default::default()
406        };
407
408        let evm = evm_config.evm_with_env(db, evm_env);
409
410        // Check that the spec ID is setup properly
411        assert_eq!(evm.cfg.spec, SpecId::CONSTANTINOPLE);
412    }
413
414    #[test]
415    fn test_evm_with_env_and_default_inspector() {
416        let evm_config = EthEvmConfig::mainnet();
417        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
418
419        let evm_env = EvmEnv::default();
420
421        let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
422
423        // Check that the EVM environment is set to default values
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_inspector_and_custom_cfg() {
430        let evm_config = EthEvmConfig::mainnet();
431        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
432
433        let cfg_env = CfgEnv::default().with_chain_id(111);
434        let block = BlockEnv::default();
435        let evm_env = EvmEnv { cfg_env: cfg_env.clone(), block_env: block };
436
437        let evm = evm_config.evm_with_env_and_inspector(db, evm_env, NoOpInspector {});
438
439        // Check that the EVM environment is set with custom configuration
440        assert_eq!(evm.cfg, cfg_env);
441        assert_eq!(evm.cfg.spec, SpecId::default());
442    }
443
444    #[test]
445    fn test_evm_with_env_inspector_and_custom_block_tx() {
446        let evm_config = EthEvmConfig::mainnet();
447        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
448
449        // Create custom block and tx environment
450        let block = BlockEnv {
451            basefee: 1000,
452            gas_limit: 10_000_000,
453            number: U256::from(42),
454            ..Default::default()
455        };
456        let evm_env = EvmEnv { block_env: block, ..Default::default() };
457
458        let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
459
460        // Verify that the block and transaction environments are set correctly
461        assert_eq!(evm.block, evm_env.block_env);
462        assert_eq!(evm.cfg.spec, SpecId::default());
463    }
464
465    #[test]
466    fn test_evm_with_env_inspector_and_spec_id() {
467        let evm_config = EthEvmConfig::mainnet();
468        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
469
470        let evm_env = EvmEnv {
471            cfg_env: CfgEnv::new().with_spec(SpecId::CONSTANTINOPLE),
472            ..Default::default()
473        };
474
475        let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
476
477        // Check that the spec ID is set properly
478        assert_eq!(evm.block, evm_env.block_env);
479        assert_eq!(evm.cfg, evm_env.cfg_env);
480        assert_eq!(evm.tx, Default::default());
481    }
482}