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_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#[derive(Debug)]
66pub struct OpEvmConfig<
67 ChainSpec = OpChainSpec,
68 N: NodePrimitives = OpPrimitives,
69 R = OpRethReceiptBuilder,
70 EvmFactory = OpEvmFactory,
71> {
72 pub executor_factory: OpBlockExecutorFactory<R, Arc<ChainSpec>, EvmFactory>,
74 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 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 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 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 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 let header = Header::default();
323
324 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 let EvmEnv { cfg_env, .. } =
337 OpEvmConfig::optimism(Arc::new(OpChainSpec { inner: chain_spec.clone() }))
338 .evm_env(&header)
339 .unwrap();
340
341 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 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 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 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 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 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 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 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 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 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 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 let block: RecoveredBlock<OpBlock> = Default::default();
488
489 let block1_hash = B256::new([0x01; 32]);
491 let block2_hash = B256::new([0x02; 32]);
492
493 let mut block1 = block.clone();
495 let mut block2 = block;
496
497 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 let receipt1 = OpReceipt::Legacy(Receipt::<Log> {
506 cumulative_gas_used: 46913,
507 logs: vec![],
508 status: true.into(),
509 });
510
511 let receipt2 = OpReceipt::Legacy(Receipt::<Log> {
513 cumulative_gas_used: 1325345,
514 logs: vec![],
515 status: true.into(),
516 });
517
518 let receipts = vec![vec![receipt1.clone()], vec![receipt2]];
520
521 let execution_outcome = ExecutionOutcome::<OpReceipt> {
524 bundle: Default::default(),
525 receipts,
526 requests: vec![],
527 first_block: 10,
528 };
529
530 let chain: Chain<OpPrimitives> =
533 Chain::new([block1, block2], execution_outcome.clone(), BTreeMap::new());
534
535 assert_eq!(chain.receipts_by_block_hash(block1_hash), Some(vec![&receipt1]));
537
538 let execution_outcome1 = ExecutionOutcome {
540 bundle: Default::default(),
541 receipts: vec![vec![receipt1]],
542 requests: vec![],
543 first_block: 10,
544 };
545
546 assert_eq!(chain.execution_outcome_at_block(10), Some(execution_outcome1));
548
549 assert_eq!(chain.execution_outcome_at_block(11), Some(execution_outcome));
551 }
552
553 #[test]
554 fn test_initialization() {
555 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 let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt::<Log> {
564 cumulative_gas_used: 46913,
565 logs: vec![],
566 status: true.into(),
567 }))]];
568
569 let requests = vec![Requests::new(vec![bytes!("dead"), bytes!("beef"), bytes!("beebee")])];
571
572 let first_block = 123;
574
575 let exec_res = ExecutionOutcome {
578 bundle: bundle.clone(),
579 receipts: receipts.clone(),
580 requests: requests.clone(),
581 first_block,
582 };
583
584 assert_eq!(
586 ExecutionOutcome::new(bundle, receipts.clone(), first_block, requests.clone()),
587 exec_res
588 );
589
590 let mut state_init: BundleStateInit = HashMap::default();
592 state_init
593 .insert(Address::new([2; 20]), (None, Some(Account::default()), HashMap::default()));
594
595 let mut revert_inner: HashMap<Address, AccountRevertInit> = HashMap::default();
597 revert_inner.insert(Address::new([2; 20]), (None, vec![]));
598
599 let mut revert_init: RevertsInit = HashMap::default();
601 revert_init.insert(123, revert_inner);
602
603 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 let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt::<Log> {
622 cumulative_gas_used: 46913,
623 logs: vec![],
624 status: true.into(),
625 }))]];
626
627 let first_block = 123;
629
630 let exec_res = ExecutionOutcome {
633 bundle: Default::default(),
634 receipts,
635 requests: vec![],
636 first_block,
637 };
638
639 assert_eq!(exec_res.block_number_to_index(12), None);
641
642 assert_eq!(exec_res.block_number_to_index(133), None);
644
645 assert_eq!(exec_res.block_number_to_index(123), Some(0));
647 }
648
649 #[test]
650 fn test_get_logs() {
651 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 let first_block = 123;
660
661 let exec_res = ExecutionOutcome {
664 bundle: Default::default(),
665 receipts,
666 requests: vec![],
667 first_block,
668 };
669
670 let logs: Vec<&Log> = exec_res.logs(123).unwrap().collect();
672
673 assert_eq!(logs, vec![&Log::<LogData>::default()]);
675 }
676
677 #[test]
678 fn test_receipts_by_block() {
679 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 let first_block = 123;
688
689 let exec_res = ExecutionOutcome {
692 bundle: Default::default(), receipts, requests: vec![], first_block, };
697
698 let receipts_by_block: Vec<_> = exec_res.receipts_by_block(123).iter().collect();
700
701 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 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 let receipts_empty = vec![];
723
724 let first_block = 123;
726
727 let exec_res = ExecutionOutcome {
730 bundle: Default::default(), receipts, requests: vec![], first_block, };
735
736 assert_eq!(exec_res.len(), 1);
738
739 assert!(!exec_res.is_empty());
741
742 let exec_res_empty_receipts: ExecutionOutcome<OpReceipt> = ExecutionOutcome {
744 bundle: Default::default(), receipts: receipts_empty, requests: vec![], first_block, };
749
750 assert_eq!(exec_res_empty_receipts.len(), 0);
752
753 assert!(exec_res_empty_receipts.is_empty());
755 }
756
757 #[test]
758 fn test_revert_to() {
759 let receipt = OpReceipt::Legacy(Receipt::<Log> {
761 cumulative_gas_used: 46913,
762 logs: vec![],
763 status: true.into(),
764 });
765
766 let receipts = vec![vec![Some(receipt.clone())], vec![Some(receipt.clone())]];
768
769 let first_block = 123;
771
772 let request = bytes!("deadbeef");
774
775 let requests =
777 vec![Requests::new(vec![request.clone()]), Requests::new(vec![request.clone()])];
778
779 let mut exec_res =
782 ExecutionOutcome { bundle: Default::default(), receipts, requests, first_block };
783
784 assert!(exec_res.revert_to(123));
786
787 assert_eq!(exec_res.receipts, vec![vec![Some(receipt)]]);
789
790 assert_eq!(exec_res.requests, vec![Requests::new(vec![request])]);
792
793 assert!(!exec_res.revert_to(133));
796
797 assert!(!exec_res.revert_to(10));
800 }
801
802 #[test]
803 fn test_extend_execution_outcome() {
804 let receipt = OpReceipt::Legacy(Receipt::<Log> {
806 cumulative_gas_used: 46913,
807 logs: vec![],
808 status: true.into(),
809 });
810
811 let receipts = vec![vec![Some(receipt.clone())]];
813
814 let request = bytes!("deadbeef");
816
817 let requests = vec![Requests::new(vec![request.clone()])];
819
820 let first_block = 123;
822
823 let mut exec_res =
825 ExecutionOutcome { bundle: Default::default(), receipts, requests, first_block };
826
827 exec_res.extend(exec_res.clone());
829
830 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 let receipt = OpReceipt::Legacy(Receipt::<Log> {
846 cumulative_gas_used: 46913,
847 logs: vec![],
848 status: true.into(),
849 });
850
851 let receipts = vec![
853 vec![Some(receipt.clone())],
854 vec![Some(receipt.clone())],
855 vec![Some(receipt.clone())],
856 ];
857
858 let first_block = 123;
860
861 let request = bytes!("deadbeef");
863
864 let requests = vec![
866 Requests::new(vec![request.clone()]),
867 Requests::new(vec![request.clone()]),
868 Requests::new(vec![request.clone()]),
869 ];
870
871 let exec_res =
874 ExecutionOutcome { bundle: Default::default(), receipts, requests, first_block };
875
876 let result = exec_res.clone().split_at(124);
878
879 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 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_eq!(result.0, Some(lower_execution_outcome));
897 assert_eq!(result.1, higher_execution_outcome);
898
899 assert_eq!(exec_res.clone().split_at(123), (None, exec_res));
901 }
902}