1#![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#[derive(Debug)]
60pub struct OpEvmConfig<
61 ChainSpec = OpChainSpec,
62 N: NodePrimitives = OpPrimitives,
63 R = OpRethReceiptBuilder,
64 EvmFactory = OpEvmFactory,
65> {
66 pub executor_factory: OpBlockExecutorFactory<R, Arc<ChainSpec>, EvmFactory>,
68 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 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 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 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 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 let header = Header::default();
308
309 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 let EvmEnv { cfg_env, .. } =
322 OpEvmConfig::optimism(Arc::new(OpChainSpec { inner: chain_spec.clone() }))
323 .evm_env(&header)
324 .unwrap();
325
326 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 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 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 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 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 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 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 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 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 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 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 let block: RecoveredBlock<OpBlock> = Default::default();
467
468 let block1_hash = B256::new([0x01; 32]);
470 let block2_hash = B256::new([0x02; 32]);
471
472 let mut block1 = block.clone();
474 let mut block2 = block;
475
476 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 let receipt1 = OpReceipt::Legacy(Receipt {
485 cumulative_gas_used: 46913,
486 logs: vec![],
487 status: true.into(),
488 });
489
490 let receipt2 = OpReceipt::Legacy(Receipt {
492 cumulative_gas_used: 1325345,
493 logs: vec![],
494 status: true.into(),
495 });
496
497 let receipts = vec![vec![receipt1.clone()], vec![receipt2]];
499
500 let execution_outcome = ExecutionOutcome::<OpReceipt> {
503 bundle: Default::default(),
504 receipts,
505 requests: vec![],
506 first_block: 10,
507 };
508
509 let chain: Chain<OpPrimitives> =
512 Chain::new([block1, block2], execution_outcome.clone(), None);
513
514 assert_eq!(chain.receipts_by_block_hash(block1_hash), Some(vec![&receipt1]));
516
517 let execution_outcome1 = ExecutionOutcome {
519 bundle: Default::default(),
520 receipts: vec![vec![receipt1]],
521 requests: vec![],
522 first_block: 10,
523 };
524
525 assert_eq!(chain.execution_outcome_at_block(10), Some(execution_outcome1));
527
528 assert_eq!(chain.execution_outcome_at_block(11), Some(execution_outcome));
530 }
531
532 #[test]
533 fn test_initialization() {
534 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 let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt {
543 cumulative_gas_used: 46913,
544 logs: vec![],
545 status: true.into(),
546 }))]];
547
548 let requests = vec![Requests::new(vec![bytes!("dead"), bytes!("beef"), bytes!("beebee")])];
550
551 let first_block = 123;
553
554 let exec_res = ExecutionOutcome {
557 bundle: bundle.clone(),
558 receipts: receipts.clone(),
559 requests: requests.clone(),
560 first_block,
561 };
562
563 assert_eq!(
565 ExecutionOutcome::new(bundle, receipts.clone(), first_block, requests.clone()),
566 exec_res
567 );
568
569 let mut state_init: BundleStateInit = HashMap::default();
571 state_init
572 .insert(Address::new([2; 20]), (None, Some(Account::default()), HashMap::default()));
573
574 let mut revert_inner: HashMap<Address, AccountRevertInit> = HashMap::default();
576 revert_inner.insert(Address::new([2; 20]), (None, vec![]));
577
578 let mut revert_init: RevertsInit = HashMap::default();
580 revert_init.insert(123, revert_inner);
581
582 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 let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt {
601 cumulative_gas_used: 46913,
602 logs: vec![],
603 status: true.into(),
604 }))]];
605
606 let first_block = 123;
608
609 let exec_res = ExecutionOutcome {
612 bundle: Default::default(),
613 receipts,
614 requests: vec![],
615 first_block,
616 };
617
618 assert_eq!(exec_res.block_number_to_index(12), None);
620
621 assert_eq!(exec_res.block_number_to_index(133), None);
623
624 assert_eq!(exec_res.block_number_to_index(123), Some(0));
626 }
627
628 #[test]
629 fn test_get_logs() {
630 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 let first_block = 123;
639
640 let exec_res = ExecutionOutcome {
643 bundle: Default::default(),
644 receipts,
645 requests: vec![],
646 first_block,
647 };
648
649 let logs: Vec<&Log> = exec_res.logs(123).unwrap().collect();
651
652 assert_eq!(logs, vec![&Log::<LogData>::default()]);
654 }
655
656 #[test]
657 fn test_receipts_by_block() {
658 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 let first_block = 123;
667
668 let exec_res = ExecutionOutcome {
671 bundle: Default::default(), receipts, requests: vec![], first_block, };
676
677 let receipts_by_block: Vec<_> = exec_res.receipts_by_block(123).iter().collect();
679
680 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 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 let receipts_empty = vec![];
702
703 let first_block = 123;
705
706 let exec_res = ExecutionOutcome {
709 bundle: Default::default(), receipts, requests: vec![], first_block, };
714
715 assert_eq!(exec_res.len(), 1);
717
718 assert!(!exec_res.is_empty());
720
721 let exec_res_empty_receipts: ExecutionOutcome<OpReceipt> = ExecutionOutcome {
723 bundle: Default::default(), receipts: receipts_empty, requests: vec![], first_block, };
728
729 assert_eq!(exec_res_empty_receipts.len(), 0);
731
732 assert!(exec_res_empty_receipts.is_empty());
734 }
735
736 #[test]
737 fn test_revert_to() {
738 let receipt = OpReceipt::Legacy(Receipt {
740 cumulative_gas_used: 46913,
741 logs: vec![],
742 status: true.into(),
743 });
744
745 let receipts = vec![vec![Some(receipt.clone())], vec![Some(receipt.clone())]];
747
748 let first_block = 123;
750
751 let request = bytes!("deadbeef");
753
754 let requests =
756 vec![Requests::new(vec![request.clone()]), Requests::new(vec![request.clone()])];
757
758 let mut exec_res =
761 ExecutionOutcome { bundle: Default::default(), receipts, requests, first_block };
762
763 assert!(exec_res.revert_to(123));
765
766 assert_eq!(exec_res.receipts, vec![vec![Some(receipt)]]);
768
769 assert_eq!(exec_res.requests, vec![Requests::new(vec![request])]);
771
772 assert!(!exec_res.revert_to(133));
775
776 assert!(!exec_res.revert_to(10));
779 }
780
781 #[test]
782 fn test_extend_execution_outcome() {
783 let receipt = OpReceipt::Legacy(Receipt {
785 cumulative_gas_used: 46913,
786 logs: vec![],
787 status: true.into(),
788 });
789
790 let receipts = vec![vec![Some(receipt.clone())]];
792
793 let request = bytes!("deadbeef");
795
796 let requests = vec![Requests::new(vec![request.clone()])];
798
799 let first_block = 123;
801
802 let mut exec_res =
804 ExecutionOutcome { bundle: Default::default(), receipts, requests, first_block };
805
806 exec_res.extend(exec_res.clone());
808
809 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 let receipt = OpReceipt::Legacy(Receipt {
825 cumulative_gas_used: 46913,
826 logs: vec![],
827 status: true.into(),
828 });
829
830 let receipts = vec![
832 vec![Some(receipt.clone())],
833 vec![Some(receipt.clone())],
834 vec![Some(receipt.clone())],
835 ];
836
837 let first_block = 123;
839
840 let request = bytes!("deadbeef");
842
843 let requests = vec![
845 Requests::new(vec![request.clone()]),
846 Requests::new(vec![request.clone()]),
847 Requests::new(vec![request.clone()]),
848 ];
849
850 let exec_res =
853 ExecutionOutcome { bundle: Default::default(), receipts, requests, first_block };
854
855 let result = exec_res.clone().split_at(124);
857
858 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 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_eq!(result.0, Some(lower_execution_outcome));
876 assert_eq!(result.1, higher_execution_outcome);
877
878 assert_eq!(exec_res.clone().split_at(123), (None, exec_res));
880 }
881}