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