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, Transaction};
22use alloy_eips::merge::SLOT_DURATION_SECS;
23use alloy_evm::{
24 eth::{EthBlockExecutionCtx, EthBlockExecutorFactory, EthTxResult},
25 EthEvmFactory, FromRecoveredTx, FromTxWithEncoded,
26};
27use alloy_primitives::{Address, B256};
28#[cfg(feature = "jit")]
29use core::any::Any;
30use core::{any::TypeId, convert::Infallible, fmt::Debug};
31use reth_chainspec::{ChainSpec, EthChainSpec, MAINNET};
32use reth_ethereum_primitives::{Block, EthPrimitives, TransactionSigned};
33use reth_evm::{
34 eth::NextEvmEnvAttributes, precompiles::PrecompilesMap, ConfigureEvm, EvmEnv, EvmFactory,
35 HaltReasonFor, JitBackend, NextBlockEnvAttributes, SenderRecoveryCache, TransactionEnvMut,
36 TxExecutionResultFor,
37};
38use reth_primitives_traits::{SealedBlock, SealedHeader};
39use revm::{
40 context::{result::ResultAndState, BlockEnv},
41 primitives::hardfork::SpecId,
42};
43
44#[cfg(feature = "std")]
45use reth_evm::{ConfigureEngineEvm, ExecutableTxIterator};
46#[allow(unused_imports)]
47use {
48 alloy_eips::Decodable2718,
49 alloy_primitives::{Bytes, U256},
50 alloy_rpc_types_engine::ExecutionData,
51 reth_chainspec::EthereumHardforks,
52 reth_evm::{EvmEnvFor, ExecutionCtxFor},
53 reth_primitives_traits::{constants::MAX_TX_GAS_LIMIT_OSAKA, SignedTransaction, TxTy},
54 reth_storage_errors::any::AnyError,
55 revm::context::CfgEnv,
56 revm::context_interface::block::BlobExcessGasAndPrice,
57};
58
59pub use alloy_evm::EthEvm;
60
61mod config;
62use alloy_evm::eth::spec::EthExecutorSpec;
63pub use config::{revm_spec, revm_spec_by_timestamp_and_block_number};
64use reth_ethereum_forks::Hardforks;
65
66#[doc(hidden)]
69pub mod execute {
70 use crate::EthEvmConfig;
71
72 #[deprecated(note = "Use `EthEvmConfig` instead")]
73 pub type EthExecutorProvider = EthEvmConfig;
74}
75
76mod build;
77pub use build::EthBlockAssembler;
78
79mod receipt;
80pub use receipt::RethReceiptBuilder;
81
82#[cfg(feature = "test-utils")]
83mod test_utils;
84#[cfg(feature = "test-utils")]
85pub use test_utils::*;
86
87pub mod factory;
88
89#[derive(Debug, Clone)]
91pub struct EthEvmConfig<C = ChainSpec, EvmFactory = EthEvmFactory> {
92 pub executor_factory: EthBlockExecutorFactory<RethReceiptBuilder, Arc<C>, EvmFactory>,
94 pub block_assembler: EthBlockAssembler<C>,
96 pub sender_recovery_cache: Option<SenderRecoveryCache>,
98}
99
100impl EthEvmConfig {
101 pub fn mainnet() -> Self {
103 Self::ethereum(MAINNET.clone())
104 }
105}
106
107impl<ChainSpec> EthEvmConfig<ChainSpec> {
108 pub fn new(chain_spec: Arc<ChainSpec>) -> Self {
110 Self::ethereum(chain_spec)
111 }
112
113 pub fn ethereum(chain_spec: Arc<ChainSpec>) -> Self {
115 Self::new_with_evm_factory(chain_spec, EthEvmFactory::default())
116 }
117}
118
119impl<ChainSpec, EvmFactory> EthEvmConfig<ChainSpec, EvmFactory> {
120 pub fn new_with_evm_factory(chain_spec: Arc<ChainSpec>, evm_factory: EvmFactory) -> Self {
122 Self {
123 block_assembler: EthBlockAssembler::new(chain_spec.clone()),
124 sender_recovery_cache: None,
125 executor_factory: EthBlockExecutorFactory::new(
126 RethReceiptBuilder::default(),
127 chain_spec,
128 evm_factory,
129 ),
130 }
131 }
132
133 pub const fn chain_spec(&self) -> &Arc<ChainSpec> {
135 self.executor_factory.spec()
136 }
137
138 pub fn with_sender_recovery_cache(mut self, cache: SenderRecoveryCache) -> Self {
140 self.sender_recovery_cache = Some(cache);
141 self
142 }
143}
144
145impl<ChainSpec, EvmF> ConfigureEvm for EthEvmConfig<ChainSpec, EvmF>
146where
147 ChainSpec: EthExecutorSpec + EthChainSpec<Header = Header> + Hardforks + 'static,
148 EvmF: EvmFactory<
149 Tx: TransactionEnvMut
150 + FromRecoveredTx<TransactionSigned>
151 + FromTxWithEncoded<TransactionSigned>,
152 Spec = SpecId,
153 BlockEnv = BlockEnv,
154 Precompiles = PrecompilesMap,
155 > + Clone
156 + Debug
157 + Send
158 + Sync
159 + Unpin
160 + 'static,
161{
162 type Primitives = EthPrimitives;
163 type Error = Infallible;
164 type NextBlockEnvCtx = NextBlockEnvAttributes;
165 type BlockExecutorFactory = EthBlockExecutorFactory<RethReceiptBuilder, Arc<ChainSpec>, EvmF>;
166 type BlockAssembler = EthBlockAssembler<ChainSpec>;
167
168 fn block_executor_factory(&self) -> &Self::BlockExecutorFactory {
169 &self.executor_factory
170 }
171
172 fn block_assembler(&self) -> &Self::BlockAssembler {
173 &self.block_assembler
174 }
175
176 fn with_jit_support_enabled(self, enabled: bool) -> Self
177 where
178 Self: Sized,
179 {
180 #[cfg(feature = "jit")]
181 {
182 let mut this = self;
183 let mut evm_factory = this.executor_factory.evm_factory().clone();
184 if let Some(factory) =
185 (&mut evm_factory as &mut dyn Any).downcast_mut::<factory::RethEvmFactory>()
186 {
187 factory.set_jit_support(enabled);
188 }
189 this.executor_factory = EthBlockExecutorFactory::new(
190 *this.executor_factory.receipt_builder(),
191 this.executor_factory.spec().clone(),
192 evm_factory,
193 );
194 this
195 }
196
197 #[cfg(not(feature = "jit"))]
198 {
199 let _ = enabled;
200 self
201 }
202 }
203
204 fn jit_backend(&self) -> Option<&dyn JitBackend> {
205 #[cfg(feature = "jit")]
206 if let Some(factory) = (self.executor_factory.evm_factory() as &dyn Any)
207 .downcast_ref::<factory::RethEvmFactory>()
208 {
209 return Some(factory);
210 }
211
212 None
213 }
214
215 fn evm_env(&self, header: &Header) -> Result<EvmEnv<SpecId>, Self::Error> {
216 Ok(EvmEnv::for_eth_block(
217 header,
218 self.chain_spec(),
219 self.chain_spec().chain().id(),
220 self.chain_spec().blob_params_at_timestamp(header.timestamp),
221 ))
222 }
223
224 fn next_evm_env(
225 &self,
226 parent: &Header,
227 attributes: &NextBlockEnvAttributes,
228 ) -> Result<EvmEnv, Self::Error> {
229 Ok(EvmEnv::for_eth_next_block(
230 parent,
231 NextEvmEnvAttributes {
232 timestamp: attributes.timestamp,
233 suggested_fee_recipient: attributes.suggested_fee_recipient,
234 prev_randao: attributes.prev_randao,
235 gas_limit: attributes.gas_limit,
236 slot_number: attributes.slot_number,
237 },
238 self.chain_spec().next_block_base_fee(parent, attributes.timestamp).unwrap_or_default(),
239 self.chain_spec(),
240 self.chain_spec().chain().id(),
241 self.chain_spec().blob_params_at_timestamp(attributes.timestamp),
242 ))
243 }
244
245 fn context_for_block<'a>(
246 &self,
247 block: &'a SealedBlock<Block>,
248 ) -> Result<EthBlockExecutionCtx<'a>, Self::Error> {
249 Ok(EthBlockExecutionCtx {
250 tx_count_hint: Some(block.transaction_count()),
251 parent_hash: block.header().parent_hash,
252 parent_beacon_block_root: block.header().parent_beacon_block_root,
253 ommers: &block.body().ommers,
254 withdrawals: block.body().withdrawals.as_ref().map(|w| Cow::Borrowed(w.as_slice())),
255 extra_data: block.header().extra_data.clone(),
256 slot_number: block.header().slot_number,
257 })
258 }
259
260 fn context_for_next_block(
261 &self,
262 parent: &SealedHeader,
263 attributes: Self::NextBlockEnvCtx,
264 ) -> Result<EthBlockExecutionCtx<'_>, Self::Error> {
265 Ok(EthBlockExecutionCtx {
266 tx_count_hint: None,
267 parent_hash: parent.hash(),
268 parent_beacon_block_root: attributes.parent_beacon_block_root,
269 ommers: &[],
270 withdrawals: attributes.withdrawals.map(|w| Cow::Owned(w.into_inner())),
271 extra_data: attributes.extra_data,
272 slot_number: attributes.slot_number,
273 })
274 }
275
276 fn reuses_prewarm_results(&self) -> bool {
277 let factory = TypeId::of::<EvmF>();
279 factory == TypeId::of::<EthEvmFactory>() ||
280 factory == TypeId::of::<factory::RethEvmFactory>()
281 }
282
283 fn prewarm_tx_result(
284 &self,
285 tx: &TransactionSigned,
286 result: ResultAndState<HaltReasonFor<Self>>,
287 ) -> Option<TxExecutionResultFor<Self>> {
288 Some(EthTxResult {
289 result,
290 blob_gas_used: tx.blob_gas_used().unwrap_or_default(),
291 tx_type: tx.tx_type(),
292 })
293 }
294
295 fn speculative_next_evm_env(
296 &self,
297 parent: &SealedHeader,
298 beneficiary: Address,
299 ) -> Option<EvmEnv> {
300 let Ok(evm_env) = self.next_evm_env(
301 parent,
302 &NextBlockEnvAttributes {
303 timestamp: parent.timestamp + SLOT_DURATION_SECS,
304 suggested_fee_recipient: beneficiary,
305 prev_randao: B256::ZERO,
306 gas_limit: parent.gas_limit,
307 parent_beacon_block_root: None,
308 withdrawals: None,
309 extra_data: Bytes::new(),
310 slot_number: None,
311 },
312 );
313 Some(evm_env)
314 }
315}
316
317#[cfg(feature = "std")]
318impl<ChainSpec, EvmF> ConfigureEngineEvm<ExecutionData> for EthEvmConfig<ChainSpec, EvmF>
319where
320 ChainSpec: EthExecutorSpec + EthChainSpec<Header = Header> + Hardforks + 'static,
321 EvmF: EvmFactory<
322 Tx: TransactionEnvMut
323 + FromRecoveredTx<TransactionSigned>
324 + FromTxWithEncoded<TransactionSigned>,
325 Spec = SpecId,
326 BlockEnv = BlockEnv,
327 Precompiles = PrecompilesMap,
328 > + Clone
329 + Debug
330 + Send
331 + Sync
332 + Unpin
333 + 'static,
334{
335 fn evm_env_for_payload(&self, payload: &ExecutionData) -> Result<EvmEnvFor<Self>, Self::Error> {
336 let timestamp = payload.payload.timestamp();
337 let block_number = payload.payload.block_number();
338
339 let blob_params = self.chain_spec().blob_params_at_timestamp(timestamp);
340 let spec =
341 revm_spec_by_timestamp_and_block_number(self.chain_spec(), timestamp, block_number);
342
343 let mut cfg_env = CfgEnv::new()
345 .with_chain_id(self.chain_spec().chain().id())
346 .with_spec_and_mainnet_gas_params(spec);
347
348 if let Some(blob_params) = &blob_params {
349 cfg_env.set_max_blobs_per_tx(blob_params.max_blobs_per_tx);
350 }
351
352 if self.chain_spec().is_osaka_active_at_timestamp(timestamp) {
353 cfg_env.tx_gas_limit_cap = Some(MAX_TX_GAS_LIMIT_OSAKA);
354 }
355
356 let blob_excess_gas_and_price =
359 payload.payload.excess_blob_gas().zip(blob_params).map(|(excess_blob_gas, params)| {
360 let blob_gasprice = params.calc_blob_fee(excess_blob_gas);
361 BlobExcessGasAndPrice { excess_blob_gas, blob_gasprice }
362 });
363
364 let block_env = BlockEnv {
365 number: U256::from(block_number),
366 beneficiary: payload.payload.fee_recipient(),
367 timestamp: U256::from(timestamp),
368 difficulty: if spec >= SpecId::MERGE {
369 U256::ZERO
370 } else {
371 payload.payload.as_v1().prev_randao.into()
372 },
373 prevrandao: (spec >= SpecId::MERGE).then(|| payload.payload.as_v1().prev_randao),
374 gas_limit: payload.payload.gas_limit(),
375 basefee: payload.payload.saturated_base_fee_per_gas(),
376 blob_excess_gas_and_price,
377 slot_num: payload.payload.as_v4().map(|v4| v4.slot_number).unwrap_or_default(),
378 };
379
380 Ok(EvmEnv { cfg_env, block_env })
381 }
382
383 fn context_for_payload<'a>(
384 &self,
385 payload: &'a ExecutionData,
386 ) -> Result<ExecutionCtxFor<'a, Self>, Self::Error> {
387 Ok(EthBlockExecutionCtx {
388 tx_count_hint: Some(payload.payload.transactions().len()),
389 parent_hash: payload.parent_hash(),
390 parent_beacon_block_root: payload.sidecar.parent_beacon_block_root(),
391 ommers: &[],
392 withdrawals: payload.payload.withdrawals().map(|w| Cow::Borrowed(w.as_slice())),
393 extra_data: payload.payload.as_v1().extra_data.clone(),
394 slot_number: payload.payload.as_v4().map(|v4| v4.slot_number),
395 })
396 }
397
398 fn tx_iterator_for_payload(
399 &self,
400 payload: &ExecutionData,
401 ) -> Result<impl ExecutableTxIterator<Self>, Self::Error> {
402 let txs = payload.payload.transactions().clone();
403 let sender_recovery_cache = self.sender_recovery_cache.clone();
404 let convert = move |tx: Bytes| {
405 let tx =
406 TxTy::<Self::Primitives>::decode_2718_exact(tx.as_ref()).map_err(AnyError::new)?;
407 let signer = if let Some(cache) = &sender_recovery_cache {
408 cache.recover(&tx)
409 } else {
410 tx.try_recover()
411 }
412 .map_err(AnyError::new)?;
413 Ok::<_, AnyError>(tx.with_signer(signer))
414 };
415
416 Ok((txs, convert))
417 }
418}
419
420#[cfg(test)]
421mod tests {
422 use super::*;
423 use alloy_consensus::Header;
424 use alloy_evm::{
425 eth::{EthEvm, EthEvmContext},
426 Database,
427 };
428 use alloy_genesis::Genesis;
429 use reth_chainspec::{Chain, ChainSpec};
430 use reth_evm::{execute::ProviderError, EvmEnv};
431 use revm::{
432 context::{
433 result::{EVMError, HaltReason},
434 BlockEnv, CfgEnv, TxEnv,
435 },
436 database::CacheDB,
437 database_interface::{DBErrorMarker, EmptyDBTyped},
438 inspector::NoOpInspector,
439 Inspector,
440 };
441
442 #[test]
443 fn test_fill_cfg_and_block_env() {
444 let header = Header::default();
446
447 let chain_spec = ChainSpec::builder()
450 .chain(Chain::mainnet())
451 .genesis(Genesis::default())
452 .london_activated()
453 .paris_activated()
454 .shanghai_activated()
455 .build();
456
457 let EvmEnv { cfg_env, .. } =
460 EthEvmConfig::new(Arc::new(chain_spec.clone())).evm_env(&header).unwrap();
461
462 assert_eq!(cfg_env.chain_id, chain_spec.chain().id());
465 }
466
467 #[test]
468 fn test_evm_with_env_default_spec() {
469 let evm_config = EthEvmConfig::mainnet();
470
471 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
472
473 let evm_env = EvmEnv::default();
474
475 let evm = evm_config.evm_with_env(db, evm_env.clone());
476
477 assert_eq!(evm.block, evm_env.block_env);
479 assert_eq!(evm.cfg, evm_env.cfg_env);
480 }
481
482 #[test]
483 fn test_evm_with_env_custom_cfg() {
484 let evm_config = EthEvmConfig::mainnet();
485
486 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
487
488 let cfg = CfgEnv::default().with_chain_id(111);
490
491 let evm_env = EvmEnv { cfg_env: cfg.clone(), ..Default::default() };
492
493 let evm = evm_config.evm_with_env(db, evm_env);
494
495 assert_eq!(evm.cfg, cfg);
497 }
498
499 #[test]
500 fn test_evm_with_env_custom_block_and_tx() {
501 let evm_config = EthEvmConfig::mainnet();
502
503 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
504
505 let block = BlockEnv {
507 basefee: 1000,
508 gas_limit: 10_000_000,
509 number: U256::from(42),
510 ..Default::default()
511 };
512
513 let evm_env = EvmEnv { block_env: block, ..Default::default() };
514
515 let evm = evm_config.evm_with_env(db, evm_env.clone());
516
517 assert_eq!(evm.block, evm_env.block_env);
519
520 assert_eq!(evm.cfg.spec, SpecId::default());
522 }
523
524 #[test]
525 fn test_evm_with_spec_id() {
526 let evm_config = EthEvmConfig::mainnet();
527
528 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
529
530 let evm_env = EvmEnv {
531 cfg_env: CfgEnv::new().with_spec_and_mainnet_gas_params(SpecId::PETERSBURG),
532 ..Default::default()
533 };
534
535 let evm = evm_config.evm_with_env(db, evm_env);
536
537 assert_eq!(evm.cfg.spec, SpecId::PETERSBURG);
539 }
540
541 #[test]
542 fn test_evm_with_env_and_default_inspector() {
543 let evm_config = EthEvmConfig::mainnet();
544 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
545
546 let evm_env = EvmEnv::default();
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 }
554
555 #[test]
556 fn test_evm_with_env_inspector_and_custom_cfg() {
557 let evm_config = EthEvmConfig::mainnet();
558 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
559
560 let cfg_env = CfgEnv::default().with_chain_id(111);
561 let block = BlockEnv::default();
562 let evm_env = EvmEnv { cfg_env: cfg_env.clone(), block_env: block };
563
564 let evm = evm_config.evm_with_env_and_inspector(db, evm_env, NoOpInspector {});
565
566 assert_eq!(evm.cfg, cfg_env);
568 assert_eq!(evm.cfg.spec, SpecId::default());
569 }
570
571 #[test]
572 fn test_evm_with_env_inspector_and_custom_block_tx() {
573 let evm_config = EthEvmConfig::mainnet();
574 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
575
576 let block = BlockEnv {
578 basefee: 1000,
579 gas_limit: 10_000_000,
580 number: U256::from(42),
581 ..Default::default()
582 };
583 let evm_env = EvmEnv { block_env: block, ..Default::default() };
584
585 let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
586
587 assert_eq!(evm.block, evm_env.block_env);
589 assert_eq!(evm.cfg.spec, SpecId::default());
590 }
591
592 #[test]
593 fn test_evm_with_env_inspector_and_spec_id() {
594 let evm_config = EthEvmConfig::mainnet();
595 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
596
597 let evm_env = EvmEnv {
598 cfg_env: CfgEnv::new().with_spec_and_mainnet_gas_params(SpecId::PETERSBURG),
599 ..Default::default()
600 };
601
602 let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
603
604 assert_eq!(evm.block, evm_env.block_env);
606 assert_eq!(evm.cfg, evm_env.cfg_env);
607 assert_eq!(evm.tx, Default::default());
608 }
609
610 #[test]
611 fn test_reuses_prewarm_results_only_for_known_factories() {
612 assert!(EthEvmConfig::mainnet().reuses_prewarm_results());
613 assert!(EthEvmConfig::new_with_evm_factory(
614 MAINNET.clone(),
615 factory::RethEvmFactory::default()
616 )
617 .reuses_prewarm_results());
618
619 #[derive(Debug, Clone)]
620 struct CustomFactory(EthEvmFactory);
621
622 impl EvmFactory for CustomFactory {
623 type Evm<DB: Database, I: Inspector<Self::Context<DB>>> =
624 EthEvm<DB, I, Self::Precompiles>;
625 type Context<DB: Database> = EthEvmContext<DB>;
626 type Tx = TxEnv;
627 type Error<DBError: DBErrorMarker> = EVMError<DBError>;
628 type HaltReason = HaltReason;
629 type Spec = SpecId;
630 type BlockEnv = BlockEnv;
631 type Precompiles = PrecompilesMap;
632
633 fn create_evm<DB: Database>(
634 &self,
635 db: DB,
636 input: EvmEnv,
637 ) -> Self::Evm<DB, NoOpInspector> {
638 self.0.create_evm(db, input)
639 }
640
641 fn create_evm_with_inspector<DB: Database, I: Inspector<Self::Context<DB>>>(
642 &self,
643 db: DB,
644 input: EvmEnv,
645 inspector: I,
646 ) -> Self::Evm<DB, I> {
647 self.0.create_evm_with_inspector(db, input, inspector)
648 }
649 }
650
651 assert!(!EthEvmConfig::new_with_evm_factory(
652 MAINNET.clone(),
653 CustomFactory(EthEvmFactory::default())
654 )
655 .reuses_prewarm_results());
656 }
657
658 #[cfg(feature = "jit")]
659 #[test]
660 fn test_jit_support_downcast_updates_reth_factory() {
661 let evm_config = EthEvmConfig::new_with_evm_factory(
662 MAINNET.clone(),
663 factory::RethEvmFactory::disabled(),
664 );
665
666 assert!(evm_config.jit_backend().is_some());
667 assert!(!evm_config.executor_factory.evm_factory().jit_support_enabled());
668
669 let evm_config = evm_config.with_jit_support();
670 assert!(evm_config.executor_factory.evm_factory().jit_support_enabled());
671
672 let evm_config = evm_config.with_jit_support_enabled(false);
673 assert!(!evm_config.executor_factory.evm_factory().jit_support_enabled());
674 }
675
676 #[cfg(feature = "jit")]
677 #[test]
678 fn test_jit_support_downcast_ignores_plain_factory() {
679 let evm_config = EthEvmConfig::mainnet();
680
681 assert!(evm_config.jit_backend().is_none());
682
683 let evm_config = evm_config.with_jit_support();
684 assert!(evm_config.jit_backend().is_none());
685 }
686}