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