1#![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#[derive(Debug, Clone)]
54pub struct EthEvmConfig<EvmFactory = EthEvmFactory> {
55 pub executor_factory: EthBlockExecutorFactory<RethReceiptBuilder, Arc<ChainSpec>, EvmFactory>,
57 pub block_assembler: EthBlockAssembler<ChainSpec>,
59}
60
61impl EthEvmConfig {
62 pub fn new(chain_spec: Arc<ChainSpec>) -> Self {
64 Self::ethereum(chain_spec)
65 }
66
67 pub fn ethereum(chain_spec: Arc<ChainSpec>) -> Self {
69 Self::new_with_evm_factory(chain_spec, EthEvmFactory::default())
70 }
71
72 pub fn mainnet() -> Self {
74 Self::ethereum(MAINNET.clone())
75 }
76}
77
78impl<EvmFactory> EthEvmConfig<EvmFactory> {
79 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 pub const fn chain_spec(&self) -> &Arc<ChainSpec> {
93 self.executor_factory.spec()
94 }
95
96 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 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 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 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 let spec_id = revm_spec_by_timestamp_and_block_number(
181 self.chain_spec(),
182 attributes.timestamp,
183 parent.number() + 1,
184 );
185
186 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 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 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 gas_limit *= elasticity_multiplier as u64;
221
222 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 basefee: basefee.unwrap_or_default(),
235 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 let header = Header::default();
283
284 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 let EvmEnv { cfg_env, .. } =
297 EthEvmConfig::new(Arc::new(chain_spec.clone())).evm_env(&header);
298
299 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 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 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 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 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 assert_eq!(evm.block, evm_env.block_env);
352
353 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 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 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 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 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 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 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}