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/SeismicSystems/seismic-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, 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
46pub mod build;
47pub use build::EthBlockAssembler;
48
49mod receipt;
50pub use receipt::RethReceiptBuilder;
51
52#[cfg(feature = "test-utils")]
53mod test_utils;
54#[cfg(feature = "test-utils")]
55pub use test_utils::*;
56
57#[derive(Debug, Clone)]
59pub struct EthEvmConfig<EvmFactory = EthEvmFactory> {
60 pub executor_factory: EthBlockExecutorFactory<RethReceiptBuilder, Arc<ChainSpec>, EvmFactory>,
62 pub block_assembler: EthBlockAssembler<ChainSpec>,
64}
65
66impl EthEvmConfig {
67 pub fn new(chain_spec: Arc<ChainSpec>) -> Self {
69 Self::ethereum(chain_spec)
70 }
71
72 pub fn ethereum(chain_spec: Arc<ChainSpec>) -> Self {
74 Self::new_with_evm_factory(chain_spec, EthEvmFactory::default())
75 }
76
77 pub fn mainnet() -> Self {
79 Self::ethereum(MAINNET.clone())
80 }
81}
82
83impl<EvmFactory> EthEvmConfig<EvmFactory> {
84 pub fn new_with_evm_factory(chain_spec: Arc<ChainSpec>, evm_factory: EvmFactory) -> Self {
86 Self {
87 block_assembler: EthBlockAssembler::new(chain_spec.clone()),
88 executor_factory: EthBlockExecutorFactory::new(
89 RethReceiptBuilder::default(),
90 chain_spec,
91 evm_factory,
92 ),
93 }
94 }
95
96 pub const fn chain_spec(&self) -> &Arc<ChainSpec> {
98 self.executor_factory.spec()
99 }
100
101 pub fn blob_max_and_target_count_by_hardfork(&self) -> Vec<(SpecId, u64, u64)> {
104 let cancun = self.chain_spec().blob_params.cancun();
105 let prague = self.chain_spec().blob_params.prague();
106 let osaka = self.chain_spec().blob_params.osaka();
107 Vec::from([
108 (SpecId::CANCUN, cancun.target_blob_count, cancun.max_blob_count),
109 (SpecId::PRAGUE, prague.target_blob_count, prague.max_blob_count),
110 (SpecId::OSAKA, osaka.target_blob_count, osaka.max_blob_count),
111 ])
112 }
113
114 pub fn with_extra_data(mut self, extra_data: Bytes) -> Self {
116 self.block_assembler.extra_data = extra_data;
117 self
118 }
119}
120
121impl<EvmF> ConfigureEvm for EthEvmConfig<EvmF>
122where
123 EvmF: EvmFactory<
124 Tx: TransactionEnv
125 + FromRecoveredTx<TransactionSigned>
126 + FromTxWithEncoded<TransactionSigned>,
127 Spec = SpecId,
128 > + Clone
130 + Debug
131 + Send
132 + Sync
133 + Unpin
134 + 'static,
135{
136 type Primitives = EthPrimitives;
137 type Error = Infallible;
138 type NextBlockEnvCtx = NextBlockEnvAttributes;
139 type BlockExecutorFactory = EthBlockExecutorFactory<RethReceiptBuilder, Arc<ChainSpec>, EvmF>;
140 type BlockAssembler = EthBlockAssembler<ChainSpec>;
141
142 fn block_executor_factory(&self) -> &Self::BlockExecutorFactory {
143 &self.executor_factory
144 }
145
146 fn block_assembler(&self) -> &Self::BlockAssembler {
147 &self.block_assembler
148 }
149
150 fn evm_env(&self, header: &Header) -> EvmEnv {
151 let spec = config::revm_spec(self.chain_spec(), header);
152
153 let cfg_env = CfgEnv::new()
155 .with_chain_id(self.chain_spec().chain().id())
156 .with_spec(spec)
157 .with_blob_max_and_target_count(self.blob_max_and_target_count_by_hardfork());
158
159 let blob_excess_gas_and_price = header
162 .excess_blob_gas
163 .zip(self.chain_spec().blob_params_at_timestamp(header.timestamp))
164 .map(|(excess_blob_gas, params)| {
165 let blob_gasprice = params.calc_blob_fee(excess_blob_gas);
166 BlobExcessGasAndPrice { excess_blob_gas, blob_gasprice }
167 });
168
169 let block_env = BlockEnv {
170 number: header.number(),
171 beneficiary: header.beneficiary(),
172 timestamp: header.timestamp(),
173 difficulty: if spec >= SpecId::MERGE { U256::ZERO } else { header.difficulty() },
174 prevrandao: if spec >= SpecId::MERGE { header.mix_hash() } else { None },
175 gas_limit: header.gas_limit(),
176 basefee: header.base_fee_per_gas().unwrap_or_default(),
177 blob_excess_gas_and_price,
178 };
179
180 EvmEnv { cfg_env, block_env }
181 }
182
183 fn next_evm_env(
184 &self,
185 parent: &Header,
186 attributes: &NextBlockEnvAttributes,
187 ) -> Result<EvmEnv, Self::Error> {
188 let spec_id = revm_spec_by_timestamp_and_block_number(
190 self.chain_spec(),
191 attributes.timestamp,
192 parent.number() + 1,
193 );
194
195 let cfg = CfgEnv::new()
197 .with_chain_id(self.chain_spec().chain().id())
198 .with_spec(spec_id)
199 .with_blob_max_and_target_count(self.blob_max_and_target_count_by_hardfork());
200
201 let blob_params = self.chain_spec().blob_params_at_timestamp(attributes.timestamp);
202 let blob_excess_gas_and_price = parent
205 .maybe_next_block_excess_blob_gas(blob_params)
206 .or_else(|| (spec_id == SpecId::CANCUN).then_some(0))
207 .map(|excess_blob_gas| {
208 let blob_gasprice =
209 blob_params.unwrap_or_else(BlobParams::cancun).calc_blob_fee(excess_blob_gas);
210 BlobExcessGasAndPrice { excess_blob_gas, blob_gasprice }
211 });
212
213 let mut basefee = parent.next_block_base_fee(
214 self.chain_spec().base_fee_params_at_timestamp(attributes.timestamp),
215 );
216
217 let mut gas_limit = attributes.gas_limit;
218
219 if self.chain_spec().fork(EthereumHardfork::London).transitions_at_block(parent.number + 1)
222 {
223 let elasticity_multiplier = self
224 .chain_spec()
225 .base_fee_params_at_timestamp(attributes.timestamp)
226 .elasticity_multiplier;
227
228 gas_limit *= elasticity_multiplier as u64;
230
231 basefee = Some(INITIAL_BASE_FEE)
233 }
234
235 let block_env = BlockEnv {
236 number: parent.number + 1,
237 beneficiary: attributes.suggested_fee_recipient,
238 timestamp: attributes.timestamp,
239 difficulty: U256::ZERO,
240 prevrandao: Some(attributes.prev_randao),
241 gas_limit,
242 basefee: basefee.unwrap_or_default(),
244 blob_excess_gas_and_price,
246 };
247
248 Ok((cfg, block_env).into())
249 }
250
251 fn context_for_block<'a>(&self, block: &'a SealedBlock<Block>) -> EthBlockExecutionCtx<'a> {
252 EthBlockExecutionCtx {
253 parent_hash: block.header().parent_hash,
254 parent_beacon_block_root: block.header().parent_beacon_block_root,
255 ommers: &block.body().ommers,
256 withdrawals: block.body().withdrawals.as_ref().map(Cow::Borrowed),
257 }
258 }
259
260 fn context_for_next_block(
261 &self,
262 parent: &SealedHeader,
263 attributes: Self::NextBlockEnvCtx,
264 ) -> EthBlockExecutionCtx<'_> {
265 EthBlockExecutionCtx {
266 parent_hash: parent.hash(),
267 parent_beacon_block_root: attributes.parent_beacon_block_root,
268 ommers: &[],
269 withdrawals: attributes.withdrawals.map(Cow::Owned),
270 }
271 }
272}
273
274#[cfg(test)]
275mod tests {
276 use super::*;
277 use alloy_consensus::Header;
278 use alloy_genesis::Genesis;
279 use reth_chainspec::{Chain, ChainSpec};
280 use reth_evm::{execute::ProviderError, EvmEnv};
281 use revm::{
282 context::{BlockEnv, CfgEnv},
283 database::CacheDB,
284 database_interface::EmptyDBTyped,
285 inspector::NoOpInspector,
286 };
287
288 #[test]
289 fn test_fill_cfg_and_block_env() {
290 let header = Header::default();
292
293 let chain_spec = ChainSpec::builder()
296 .chain(Chain::mainnet())
297 .genesis(Genesis::default())
298 .london_activated()
299 .paris_activated()
300 .shanghai_activated()
301 .build();
302
303 let EvmEnv { cfg_env, .. } =
306 EthEvmConfig::new(Arc::new(chain_spec.clone())).evm_env(&header);
307
308 assert_eq!(cfg_env.chain_id, chain_spec.chain().id());
311 }
312
313 #[test]
314 fn test_evm_with_env_default_spec() {
315 let evm_config = EthEvmConfig::mainnet();
316
317 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
318
319 let evm_env = EvmEnv::default();
320
321 let evm = evm_config.evm_with_env(db, evm_env.clone());
322
323 assert_eq!(evm.block, evm_env.block_env);
325 assert_eq!(evm.cfg, evm_env.cfg_env);
326 }
327
328 #[test]
329 fn test_evm_with_env_custom_cfg() {
330 let evm_config = EthEvmConfig::mainnet();
331
332 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
333
334 let cfg = CfgEnv::default().with_chain_id(111);
336
337 let evm_env = EvmEnv { cfg_env: cfg.clone(), ..Default::default() };
338
339 let evm = evm_config.evm_with_env(db, evm_env);
340
341 assert_eq!(evm.cfg, cfg);
343 }
344
345 #[test]
346 fn test_evm_with_env_custom_block_and_tx() {
347 let evm_config = EthEvmConfig::mainnet();
348
349 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
350
351 let block =
353 BlockEnv { basefee: 1000, gas_limit: 10_000_000, number: 42, ..Default::default() };
354
355 let evm_env = EvmEnv { block_env: block, ..Default::default() };
356
357 let evm = evm_config.evm_with_env(db, evm_env.clone());
358
359 assert_eq!(evm.block, evm_env.block_env);
361
362 assert_eq!(evm.cfg.spec, SpecId::default());
364 }
365
366 #[test]
367 fn test_evm_with_spec_id() {
368 let evm_config = EthEvmConfig::mainnet();
369
370 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
371
372 let evm_env = EvmEnv {
373 cfg_env: CfgEnv::new().with_spec(SpecId::CONSTANTINOPLE),
374 ..Default::default()
375 };
376
377 let evm = evm_config.evm_with_env(db, evm_env);
378
379 assert_eq!(evm.cfg.spec, SpecId::CONSTANTINOPLE);
381 }
382
383 #[test]
384 fn test_evm_with_env_and_default_inspector() {
385 let evm_config = EthEvmConfig::mainnet();
386 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
387
388 let evm_env = EvmEnv::default();
389
390 let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
391
392 assert_eq!(evm.block, evm_env.block_env);
394 assert_eq!(evm.cfg, evm_env.cfg_env);
395 }
396
397 #[test]
398 fn test_evm_with_env_inspector_and_custom_cfg() {
399 let evm_config = EthEvmConfig::mainnet();
400 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
401
402 let cfg_env = CfgEnv::default().with_chain_id(111);
403 let block = BlockEnv::default();
404 let evm_env = EvmEnv { cfg_env: cfg_env.clone(), block_env: block };
405
406 let evm = evm_config.evm_with_env_and_inspector(db, evm_env, NoOpInspector {});
407
408 assert_eq!(evm.cfg, cfg_env);
410 assert_eq!(evm.cfg.spec, SpecId::default());
411 }
412
413 #[test]
414 fn test_evm_with_env_inspector_and_custom_block_tx() {
415 let evm_config = EthEvmConfig::mainnet();
416 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
417
418 let block =
420 BlockEnv { basefee: 1000, gas_limit: 10_000_000, number: 42, ..Default::default() };
421 let evm_env = EvmEnv { block_env: block, ..Default::default() };
422
423 let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
424
425 assert_eq!(evm.block, evm_env.block_env);
427 assert_eq!(evm.cfg.spec, SpecId::default());
428 }
429
430 #[test]
431 fn test_evm_with_env_inspector_and_spec_id() {
432 let evm_config = EthEvmConfig::mainnet();
433 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
434
435 let evm_env = EvmEnv {
436 cfg_env: CfgEnv::new().with_spec(SpecId::CONSTANTINOPLE),
437 ..Default::default()
438 };
439
440 let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
441
442 assert_eq!(evm.block, evm_env.block_env);
444 assert_eq!(evm.cfg, evm_env.cfg_env);
445 assert_eq!(evm.tx, Default::default());
446 }
447}