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::{Bytes, 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        let transactions = payload.payload.transactions().clone().into_iter();
269        let convert = |encoded: Bytes| {
270            let tx = TxTy::<Self::Primitives>::decode_2718_exact(encoded.as_ref())
271                .map_err(AnyError::new)?;
272            let signer = tx.try_recover().map_err(AnyError::new)?;
273            Ok::<_, AnyError>(WithEncoded::new(encoded, tx.with_signer(signer)))
274        };
275
276        Ok((transactions, convert))
277    }
278}
279
280#[cfg(test)]
281mod tests {
282    use super::*;
283    use alloy_consensus::{Header, Receipt};
284    use alloy_eips::eip7685::Requests;
285    use alloy_genesis::Genesis;
286    use alloy_primitives::{bytes, map::HashMap, Address, LogData, B256};
287    use op_revm::OpSpecId;
288    use reth_chainspec::ChainSpec;
289    use reth_evm::execute::ProviderError;
290    use reth_execution_types::{
291        AccountRevertInit, BundleStateInit, Chain, ExecutionOutcome, RevertsInit,
292    };
293    use reth_optimism_chainspec::{OpChainSpec, BASE_MAINNET};
294    use reth_optimism_primitives::{OpBlock, OpPrimitives, OpReceipt};
295    use reth_primitives_traits::{Account, RecoveredBlock};
296    use revm::{
297        database::{BundleState, CacheDB},
298        database_interface::EmptyDBTyped,
299        inspector::NoOpInspector,
300        primitives::Log,
301        state::AccountInfo,
302    };
303    use std::sync::Arc;
304
305    fn test_evm_config() -> OpEvmConfig {
306        OpEvmConfig::optimism(BASE_MAINNET.clone())
307    }
308
309    #[test]
310    fn test_fill_cfg_and_block_env() {
311        // Create a default header
312        let header = Header::default();
313
314        // Build the ChainSpec for Ethereum mainnet, activating London, Paris, and Shanghai
315        // hardforks
316        let chain_spec = ChainSpec::builder()
317            .chain(0.into())
318            .genesis(Genesis::default())
319            .london_activated()
320            .paris_activated()
321            .shanghai_activated()
322            .build();
323
324        // Use the `OpEvmConfig` to create the `cfg_env` and `block_env` based on the ChainSpec,
325        // Header, and total difficulty
326        let EvmEnv { cfg_env, .. } =
327            OpEvmConfig::optimism(Arc::new(OpChainSpec { inner: chain_spec.clone() }))
328                .evm_env(&header)
329                .unwrap();
330
331        // Assert that the chain ID in the `cfg_env` is correctly set to the chain ID of the
332        // ChainSpec
333        assert_eq!(cfg_env.chain_id, chain_spec.chain().id());
334    }
335
336    #[test]
337    fn test_evm_with_env_default_spec() {
338        let evm_config = test_evm_config();
339
340        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
341
342        let evm_env = EvmEnv::default();
343
344        let evm = evm_config.evm_with_env(db, evm_env.clone());
345
346        // Check that the EVM environment
347        assert_eq!(evm.cfg, evm_env.cfg_env);
348    }
349
350    #[test]
351    fn test_evm_with_env_custom_cfg() {
352        let evm_config = test_evm_config();
353
354        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
355
356        // Create a custom configuration environment with a chain ID of 111
357        let cfg = CfgEnv::new().with_chain_id(111).with_spec(OpSpecId::default());
358
359        let evm_env = EvmEnv { cfg_env: cfg.clone(), ..Default::default() };
360
361        let evm = evm_config.evm_with_env(db, evm_env);
362
363        // Check that the EVM environment is initialized with the custom environment
364        assert_eq!(evm.cfg, cfg);
365    }
366
367    #[test]
368    fn test_evm_with_env_custom_block_and_tx() {
369        let evm_config = test_evm_config();
370
371        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
372
373        // Create customs block and tx env
374        let block = BlockEnv {
375            basefee: 1000,
376            gas_limit: 10_000_000,
377            number: U256::from(42),
378            ..Default::default()
379        };
380
381        let evm_env = EvmEnv { block_env: block, ..Default::default() };
382
383        let evm = evm_config.evm_with_env(db, evm_env.clone());
384
385        // Verify that the block and transaction environments are set correctly
386        assert_eq!(evm.block, evm_env.block_env);
387    }
388
389    #[test]
390    fn test_evm_with_spec_id() {
391        let evm_config = test_evm_config();
392
393        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
394
395        let evm_env =
396            EvmEnv { cfg_env: CfgEnv::new().with_spec(OpSpecId::ECOTONE), ..Default::default() };
397
398        let evm = evm_config.evm_with_env(db, evm_env.clone());
399
400        assert_eq!(evm.cfg, evm_env.cfg_env);
401    }
402
403    #[test]
404    fn test_evm_with_env_and_default_inspector() {
405        let evm_config = test_evm_config();
406        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
407
408        let evm_env = EvmEnv { cfg_env: Default::default(), ..Default::default() };
409
410        let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
411
412        // Check that the EVM environment is set to default values
413        assert_eq!(evm.block, evm_env.block_env);
414        assert_eq!(evm.cfg, evm_env.cfg_env);
415    }
416
417    #[test]
418    fn test_evm_with_env_inspector_and_custom_cfg() {
419        let evm_config = test_evm_config();
420        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
421
422        let cfg = CfgEnv::new().with_chain_id(111).with_spec(OpSpecId::default());
423        let block = BlockEnv::default();
424        let evm_env = EvmEnv { block_env: block, cfg_env: cfg.clone() };
425
426        let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
427
428        // Check that the EVM environment is set with custom configuration
429        assert_eq!(evm.cfg, cfg);
430        assert_eq!(evm.block, evm_env.block_env);
431    }
432
433    #[test]
434    fn test_evm_with_env_inspector_and_custom_block_tx() {
435        let evm_config = test_evm_config();
436        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
437
438        // Create custom block and tx environment
439        let block = BlockEnv {
440            basefee: 1000,
441            gas_limit: 10_000_000,
442            number: U256::from(42),
443            ..Default::default()
444        };
445        let evm_env = EvmEnv { block_env: block, ..Default::default() };
446
447        let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
448
449        // Verify that the block and transaction environments are set correctly
450        assert_eq!(evm.block, evm_env.block_env);
451    }
452
453    #[test]
454    fn test_evm_with_env_inspector_and_spec_id() {
455        let evm_config = test_evm_config();
456        let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
457
458        let evm_env =
459            EvmEnv { cfg_env: CfgEnv::new().with_spec(OpSpecId::ECOTONE), ..Default::default() };
460
461        let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
462
463        // Check that the spec ID is set properly
464        assert_eq!(evm.cfg, evm_env.cfg_env);
465        assert_eq!(evm.block, evm_env.block_env);
466    }
467
468    #[test]
469    fn receipts_by_block_hash() {
470        // Create a default recovered block
471        let block: RecoveredBlock<OpBlock> = Default::default();
472
473        // Define block hashes for block1 and block2
474        let block1_hash = B256::new([0x01; 32]);
475        let block2_hash = B256::new([0x02; 32]);
476
477        // Clone the default block into block1 and block2
478        let mut block1 = block.clone();
479        let mut block2 = block;
480
481        // Set the hashes of block1 and block2
482        block1.set_block_number(10);
483        block1.set_hash(block1_hash);
484
485        block2.set_block_number(11);
486        block2.set_hash(block2_hash);
487
488        // Create a random receipt object, receipt1
489        let receipt1 = OpReceipt::Legacy(Receipt::<Log> {
490            cumulative_gas_used: 46913,
491            logs: vec![],
492            status: true.into(),
493        });
494
495        // Create another random receipt object, receipt2
496        let receipt2 = OpReceipt::Legacy(Receipt::<Log> {
497            cumulative_gas_used: 1325345,
498            logs: vec![],
499            status: true.into(),
500        });
501
502        // Create a Receipts object with a vector of receipt vectors
503        let receipts = vec![vec![receipt1.clone()], vec![receipt2]];
504
505        // Create an ExecutionOutcome object with the created bundle, receipts, an empty requests
506        // vector, and first_block set to 10
507        let execution_outcome = ExecutionOutcome::<OpReceipt> {
508            bundle: Default::default(),
509            receipts,
510            requests: vec![],
511            first_block: 10,
512        };
513
514        // Create a Chain object with a BTreeMap of blocks mapped to their block numbers,
515        // including block1_hash and block2_hash, and the execution_outcome
516        let chain: Chain<OpPrimitives> =
517            Chain::new([block1, block2], execution_outcome.clone(), None);
518
519        // Assert that the proper receipt vector is returned for block1_hash
520        assert_eq!(chain.receipts_by_block_hash(block1_hash), Some(vec![&receipt1]));
521
522        // Create an ExecutionOutcome object with a single receipt vector containing receipt1
523        let execution_outcome1 = ExecutionOutcome {
524            bundle: Default::default(),
525            receipts: vec![vec![receipt1]],
526            requests: vec![],
527            first_block: 10,
528        };
529
530        // Assert that the execution outcome at the first block contains only the first receipt
531        assert_eq!(chain.execution_outcome_at_block(10), Some(execution_outcome1));
532
533        // Assert that the execution outcome at the tip block contains the whole execution outcome
534        assert_eq!(chain.execution_outcome_at_block(11), Some(execution_outcome));
535    }
536
537    #[test]
538    fn test_initialization() {
539        // Create a new BundleState object with initial data
540        let bundle = BundleState::new(
541            vec![(Address::new([2; 20]), None, Some(AccountInfo::default()), HashMap::default())],
542            vec![vec![(Address::new([2; 20]), None, vec![])]],
543            vec![],
544        );
545
546        // Create a Receipts object with a vector of receipt vectors
547        let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt::<Log> {
548            cumulative_gas_used: 46913,
549            logs: vec![],
550            status: true.into(),
551        }))]];
552
553        // Create a Requests object with a vector of requests
554        let requests = vec![Requests::new(vec![bytes!("dead"), bytes!("beef"), bytes!("beebee")])];
555
556        // Define the first block number
557        let first_block = 123;
558
559        // Create a ExecutionOutcome object with the created bundle, receipts, requests, and
560        // first_block
561        let exec_res = ExecutionOutcome {
562            bundle: bundle.clone(),
563            receipts: receipts.clone(),
564            requests: requests.clone(),
565            first_block,
566        };
567
568        // Assert that creating a new ExecutionOutcome using the constructor matches exec_res
569        assert_eq!(
570            ExecutionOutcome::new(bundle, receipts.clone(), first_block, requests.clone()),
571            exec_res
572        );
573
574        // Create a BundleStateInit object and insert initial data
575        let mut state_init: BundleStateInit = HashMap::default();
576        state_init
577            .insert(Address::new([2; 20]), (None, Some(Account::default()), HashMap::default()));
578
579        // Create a HashMap for account reverts and insert initial data
580        let mut revert_inner: HashMap<Address, AccountRevertInit> = HashMap::default();
581        revert_inner.insert(Address::new([2; 20]), (None, vec![]));
582
583        // Create a RevertsInit object and insert the revert_inner data
584        let mut revert_init: RevertsInit = HashMap::default();
585        revert_init.insert(123, revert_inner);
586
587        // Assert that creating a new ExecutionOutcome using the new_init method matches
588        // exec_res
589        assert_eq!(
590            ExecutionOutcome::new_init(
591                state_init,
592                revert_init,
593                vec![],
594                receipts,
595                first_block,
596                requests,
597            ),
598            exec_res
599        );
600    }
601
602    #[test]
603    fn test_block_number_to_index() {
604        // Create a Receipts object with a vector of receipt vectors
605        let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt::<Log> {
606            cumulative_gas_used: 46913,
607            logs: vec![],
608            status: true.into(),
609        }))]];
610
611        // Define the first block number
612        let first_block = 123;
613
614        // Create a ExecutionOutcome object with the created bundle, receipts, requests, and
615        // first_block
616        let exec_res = ExecutionOutcome {
617            bundle: Default::default(),
618            receipts,
619            requests: vec![],
620            first_block,
621        };
622
623        // Test before the first block
624        assert_eq!(exec_res.block_number_to_index(12), None);
625
626        // Test after the first block but index larger than receipts length
627        assert_eq!(exec_res.block_number_to_index(133), None);
628
629        // Test after the first block
630        assert_eq!(exec_res.block_number_to_index(123), Some(0));
631    }
632
633    #[test]
634    fn test_get_logs() {
635        // Create a Receipts object with a vector of receipt vectors
636        let receipts = vec![vec![OpReceipt::Legacy(Receipt::<Log> {
637            cumulative_gas_used: 46913,
638            logs: vec![Log::<LogData>::default()],
639            status: true.into(),
640        })]];
641
642        // Define the first block number
643        let first_block = 123;
644
645        // Create a ExecutionOutcome object with the created bundle, receipts, requests, and
646        // first_block
647        let exec_res = ExecutionOutcome {
648            bundle: Default::default(),
649            receipts,
650            requests: vec![],
651            first_block,
652        };
653
654        // Get logs for block number 123
655        let logs: Vec<&Log> = exec_res.logs(123).unwrap().collect();
656
657        // Assert that the logs match the expected logs
658        assert_eq!(logs, vec![&Log::<LogData>::default()]);
659    }
660
661    #[test]
662    fn test_receipts_by_block() {
663        // Create a Receipts object with a vector of receipt vectors
664        let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt::<Log> {
665            cumulative_gas_used: 46913,
666            logs: vec![Log::<LogData>::default()],
667            status: true.into(),
668        }))]];
669
670        // Define the first block number
671        let first_block = 123;
672
673        // Create a ExecutionOutcome object with the created bundle, receipts, requests, and
674        // first_block
675        let exec_res = ExecutionOutcome {
676            bundle: Default::default(), // Default value for bundle
677            receipts,                   // Include the created receipts
678            requests: vec![],           // Empty vector for requests
679            first_block,                // Set the first block number
680        };
681
682        // Get receipts for block number 123 and convert the result into a vector
683        let receipts_by_block: Vec<_> = exec_res.receipts_by_block(123).iter().collect();
684
685        // Assert that the receipts for block number 123 match the expected receipts
686        assert_eq!(
687            receipts_by_block,
688            vec![&Some(OpReceipt::Legacy(Receipt::<Log> {
689                cumulative_gas_used: 46913,
690                logs: vec![Log::<LogData>::default()],
691                status: true.into(),
692            }))]
693        );
694    }
695
696    #[test]
697    fn test_receipts_len() {
698        // Create a Receipts object with a vector of receipt vectors
699        let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt::<Log> {
700            cumulative_gas_used: 46913,
701            logs: vec![Log::<LogData>::default()],
702            status: true.into(),
703        }))]];
704
705        // Create an empty Receipts object
706        let receipts_empty = vec![];
707
708        // Define the first block number
709        let first_block = 123;
710
711        // Create a ExecutionOutcome object with the created bundle, receipts, requests, and
712        // first_block
713        let exec_res = ExecutionOutcome {
714            bundle: Default::default(), // Default value for bundle
715            receipts,                   // Include the created receipts
716            requests: vec![],           // Empty vector for requests
717            first_block,                // Set the first block number
718        };
719
720        // Assert that the length of receipts in exec_res is 1
721        assert_eq!(exec_res.len(), 1);
722
723        // Assert that exec_res is not empty
724        assert!(!exec_res.is_empty());
725
726        // Create a ExecutionOutcome object with an empty Receipts object
727        let exec_res_empty_receipts: ExecutionOutcome<OpReceipt> = ExecutionOutcome {
728            bundle: Default::default(), // Default value for bundle
729            receipts: receipts_empty,   // Include the empty receipts
730            requests: vec![],           // Empty vector for requests
731            first_block,                // Set the first block number
732        };
733
734        // Assert that the length of receipts in exec_res_empty_receipts is 0
735        assert_eq!(exec_res_empty_receipts.len(), 0);
736
737        // Assert that exec_res_empty_receipts is empty
738        assert!(exec_res_empty_receipts.is_empty());
739    }
740
741    #[test]
742    fn test_revert_to() {
743        // Create a random receipt object
744        let receipt = OpReceipt::Legacy(Receipt::<Log> {
745            cumulative_gas_used: 46913,
746            logs: vec![],
747            status: true.into(),
748        });
749
750        // Create a Receipts object with a vector of receipt vectors
751        let receipts = vec![vec![Some(receipt.clone())], vec![Some(receipt.clone())]];
752
753        // Define the first block number
754        let first_block = 123;
755
756        // Create a request.
757        let request = bytes!("deadbeef");
758
759        // Create a vector of Requests containing the request.
760        let requests =
761            vec![Requests::new(vec![request.clone()]), Requests::new(vec![request.clone()])];
762
763        // Create a ExecutionOutcome object with the created bundle, receipts, requests, and
764        // first_block
765        let mut exec_res =
766            ExecutionOutcome { bundle: Default::default(), receipts, requests, first_block };
767
768        // Assert that the revert_to method returns true when reverting to the initial block number.
769        assert!(exec_res.revert_to(123));
770
771        // Assert that the receipts are properly cut after reverting to the initial block number.
772        assert_eq!(exec_res.receipts, vec![vec![Some(receipt)]]);
773
774        // Assert that the requests are properly cut after reverting to the initial block number.
775        assert_eq!(exec_res.requests, vec![Requests::new(vec![request])]);
776
777        // Assert that the revert_to method returns false when attempting to revert to a block
778        // number greater than the initial block number.
779        assert!(!exec_res.revert_to(133));
780
781        // Assert that the revert_to method returns false when attempting to revert to a block
782        // number less than the initial block number.
783        assert!(!exec_res.revert_to(10));
784    }
785
786    #[test]
787    fn test_extend_execution_outcome() {
788        // Create a Receipt object with specific attributes.
789        let receipt = OpReceipt::Legacy(Receipt::<Log> {
790            cumulative_gas_used: 46913,
791            logs: vec![],
792            status: true.into(),
793        });
794
795        // Create a Receipts object containing the receipt.
796        let receipts = vec![vec![Some(receipt.clone())]];
797
798        // Create a request.
799        let request = bytes!("deadbeef");
800
801        // Create a vector of Requests containing the request.
802        let requests = vec![Requests::new(vec![request.clone()])];
803
804        // Define the initial block number.
805        let first_block = 123;
806
807        // Create an ExecutionOutcome object.
808        let mut exec_res =
809            ExecutionOutcome { bundle: Default::default(), receipts, requests, first_block };
810
811        // Extend the ExecutionOutcome object by itself.
812        exec_res.extend(exec_res.clone());
813
814        // Assert the extended ExecutionOutcome matches the expected outcome.
815        assert_eq!(
816            exec_res,
817            ExecutionOutcome {
818                bundle: Default::default(),
819                receipts: vec![vec![Some(receipt.clone())], vec![Some(receipt)]],
820                requests: vec![Requests::new(vec![request.clone()]), Requests::new(vec![request])],
821                first_block: 123,
822            }
823        );
824    }
825
826    #[test]
827    fn test_split_at_execution_outcome() {
828        // Create a random receipt object
829        let receipt = OpReceipt::Legacy(Receipt::<Log> {
830            cumulative_gas_used: 46913,
831            logs: vec![],
832            status: true.into(),
833        });
834
835        // Create a Receipts object with a vector of receipt vectors
836        let receipts = vec![
837            vec![Some(receipt.clone())],
838            vec![Some(receipt.clone())],
839            vec![Some(receipt.clone())],
840        ];
841
842        // Define the first block number
843        let first_block = 123;
844
845        // Create a request.
846        let request = bytes!("deadbeef");
847
848        // Create a vector of Requests containing the request.
849        let requests = vec![
850            Requests::new(vec![request.clone()]),
851            Requests::new(vec![request.clone()]),
852            Requests::new(vec![request.clone()]),
853        ];
854
855        // Create a ExecutionOutcome object with the created bundle, receipts, requests, and
856        // first_block
857        let exec_res =
858            ExecutionOutcome { bundle: Default::default(), receipts, requests, first_block };
859
860        // Split the ExecutionOutcome at block number 124
861        let result = exec_res.clone().split_at(124);
862
863        // Define the expected lower ExecutionOutcome after splitting
864        let lower_execution_outcome = ExecutionOutcome {
865            bundle: Default::default(),
866            receipts: vec![vec![Some(receipt.clone())]],
867            requests: vec![Requests::new(vec![request.clone()])],
868            first_block,
869        };
870
871        // Define the expected higher ExecutionOutcome after splitting
872        let higher_execution_outcome = ExecutionOutcome {
873            bundle: Default::default(),
874            receipts: vec![vec![Some(receipt.clone())], vec![Some(receipt)]],
875            requests: vec![Requests::new(vec![request.clone()]), Requests::new(vec![request])],
876            first_block: 124,
877        };
878
879        // Assert that the split result matches the expected lower and higher outcomes
880        assert_eq!(result.0, Some(lower_execution_outcome));
881        assert_eq!(result.1, higher_execution_outcome);
882
883        // Assert that splitting at the first block number returns None for the lower outcome
884        assert_eq!(exec_res.clone().split_at(123), (None, exec_res));
885    }
886}