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