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