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, vec::Vec};
21use alloy_consensus::{BlockHeader, Header};
22pub use alloy_evm::EthEvm;
23use alloy_evm::{
24    eth::{EthBlockExecutionCtx, EthBlockExecutorFactory},
25    EthEvmFactory, FromRecoveredTx, FromTxWithEncoded,
26};
27use alloy_primitives::{Bytes, U256};
28use core::{convert::Infallible, fmt::Debug};
29use reth_chainspec::{ChainSpec, EthChainSpec, HardforkBlobParams, MAINNET};
30use reth_ethereum_primitives::{Block, EthPrimitives, TransactionSigned};
31use reth_evm::{ConfigureEvm, EvmEnv, EvmFactory, NextBlockEnvAttributes, TransactionEnv};
32use reth_primitives_traits::{SealedBlock, SealedHeader};
33use revm::{
34    context::{BlockEnv, CfgEnv},
35    context_interface::block::BlobExcessGasAndPrice,
36    primitives::hardfork::SpecId,
37};
38
39mod config;
40use alloy_eips::{eip1559::INITIAL_BASE_FEE, eip7840::BlobParams};
41pub use config::{revm_spec, revm_spec_by_timestamp_and_block_number};
42use reth_ethereum_forks::EthereumHardfork;
43
44pub mod execute;
45
46mod build;
47pub use build::EthBlockAssembler;
48
49mod receipt;
50pub use receipt::RethReceiptBuilder;
51
52/// Ethereum-related EVM configuration.
53#[derive(Debug, Clone)]
54pub struct EthEvmConfig<EvmFactory = EthEvmFactory> {
55    /// Inner [`EthBlockExecutorFactory`].
56    pub executor_factory: EthBlockExecutorFactory<RethReceiptBuilder, Arc<ChainSpec>, EvmFactory>,
57    /// Ethereum block assembler.
58    pub block_assembler: EthBlockAssembler<ChainSpec>,
59}
60
61impl EthEvmConfig {
62    /// Creates a new Ethereum EVM configuration with the given chain spec.
63    pub fn new(chain_spec: Arc<ChainSpec>) -> Self {
64        Self::ethereum(chain_spec)
65    }
66
67    /// Creates a new Ethereum EVM configuration.
68    pub fn ethereum(chain_spec: Arc<ChainSpec>) -> Self {
69        Self::new_with_evm_factory(chain_spec, EthEvmFactory::default())
70    }
71
72    /// Creates a new Ethereum EVM configuration for the ethereum mainnet.
73    pub fn mainnet() -> Self {
74        Self::ethereum(MAINNET.clone())
75    }
76}
77
78impl<EvmFactory> EthEvmConfig<EvmFactory> {
79    /// Creates a new Ethereum EVM configuration with the given chain spec and EVM factory.
80    pub fn new_with_evm_factory(chain_spec: Arc<ChainSpec>, evm_factory: EvmFactory) -> Self {
81        Self {
82            block_assembler: EthBlockAssembler::new(chain_spec.clone()),
83            executor_factory: EthBlockExecutorFactory::new(
84                RethReceiptBuilder::default(),
85                chain_spec,
86                evm_factory,
87            ),
88        }
89    }
90
91    /// Returns the chain spec associated with this configuration.
92    pub const fn chain_spec(&self) -> &Arc<ChainSpec> {
93        self.executor_factory.spec()
94    }
95
96    /// Returns blob params by hard fork as specified in chain spec.
97    /// Blob params are in format `(spec id, target blob count, max blob count)`.
98    pub fn blob_max_and_target_count_by_hardfork(&self) -> Vec<(SpecId, u64, u64)> {
99        let HardforkBlobParams { cancun, prague } = self.chain_spec().blob_params;
100        Vec::from([
101            (SpecId::CANCUN, cancun.target_blob_count, cancun.max_blob_count),
102            (SpecId::PRAGUE, prague.target_blob_count, prague.max_blob_count),
103        ])
104    }
105
106    /// Sets the extra data for the block assembler.
107    pub fn with_extra_data(mut self, extra_data: Bytes) -> Self {
108        self.block_assembler.extra_data = extra_data;
109        self
110    }
111}
112
113impl<EvmF> ConfigureEvm for EthEvmConfig<EvmF>
114where
115    EvmF: EvmFactory<
116            Tx: TransactionEnv
117                    + FromRecoveredTx<TransactionSigned>
118                    + FromTxWithEncoded<TransactionSigned>,
119            Spec = SpecId,
120        > + Clone
121        + Debug
122        + Send
123        + Sync
124        + Unpin
125        + 'static,
126{
127    type Primitives = EthPrimitives;
128    type Error = Infallible;
129    type NextBlockEnvCtx = NextBlockEnvAttributes;
130    type BlockExecutorFactory = EthBlockExecutorFactory<RethReceiptBuilder, Arc<ChainSpec>, EvmF>;
131    type BlockAssembler = EthBlockAssembler<ChainSpec>;
132
133    fn block_executor_factory(&self) -> &Self::BlockExecutorFactory {
134        &self.executor_factory
135    }
136
137    fn block_assembler(&self) -> &Self::BlockAssembler {
138        &self.block_assembler
139    }
140
141    fn evm_env(&self, header: &Header) -> EvmEnv {
142        let spec = config::revm_spec(self.chain_spec(), header);
143
144        // configure evm env based on parent block
145        let cfg_env = CfgEnv::new()
146            .with_chain_id(self.chain_spec().chain().id())
147            .with_spec(spec)
148            .with_blob_max_and_target_count(self.blob_max_and_target_count_by_hardfork());
149
150        // derive the EIP-4844 blob fees from the header's `excess_blob_gas` and the current
151        // blobparams
152        let blob_excess_gas_and_price = header
153            .excess_blob_gas
154            .zip(self.chain_spec().blob_params_at_timestamp(header.timestamp))
155            .map(|(excess_blob_gas, params)| {
156                let blob_gasprice = params.calc_blob_fee(excess_blob_gas);
157                BlobExcessGasAndPrice { excess_blob_gas, blob_gasprice }
158            });
159
160        let block_env = BlockEnv {
161            number: header.number(),
162            beneficiary: header.beneficiary(),
163            timestamp: header.timestamp(),
164            difficulty: if spec >= SpecId::MERGE { U256::ZERO } else { header.difficulty() },
165            prevrandao: if spec >= SpecId::MERGE { header.mix_hash() } else { None },
166            gas_limit: header.gas_limit(),
167            basefee: header.base_fee_per_gas().unwrap_or_default(),
168            blob_excess_gas_and_price,
169        };
170
171        EvmEnv { cfg_env, block_env }
172    }
173
174    fn next_evm_env(
175        &self,
176        parent: &Header,
177        attributes: &NextBlockEnvAttributes,
178    ) -> Result<EvmEnv, Self::Error> {
179        // ensure we're not missing any timestamp based hardforks
180        let spec_id = revm_spec_by_timestamp_and_block_number(
181            self.chain_spec(),
182            attributes.timestamp,
183            parent.number() + 1,
184        );
185
186        // configure evm env based on parent block
187        let cfg = CfgEnv::new()
188            .with_chain_id(self.chain_spec().chain().id())
189            .with_spec(spec_id)
190            .with_blob_max_and_target_count(self.blob_max_and_target_count_by_hardfork());
191
192        let blob_params = self.chain_spec().blob_params_at_timestamp(attributes.timestamp);
193        // if the parent block did not have excess blob gas (i.e. it was pre-cancun), but it is
194        // cancun now, we need to set the excess blob gas to the default value(0)
195        let blob_excess_gas_and_price = parent
196            .maybe_next_block_excess_blob_gas(blob_params)
197            .or_else(|| (spec_id == SpecId::CANCUN).then_some(0))
198            .map(|excess_blob_gas| {
199                let blob_gasprice =
200                    blob_params.unwrap_or_else(BlobParams::cancun).calc_blob_fee(excess_blob_gas);
201                BlobExcessGasAndPrice { excess_blob_gas, blob_gasprice }
202            });
203
204        let mut basefee = parent.next_block_base_fee(
205            self.chain_spec().base_fee_params_at_timestamp(attributes.timestamp),
206        );
207
208        let mut gas_limit = attributes.gas_limit;
209
210        // If we are on the London fork boundary, we need to multiply the parent's gas limit by the
211        // elasticity multiplier to get the new gas limit.
212        if self.chain_spec().fork(EthereumHardfork::London).transitions_at_block(parent.number + 1)
213        {
214            let elasticity_multiplier = self
215                .chain_spec()
216                .base_fee_params_at_timestamp(attributes.timestamp)
217                .elasticity_multiplier;
218
219            // multiply the gas limit by the elasticity multiplier
220            gas_limit *= elasticity_multiplier as u64;
221
222            // set the base fee to the initial base fee from the EIP-1559 spec
223            basefee = Some(INITIAL_BASE_FEE)
224        }
225
226        let block_env = BlockEnv {
227            number: parent.number + 1,
228            beneficiary: attributes.suggested_fee_recipient,
229            timestamp: attributes.timestamp,
230            difficulty: U256::ZERO,
231            prevrandao: Some(attributes.prev_randao),
232            gas_limit,
233            // calculate basefee based on parent block's gas usage
234            basefee: basefee.unwrap_or_default(),
235            // calculate excess gas based on parent block's blob gas usage
236            blob_excess_gas_and_price,
237        };
238
239        Ok((cfg, block_env).into())
240    }
241
242    fn context_for_block<'a>(&self, block: &'a SealedBlock<Block>) -> EthBlockExecutionCtx<'a> {
243        EthBlockExecutionCtx {
244            parent_hash: block.header().parent_hash,
245            parent_beacon_block_root: block.header().parent_beacon_block_root,
246            ommers: &block.body().ommers,
247            withdrawals: block.body().withdrawals.as_ref().map(Cow::Borrowed),
248        }
249    }
250
251    fn context_for_next_block(
252        &self,
253        parent: &SealedHeader,
254        attributes: Self::NextBlockEnvCtx,
255    ) -> EthBlockExecutionCtx<'_> {
256        EthBlockExecutionCtx {
257            parent_hash: parent.hash(),
258            parent_beacon_block_root: attributes.parent_beacon_block_root,
259            ommers: &[],
260            withdrawals: attributes.withdrawals.map(Cow::Owned),
261        }
262    }
263}
264
265#[cfg(test)]
266mod tests {
267    use super::*;
268    use alloy_consensus::Header;
269    use alloy_genesis::Genesis;
270    use reth_chainspec::{Chain, ChainSpec};
271    use reth_evm::{execute::ProviderError, EvmEnv};
272    use revm::{
273        context::{BlockEnv, CfgEnv},
274        database::CacheDB,
275        database_interface::EmptyDBTyped,
276        inspector::NoOpInspector,
277    };
278
279    #[test]
280    fn test_fill_cfg_and_block_env() {
281        // Create a default header
282        let header = Header::default();
283
284        // Build the ChainSpec for Ethereum mainnet, activating London, Paris, and Shanghai
285        // hardforks
286        let chain_spec = ChainSpec::builder()
287            .chain(Chain::mainnet())
288            .genesis(Genesis::default())
289            .london_activated()
290            .paris_activated()
291            .shanghai_activated()
292            .build();
293
294        // Use the `EthEvmConfig` to fill the `cfg_env` and `block_env` based on the ChainSpec,
295        // Header, and total difficulty
296        let EvmEnv { cfg_env, .. } =
297            EthEvmConfig::new(Arc::new(chain_spec.clone())).evm_env(&header);
298
299        // Assert that the chain ID in the `cfg_env` is correctly set to the chain ID of the
300        // ChainSpec
301        assert_eq!(cfg_env.chain_id, chain_spec.chain().id());
302    }
303
304    #[test]
305    fn test_evm_with_env_default_spec() {
306        let evm_config = EthEvmConfig::mainnet();
307
308        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
309
310        let evm_env = EvmEnv::default();
311
312        let evm = evm_config.evm_with_env(db, evm_env.clone());
313
314        // Check that the EVM environment
315        assert_eq!(evm.block, evm_env.block_env);
316        assert_eq!(evm.cfg, evm_env.cfg_env);
317    }
318
319    #[test]
320    fn test_evm_with_env_custom_cfg() {
321        let evm_config = EthEvmConfig::mainnet();
322
323        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
324
325        // Create a custom configuration environment with a chain ID of 111
326        let cfg = CfgEnv::default().with_chain_id(111);
327
328        let evm_env = EvmEnv { cfg_env: cfg.clone(), ..Default::default() };
329
330        let evm = evm_config.evm_with_env(db, evm_env);
331
332        // Check that the EVM environment is initialized with the custom environment
333        assert_eq!(evm.cfg, cfg);
334    }
335
336    #[test]
337    fn test_evm_with_env_custom_block_and_tx() {
338        let evm_config = EthEvmConfig::mainnet();
339
340        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
341
342        // Create customs block and tx env
343        let block =
344            BlockEnv { basefee: 1000, gas_limit: 10_000_000, number: 42, ..Default::default() };
345
346        let evm_env = EvmEnv { block_env: block, ..Default::default() };
347
348        let evm = evm_config.evm_with_env(db, evm_env.clone());
349
350        // Verify that the block and transaction environments are set correctly
351        assert_eq!(evm.block, evm_env.block_env);
352
353        // Default spec ID
354        assert_eq!(evm.cfg.spec, SpecId::default());
355    }
356
357    #[test]
358    fn test_evm_with_spec_id() {
359        let evm_config = EthEvmConfig::mainnet();
360
361        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
362
363        let evm_env = EvmEnv {
364            cfg_env: CfgEnv::new().with_spec(SpecId::CONSTANTINOPLE),
365            ..Default::default()
366        };
367
368        let evm = evm_config.evm_with_env(db, evm_env);
369
370        // Check that the spec ID is setup properly
371        assert_eq!(evm.cfg.spec, SpecId::CONSTANTINOPLE);
372    }
373
374    #[test]
375    fn test_evm_with_env_and_default_inspector() {
376        let evm_config = EthEvmConfig::mainnet();
377        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
378
379        let evm_env = EvmEnv::default();
380
381        let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
382
383        // Check that the EVM environment is set to default values
384        assert_eq!(evm.block, evm_env.block_env);
385        assert_eq!(evm.cfg, evm_env.cfg_env);
386    }
387
388    #[test]
389    fn test_evm_with_env_inspector_and_custom_cfg() {
390        let evm_config = EthEvmConfig::mainnet();
391        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
392
393        let cfg_env = CfgEnv::default().with_chain_id(111);
394        let block = BlockEnv::default();
395        let evm_env = EvmEnv { cfg_env: cfg_env.clone(), block_env: block };
396
397        let evm = evm_config.evm_with_env_and_inspector(db, evm_env, NoOpInspector {});
398
399        // Check that the EVM environment is set with custom configuration
400        assert_eq!(evm.cfg, cfg_env);
401        assert_eq!(evm.cfg.spec, SpecId::default());
402    }
403
404    #[test]
405    fn test_evm_with_env_inspector_and_custom_block_tx() {
406        let evm_config = EthEvmConfig::mainnet();
407        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
408
409        // Create custom block and tx environment
410        let block =
411            BlockEnv { basefee: 1000, gas_limit: 10_000_000, number: 42, ..Default::default() };
412        let evm_env = EvmEnv { block_env: block, ..Default::default() };
413
414        let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
415
416        // Verify that the block and transaction environments are set correctly
417        assert_eq!(evm.block, evm_env.block_env);
418        assert_eq!(evm.cfg.spec, SpecId::default());
419    }
420
421    #[test]
422    fn test_evm_with_env_inspector_and_spec_id() {
423        let evm_config = EthEvmConfig::mainnet();
424        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
425
426        let evm_env = EvmEnv {
427            cfg_env: CfgEnv::new().with_spec(SpecId::CONSTANTINOPLE),
428            ..Default::default()
429        };
430
431        let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
432
433        // Check that the spec ID is set properly
434        assert_eq!(evm.block, evm_env.block_env);
435        assert_eq!(evm.cfg, evm_env.cfg_env);
436        assert_eq!(evm.tx, Default::default());
437    }
438}