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