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