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))]
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};
26#[cfg(feature = "jit")]
27use core::any::Any;
28use core::{convert::Infallible, fmt::Debug};
29use reth_chainspec::{ChainSpec, EthChainSpec, MAINNET};
30use reth_ethereum_primitives::{Block, EthPrimitives, TransactionSigned};
31use reth_evm::{
32 eth::NextEvmEnvAttributes, precompiles::PrecompilesMap, ConfigureEvm, EvmEnv, EvmFactory,
33 JitBackend, NextBlockEnvAttributes, TransactionEnvMut,
34};
35use reth_primitives_traits::{SealedBlock, SealedHeader};
36use revm::{context::BlockEnv, primitives::hardfork::SpecId};
37
38#[cfg(feature = "std")]
39use reth_evm::{ConfigureEngineEvm, ExecutableTxIterator};
40#[allow(unused_imports)]
41use {
42 alloy_eips::Decodable2718,
43 alloy_primitives::{Bytes, U256},
44 alloy_rpc_types_engine::ExecutionData,
45 reth_chainspec::EthereumHardforks,
46 reth_evm::{EvmEnvFor, ExecutionCtxFor},
47 reth_primitives_traits::{constants::MAX_TX_GAS_LIMIT_OSAKA, SignedTransaction, TxTy},
48 reth_storage_errors::any::AnyError,
49 revm::context::CfgEnv,
50 revm::context_interface::block::BlobExcessGasAndPrice,
51};
52
53pub use alloy_evm::EthEvm;
54
55mod config;
56use alloy_evm::eth::spec::EthExecutorSpec;
57pub use config::{revm_spec, revm_spec_by_timestamp_and_block_number};
58use reth_ethereum_forks::Hardforks;
59
60#[doc(hidden)]
63pub mod execute {
64 use crate::EthEvmConfig;
65
66 #[deprecated(note = "Use `EthEvmConfig` instead")]
67 pub type EthExecutorProvider = EthEvmConfig;
68}
69
70mod build;
71pub use build::EthBlockAssembler;
72
73mod receipt;
74pub use receipt::RethReceiptBuilder;
75
76#[cfg(feature = "test-utils")]
77mod test_utils;
78#[cfg(feature = "test-utils")]
79pub use test_utils::*;
80
81pub mod factory;
82
83#[derive(Debug, Clone)]
85pub struct EthEvmConfig<C = ChainSpec, EvmFactory = EthEvmFactory> {
86 pub executor_factory: EthBlockExecutorFactory<RethReceiptBuilder, Arc<C>, EvmFactory>,
88 pub block_assembler: EthBlockAssembler<C>,
90}
91
92impl EthEvmConfig {
93 pub fn mainnet() -> Self {
95 Self::ethereum(MAINNET.clone())
96 }
97}
98
99impl<ChainSpec> EthEvmConfig<ChainSpec> {
100 pub fn new(chain_spec: Arc<ChainSpec>) -> Self {
102 Self::ethereum(chain_spec)
103 }
104
105 pub fn ethereum(chain_spec: Arc<ChainSpec>) -> Self {
107 Self::new_with_evm_factory(chain_spec, EthEvmFactory::default())
108 }
109}
110
111impl<ChainSpec, EvmFactory> EthEvmConfig<ChainSpec, EvmFactory> {
112 pub fn new_with_evm_factory(chain_spec: Arc<ChainSpec>, evm_factory: EvmFactory) -> Self {
114 Self {
115 block_assembler: EthBlockAssembler::new(chain_spec.clone()),
116 executor_factory: EthBlockExecutorFactory::new(
117 RethReceiptBuilder::default(),
118 chain_spec,
119 evm_factory,
120 ),
121 }
122 }
123
124 pub const fn chain_spec(&self) -> &Arc<ChainSpec> {
126 self.executor_factory.spec()
127 }
128}
129
130impl<ChainSpec, EvmF> ConfigureEvm for EthEvmConfig<ChainSpec, EvmF>
131where
132 ChainSpec: EthExecutorSpec + EthChainSpec<Header = Header> + Hardforks + 'static,
133 EvmF: EvmFactory<
134 Tx: TransactionEnvMut
135 + FromRecoveredTx<TransactionSigned>
136 + FromTxWithEncoded<TransactionSigned>,
137 Spec = SpecId,
138 BlockEnv = BlockEnv,
139 Precompiles = PrecompilesMap,
140 > + Clone
141 + Debug
142 + Send
143 + Sync
144 + Unpin
145 + 'static,
146{
147 type Primitives = EthPrimitives;
148 type Error = Infallible;
149 type NextBlockEnvCtx = NextBlockEnvAttributes;
150 type BlockExecutorFactory = EthBlockExecutorFactory<RethReceiptBuilder, Arc<ChainSpec>, EvmF>;
151 type BlockAssembler = EthBlockAssembler<ChainSpec>;
152
153 fn block_executor_factory(&self) -> &Self::BlockExecutorFactory {
154 &self.executor_factory
155 }
156
157 fn block_assembler(&self) -> &Self::BlockAssembler {
158 &self.block_assembler
159 }
160
161 fn with_jit_support_enabled(self, enabled: bool) -> Self
162 where
163 Self: Sized,
164 {
165 #[cfg(feature = "jit")]
166 {
167 let mut this = self;
168 let mut evm_factory = this.executor_factory.evm_factory().clone();
169 if let Some(factory) =
170 (&mut evm_factory as &mut dyn Any).downcast_mut::<factory::RethEvmFactory>()
171 {
172 factory.set_jit_support(enabled);
173 }
174 this.executor_factory = EthBlockExecutorFactory::new(
175 *this.executor_factory.receipt_builder(),
176 this.executor_factory.spec().clone(),
177 evm_factory,
178 );
179 this
180 }
181
182 #[cfg(not(feature = "jit"))]
183 {
184 let _ = enabled;
185 self
186 }
187 }
188
189 fn jit_backend(&self) -> Option<&dyn JitBackend> {
190 #[cfg(feature = "jit")]
191 if let Some(factory) = (self.executor_factory.evm_factory() as &dyn Any)
192 .downcast_ref::<factory::RethEvmFactory>()
193 {
194 return Some(factory);
195 }
196
197 None
198 }
199
200 fn evm_env(&self, header: &Header) -> Result<EvmEnv<SpecId>, Self::Error> {
201 Ok(EvmEnv::for_eth_block(
202 header,
203 self.chain_spec(),
204 self.chain_spec().chain().id(),
205 self.chain_spec().blob_params_at_timestamp(header.timestamp),
206 ))
207 }
208
209 fn next_evm_env(
210 &self,
211 parent: &Header,
212 attributes: &NextBlockEnvAttributes,
213 ) -> Result<EvmEnv, Self::Error> {
214 Ok(EvmEnv::for_eth_next_block(
215 parent,
216 NextEvmEnvAttributes {
217 timestamp: attributes.timestamp,
218 suggested_fee_recipient: attributes.suggested_fee_recipient,
219 prev_randao: attributes.prev_randao,
220 gas_limit: attributes.gas_limit,
221 slot_number: attributes.slot_number,
222 },
223 self.chain_spec().next_block_base_fee(parent, attributes.timestamp).unwrap_or_default(),
224 self.chain_spec(),
225 self.chain_spec().chain().id(),
226 self.chain_spec().blob_params_at_timestamp(attributes.timestamp),
227 ))
228 }
229
230 fn context_for_block<'a>(
231 &self,
232 block: &'a SealedBlock<Block>,
233 ) -> Result<EthBlockExecutionCtx<'a>, Self::Error> {
234 Ok(EthBlockExecutionCtx {
235 tx_count_hint: Some(block.transaction_count()),
236 parent_hash: block.header().parent_hash,
237 parent_beacon_block_root: block.header().parent_beacon_block_root,
238 ommers: &block.body().ommers,
239 withdrawals: block.body().withdrawals.as_ref().map(|w| Cow::Borrowed(w.as_slice())),
240 extra_data: block.header().extra_data.clone(),
241 slot_number: block.header().slot_number,
242 })
243 }
244
245 fn context_for_next_block(
246 &self,
247 parent: &SealedHeader,
248 attributes: Self::NextBlockEnvCtx,
249 ) -> Result<EthBlockExecutionCtx<'_>, Self::Error> {
250 Ok(EthBlockExecutionCtx {
251 tx_count_hint: None,
252 parent_hash: parent.hash(),
253 parent_beacon_block_root: attributes.parent_beacon_block_root,
254 ommers: &[],
255 withdrawals: attributes.withdrawals.map(|w| Cow::Owned(w.into_inner())),
256 extra_data: attributes.extra_data,
257 slot_number: attributes.slot_number,
258 })
259 }
260}
261
262#[cfg(feature = "std")]
263impl<ChainSpec, EvmF> ConfigureEngineEvm<ExecutionData> for EthEvmConfig<ChainSpec, EvmF>
264where
265 ChainSpec: EthExecutorSpec + EthChainSpec<Header = Header> + Hardforks + 'static,
266 EvmF: EvmFactory<
267 Tx: TransactionEnvMut
268 + FromRecoveredTx<TransactionSigned>
269 + FromTxWithEncoded<TransactionSigned>,
270 Spec = SpecId,
271 BlockEnv = BlockEnv,
272 Precompiles = PrecompilesMap,
273 > + Clone
274 + Debug
275 + Send
276 + Sync
277 + Unpin
278 + 'static,
279{
280 fn evm_env_for_payload(&self, payload: &ExecutionData) -> Result<EvmEnvFor<Self>, Self::Error> {
281 let timestamp = payload.payload.timestamp();
282 let block_number = payload.payload.block_number();
283
284 let blob_params = self.chain_spec().blob_params_at_timestamp(timestamp);
285 let spec =
286 revm_spec_by_timestamp_and_block_number(self.chain_spec(), timestamp, block_number);
287
288 let mut cfg_env = CfgEnv::new()
290 .with_chain_id(self.chain_spec().chain().id())
291 .with_spec_and_mainnet_gas_params(spec);
292
293 if let Some(blob_params) = &blob_params {
294 cfg_env.set_max_blobs_per_tx(blob_params.max_blobs_per_tx);
295 }
296
297 if self.chain_spec().is_osaka_active_at_timestamp(timestamp) {
298 cfg_env.tx_gas_limit_cap = Some(MAX_TX_GAS_LIMIT_OSAKA);
299 }
300
301 let blob_excess_gas_and_price =
304 payload.payload.excess_blob_gas().zip(blob_params).map(|(excess_blob_gas, params)| {
305 let blob_gasprice = params.calc_blob_fee(excess_blob_gas);
306 BlobExcessGasAndPrice { excess_blob_gas, blob_gasprice }
307 });
308
309 let block_env = BlockEnv {
310 number: U256::from(block_number),
311 beneficiary: payload.payload.fee_recipient(),
312 timestamp: U256::from(timestamp),
313 difficulty: if spec >= SpecId::MERGE {
314 U256::ZERO
315 } else {
316 payload.payload.as_v1().prev_randao.into()
317 },
318 prevrandao: (spec >= SpecId::MERGE).then(|| payload.payload.as_v1().prev_randao),
319 gas_limit: payload.payload.gas_limit(),
320 basefee: payload.payload.saturated_base_fee_per_gas(),
321 blob_excess_gas_and_price,
322 slot_num: payload.payload.as_v4().map(|v4| v4.slot_number).unwrap_or_default(),
323 };
324
325 Ok(EvmEnv { cfg_env, block_env })
326 }
327
328 fn context_for_payload<'a>(
329 &self,
330 payload: &'a ExecutionData,
331 ) -> Result<ExecutionCtxFor<'a, Self>, Self::Error> {
332 Ok(EthBlockExecutionCtx {
333 tx_count_hint: Some(payload.payload.transactions().len()),
334 parent_hash: payload.parent_hash(),
335 parent_beacon_block_root: payload.sidecar.parent_beacon_block_root(),
336 ommers: &[],
337 withdrawals: payload.payload.withdrawals().map(|w| Cow::Borrowed(w.as_slice())),
338 extra_data: payload.payload.as_v1().extra_data.clone(),
339 slot_number: payload.payload.as_v4().map(|v4| v4.slot_number),
340 })
341 }
342
343 fn tx_iterator_for_payload(
344 &self,
345 payload: &ExecutionData,
346 ) -> Result<impl ExecutableTxIterator<Self>, Self::Error> {
347 let txs = payload.payload.transactions().clone();
348 let convert = |tx: Bytes| {
349 let tx =
350 TxTy::<Self::Primitives>::decode_2718_exact(tx.as_ref()).map_err(AnyError::new)?;
351 let signer = tx.try_recover().map_err(AnyError::new)?;
352 Ok::<_, AnyError>(tx.with_signer(signer))
353 };
354
355 Ok((txs, convert))
356 }
357}
358
359#[cfg(test)]
360mod tests {
361 use super::*;
362 use alloy_consensus::Header;
363 use alloy_genesis::Genesis;
364 use reth_chainspec::{Chain, ChainSpec};
365 use reth_evm::{execute::ProviderError, EvmEnv};
366 use revm::{
367 context::{BlockEnv, CfgEnv},
368 database::CacheDB,
369 database_interface::EmptyDBTyped,
370 inspector::NoOpInspector,
371 };
372
373 #[test]
374 fn test_fill_cfg_and_block_env() {
375 let header = Header::default();
377
378 let chain_spec = ChainSpec::builder()
381 .chain(Chain::mainnet())
382 .genesis(Genesis::default())
383 .london_activated()
384 .paris_activated()
385 .shanghai_activated()
386 .build();
387
388 let EvmEnv { cfg_env, .. } =
391 EthEvmConfig::new(Arc::new(chain_spec.clone())).evm_env(&header).unwrap();
392
393 assert_eq!(cfg_env.chain_id, chain_spec.chain().id());
396 }
397
398 #[test]
399 fn test_evm_with_env_default_spec() {
400 let evm_config = EthEvmConfig::mainnet();
401
402 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
403
404 let evm_env = EvmEnv::default();
405
406 let evm = evm_config.evm_with_env(db, evm_env.clone());
407
408 assert_eq!(evm.block, evm_env.block_env);
410 assert_eq!(evm.cfg, evm_env.cfg_env);
411 }
412
413 #[test]
414 fn test_evm_with_env_custom_cfg() {
415 let evm_config = EthEvmConfig::mainnet();
416
417 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
418
419 let cfg = CfgEnv::default().with_chain_id(111);
421
422 let evm_env = EvmEnv { cfg_env: cfg.clone(), ..Default::default() };
423
424 let evm = evm_config.evm_with_env(db, evm_env);
425
426 assert_eq!(evm.cfg, cfg);
428 }
429
430 #[test]
431 fn test_evm_with_env_custom_block_and_tx() {
432 let evm_config = EthEvmConfig::mainnet();
433
434 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
435
436 let block = BlockEnv {
438 basefee: 1000,
439 gas_limit: 10_000_000,
440 number: U256::from(42),
441 ..Default::default()
442 };
443
444 let evm_env = EvmEnv { block_env: block, ..Default::default() };
445
446 let evm = evm_config.evm_with_env(db, evm_env.clone());
447
448 assert_eq!(evm.block, evm_env.block_env);
450
451 assert_eq!(evm.cfg.spec, SpecId::default());
453 }
454
455 #[test]
456 fn test_evm_with_spec_id() {
457 let evm_config = EthEvmConfig::mainnet();
458
459 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
460
461 let evm_env = EvmEnv {
462 cfg_env: CfgEnv::new().with_spec_and_mainnet_gas_params(SpecId::PETERSBURG),
463 ..Default::default()
464 };
465
466 let evm = evm_config.evm_with_env(db, evm_env);
467
468 assert_eq!(evm.cfg.spec, SpecId::PETERSBURG);
470 }
471
472 #[test]
473 fn test_evm_with_env_and_default_inspector() {
474 let evm_config = EthEvmConfig::mainnet();
475 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
476
477 let evm_env = EvmEnv::default();
478
479 let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
480
481 assert_eq!(evm.block, evm_env.block_env);
483 assert_eq!(evm.cfg, evm_env.cfg_env);
484 }
485
486 #[test]
487 fn test_evm_with_env_inspector_and_custom_cfg() {
488 let evm_config = EthEvmConfig::mainnet();
489 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
490
491 let cfg_env = CfgEnv::default().with_chain_id(111);
492 let block = BlockEnv::default();
493 let evm_env = EvmEnv { cfg_env: cfg_env.clone(), block_env: block };
494
495 let evm = evm_config.evm_with_env_and_inspector(db, evm_env, NoOpInspector {});
496
497 assert_eq!(evm.cfg, cfg_env);
499 assert_eq!(evm.cfg.spec, SpecId::default());
500 }
501
502 #[test]
503 fn test_evm_with_env_inspector_and_custom_block_tx() {
504 let evm_config = EthEvmConfig::mainnet();
505 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
506
507 let block = BlockEnv {
509 basefee: 1000,
510 gas_limit: 10_000_000,
511 number: U256::from(42),
512 ..Default::default()
513 };
514 let evm_env = EvmEnv { block_env: block, ..Default::default() };
515
516 let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
517
518 assert_eq!(evm.block, evm_env.block_env);
520 assert_eq!(evm.cfg.spec, SpecId::default());
521 }
522
523 #[test]
524 fn test_evm_with_env_inspector_and_spec_id() {
525 let evm_config = EthEvmConfig::mainnet();
526 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
527
528 let evm_env = EvmEnv {
529 cfg_env: CfgEnv::new().with_spec_and_mainnet_gas_params(SpecId::PETERSBURG),
530 ..Default::default()
531 };
532
533 let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
534
535 assert_eq!(evm.block, evm_env.block_env);
537 assert_eq!(evm.cfg, evm_env.cfg_env);
538 assert_eq!(evm.tx, Default::default());
539 }
540
541 #[cfg(feature = "jit")]
542 #[test]
543 fn test_jit_support_downcast_updates_reth_factory() {
544 let evm_config = EthEvmConfig::new_with_evm_factory(
545 MAINNET.clone(),
546 factory::RethEvmFactory::disabled(),
547 );
548
549 assert!(evm_config.jit_backend().is_some());
550 assert!(!evm_config.executor_factory.evm_factory().jit_support_enabled());
551
552 let evm_config = evm_config.with_jit_support();
553 assert!(evm_config.executor_factory.evm_factory().jit_support_enabled());
554
555 let evm_config = evm_config.with_jit_support_enabled(false);
556 assert!(!evm_config.executor_factory.evm_factory().jit_support_enabled());
557 }
558
559 #[cfg(feature = "jit")]
560 #[test]
561 fn test_jit_support_downcast_ignores_plain_factory() {
562 let evm_config = EthEvmConfig::mainnet();
563
564 assert!(evm_config.jit_backend().is_none());
565
566 let evm_config = evm_config.with_jit_support();
567 assert!(evm_config.jit_backend().is_none());
568 }
569}