reth_optimism_evm/
lib.rs

1//! EVM config for vanilla optimism.
2
3#![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))]
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_eips::Decodable2718;
17use alloy_evm::{EvmFactory, FromRecoveredTx, FromTxWithEncoded};
18use alloy_op_evm::block::{receipt_builder::OpReceiptBuilder, OpTxEnv};
19use alloy_primitives::U256;
20use core::fmt::Debug;
21use op_alloy_consensus::EIP1559ParamError;
22use op_alloy_rpc_types_engine::OpExecutionData;
23use op_revm::{OpSpecId, OpTransaction};
24use reth_chainspec::EthChainSpec;
25use reth_evm::{
26    eth::NextEvmEnvAttributes, precompiles::PrecompilesMap, ConfigureEngineEvm, ConfigureEvm,
27    EvmEnv, EvmEnvFor, ExecutableTxIterator, ExecutionCtxFor, TransactionEnv,
28};
29use reth_optimism_chainspec::OpChainSpec;
30use reth_optimism_forks::OpHardforks;
31use reth_optimism_primitives::{DepositReceipt, OpPrimitives};
32use reth_primitives_traits::{
33    NodePrimitives, SealedBlock, SealedHeader, SignedTransaction, TxTy, WithEncoded,
34};
35use reth_storage_errors::any::AnyError;
36use revm::{
37    context::{BlockEnv, CfgEnv, TxEnv},
38    context_interface::block::BlobExcessGasAndPrice,
39    primitives::hardfork::SpecId,
40};
41
42mod config;
43pub use config::{revm_spec, revm_spec_by_timestamp_after_bedrock, OpNextBlockEnvAttributes};
44mod execute;
45pub use execute::*;
46pub mod l1;
47pub use l1::*;
48mod receipts;
49pub use receipts::*;
50mod build;
51pub use build::OpBlockAssembler;
52
53mod error;
54pub use error::OpBlockExecutionError;
55
56pub use alloy_op_evm::{OpBlockExecutionCtx, OpBlockExecutorFactory, OpEvm, OpEvmFactory};
57
58/// Optimism-related EVM configuration.
59#[derive(Debug)]
60pub struct OpEvmConfig<
61    ChainSpec = OpChainSpec,
62    N: NodePrimitives = OpPrimitives,
63    R = OpRethReceiptBuilder,
64    EvmFactory = OpEvmFactory,
65> {
66    /// Inner [`OpBlockExecutorFactory`].
67    pub executor_factory: OpBlockExecutorFactory<R, Arc<ChainSpec>, EvmFactory>,
68    /// Optimism block assembler.
69    pub block_assembler: OpBlockAssembler<ChainSpec>,
70    #[doc(hidden)]
71    pub _pd: core::marker::PhantomData<N>,
72}
73
74impl<ChainSpec, N: NodePrimitives, R: Clone, EvmFactory: Clone> Clone
75    for OpEvmConfig<ChainSpec, N, R, EvmFactory>
76{
77    fn clone(&self) -> Self {
78        Self {
79            executor_factory: self.executor_factory.clone(),
80            block_assembler: self.block_assembler.clone(),
81            _pd: self._pd,
82        }
83    }
84}
85
86impl<ChainSpec: OpHardforks> OpEvmConfig<ChainSpec> {
87    /// Creates a new [`OpEvmConfig`] with the given chain spec for OP chains.
88    pub fn optimism(chain_spec: Arc<ChainSpec>) -> Self {
89        Self::new(chain_spec, OpRethReceiptBuilder::default())
90    }
91}
92
93impl<ChainSpec: OpHardforks, N: NodePrimitives, R> OpEvmConfig<ChainSpec, N, R> {
94    /// Creates a new [`OpEvmConfig`] with the given chain spec.
95    pub fn new(chain_spec: Arc<ChainSpec>, receipt_builder: R) -> Self {
96        Self {
97            block_assembler: OpBlockAssembler::new(chain_spec.clone()),
98            executor_factory: OpBlockExecutorFactory::new(
99                receipt_builder,
100                chain_spec,
101                OpEvmFactory::default(),
102            ),
103            _pd: core::marker::PhantomData,
104        }
105    }
106}
107
108impl<ChainSpec, N, R, EvmFactory> OpEvmConfig<ChainSpec, N, R, EvmFactory>
109where
110    ChainSpec: OpHardforks,
111    N: NodePrimitives,
112{
113    /// Returns the chain spec associated with this configuration.
114    pub const fn chain_spec(&self) -> &Arc<ChainSpec> {
115        self.executor_factory.spec()
116    }
117}
118
119impl<ChainSpec, N, R, EvmF> ConfigureEvm for OpEvmConfig<ChainSpec, N, R, EvmF>
120where
121    ChainSpec: EthChainSpec<Header = Header> + OpHardforks,
122    N: NodePrimitives<
123        Receipt = R::Receipt,
124        SignedTx = R::Transaction,
125        BlockHeader = Header,
126        BlockBody = alloy_consensus::BlockBody<R::Transaction>,
127        Block = alloy_consensus::Block<R::Transaction>,
128    >,
129    OpTransaction<TxEnv>: FromRecoveredTx<N::SignedTx> + FromTxWithEncoded<N::SignedTx>,
130    R: OpReceiptBuilder<Receipt: DepositReceipt, Transaction: SignedTransaction>,
131    EvmF: EvmFactory<
132            Tx: FromRecoveredTx<R::Transaction>
133                    + FromTxWithEncoded<R::Transaction>
134                    + TransactionEnv
135                    + OpTxEnv,
136            Precompiles = PrecompilesMap,
137            Spec = OpSpecId,
138            BlockEnv = BlockEnv,
139        > + Debug,
140    Self: Send + Sync + Unpin + Clone + 'static,
141{
142    type Primitives = N;
143    type Error = EIP1559ParamError;
144    type NextBlockEnvCtx = OpNextBlockEnvAttributes;
145    type BlockExecutorFactory = OpBlockExecutorFactory<R, Arc<ChainSpec>, EvmF>;
146    type BlockAssembler = OpBlockAssembler<ChainSpec>;
147
148    fn block_executor_factory(&self) -> &Self::BlockExecutorFactory {
149        &self.executor_factory
150    }
151
152    fn block_assembler(&self) -> &Self::BlockAssembler {
153        &self.block_assembler
154    }
155
156    fn evm_env(&self, header: &Header) -> Result<EvmEnv<OpSpecId>, Self::Error> {
157        Ok(EvmEnv::for_op_block(header, self.chain_spec(), self.chain_spec().chain().id()))
158    }
159
160    fn next_evm_env(
161        &self,
162        parent: &Header,
163        attributes: &Self::NextBlockEnvCtx,
164    ) -> Result<EvmEnv<OpSpecId>, Self::Error> {
165        Ok(EvmEnv::for_op_next_block(
166            parent,
167            NextEvmEnvAttributes {
168                timestamp: attributes.timestamp,
169                suggested_fee_recipient: attributes.suggested_fee_recipient,
170                prev_randao: attributes.prev_randao,
171                gas_limit: attributes.gas_limit,
172            },
173            self.chain_spec().next_block_base_fee(parent, attributes.timestamp).unwrap_or_default(),
174            self.chain_spec(),
175            self.chain_spec().chain().id(),
176        ))
177    }
178
179    fn context_for_block(
180        &self,
181        block: &'_ SealedBlock<N::Block>,
182    ) -> Result<OpBlockExecutionCtx, Self::Error> {
183        Ok(OpBlockExecutionCtx {
184            parent_hash: block.header().parent_hash(),
185            parent_beacon_block_root: block.header().parent_beacon_block_root(),
186            extra_data: block.header().extra_data().clone(),
187        })
188    }
189
190    fn context_for_next_block(
191        &self,
192        parent: &SealedHeader<N::BlockHeader>,
193        attributes: Self::NextBlockEnvCtx,
194    ) -> Result<OpBlockExecutionCtx, Self::Error> {
195        Ok(OpBlockExecutionCtx {
196            parent_hash: parent.hash(),
197            parent_beacon_block_root: attributes.parent_beacon_block_root,
198            extra_data: attributes.extra_data,
199        })
200    }
201}
202
203impl<ChainSpec, N, R> ConfigureEngineEvm<OpExecutionData> for OpEvmConfig<ChainSpec, N, R>
204where
205    ChainSpec: EthChainSpec<Header = Header> + OpHardforks,
206    N: NodePrimitives<
207        Receipt = R::Receipt,
208        SignedTx = R::Transaction,
209        BlockHeader = Header,
210        BlockBody = alloy_consensus::BlockBody<R::Transaction>,
211        Block = alloy_consensus::Block<R::Transaction>,
212    >,
213    OpTransaction<TxEnv>: FromRecoveredTx<N::SignedTx> + FromTxWithEncoded<N::SignedTx>,
214    R: OpReceiptBuilder<Receipt: DepositReceipt, Transaction: SignedTransaction>,
215    Self: Send + Sync + Unpin + Clone + 'static,
216{
217    fn evm_env_for_payload(
218        &self,
219        payload: &OpExecutionData,
220    ) -> Result<EvmEnvFor<Self>, Self::Error> {
221        let timestamp = payload.payload.timestamp();
222        let block_number = payload.payload.block_number();
223
224        let spec = revm_spec_by_timestamp_after_bedrock(self.chain_spec(), timestamp);
225
226        let cfg_env = CfgEnv::new().with_chain_id(self.chain_spec().chain().id()).with_spec(spec);
227
228        let blob_excess_gas_and_price = spec
229            .into_eth_spec()
230            .is_enabled_in(SpecId::CANCUN)
231            .then_some(BlobExcessGasAndPrice { excess_blob_gas: 0, blob_gasprice: 1 });
232
233        let block_env = BlockEnv {
234            number: U256::from(block_number),
235            beneficiary: payload.payload.as_v1().fee_recipient,
236            timestamp: U256::from(timestamp),
237            difficulty: if spec.into_eth_spec() >= SpecId::MERGE {
238                U256::ZERO
239            } else {
240                payload.payload.as_v1().prev_randao.into()
241            },
242            prevrandao: (spec.into_eth_spec() >= SpecId::MERGE)
243                .then(|| payload.payload.as_v1().prev_randao),
244            gas_limit: payload.payload.as_v1().gas_limit,
245            basefee: payload.payload.as_v1().base_fee_per_gas.to(),
246            // EIP-4844 excess blob gas of this block, introduced in Cancun
247            blob_excess_gas_and_price,
248        };
249
250        Ok(EvmEnv { cfg_env, block_env })
251    }
252
253    fn context_for_payload<'a>(
254        &self,
255        payload: &'a OpExecutionData,
256    ) -> Result<ExecutionCtxFor<'a, Self>, Self::Error> {
257        Ok(OpBlockExecutionCtx {
258            parent_hash: payload.parent_hash(),
259            parent_beacon_block_root: payload.sidecar.parent_beacon_block_root(),
260            extra_data: payload.payload.as_v1().extra_data.clone(),
261        })
262    }
263
264    fn tx_iterator_for_payload(
265        &self,
266        payload: &OpExecutionData,
267    ) -> Result<impl ExecutableTxIterator<Self>, Self::Error> {
268        Ok(payload.payload.transactions().clone().into_iter().map(|encoded| {
269            let tx = TxTy::<Self::Primitives>::decode_2718_exact(encoded.as_ref())
270                .map_err(AnyError::new)?;
271            let signer = tx.try_recover().map_err(AnyError::new)?;
272            Ok::<_, AnyError>(WithEncoded::new(encoded, tx.with_signer(signer)))
273        }))
274    }
275}
276
277#[cfg(test)]
278mod tests {
279    use super::*;
280    use alloy_consensus::{Header, Receipt};
281    use alloy_eips::eip7685::Requests;
282    use alloy_genesis::Genesis;
283    use alloy_primitives::{bytes, map::HashMap, Address, LogData, B256};
284    use op_revm::OpSpecId;
285    use reth_chainspec::ChainSpec;
286    use reth_evm::execute::ProviderError;
287    use reth_execution_types::{
288        AccountRevertInit, BundleStateInit, Chain, ExecutionOutcome, RevertsInit,
289    };
290    use reth_optimism_chainspec::{OpChainSpec, BASE_MAINNET};
291    use reth_optimism_primitives::{OpBlock, OpPrimitives, OpReceipt};
292    use reth_primitives_traits::{Account, RecoveredBlock};
293    use revm::{
294        database::{BundleState, CacheDB},
295        database_interface::EmptyDBTyped,
296        inspector::NoOpInspector,
297        primitives::Log,
298        state::AccountInfo,
299    };
300    use std::sync::Arc;
301
302    fn test_evm_config() -> OpEvmConfig {
303        OpEvmConfig::optimism(BASE_MAINNET.clone())
304    }
305
306    #[test]
307    fn test_fill_cfg_and_block_env() {
308        // Create a default header
309        let header = Header::default();
310
311        // Build the ChainSpec for Ethereum mainnet, activating London, Paris, and Shanghai
312        // hardforks
313        let chain_spec = ChainSpec::builder()
314            .chain(0.into())
315            .genesis(Genesis::default())
316            .london_activated()
317            .paris_activated()
318            .shanghai_activated()
319            .build();
320
321        // Use the `OpEvmConfig` to create the `cfg_env` and `block_env` based on the ChainSpec,
322        // Header, and total difficulty
323        let EvmEnv { cfg_env, .. } =
324            OpEvmConfig::optimism(Arc::new(OpChainSpec { inner: chain_spec.clone() }))
325                .evm_env(&header)
326                .unwrap();
327
328        // Assert that the chain ID in the `cfg_env` is correctly set to the chain ID of the
329        // ChainSpec
330        assert_eq!(cfg_env.chain_id, chain_spec.chain().id());
331    }
332
333    #[test]
334    fn test_evm_with_env_default_spec() {
335        let evm_config = test_evm_config();
336
337        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
338
339        let evm_env = EvmEnv::default();
340
341        let evm = evm_config.evm_with_env(db, evm_env.clone());
342
343        // Check that the EVM environment
344        assert_eq!(evm.cfg, evm_env.cfg_env);
345    }
346
347    #[test]
348    fn test_evm_with_env_custom_cfg() {
349        let evm_config = test_evm_config();
350
351        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
352
353        // Create a custom configuration environment with a chain ID of 111
354        let cfg = CfgEnv::new().with_chain_id(111).with_spec(OpSpecId::default());
355
356        let evm_env = EvmEnv { cfg_env: cfg.clone(), ..Default::default() };
357
358        let evm = evm_config.evm_with_env(db, evm_env);
359
360        // Check that the EVM environment is initialized with the custom environment
361        assert_eq!(evm.cfg, cfg);
362    }
363
364    #[test]
365    fn test_evm_with_env_custom_block_and_tx() {
366        let evm_config = test_evm_config();
367
368        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
369
370        // Create customs block and tx env
371        let block = BlockEnv {
372            basefee: 1000,
373            gas_limit: 10_000_000,
374            number: U256::from(42),
375            ..Default::default()
376        };
377
378        let evm_env = EvmEnv { block_env: block, ..Default::default() };
379
380        let evm = evm_config.evm_with_env(db, evm_env.clone());
381
382        // Verify that the block and transaction environments are set correctly
383        assert_eq!(evm.block, evm_env.block_env);
384    }
385
386    #[test]
387    fn test_evm_with_spec_id() {
388        let evm_config = test_evm_config();
389
390        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
391
392        let evm_env =
393            EvmEnv { cfg_env: CfgEnv::new().with_spec(OpSpecId::ECOTONE), ..Default::default() };
394
395        let evm = evm_config.evm_with_env(db, evm_env.clone());
396
397        assert_eq!(evm.cfg, evm_env.cfg_env);
398    }
399
400    #[test]
401    fn test_evm_with_env_and_default_inspector() {
402        let evm_config = test_evm_config();
403        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
404
405        let evm_env = EvmEnv { cfg_env: Default::default(), ..Default::default() };
406
407        let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
408
409        // Check that the EVM environment is set to default values
410        assert_eq!(evm.block, evm_env.block_env);
411        assert_eq!(evm.cfg, evm_env.cfg_env);
412    }
413
414    #[test]
415    fn test_evm_with_env_inspector_and_custom_cfg() {
416        let evm_config = test_evm_config();
417        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
418
419        let cfg = CfgEnv::new().with_chain_id(111).with_spec(OpSpecId::default());
420        let block = BlockEnv::default();
421        let evm_env = EvmEnv { block_env: block, cfg_env: cfg.clone() };
422
423        let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
424
425        // Check that the EVM environment is set with custom configuration
426        assert_eq!(evm.cfg, cfg);
427        assert_eq!(evm.block, evm_env.block_env);
428    }
429
430    #[test]
431    fn test_evm_with_env_inspector_and_custom_block_tx() {
432        let evm_config = test_evm_config();
433        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
434
435        // Create custom block and tx environment
436        let block = BlockEnv {
437            basefee: 1000,
438            gas_limit: 10_000_000,
439            number: U256::from(42),
440            ..Default::default()
441        };
442        let evm_env = EvmEnv { block_env: block, ..Default::default() };
443
444        let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
445
446        // Verify that the block and transaction environments are set correctly
447        assert_eq!(evm.block, evm_env.block_env);
448    }
449
450    #[test]
451    fn test_evm_with_env_inspector_and_spec_id() {
452        let evm_config = test_evm_config();
453        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
454
455        let evm_env =
456            EvmEnv { cfg_env: CfgEnv::new().with_spec(OpSpecId::ECOTONE), ..Default::default() };
457
458        let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
459
460        // Check that the spec ID is set properly
461        assert_eq!(evm.cfg, evm_env.cfg_env);
462        assert_eq!(evm.block, evm_env.block_env);
463    }
464
465    #[test]
466    fn receipts_by_block_hash() {
467        // Create a default recovered block
468        let block: RecoveredBlock<OpBlock> = Default::default();
469
470        // Define block hashes for block1 and block2
471        let block1_hash = B256::new([0x01; 32]);
472        let block2_hash = B256::new([0x02; 32]);
473
474        // Clone the default block into block1 and block2
475        let mut block1 = block.clone();
476        let mut block2 = block;
477
478        // Set the hashes of block1 and block2
479        block1.set_block_number(10);
480        block1.set_hash(block1_hash);
481
482        block2.set_block_number(11);
483        block2.set_hash(block2_hash);
484
485        // Create a random receipt object, receipt1
486        let receipt1 = OpReceipt::Legacy(Receipt {
487            cumulative_gas_used: 46913,
488            logs: vec![],
489            status: true.into(),
490        });
491
492        // Create another random receipt object, receipt2
493        let receipt2 = OpReceipt::Legacy(Receipt {
494            cumulative_gas_used: 1325345,
495            logs: vec![],
496            status: true.into(),
497        });
498
499        // Create a Receipts object with a vector of receipt vectors
500        let receipts = vec![vec![receipt1.clone()], vec![receipt2]];
501
502        // Create an ExecutionOutcome object with the created bundle, receipts, an empty requests
503        // vector, and first_block set to 10
504        let execution_outcome = ExecutionOutcome::<OpReceipt> {
505            bundle: Default::default(),
506            receipts,
507            requests: vec![],
508            first_block: 10,
509        };
510
511        // Create a Chain object with a BTreeMap of blocks mapped to their block numbers,
512        // including block1_hash and block2_hash, and the execution_outcome
513        let chain: Chain<OpPrimitives> =
514            Chain::new([block1, block2], execution_outcome.clone(), None);
515
516        // Assert that the proper receipt vector is returned for block1_hash
517        assert_eq!(chain.receipts_by_block_hash(block1_hash), Some(vec![&receipt1]));
518
519        // Create an ExecutionOutcome object with a single receipt vector containing receipt1
520        let execution_outcome1 = ExecutionOutcome {
521            bundle: Default::default(),
522            receipts: vec![vec![receipt1]],
523            requests: vec![],
524            first_block: 10,
525        };
526
527        // Assert that the execution outcome at the first block contains only the first receipt
528        assert_eq!(chain.execution_outcome_at_block(10), Some(execution_outcome1));
529
530        // Assert that the execution outcome at the tip block contains the whole execution outcome
531        assert_eq!(chain.execution_outcome_at_block(11), Some(execution_outcome));
532    }
533
534    #[test]
535    fn test_initialization() {
536        // Create a new BundleState object with initial data
537        let bundle = BundleState::new(
538            vec![(Address::new([2; 20]), None, Some(AccountInfo::default()), HashMap::default())],
539            vec![vec![(Address::new([2; 20]), None, vec![])]],
540            vec![],
541        );
542
543        // Create a Receipts object with a vector of receipt vectors
544        let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt {
545            cumulative_gas_used: 46913,
546            logs: vec![],
547            status: true.into(),
548        }))]];
549
550        // Create a Requests object with a vector of requests
551        let requests = vec![Requests::new(vec![bytes!("dead"), bytes!("beef"), bytes!("beebee")])];
552
553        // Define the first block number
554        let first_block = 123;
555
556        // Create a ExecutionOutcome object with the created bundle, receipts, requests, and
557        // first_block
558        let exec_res = ExecutionOutcome {
559            bundle: bundle.clone(),
560            receipts: receipts.clone(),
561            requests: requests.clone(),
562            first_block,
563        };
564
565        // Assert that creating a new ExecutionOutcome using the constructor matches exec_res
566        assert_eq!(
567            ExecutionOutcome::new(bundle, receipts.clone(), first_block, requests.clone()),
568            exec_res
569        );
570
571        // Create a BundleStateInit object and insert initial data
572        let mut state_init: BundleStateInit = HashMap::default();
573        state_init
574            .insert(Address::new([2; 20]), (None, Some(Account::default()), HashMap::default()));
575
576        // Create a HashMap for account reverts and insert initial data
577        let mut revert_inner: HashMap<Address, AccountRevertInit> = HashMap::default();
578        revert_inner.insert(Address::new([2; 20]), (None, vec![]));
579
580        // Create a RevertsInit object and insert the revert_inner data
581        let mut revert_init: RevertsInit = HashMap::default();
582        revert_init.insert(123, revert_inner);
583
584        // Assert that creating a new ExecutionOutcome using the new_init method matches
585        // exec_res
586        assert_eq!(
587            ExecutionOutcome::new_init(
588                state_init,
589                revert_init,
590                vec![],
591                receipts,
592                first_block,
593                requests,
594            ),
595            exec_res
596        );
597    }
598
599    #[test]
600    fn test_block_number_to_index() {
601        // Create a Receipts object with a vector of receipt vectors
602        let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt {
603            cumulative_gas_used: 46913,
604            logs: vec![],
605            status: true.into(),
606        }))]];
607
608        // Define the first block number
609        let first_block = 123;
610
611        // Create a ExecutionOutcome object with the created bundle, receipts, requests, and
612        // first_block
613        let exec_res = ExecutionOutcome {
614            bundle: Default::default(),
615            receipts,
616            requests: vec![],
617            first_block,
618        };
619
620        // Test before the first block
621        assert_eq!(exec_res.block_number_to_index(12), None);
622
623        // Test after the first block but index larger than receipts length
624        assert_eq!(exec_res.block_number_to_index(133), None);
625
626        // Test after the first block
627        assert_eq!(exec_res.block_number_to_index(123), Some(0));
628    }
629
630    #[test]
631    fn test_get_logs() {
632        // Create a Receipts object with a vector of receipt vectors
633        let receipts = vec![vec![OpReceipt::Legacy(Receipt {
634            cumulative_gas_used: 46913,
635            logs: vec![Log::<LogData>::default()],
636            status: true.into(),
637        })]];
638
639        // Define the first block number
640        let first_block = 123;
641
642        // Create a ExecutionOutcome object with the created bundle, receipts, requests, and
643        // first_block
644        let exec_res = ExecutionOutcome {
645            bundle: Default::default(),
646            receipts,
647            requests: vec![],
648            first_block,
649        };
650
651        // Get logs for block number 123
652        let logs: Vec<&Log> = exec_res.logs(123).unwrap().collect();
653
654        // Assert that the logs match the expected logs
655        assert_eq!(logs, vec![&Log::<LogData>::default()]);
656    }
657
658    #[test]
659    fn test_receipts_by_block() {
660        // Create a Receipts object with a vector of receipt vectors
661        let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt {
662            cumulative_gas_used: 46913,
663            logs: vec![Log::<LogData>::default()],
664            status: true.into(),
665        }))]];
666
667        // Define the first block number
668        let first_block = 123;
669
670        // Create a ExecutionOutcome object with the created bundle, receipts, requests, and
671        // first_block
672        let exec_res = ExecutionOutcome {
673            bundle: Default::default(), // Default value for bundle
674            receipts,                   // Include the created receipts
675            requests: vec![],           // Empty vector for requests
676            first_block,                // Set the first block number
677        };
678
679        // Get receipts for block number 123 and convert the result into a vector
680        let receipts_by_block: Vec<_> = exec_res.receipts_by_block(123).iter().collect();
681
682        // Assert that the receipts for block number 123 match the expected receipts
683        assert_eq!(
684            receipts_by_block,
685            vec![&Some(OpReceipt::Legacy(Receipt {
686                cumulative_gas_used: 46913,
687                logs: vec![Log::<LogData>::default()],
688                status: true.into(),
689            }))]
690        );
691    }
692
693    #[test]
694    fn test_receipts_len() {
695        // Create a Receipts object with a vector of receipt vectors
696        let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt {
697            cumulative_gas_used: 46913,
698            logs: vec![Log::<LogData>::default()],
699            status: true.into(),
700        }))]];
701
702        // Create an empty Receipts object
703        let receipts_empty = vec![];
704
705        // Define the first block number
706        let first_block = 123;
707
708        // Create a ExecutionOutcome object with the created bundle, receipts, requests, and
709        // first_block
710        let exec_res = ExecutionOutcome {
711            bundle: Default::default(), // Default value for bundle
712            receipts,                   // Include the created receipts
713            requests: vec![],           // Empty vector for requests
714            first_block,                // Set the first block number
715        };
716
717        // Assert that the length of receipts in exec_res is 1
718        assert_eq!(exec_res.len(), 1);
719
720        // Assert that exec_res is not empty
721        assert!(!exec_res.is_empty());
722
723        // Create a ExecutionOutcome object with an empty Receipts object
724        let exec_res_empty_receipts: ExecutionOutcome<OpReceipt> = ExecutionOutcome {
725            bundle: Default::default(), // Default value for bundle
726            receipts: receipts_empty,   // Include the empty receipts
727            requests: vec![],           // Empty vector for requests
728            first_block,                // Set the first block number
729        };
730
731        // Assert that the length of receipts in exec_res_empty_receipts is 0
732        assert_eq!(exec_res_empty_receipts.len(), 0);
733
734        // Assert that exec_res_empty_receipts is empty
735        assert!(exec_res_empty_receipts.is_empty());
736    }
737
738    #[test]
739    fn test_revert_to() {
740        // Create a random receipt object
741        let receipt = OpReceipt::Legacy(Receipt {
742            cumulative_gas_used: 46913,
743            logs: vec![],
744            status: true.into(),
745        });
746
747        // Create a Receipts object with a vector of receipt vectors
748        let receipts = vec![vec![Some(receipt.clone())], vec![Some(receipt.clone())]];
749
750        // Define the first block number
751        let first_block = 123;
752
753        // Create a request.
754        let request = bytes!("deadbeef");
755
756        // Create a vector of Requests containing the request.
757        let requests =
758            vec![Requests::new(vec![request.clone()]), Requests::new(vec![request.clone()])];
759
760        // Create a ExecutionOutcome object with the created bundle, receipts, requests, and
761        // first_block
762        let mut exec_res =
763            ExecutionOutcome { bundle: Default::default(), receipts, requests, first_block };
764
765        // Assert that the revert_to method returns true when reverting to the initial block number.
766        assert!(exec_res.revert_to(123));
767
768        // Assert that the receipts are properly cut after reverting to the initial block number.
769        assert_eq!(exec_res.receipts, vec![vec![Some(receipt)]]);
770
771        // Assert that the requests are properly cut after reverting to the initial block number.
772        assert_eq!(exec_res.requests, vec![Requests::new(vec![request])]);
773
774        // Assert that the revert_to method returns false when attempting to revert to a block
775        // number greater than the initial block number.
776        assert!(!exec_res.revert_to(133));
777
778        // Assert that the revert_to method returns false when attempting to revert to a block
779        // number less than the initial block number.
780        assert!(!exec_res.revert_to(10));
781    }
782
783    #[test]
784    fn test_extend_execution_outcome() {
785        // Create a Receipt object with specific attributes.
786        let receipt = OpReceipt::Legacy(Receipt {
787            cumulative_gas_used: 46913,
788            logs: vec![],
789            status: true.into(),
790        });
791
792        // Create a Receipts object containing the receipt.
793        let receipts = vec![vec![Some(receipt.clone())]];
794
795        // Create a request.
796        let request = bytes!("deadbeef");
797
798        // Create a vector of Requests containing the request.
799        let requests = vec![Requests::new(vec![request.clone()])];
800
801        // Define the initial block number.
802        let first_block = 123;
803
804        // Create an ExecutionOutcome object.
805        let mut exec_res =
806            ExecutionOutcome { bundle: Default::default(), receipts, requests, first_block };
807
808        // Extend the ExecutionOutcome object by itself.
809        exec_res.extend(exec_res.clone());
810
811        // Assert the extended ExecutionOutcome matches the expected outcome.
812        assert_eq!(
813            exec_res,
814            ExecutionOutcome {
815                bundle: Default::default(),
816                receipts: vec![vec![Some(receipt.clone())], vec![Some(receipt)]],
817                requests: vec![Requests::new(vec![request.clone()]), Requests::new(vec![request])],
818                first_block: 123,
819            }
820        );
821    }
822
823    #[test]
824    fn test_split_at_execution_outcome() {
825        // Create a random receipt object
826        let receipt = OpReceipt::Legacy(Receipt {
827            cumulative_gas_used: 46913,
828            logs: vec![],
829            status: true.into(),
830        });
831
832        // Create a Receipts object with a vector of receipt vectors
833        let receipts = vec![
834            vec![Some(receipt.clone())],
835            vec![Some(receipt.clone())],
836            vec![Some(receipt.clone())],
837        ];
838
839        // Define the first block number
840        let first_block = 123;
841
842        // Create a request.
843        let request = bytes!("deadbeef");
844
845        // Create a vector of Requests containing the request.
846        let requests = vec![
847            Requests::new(vec![request.clone()]),
848            Requests::new(vec![request.clone()]),
849            Requests::new(vec![request.clone()]),
850        ];
851
852        // Create a ExecutionOutcome object with the created bundle, receipts, requests, and
853        // first_block
854        let exec_res =
855            ExecutionOutcome { bundle: Default::default(), receipts, requests, first_block };
856
857        // Split the ExecutionOutcome at block number 124
858        let result = exec_res.clone().split_at(124);
859
860        // Define the expected lower ExecutionOutcome after splitting
861        let lower_execution_outcome = ExecutionOutcome {
862            bundle: Default::default(),
863            receipts: vec![vec![Some(receipt.clone())]],
864            requests: vec![Requests::new(vec![request.clone()])],
865            first_block,
866        };
867
868        // Define the expected higher ExecutionOutcome after splitting
869        let higher_execution_outcome = ExecutionOutcome {
870            bundle: Default::default(),
871            receipts: vec![vec![Some(receipt.clone())], vec![Some(receipt)]],
872            requests: vec![Requests::new(vec![request.clone()]), Requests::new(vec![request])],
873            first_block: 124,
874        };
875
876        // Assert that the split result matches the expected lower and higher outcomes
877        assert_eq!(result.0, Some(lower_execution_outcome));
878        assert_eq!(result.1, higher_execution_outcome);
879
880        // Assert that splitting at the first block number returns None for the lower outcome
881        assert_eq!(exec_res.clone().split_at(123), (None, exec_res));
882    }
883}