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, OpTxEnv};
19use alloy_primitives::{Bytes, 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 + OpTxEnv,
136 Precompiles = PrecompilesMap,
137 Spec = OpSpecId,
138 BlockEnv = BlockEnv,
139 > + Debug,
140 Self: Send + Sync + Unpin + Clone + 'static,
141{
142 type Primitives = N;
143 type Error = EIP1559ParamError;
144 type NextBlockEnvCtx = OpNextBlockEnvAttributes;
145 type BlockExecutorFactory = OpBlockExecutorFactory<R, Arc<ChainSpec>, EvmF>;
146 type BlockAssembler = OpBlockAssembler<ChainSpec>;
147
148 fn block_executor_factory(&self) -> &Self::BlockExecutorFactory {
149 &self.executor_factory
150 }
151
152 fn block_assembler(&self) -> &Self::BlockAssembler {
153 &self.block_assembler
154 }
155
156 fn evm_env(&self, header: &Header) -> Result<EvmEnv<OpSpecId>, Self::Error> {
157 Ok(EvmEnv::for_op_block(header, self.chain_spec(), self.chain_spec().chain().id()))
158 }
159
160 fn next_evm_env(
161 &self,
162 parent: &Header,
163 attributes: &Self::NextBlockEnvCtx,
164 ) -> Result<EvmEnv<OpSpecId>, Self::Error> {
165 Ok(EvmEnv::for_op_next_block(
166 parent,
167 NextEvmEnvAttributes {
168 timestamp: attributes.timestamp,
169 suggested_fee_recipient: attributes.suggested_fee_recipient,
170 prev_randao: attributes.prev_randao,
171 gas_limit: attributes.gas_limit,
172 },
173 self.chain_spec().next_block_base_fee(parent, attributes.timestamp).unwrap_or_default(),
174 self.chain_spec(),
175 self.chain_spec().chain().id(),
176 ))
177 }
178
179 fn context_for_block(
180 &self,
181 block: &'_ SealedBlock<N::Block>,
182 ) -> Result<OpBlockExecutionCtx, Self::Error> {
183 Ok(OpBlockExecutionCtx {
184 parent_hash: block.header().parent_hash(),
185 parent_beacon_block_root: block.header().parent_beacon_block_root(),
186 extra_data: block.header().extra_data().clone(),
187 })
188 }
189
190 fn context_for_next_block(
191 &self,
192 parent: &SealedHeader<N::BlockHeader>,
193 attributes: Self::NextBlockEnvCtx,
194 ) -> Result<OpBlockExecutionCtx, Self::Error> {
195 Ok(OpBlockExecutionCtx {
196 parent_hash: parent.hash(),
197 parent_beacon_block_root: attributes.parent_beacon_block_root,
198 extra_data: attributes.extra_data,
199 })
200 }
201}
202
203impl<ChainSpec, N, R> ConfigureEngineEvm<OpExecutionData> for OpEvmConfig<ChainSpec, N, R>
204where
205 ChainSpec: EthChainSpec<Header = Header> + OpHardforks,
206 N: NodePrimitives<
207 Receipt = R::Receipt,
208 SignedTx = R::Transaction,
209 BlockHeader = Header,
210 BlockBody = alloy_consensus::BlockBody<R::Transaction>,
211 Block = alloy_consensus::Block<R::Transaction>,
212 >,
213 OpTransaction<TxEnv>: FromRecoveredTx<N::SignedTx> + FromTxWithEncoded<N::SignedTx>,
214 R: OpReceiptBuilder<Receipt: DepositReceipt, Transaction: SignedTransaction>,
215 Self: Send + Sync + Unpin + Clone + 'static,
216{
217 fn evm_env_for_payload(
218 &self,
219 payload: &OpExecutionData,
220 ) -> Result<EvmEnvFor<Self>, Self::Error> {
221 let timestamp = payload.payload.timestamp();
222 let block_number = payload.payload.block_number();
223
224 let spec = revm_spec_by_timestamp_after_bedrock(self.chain_spec(), timestamp);
225
226 let cfg_env = CfgEnv::new().with_chain_id(self.chain_spec().chain().id()).with_spec(spec);
227
228 let blob_excess_gas_and_price = spec
229 .into_eth_spec()
230 .is_enabled_in(SpecId::CANCUN)
231 .then_some(BlobExcessGasAndPrice { excess_blob_gas: 0, blob_gasprice: 1 });
232
233 let block_env = BlockEnv {
234 number: U256::from(block_number),
235 beneficiary: payload.payload.as_v1().fee_recipient,
236 timestamp: U256::from(timestamp),
237 difficulty: if spec.into_eth_spec() >= SpecId::MERGE {
238 U256::ZERO
239 } else {
240 payload.payload.as_v1().prev_randao.into()
241 },
242 prevrandao: (spec.into_eth_spec() >= SpecId::MERGE)
243 .then(|| payload.payload.as_v1().prev_randao),
244 gas_limit: payload.payload.as_v1().gas_limit,
245 basefee: payload.payload.as_v1().base_fee_per_gas.to(),
246 blob_excess_gas_and_price,
248 };
249
250 Ok(EvmEnv { cfg_env, block_env })
251 }
252
253 fn context_for_payload<'a>(
254 &self,
255 payload: &'a OpExecutionData,
256 ) -> Result<ExecutionCtxFor<'a, Self>, Self::Error> {
257 Ok(OpBlockExecutionCtx {
258 parent_hash: payload.parent_hash(),
259 parent_beacon_block_root: payload.sidecar.parent_beacon_block_root(),
260 extra_data: payload.payload.as_v1().extra_data.clone(),
261 })
262 }
263
264 fn tx_iterator_for_payload(
265 &self,
266 payload: &OpExecutionData,
267 ) -> Result<impl ExecutableTxIterator<Self>, Self::Error> {
268 let transactions = payload.payload.transactions().clone().into_iter();
269 let convert = |encoded: Bytes| {
270 let tx = TxTy::<Self::Primitives>::decode_2718_exact(encoded.as_ref())
271 .map_err(AnyError::new)?;
272 let signer = tx.try_recover().map_err(AnyError::new)?;
273 Ok::<_, AnyError>(WithEncoded::new(encoded, tx.with_signer(signer)))
274 };
275
276 Ok((transactions, convert))
277 }
278}
279
280#[cfg(test)]
281mod tests {
282 use super::*;
283 use alloy_consensus::{Header, Receipt};
284 use alloy_eips::eip7685::Requests;
285 use alloy_genesis::Genesis;
286 use alloy_primitives::{bytes, map::HashMap, Address, LogData, B256};
287 use op_revm::OpSpecId;
288 use reth_chainspec::ChainSpec;
289 use reth_evm::execute::ProviderError;
290 use reth_execution_types::{
291 AccountRevertInit, BundleStateInit, Chain, ExecutionOutcome, RevertsInit,
292 };
293 use reth_optimism_chainspec::{OpChainSpec, BASE_MAINNET};
294 use reth_optimism_primitives::{OpBlock, OpPrimitives, OpReceipt};
295 use reth_primitives_traits::{Account, RecoveredBlock};
296 use revm::{
297 database::{BundleState, CacheDB},
298 database_interface::EmptyDBTyped,
299 inspector::NoOpInspector,
300 primitives::Log,
301 state::AccountInfo,
302 };
303 use std::sync::Arc;
304
305 fn test_evm_config() -> OpEvmConfig {
306 OpEvmConfig::optimism(BASE_MAINNET.clone())
307 }
308
309 #[test]
310 fn test_fill_cfg_and_block_env() {
311 let header = Header::default();
313
314 let chain_spec = ChainSpec::builder()
317 .chain(0.into())
318 .genesis(Genesis::default())
319 .london_activated()
320 .paris_activated()
321 .shanghai_activated()
322 .build();
323
324 let EvmEnv { cfg_env, .. } =
327 OpEvmConfig::optimism(Arc::new(OpChainSpec { inner: chain_spec.clone() }))
328 .evm_env(&header)
329 .unwrap();
330
331 assert_eq!(cfg_env.chain_id, chain_spec.chain().id());
334 }
335
336 #[test]
337 fn test_evm_with_env_default_spec() {
338 let evm_config = test_evm_config();
339
340 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
341
342 let evm_env = EvmEnv::default();
343
344 let evm = evm_config.evm_with_env(db, evm_env.clone());
345
346 assert_eq!(evm.cfg, evm_env.cfg_env);
348 }
349
350 #[test]
351 fn test_evm_with_env_custom_cfg() {
352 let evm_config = test_evm_config();
353
354 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
355
356 let cfg = CfgEnv::new().with_chain_id(111).with_spec(OpSpecId::default());
358
359 let evm_env = EvmEnv { cfg_env: cfg.clone(), ..Default::default() };
360
361 let evm = evm_config.evm_with_env(db, evm_env);
362
363 assert_eq!(evm.cfg, cfg);
365 }
366
367 #[test]
368 fn test_evm_with_env_custom_block_and_tx() {
369 let evm_config = test_evm_config();
370
371 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
372
373 let block = BlockEnv {
375 basefee: 1000,
376 gas_limit: 10_000_000,
377 number: U256::from(42),
378 ..Default::default()
379 };
380
381 let evm_env = EvmEnv { block_env: block, ..Default::default() };
382
383 let evm = evm_config.evm_with_env(db, evm_env.clone());
384
385 assert_eq!(evm.block, evm_env.block_env);
387 }
388
389 #[test]
390 fn test_evm_with_spec_id() {
391 let evm_config = test_evm_config();
392
393 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
394
395 let evm_env =
396 EvmEnv { cfg_env: CfgEnv::new().with_spec(OpSpecId::ECOTONE), ..Default::default() };
397
398 let evm = evm_config.evm_with_env(db, evm_env.clone());
399
400 assert_eq!(evm.cfg, evm_env.cfg_env);
401 }
402
403 #[test]
404 fn test_evm_with_env_and_default_inspector() {
405 let evm_config = test_evm_config();
406 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
407
408 let evm_env = EvmEnv { cfg_env: Default::default(), ..Default::default() };
409
410 let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
411
412 assert_eq!(evm.block, evm_env.block_env);
414 assert_eq!(evm.cfg, evm_env.cfg_env);
415 }
416
417 #[test]
418 fn test_evm_with_env_inspector_and_custom_cfg() {
419 let evm_config = test_evm_config();
420 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
421
422 let cfg = CfgEnv::new().with_chain_id(111).with_spec(OpSpecId::default());
423 let block = BlockEnv::default();
424 let evm_env = EvmEnv { block_env: block, cfg_env: cfg.clone() };
425
426 let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
427
428 assert_eq!(evm.cfg, cfg);
430 assert_eq!(evm.block, evm_env.block_env);
431 }
432
433 #[test]
434 fn test_evm_with_env_inspector_and_custom_block_tx() {
435 let evm_config = test_evm_config();
436 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
437
438 let block = BlockEnv {
440 basefee: 1000,
441 gas_limit: 10_000_000,
442 number: U256::from(42),
443 ..Default::default()
444 };
445 let evm_env = EvmEnv { block_env: block, ..Default::default() };
446
447 let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
448
449 assert_eq!(evm.block, evm_env.block_env);
451 }
452
453 #[test]
454 fn test_evm_with_env_inspector_and_spec_id() {
455 let evm_config = test_evm_config();
456 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
457
458 let evm_env =
459 EvmEnv { cfg_env: CfgEnv::new().with_spec(OpSpecId::ECOTONE), ..Default::default() };
460
461 let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
462
463 assert_eq!(evm.cfg, evm_env.cfg_env);
465 assert_eq!(evm.block, evm_env.block_env);
466 }
467
468 #[test]
469 fn receipts_by_block_hash() {
470 let block: RecoveredBlock<OpBlock> = Default::default();
472
473 let block1_hash = B256::new([0x01; 32]);
475 let block2_hash = B256::new([0x02; 32]);
476
477 let mut block1 = block.clone();
479 let mut block2 = block;
480
481 block1.set_block_number(10);
483 block1.set_hash(block1_hash);
484
485 block2.set_block_number(11);
486 block2.set_hash(block2_hash);
487
488 let receipt1 = OpReceipt::Legacy(Receipt::<Log> {
490 cumulative_gas_used: 46913,
491 logs: vec![],
492 status: true.into(),
493 });
494
495 let receipt2 = OpReceipt::Legacy(Receipt::<Log> {
497 cumulative_gas_used: 1325345,
498 logs: vec![],
499 status: true.into(),
500 });
501
502 let receipts = vec![vec![receipt1.clone()], vec![receipt2]];
504
505 let execution_outcome = ExecutionOutcome::<OpReceipt> {
508 bundle: Default::default(),
509 receipts,
510 requests: vec![],
511 first_block: 10,
512 };
513
514 let chain: Chain<OpPrimitives> =
517 Chain::new([block1, block2], execution_outcome.clone(), None);
518
519 assert_eq!(chain.receipts_by_block_hash(block1_hash), Some(vec![&receipt1]));
521
522 let execution_outcome1 = ExecutionOutcome {
524 bundle: Default::default(),
525 receipts: vec![vec![receipt1]],
526 requests: vec![],
527 first_block: 10,
528 };
529
530 assert_eq!(chain.execution_outcome_at_block(10), Some(execution_outcome1));
532
533 assert_eq!(chain.execution_outcome_at_block(11), Some(execution_outcome));
535 }
536
537 #[test]
538 fn test_initialization() {
539 let bundle = BundleState::new(
541 vec![(Address::new([2; 20]), None, Some(AccountInfo::default()), HashMap::default())],
542 vec![vec![(Address::new([2; 20]), None, vec![])]],
543 vec![],
544 );
545
546 let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt::<Log> {
548 cumulative_gas_used: 46913,
549 logs: vec![],
550 status: true.into(),
551 }))]];
552
553 let requests = vec![Requests::new(vec![bytes!("dead"), bytes!("beef"), bytes!("beebee")])];
555
556 let first_block = 123;
558
559 let exec_res = ExecutionOutcome {
562 bundle: bundle.clone(),
563 receipts: receipts.clone(),
564 requests: requests.clone(),
565 first_block,
566 };
567
568 assert_eq!(
570 ExecutionOutcome::new(bundle, receipts.clone(), first_block, requests.clone()),
571 exec_res
572 );
573
574 let mut state_init: BundleStateInit = HashMap::default();
576 state_init
577 .insert(Address::new([2; 20]), (None, Some(Account::default()), HashMap::default()));
578
579 let mut revert_inner: HashMap<Address, AccountRevertInit> = HashMap::default();
581 revert_inner.insert(Address::new([2; 20]), (None, vec![]));
582
583 let mut revert_init: RevertsInit = HashMap::default();
585 revert_init.insert(123, revert_inner);
586
587 assert_eq!(
590 ExecutionOutcome::new_init(
591 state_init,
592 revert_init,
593 vec![],
594 receipts,
595 first_block,
596 requests,
597 ),
598 exec_res
599 );
600 }
601
602 #[test]
603 fn test_block_number_to_index() {
604 let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt::<Log> {
606 cumulative_gas_used: 46913,
607 logs: vec![],
608 status: true.into(),
609 }))]];
610
611 let first_block = 123;
613
614 let exec_res = ExecutionOutcome {
617 bundle: Default::default(),
618 receipts,
619 requests: vec![],
620 first_block,
621 };
622
623 assert_eq!(exec_res.block_number_to_index(12), None);
625
626 assert_eq!(exec_res.block_number_to_index(133), None);
628
629 assert_eq!(exec_res.block_number_to_index(123), Some(0));
631 }
632
633 #[test]
634 fn test_get_logs() {
635 let receipts = vec![vec![OpReceipt::Legacy(Receipt::<Log> {
637 cumulative_gas_used: 46913,
638 logs: vec![Log::<LogData>::default()],
639 status: true.into(),
640 })]];
641
642 let first_block = 123;
644
645 let exec_res = ExecutionOutcome {
648 bundle: Default::default(),
649 receipts,
650 requests: vec![],
651 first_block,
652 };
653
654 let logs: Vec<&Log> = exec_res.logs(123).unwrap().collect();
656
657 assert_eq!(logs, vec![&Log::<LogData>::default()]);
659 }
660
661 #[test]
662 fn test_receipts_by_block() {
663 let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt::<Log> {
665 cumulative_gas_used: 46913,
666 logs: vec![Log::<LogData>::default()],
667 status: true.into(),
668 }))]];
669
670 let first_block = 123;
672
673 let exec_res = ExecutionOutcome {
676 bundle: Default::default(), receipts, requests: vec![], first_block, };
681
682 let receipts_by_block: Vec<_> = exec_res.receipts_by_block(123).iter().collect();
684
685 assert_eq!(
687 receipts_by_block,
688 vec![&Some(OpReceipt::Legacy(Receipt::<Log> {
689 cumulative_gas_used: 46913,
690 logs: vec![Log::<LogData>::default()],
691 status: true.into(),
692 }))]
693 );
694 }
695
696 #[test]
697 fn test_receipts_len() {
698 let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt::<Log> {
700 cumulative_gas_used: 46913,
701 logs: vec![Log::<LogData>::default()],
702 status: true.into(),
703 }))]];
704
705 let receipts_empty = vec![];
707
708 let first_block = 123;
710
711 let exec_res = ExecutionOutcome {
714 bundle: Default::default(), receipts, requests: vec![], first_block, };
719
720 assert_eq!(exec_res.len(), 1);
722
723 assert!(!exec_res.is_empty());
725
726 let exec_res_empty_receipts: ExecutionOutcome<OpReceipt> = ExecutionOutcome {
728 bundle: Default::default(), receipts: receipts_empty, requests: vec![], first_block, };
733
734 assert_eq!(exec_res_empty_receipts.len(), 0);
736
737 assert!(exec_res_empty_receipts.is_empty());
739 }
740
741 #[test]
742 fn test_revert_to() {
743 let receipt = OpReceipt::Legacy(Receipt::<Log> {
745 cumulative_gas_used: 46913,
746 logs: vec![],
747 status: true.into(),
748 });
749
750 let receipts = vec![vec![Some(receipt.clone())], vec![Some(receipt.clone())]];
752
753 let first_block = 123;
755
756 let request = bytes!("deadbeef");
758
759 let requests =
761 vec![Requests::new(vec![request.clone()]), Requests::new(vec![request.clone()])];
762
763 let mut exec_res =
766 ExecutionOutcome { bundle: Default::default(), receipts, requests, first_block };
767
768 assert!(exec_res.revert_to(123));
770
771 assert_eq!(exec_res.receipts, vec![vec![Some(receipt)]]);
773
774 assert_eq!(exec_res.requests, vec![Requests::new(vec![request])]);
776
777 assert!(!exec_res.revert_to(133));
780
781 assert!(!exec_res.revert_to(10));
784 }
785
786 #[test]
787 fn test_extend_execution_outcome() {
788 let receipt = OpReceipt::Legacy(Receipt::<Log> {
790 cumulative_gas_used: 46913,
791 logs: vec![],
792 status: true.into(),
793 });
794
795 let receipts = vec![vec![Some(receipt.clone())]];
797
798 let request = bytes!("deadbeef");
800
801 let requests = vec![Requests::new(vec![request.clone()])];
803
804 let first_block = 123;
806
807 let mut exec_res =
809 ExecutionOutcome { bundle: Default::default(), receipts, requests, first_block };
810
811 exec_res.extend(exec_res.clone());
813
814 assert_eq!(
816 exec_res,
817 ExecutionOutcome {
818 bundle: Default::default(),
819 receipts: vec![vec![Some(receipt.clone())], vec![Some(receipt)]],
820 requests: vec![Requests::new(vec![request.clone()]), Requests::new(vec![request])],
821 first_block: 123,
822 }
823 );
824 }
825
826 #[test]
827 fn test_split_at_execution_outcome() {
828 let receipt = OpReceipt::Legacy(Receipt::<Log> {
830 cumulative_gas_used: 46913,
831 logs: vec![],
832 status: true.into(),
833 });
834
835 let receipts = vec![
837 vec![Some(receipt.clone())],
838 vec![Some(receipt.clone())],
839 vec![Some(receipt.clone())],
840 ];
841
842 let first_block = 123;
844
845 let request = bytes!("deadbeef");
847
848 let requests = vec![
850 Requests::new(vec![request.clone()]),
851 Requests::new(vec![request.clone()]),
852 Requests::new(vec![request.clone()]),
853 ];
854
855 let exec_res =
858 ExecutionOutcome { bundle: Default::default(), receipts, requests, first_block };
859
860 let result = exec_res.clone().split_at(124);
862
863 let lower_execution_outcome = ExecutionOutcome {
865 bundle: Default::default(),
866 receipts: vec![vec![Some(receipt.clone())]],
867 requests: vec![Requests::new(vec![request.clone()])],
868 first_block,
869 };
870
871 let higher_execution_outcome = ExecutionOutcome {
873 bundle: Default::default(),
874 receipts: vec![vec![Some(receipt.clone())], vec![Some(receipt)]],
875 requests: vec![Requests::new(vec![request.clone()]), Requests::new(vec![request])],
876 first_block: 124,
877 };
878
879 assert_eq!(result.0, Some(lower_execution_outcome));
881 assert_eq!(result.1, higher_execution_outcome);
882
883 assert_eq!(exec_res.clone().split_at(123), (None, exec_res));
885 }
886}