1#![doc(
4 html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png",
5 html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256",
6 issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
7)]
8#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
9#![cfg_attr(not(feature = "std"), no_std)]
10#![cfg_attr(not(test), warn(unused_crate_dependencies))]
11
12extern crate alloc;
13
14use alloc::sync::Arc;
15use alloy_consensus::{BlockHeader, Header};
16use alloy_evm::{FromRecoveredTx, FromTxWithEncoded};
17use alloy_op_evm::{block::receipt_builder::OpReceiptBuilder, OpBlockExecutionCtx};
18use alloy_primitives::U256;
19use core::fmt::Debug;
20use op_alloy_consensus::EIP1559ParamError;
21use op_revm::{OpSpecId, OpTransaction};
22use reth_chainspec::EthChainSpec;
23use reth_evm::{ConfigureEvm, EvmEnv};
24use reth_optimism_chainspec::OpChainSpec;
25use reth_optimism_consensus::next_block_base_fee;
26use reth_optimism_forks::OpHardforks;
27use reth_optimism_primitives::{DepositReceipt, OpPrimitives};
28use reth_primitives_traits::{NodePrimitives, SealedBlock, SealedHeader, SignedTransaction};
29use revm::{
30 context::{BlockEnv, CfgEnv, TxEnv},
31 context_interface::block::BlobExcessGasAndPrice,
32 primitives::hardfork::SpecId,
33};
34
35mod config;
36pub use config::{revm_spec, revm_spec_by_timestamp_after_bedrock, OpNextBlockEnvAttributes};
37mod execute;
38pub use execute::*;
39pub mod l1;
40pub use l1::*;
41mod receipts;
42pub use receipts::*;
43mod build;
44pub use build::OpBlockAssembler;
45
46mod error;
47pub use error::OpBlockExecutionError;
48
49pub use alloy_op_evm::{OpBlockExecutorFactory, OpEvm, OpEvmFactory};
50
51#[derive(Debug)]
53pub struct OpEvmConfig<
54 ChainSpec = OpChainSpec,
55 N: NodePrimitives = OpPrimitives,
56 R = OpRethReceiptBuilder,
57> {
58 pub executor_factory: OpBlockExecutorFactory<R, Arc<ChainSpec>>,
60 pub block_assembler: OpBlockAssembler<ChainSpec>,
62 _pd: core::marker::PhantomData<N>,
63}
64
65impl<ChainSpec, N: NodePrimitives, R: Clone> Clone for OpEvmConfig<ChainSpec, N, R> {
66 fn clone(&self) -> Self {
67 Self {
68 executor_factory: self.executor_factory.clone(),
69 block_assembler: self.block_assembler.clone(),
70 _pd: self._pd,
71 }
72 }
73}
74
75impl<ChainSpec> OpEvmConfig<ChainSpec> {
76 pub fn optimism(chain_spec: Arc<ChainSpec>) -> Self {
78 Self::new(chain_spec, OpRethReceiptBuilder::default())
79 }
80}
81
82impl<ChainSpec, N: NodePrimitives, R> OpEvmConfig<ChainSpec, N, R> {
83 pub fn new(chain_spec: Arc<ChainSpec>, receipt_builder: R) -> Self {
85 Self {
86 block_assembler: OpBlockAssembler::new(chain_spec.clone()),
87 executor_factory: OpBlockExecutorFactory::new(
88 receipt_builder,
89 chain_spec,
90 OpEvmFactory::default(),
91 ),
92 _pd: core::marker::PhantomData,
93 }
94 }
95
96 pub const fn chain_spec(&self) -> &Arc<ChainSpec> {
98 self.executor_factory.spec()
99 }
100}
101
102impl<ChainSpec, N, R> ConfigureEvm for OpEvmConfig<ChainSpec, N, R>
103where
104 ChainSpec: EthChainSpec + OpHardforks,
105 N: NodePrimitives<
106 Receipt = R::Receipt,
107 SignedTx = R::Transaction,
108 BlockHeader = Header,
109 BlockBody = alloy_consensus::BlockBody<R::Transaction>,
110 Block = alloy_consensus::Block<R::Transaction>,
111 >,
112 OpTransaction<TxEnv>: FromRecoveredTx<N::SignedTx> + FromTxWithEncoded<N::SignedTx>,
113 R: OpReceiptBuilder<Receipt: DepositReceipt, Transaction: SignedTransaction>,
114 Self: Send + Sync + Unpin + Clone + 'static,
115{
116 type Primitives = N;
117 type Error = EIP1559ParamError;
118 type NextBlockEnvCtx = OpNextBlockEnvAttributes;
119 type BlockExecutorFactory = OpBlockExecutorFactory<R, Arc<ChainSpec>>;
120 type BlockAssembler = OpBlockAssembler<ChainSpec>;
121
122 fn block_executor_factory(&self) -> &Self::BlockExecutorFactory {
123 &self.executor_factory
124 }
125
126 fn block_assembler(&self) -> &Self::BlockAssembler {
127 &self.block_assembler
128 }
129
130 fn evm_env(&self, header: &Header) -> EvmEnv<OpSpecId> {
131 let spec = config::revm_spec(self.chain_spec(), header);
132
133 let cfg_env = CfgEnv::new().with_chain_id(self.chain_spec().chain().id()).with_spec(spec);
134
135 let block_env = BlockEnv {
136 number: header.number(),
137 beneficiary: header.beneficiary(),
138 timestamp: header.timestamp(),
139 difficulty: if spec.into_eth_spec() >= SpecId::MERGE {
140 U256::ZERO
141 } else {
142 header.difficulty()
143 },
144 prevrandao: if spec.into_eth_spec() >= SpecId::MERGE {
145 header.mix_hash()
146 } else {
147 None
148 },
149 gas_limit: header.gas_limit(),
150 basefee: header.base_fee_per_gas().unwrap_or_default(),
151 blob_excess_gas_and_price: header.excess_blob_gas().map(|excess_blob_gas| {
153 BlobExcessGasAndPrice::new(excess_blob_gas, spec.into_eth_spec() >= SpecId::PRAGUE)
154 }),
155 };
156
157 EvmEnv { cfg_env, block_env }
158 }
159
160 fn next_evm_env(
161 &self,
162 parent: &Header,
163 attributes: &Self::NextBlockEnvCtx,
164 ) -> Result<EvmEnv<OpSpecId>, Self::Error> {
165 let spec_id = revm_spec_by_timestamp_after_bedrock(self.chain_spec(), attributes.timestamp);
167
168 let cfg_env =
170 CfgEnv::new().with_chain_id(self.chain_spec().chain().id()).with_spec(spec_id);
171
172 let blob_excess_gas_and_price = parent
175 .maybe_next_block_excess_blob_gas(
176 self.chain_spec().blob_params_at_timestamp(attributes.timestamp),
177 )
178 .or_else(|| (spec_id.into_eth_spec().is_enabled_in(SpecId::CANCUN)).then_some(0))
179 .map(|gas| BlobExcessGasAndPrice::new(gas, false));
180
181 let block_env = BlockEnv {
182 number: parent.number() + 1,
183 beneficiary: attributes.suggested_fee_recipient,
184 timestamp: attributes.timestamp,
185 difficulty: U256::ZERO,
186 prevrandao: Some(attributes.prev_randao),
187 gas_limit: attributes.gas_limit,
188 basefee: next_block_base_fee(self.chain_spec(), parent, attributes.timestamp)?,
190 blob_excess_gas_and_price,
192 };
193
194 Ok(EvmEnv { cfg_env, block_env })
195 }
196
197 fn context_for_block(&self, block: &'_ SealedBlock<N::Block>) -> OpBlockExecutionCtx {
198 OpBlockExecutionCtx {
199 parent_hash: block.header().parent_hash(),
200 parent_beacon_block_root: block.header().parent_beacon_block_root(),
201 extra_data: block.header().extra_data().clone(),
202 }
203 }
204
205 fn context_for_next_block(
206 &self,
207 parent: &SealedHeader<N::BlockHeader>,
208 attributes: Self::NextBlockEnvCtx,
209 ) -> OpBlockExecutionCtx {
210 OpBlockExecutionCtx {
211 parent_hash: parent.hash(),
212 parent_beacon_block_root: attributes.parent_beacon_block_root,
213 extra_data: attributes.extra_data,
214 }
215 }
216}
217#[cfg(test)]
218mod tests {
219 use super::*;
220 use alloy_consensus::{Header, Receipt};
221 use alloy_eips::eip7685::Requests;
222 use alloy_genesis::Genesis;
223 use alloy_primitives::{bytes, map::HashMap, Address, LogData, B256};
224 use op_revm::OpSpecId;
225 use reth_chainspec::ChainSpec;
226 use reth_evm::execute::ProviderError;
227 use reth_execution_types::{
228 AccountRevertInit, BundleStateInit, Chain, ExecutionOutcome, RevertsInit,
229 };
230 use reth_optimism_chainspec::BASE_MAINNET;
231 use reth_optimism_primitives::{OpBlock, OpPrimitives, OpReceipt};
232 use reth_primitives_traits::{Account, RecoveredBlock};
233 use revm::{
234 database::{BundleState, CacheDB},
235 database_interface::EmptyDBTyped,
236 inspector::NoOpInspector,
237 primitives::Log,
238 state::AccountInfo,
239 };
240 use std::sync::Arc;
241
242 fn test_evm_config() -> OpEvmConfig {
243 OpEvmConfig::optimism(BASE_MAINNET.clone())
244 }
245
246 #[test]
247 fn test_fill_cfg_and_block_env() {
248 let header = Header::default();
250
251 let chain_spec = ChainSpec::builder()
254 .chain(0.into())
255 .genesis(Genesis::default())
256 .london_activated()
257 .paris_activated()
258 .shanghai_activated()
259 .build();
260
261 let EvmEnv { cfg_env, .. } =
264 OpEvmConfig::optimism(Arc::new(OpChainSpec { inner: chain_spec.clone() }))
265 .evm_env(&header);
266
267 assert_eq!(cfg_env.chain_id, chain_spec.chain().id());
270 }
271
272 #[test]
273 fn test_evm_with_env_default_spec() {
274 let evm_config = test_evm_config();
275
276 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
277
278 let evm_env = EvmEnv::default();
279
280 let evm = evm_config.evm_with_env(db, evm_env.clone());
281
282 assert_eq!(evm.cfg, evm_env.cfg_env);
284 }
285
286 #[test]
287 fn test_evm_with_env_custom_cfg() {
288 let evm_config = test_evm_config();
289
290 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
291
292 let cfg = CfgEnv::new().with_chain_id(111).with_spec(OpSpecId::default());
294
295 let evm_env = EvmEnv { cfg_env: cfg.clone(), ..Default::default() };
296
297 let evm = evm_config.evm_with_env(db, evm_env);
298
299 assert_eq!(evm.cfg, cfg);
301 }
302
303 #[test]
304 fn test_evm_with_env_custom_block_and_tx() {
305 let evm_config = test_evm_config();
306
307 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
308
309 let block =
311 BlockEnv { basefee: 1000, gas_limit: 10_000_000, number: 42, ..Default::default() };
312
313 let evm_env = EvmEnv { block_env: block, ..Default::default() };
314
315 let evm = evm_config.evm_with_env(db, evm_env.clone());
316
317 assert_eq!(evm.block, evm_env.block_env);
319 }
320
321 #[test]
322 fn test_evm_with_spec_id() {
323 let evm_config = test_evm_config();
324
325 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
326
327 let evm_env =
328 EvmEnv { cfg_env: CfgEnv::new().with_spec(OpSpecId::ECOTONE), ..Default::default() };
329
330 let evm = evm_config.evm_with_env(db, evm_env.clone());
331
332 assert_eq!(evm.cfg, evm_env.cfg_env);
333 }
334
335 #[test]
336 fn test_evm_with_env_and_default_inspector() {
337 let evm_config = test_evm_config();
338 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
339
340 let evm_env = EvmEnv { cfg_env: Default::default(), ..Default::default() };
341
342 let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
343
344 assert_eq!(evm.block, evm_env.block_env);
346 assert_eq!(evm.cfg, evm_env.cfg_env);
347 }
348
349 #[test]
350 fn test_evm_with_env_inspector_and_custom_cfg() {
351 let evm_config = test_evm_config();
352 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
353
354 let cfg = CfgEnv::new().with_chain_id(111).with_spec(OpSpecId::default());
355 let block = BlockEnv::default();
356 let evm_env = EvmEnv { block_env: block, cfg_env: cfg.clone() };
357
358 let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
359
360 assert_eq!(evm.cfg, cfg);
362 assert_eq!(evm.block, evm_env.block_env);
363 }
364
365 #[test]
366 fn test_evm_with_env_inspector_and_custom_block_tx() {
367 let evm_config = test_evm_config();
368 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
369
370 let block =
372 BlockEnv { basefee: 1000, gas_limit: 10_000_000, number: 42, ..Default::default() };
373 let evm_env = EvmEnv { block_env: block, ..Default::default() };
374
375 let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
376
377 assert_eq!(evm.block, evm_env.block_env);
379 }
380
381 #[test]
382 fn test_evm_with_env_inspector_and_spec_id() {
383 let evm_config = test_evm_config();
384 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
385
386 let evm_env =
387 EvmEnv { cfg_env: CfgEnv::new().with_spec(OpSpecId::ECOTONE), ..Default::default() };
388
389 let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
390
391 assert_eq!(evm.cfg, evm_env.cfg_env);
393 assert_eq!(evm.block, evm_env.block_env);
394 }
395
396 #[test]
397 fn receipts_by_block_hash() {
398 let block: RecoveredBlock<OpBlock> = Default::default();
400
401 let block1_hash = B256::new([0x01; 32]);
403 let block2_hash = B256::new([0x02; 32]);
404
405 let mut block1 = block.clone();
407 let mut block2 = block;
408
409 block1.set_block_number(10);
411 block1.set_hash(block1_hash);
412
413 block2.set_block_number(11);
414 block2.set_hash(block2_hash);
415
416 let receipt1 = OpReceipt::Legacy(Receipt {
418 cumulative_gas_used: 46913,
419 logs: vec![],
420 status: true.into(),
421 });
422
423 let receipt2 = OpReceipt::Legacy(Receipt {
425 cumulative_gas_used: 1325345,
426 logs: vec![],
427 status: true.into(),
428 });
429
430 let receipts = vec![vec![receipt1.clone()], vec![receipt2]];
432
433 let execution_outcome = ExecutionOutcome::<OpReceipt> {
436 bundle: Default::default(),
437 receipts,
438 requests: vec![],
439 first_block: 10,
440 };
441
442 let chain: Chain<OpPrimitives> =
445 Chain::new([block1, block2], execution_outcome.clone(), None);
446
447 assert_eq!(chain.receipts_by_block_hash(block1_hash), Some(vec![&receipt1]));
449
450 let execution_outcome1 = ExecutionOutcome {
452 bundle: Default::default(),
453 receipts: vec![vec![receipt1]],
454 requests: vec![],
455 first_block: 10,
456 };
457
458 assert_eq!(chain.execution_outcome_at_block(10), Some(execution_outcome1));
460
461 assert_eq!(chain.execution_outcome_at_block(11), Some(execution_outcome));
463 }
464
465 #[test]
466 fn test_initialisation() {
467 let bundle = BundleState::new(
469 vec![(Address::new([2; 20]), None, Some(AccountInfo::default()), HashMap::default())],
470 vec![vec![(Address::new([2; 20]), None, vec![])]],
471 vec![],
472 );
473
474 let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt {
476 cumulative_gas_used: 46913,
477 logs: vec![],
478 status: true.into(),
479 }))]];
480
481 let requests = vec![Requests::new(vec![bytes!("dead"), bytes!("beef"), bytes!("beebee")])];
483
484 let first_block = 123;
486
487 let exec_res = ExecutionOutcome {
490 bundle: bundle.clone(),
491 receipts: receipts.clone(),
492 requests: requests.clone(),
493 first_block,
494 };
495
496 assert_eq!(
498 ExecutionOutcome::new(bundle, receipts.clone(), first_block, requests.clone()),
499 exec_res
500 );
501
502 let mut state_init: BundleStateInit = HashMap::default();
504 state_init
505 .insert(Address::new([2; 20]), (None, Some(Account::default()), HashMap::default()));
506
507 let mut revert_inner: HashMap<Address, AccountRevertInit> = HashMap::default();
509 revert_inner.insert(Address::new([2; 20]), (None, vec![]));
510
511 let mut revert_init: RevertsInit = HashMap::default();
513 revert_init.insert(123, revert_inner);
514
515 assert_eq!(
518 ExecutionOutcome::new_init(
519 state_init,
520 revert_init,
521 vec![],
522 receipts,
523 first_block,
524 requests,
525 ),
526 exec_res
527 );
528 }
529
530 #[test]
531 fn test_block_number_to_index() {
532 let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt {
534 cumulative_gas_used: 46913,
535 logs: vec![],
536 status: true.into(),
537 }))]];
538
539 let first_block = 123;
541
542 let exec_res = ExecutionOutcome {
545 bundle: Default::default(),
546 receipts,
547 requests: vec![],
548 first_block,
549 };
550
551 assert_eq!(exec_res.block_number_to_index(12), None);
553
554 assert_eq!(exec_res.block_number_to_index(133), None);
556
557 assert_eq!(exec_res.block_number_to_index(123), Some(0));
559 }
560
561 #[test]
562 fn test_get_logs() {
563 let receipts = vec![vec![OpReceipt::Legacy(Receipt {
565 cumulative_gas_used: 46913,
566 logs: vec![Log::<LogData>::default()],
567 status: true.into(),
568 })]];
569
570 let first_block = 123;
572
573 let exec_res = ExecutionOutcome {
576 bundle: Default::default(),
577 receipts,
578 requests: vec![],
579 first_block,
580 };
581
582 let logs: Vec<&Log> = exec_res.logs(123).unwrap().collect();
584
585 assert_eq!(logs, vec![&Log::<LogData>::default()]);
587 }
588
589 #[test]
590 fn test_receipts_by_block() {
591 let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt {
593 cumulative_gas_used: 46913,
594 logs: vec![Log::<LogData>::default()],
595 status: true.into(),
596 }))]];
597
598 let first_block = 123;
600
601 let exec_res = ExecutionOutcome {
604 bundle: Default::default(), receipts, requests: vec![], first_block, };
609
610 let receipts_by_block: Vec<_> = exec_res.receipts_by_block(123).iter().collect();
612
613 assert_eq!(
615 receipts_by_block,
616 vec![&Some(OpReceipt::Legacy(Receipt {
617 cumulative_gas_used: 46913,
618 logs: vec![Log::<LogData>::default()],
619 status: true.into(),
620 }))]
621 );
622 }
623
624 #[test]
625 fn test_receipts_len() {
626 let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt {
628 cumulative_gas_used: 46913,
629 logs: vec![Log::<LogData>::default()],
630 status: true.into(),
631 }))]];
632
633 let receipts_empty = vec![];
635
636 let first_block = 123;
638
639 let exec_res = ExecutionOutcome {
642 bundle: Default::default(), receipts, requests: vec![], first_block, };
647
648 assert_eq!(exec_res.len(), 1);
650
651 assert!(!exec_res.is_empty());
653
654 let exec_res_empty_receipts: ExecutionOutcome<OpReceipt> = ExecutionOutcome {
656 bundle: Default::default(), receipts: receipts_empty, requests: vec![], first_block, };
661
662 assert_eq!(exec_res_empty_receipts.len(), 0);
664
665 assert!(exec_res_empty_receipts.is_empty());
667 }
668
669 #[test]
670 fn test_revert_to() {
671 let receipt = OpReceipt::Legacy(Receipt {
673 cumulative_gas_used: 46913,
674 logs: vec![],
675 status: true.into(),
676 });
677
678 let receipts = vec![vec![Some(receipt.clone())], vec![Some(receipt.clone())]];
680
681 let first_block = 123;
683
684 let request = bytes!("deadbeef");
686
687 let requests =
689 vec![Requests::new(vec![request.clone()]), Requests::new(vec![request.clone()])];
690
691 let mut exec_res =
694 ExecutionOutcome { bundle: Default::default(), receipts, requests, first_block };
695
696 assert!(exec_res.revert_to(123));
698
699 assert_eq!(exec_res.receipts, vec![vec![Some(receipt)]]);
701
702 assert_eq!(exec_res.requests, vec![Requests::new(vec![request])]);
704
705 assert!(!exec_res.revert_to(133));
708
709 assert!(!exec_res.revert_to(10));
712 }
713
714 #[test]
715 fn test_extend_execution_outcome() {
716 let receipt = OpReceipt::Legacy(Receipt {
718 cumulative_gas_used: 46913,
719 logs: vec![],
720 status: true.into(),
721 });
722
723 let receipts = vec![vec![Some(receipt.clone())]];
725
726 let request = bytes!("deadbeef");
728
729 let requests = vec![Requests::new(vec![request.clone()])];
731
732 let first_block = 123;
734
735 let mut exec_res =
737 ExecutionOutcome { bundle: Default::default(), receipts, requests, first_block };
738
739 exec_res.extend(exec_res.clone());
741
742 assert_eq!(
744 exec_res,
745 ExecutionOutcome {
746 bundle: Default::default(),
747 receipts: vec![vec![Some(receipt.clone())], vec![Some(receipt)]],
748 requests: vec![Requests::new(vec![request.clone()]), Requests::new(vec![request])],
749 first_block: 123,
750 }
751 );
752 }
753
754 #[test]
755 fn test_split_at_execution_outcome() {
756 let receipt = OpReceipt::Legacy(Receipt {
758 cumulative_gas_used: 46913,
759 logs: vec![],
760 status: true.into(),
761 });
762
763 let receipts = vec![
765 vec![Some(receipt.clone())],
766 vec![Some(receipt.clone())],
767 vec![Some(receipt.clone())],
768 ];
769
770 let first_block = 123;
772
773 let request = bytes!("deadbeef");
775
776 let requests = vec![
778 Requests::new(vec![request.clone()]),
779 Requests::new(vec![request.clone()]),
780 Requests::new(vec![request.clone()]),
781 ];
782
783 let exec_res =
786 ExecutionOutcome { bundle: Default::default(), receipts, requests, first_block };
787
788 let result = exec_res.clone().split_at(124);
790
791 let lower_execution_outcome = ExecutionOutcome {
793 bundle: Default::default(),
794 receipts: vec![vec![Some(receipt.clone())]],
795 requests: vec![Requests::new(vec![request.clone()])],
796 first_block,
797 };
798
799 let higher_execution_outcome = ExecutionOutcome {
801 bundle: Default::default(),
802 receipts: vec![vec![Some(receipt.clone())], vec![Some(receipt)]],
803 requests: vec![Requests::new(vec![request.clone()]), Requests::new(vec![request])],
804 first_block: 124,
805 };
806
807 assert_eq!(result.0, Some(lower_execution_outcome));
809 assert_eq!(result.1, higher_execution_outcome);
810
811 assert_eq!(exec_res.clone().split_at(123), (None, exec_res));
813 }
814}