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 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, 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 },
179 self.chain_spec().next_block_base_fee(parent, attributes.timestamp).unwrap_or_default(),
180 self.chain_spec(),
181 self.chain_spec().chain().id(),
182 self.chain_spec().blob_params_at_timestamp(attributes.timestamp),
183 ))
184 }
185
186 fn context_for_block<'a>(
187 &self,
188 block: &'a SealedBlock<Block>,
189 ) -> Result<EthBlockExecutionCtx<'a>, Self::Error> {
190 Ok(EthBlockExecutionCtx {
191 parent_hash: block.header().parent_hash,
192 parent_beacon_block_root: block.header().parent_beacon_block_root,
193 ommers: &block.body().ommers,
194 withdrawals: block.body().withdrawals.as_ref().map(Cow::Borrowed),
195 })
196 }
197
198 fn context_for_next_block(
199 &self,
200 parent: &SealedHeader,
201 attributes: Self::NextBlockEnvCtx,
202 ) -> Result<EthBlockExecutionCtx<'_>, Self::Error> {
203 Ok(EthBlockExecutionCtx {
204 parent_hash: parent.hash(),
205 parent_beacon_block_root: attributes.parent_beacon_block_root,
206 ommers: &[],
207 withdrawals: attributes.withdrawals.map(Cow::Owned),
208 })
209 }
210}
211
212impl<ChainSpec, EvmF> ConfigureEngineEvm<ExecutionData> for EthEvmConfig<ChainSpec, EvmF>
213where
214 ChainSpec: EthExecutorSpec + EthChainSpec<Header = Header> + Hardforks + 'static,
215 EvmF: EvmFactory<
216 Tx: TransactionEnv
217 + FromRecoveredTx<TransactionSigned>
218 + FromTxWithEncoded<TransactionSigned>,
219 Spec = SpecId,
220 Precompiles = PrecompilesMap,
221 > + Clone
222 + Debug
223 + Send
224 + Sync
225 + Unpin
226 + 'static,
227{
228 fn evm_env_for_payload(&self, payload: &ExecutionData) -> Result<EvmEnvFor<Self>, Self::Error> {
229 let timestamp = payload.payload.timestamp();
230 let block_number = payload.payload.block_number();
231
232 let blob_params = self.chain_spec().blob_params_at_timestamp(timestamp);
233 let spec =
234 revm_spec_by_timestamp_and_block_number(self.chain_spec(), timestamp, block_number);
235
236 let mut cfg_env =
238 CfgEnv::new().with_chain_id(self.chain_spec().chain().id()).with_spec(spec);
239
240 if let Some(blob_params) = &blob_params {
241 cfg_env.set_max_blobs_per_tx(blob_params.max_blobs_per_tx);
242 }
243
244 if self.chain_spec().is_osaka_active_at_timestamp(timestamp) {
245 cfg_env.tx_gas_limit_cap = Some(MAX_TX_GAS_LIMIT_OSAKA);
246 }
247
248 let blob_excess_gas_and_price =
251 payload.payload.excess_blob_gas().zip(blob_params).map(|(excess_blob_gas, params)| {
252 let blob_gasprice = params.calc_blob_fee(excess_blob_gas);
253 BlobExcessGasAndPrice { excess_blob_gas, blob_gasprice }
254 });
255
256 let block_env = BlockEnv {
257 number: U256::from(block_number),
258 beneficiary: payload.payload.fee_recipient(),
259 timestamp: U256::from(timestamp),
260 difficulty: if spec >= SpecId::MERGE {
261 U256::ZERO
262 } else {
263 payload.payload.as_v1().prev_randao.into()
264 },
265 prevrandao: (spec >= SpecId::MERGE).then(|| payload.payload.as_v1().prev_randao),
266 gas_limit: payload.payload.gas_limit(),
267 basefee: payload.payload.saturated_base_fee_per_gas(),
268 blob_excess_gas_and_price,
269 };
270
271 Ok(EvmEnv { cfg_env, block_env })
272 }
273
274 fn context_for_payload<'a>(
275 &self,
276 payload: &'a ExecutionData,
277 ) -> Result<ExecutionCtxFor<'a, Self>, Self::Error> {
278 Ok(EthBlockExecutionCtx {
279 parent_hash: payload.parent_hash(),
280 parent_beacon_block_root: payload.sidecar.parent_beacon_block_root(),
281 ommers: &[],
282 withdrawals: payload.payload.withdrawals().map(|w| Cow::Owned(w.clone().into())),
283 })
284 }
285
286 fn tx_iterator_for_payload(
287 &self,
288 payload: &ExecutionData,
289 ) -> Result<impl ExecutableTxIterator<Self>, Self::Error> {
290 Ok(payload.payload.transactions().clone().into_iter().map(|tx| {
291 let tx =
292 TxTy::<Self::Primitives>::decode_2718_exact(tx.as_ref()).map_err(AnyError::new)?;
293 let signer = tx.try_recover().map_err(AnyError::new)?;
294 Ok::<_, AnyError>(tx.with_signer(signer))
295 }))
296 }
297}
298
299#[cfg(test)]
300mod tests {
301 use super::*;
302 use alloy_consensus::Header;
303 use alloy_genesis::Genesis;
304 use reth_chainspec::{Chain, ChainSpec};
305 use reth_evm::{execute::ProviderError, EvmEnv};
306 use revm::{
307 context::{BlockEnv, CfgEnv},
308 database::CacheDB,
309 database_interface::EmptyDBTyped,
310 inspector::NoOpInspector,
311 };
312
313 #[test]
314 fn test_fill_cfg_and_block_env() {
315 let header = Header::default();
317
318 let chain_spec = ChainSpec::builder()
321 .chain(Chain::mainnet())
322 .genesis(Genesis::default())
323 .london_activated()
324 .paris_activated()
325 .shanghai_activated()
326 .build();
327
328 let EvmEnv { cfg_env, .. } =
331 EthEvmConfig::new(Arc::new(chain_spec.clone())).evm_env(&header).unwrap();
332
333 assert_eq!(cfg_env.chain_id, chain_spec.chain().id());
336 }
337
338 #[test]
339 fn test_evm_with_env_default_spec() {
340 let evm_config = EthEvmConfig::mainnet();
341
342 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
343
344 let evm_env = EvmEnv::default();
345
346 let evm = evm_config.evm_with_env(db, evm_env.clone());
347
348 assert_eq!(evm.block, evm_env.block_env);
350 assert_eq!(evm.cfg, evm_env.cfg_env);
351 }
352
353 #[test]
354 fn test_evm_with_env_custom_cfg() {
355 let evm_config = EthEvmConfig::mainnet();
356
357 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
358
359 let cfg = CfgEnv::default().with_chain_id(111);
361
362 let evm_env = EvmEnv { cfg_env: cfg.clone(), ..Default::default() };
363
364 let evm = evm_config.evm_with_env(db, evm_env);
365
366 assert_eq!(evm.cfg, cfg);
368 }
369
370 #[test]
371 fn test_evm_with_env_custom_block_and_tx() {
372 let evm_config = EthEvmConfig::mainnet();
373
374 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
375
376 let block = BlockEnv {
378 basefee: 1000,
379 gas_limit: 10_000_000,
380 number: U256::from(42),
381 ..Default::default()
382 };
383
384 let evm_env = EvmEnv { block_env: block, ..Default::default() };
385
386 let evm = evm_config.evm_with_env(db, evm_env.clone());
387
388 assert_eq!(evm.block, evm_env.block_env);
390
391 assert_eq!(evm.cfg.spec, SpecId::default());
393 }
394
395 #[test]
396 fn test_evm_with_spec_id() {
397 let evm_config = EthEvmConfig::mainnet();
398
399 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
400
401 let evm_env = EvmEnv {
402 cfg_env: CfgEnv::new().with_spec(SpecId::CONSTANTINOPLE),
403 ..Default::default()
404 };
405
406 let evm = evm_config.evm_with_env(db, evm_env);
407
408 assert_eq!(evm.cfg.spec, SpecId::CONSTANTINOPLE);
410 }
411
412 #[test]
413 fn test_evm_with_env_and_default_inspector() {
414 let evm_config = EthEvmConfig::mainnet();
415 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
416
417 let evm_env = EvmEnv::default();
418
419 let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
420
421 assert_eq!(evm.block, evm_env.block_env);
423 assert_eq!(evm.cfg, evm_env.cfg_env);
424 }
425
426 #[test]
427 fn test_evm_with_env_inspector_and_custom_cfg() {
428 let evm_config = EthEvmConfig::mainnet();
429 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
430
431 let cfg_env = CfgEnv::default().with_chain_id(111);
432 let block = BlockEnv::default();
433 let evm_env = EvmEnv { cfg_env: cfg_env.clone(), block_env: block };
434
435 let evm = evm_config.evm_with_env_and_inspector(db, evm_env, NoOpInspector {});
436
437 assert_eq!(evm.cfg, cfg_env);
439 assert_eq!(evm.cfg.spec, SpecId::default());
440 }
441
442 #[test]
443 fn test_evm_with_env_inspector_and_custom_block_tx() {
444 let evm_config = EthEvmConfig::mainnet();
445 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
446
447 let block = BlockEnv {
449 basefee: 1000,
450 gas_limit: 10_000_000,
451 number: U256::from(42),
452 ..Default::default()
453 };
454 let evm_env = EvmEnv { block_env: block, ..Default::default() };
455
456 let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
457
458 assert_eq!(evm.block, evm_env.block_env);
460 assert_eq!(evm.cfg.spec, SpecId::default());
461 }
462
463 #[test]
464 fn test_evm_with_env_inspector_and_spec_id() {
465 let evm_config = EthEvmConfig::mainnet();
466 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
467
468 let evm_env = EvmEnv {
469 cfg_env: CfgEnv::new().with_spec(SpecId::CONSTANTINOPLE),
470 ..Default::default()
471 };
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, evm_env.cfg_env);
478 assert_eq!(evm.tx, Default::default());
479 }
480}