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, 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_eips::Decodable2718;
17use alloy_evm::{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 ConfigureEngineEvm, ConfigureEvm, EvmEnv, EvmEnvFor, ExecutableTxIterator, ExecutionCtxFor,
27};
28use reth_optimism_chainspec::OpChainSpec;
29use reth_optimism_forks::OpHardforks;
30use reth_optimism_primitives::{DepositReceipt, OpPrimitives};
31use reth_primitives_traits::{
32 NodePrimitives, SealedBlock, SealedHeader, SignedTransaction, TxTy, WithEncoded,
33};
34use reth_storage_errors::any::AnyError;
35use revm::{
36 context::{BlockEnv, CfgEnv, TxEnv},
37 context_interface::block::BlobExcessGasAndPrice,
38 primitives::hardfork::SpecId,
39};
40
41mod config;
42pub use config::{revm_spec, revm_spec_by_timestamp_after_bedrock, OpNextBlockEnvAttributes};
43mod execute;
44pub use execute::*;
45pub mod l1;
46pub use l1::*;
47mod receipts;
48pub use receipts::*;
49mod build;
50pub use build::OpBlockAssembler;
51
52mod error;
53pub use error::OpBlockExecutionError;
54
55pub use alloy_op_evm::{OpBlockExecutionCtx, OpBlockExecutorFactory, OpEvm, OpEvmFactory};
56
57#[derive(Debug)]
59pub struct OpEvmConfig<
60 ChainSpec = OpChainSpec,
61 N: NodePrimitives = OpPrimitives,
62 R = OpRethReceiptBuilder,
63> {
64 pub executor_factory: OpBlockExecutorFactory<R, Arc<ChainSpec>>,
66 pub block_assembler: OpBlockAssembler<ChainSpec>,
68 _pd: core::marker::PhantomData<N>,
69}
70
71impl<ChainSpec, N: NodePrimitives, R: Clone> Clone for OpEvmConfig<ChainSpec, N, R> {
72 fn clone(&self) -> Self {
73 Self {
74 executor_factory: self.executor_factory.clone(),
75 block_assembler: self.block_assembler.clone(),
76 _pd: self._pd,
77 }
78 }
79}
80
81impl<ChainSpec: OpHardforks> OpEvmConfig<ChainSpec> {
82 pub fn optimism(chain_spec: Arc<ChainSpec>) -> Self {
84 Self::new(chain_spec, OpRethReceiptBuilder::default())
85 }
86}
87
88impl<ChainSpec: OpHardforks, N: NodePrimitives, R> OpEvmConfig<ChainSpec, N, R> {
89 pub fn new(chain_spec: Arc<ChainSpec>, receipt_builder: R) -> Self {
91 Self {
92 block_assembler: OpBlockAssembler::new(chain_spec.clone()),
93 executor_factory: OpBlockExecutorFactory::new(
94 receipt_builder,
95 chain_spec,
96 OpEvmFactory::default(),
97 ),
98 _pd: core::marker::PhantomData,
99 }
100 }
101
102 pub const fn chain_spec(&self) -> &Arc<ChainSpec> {
104 self.executor_factory.spec()
105 }
106}
107
108impl<ChainSpec, N, R> ConfigureEvm for OpEvmConfig<ChainSpec, N, R>
109where
110 ChainSpec: EthChainSpec<Header = Header> + OpHardforks,
111 N: NodePrimitives<
112 Receipt = R::Receipt,
113 SignedTx = R::Transaction,
114 BlockHeader = Header,
115 BlockBody = alloy_consensus::BlockBody<R::Transaction>,
116 Block = alloy_consensus::Block<R::Transaction>,
117 >,
118 OpTransaction<TxEnv>: FromRecoveredTx<N::SignedTx> + FromTxWithEncoded<N::SignedTx>,
119 R: OpReceiptBuilder<Receipt: DepositReceipt, Transaction: SignedTransaction>,
120 Self: Send + Sync + Unpin + Clone + 'static,
121{
122 type Primitives = N;
123 type Error = EIP1559ParamError;
124 type NextBlockEnvCtx = OpNextBlockEnvAttributes;
125 type BlockExecutorFactory = OpBlockExecutorFactory<R, Arc<ChainSpec>>;
126 type BlockAssembler = OpBlockAssembler<ChainSpec>;
127
128 fn block_executor_factory(&self) -> &Self::BlockExecutorFactory {
129 &self.executor_factory
130 }
131
132 fn block_assembler(&self) -> &Self::BlockAssembler {
133 &self.block_assembler
134 }
135
136 fn evm_env(&self, header: &Header) -> EvmEnv<OpSpecId> {
137 let spec = config::revm_spec(self.chain_spec(), header);
138
139 let cfg_env = CfgEnv::new().with_chain_id(self.chain_spec().chain().id()).with_spec(spec);
140
141 let blob_excess_gas_and_price = spec
142 .into_eth_spec()
143 .is_enabled_in(SpecId::CANCUN)
144 .then_some(BlobExcessGasAndPrice { excess_blob_gas: 0, blob_gasprice: 1 });
145
146 let block_env = BlockEnv {
147 number: U256::from(header.number()),
148 beneficiary: header.beneficiary(),
149 timestamp: U256::from(header.timestamp()),
150 difficulty: if spec.into_eth_spec() >= SpecId::MERGE {
151 U256::ZERO
152 } else {
153 header.difficulty()
154 },
155 prevrandao: if spec.into_eth_spec() >= SpecId::MERGE {
156 header.mix_hash()
157 } else {
158 None
159 },
160 gas_limit: header.gas_limit(),
161 basefee: header.base_fee_per_gas().unwrap_or_default(),
162 blob_excess_gas_and_price,
164 };
165
166 EvmEnv { cfg_env, block_env }
167 }
168
169 fn next_evm_env(
170 &self,
171 parent: &Header,
172 attributes: &Self::NextBlockEnvCtx,
173 ) -> Result<EvmEnv<OpSpecId>, Self::Error> {
174 let spec_id = revm_spec_by_timestamp_after_bedrock(self.chain_spec(), attributes.timestamp);
176
177 let cfg_env =
179 CfgEnv::new().with_chain_id(self.chain_spec().chain().id()).with_spec(spec_id);
180
181 let blob_excess_gas_and_price = spec_id
184 .into_eth_spec()
185 .is_enabled_in(SpecId::CANCUN)
186 .then_some(BlobExcessGasAndPrice { excess_blob_gas: 0, blob_gasprice: 1 });
187
188 let block_env = BlockEnv {
189 number: U256::from(parent.number() + 1),
190 beneficiary: attributes.suggested_fee_recipient,
191 timestamp: U256::from(attributes.timestamp),
192 difficulty: U256::ZERO,
193 prevrandao: Some(attributes.prev_randao),
194 gas_limit: attributes.gas_limit,
195 basefee: self
197 .chain_spec()
198 .next_block_base_fee(parent, attributes.timestamp)
199 .unwrap_or_default(),
200 blob_excess_gas_and_price,
202 };
203
204 Ok(EvmEnv { cfg_env, block_env })
205 }
206
207 fn context_for_block(&self, block: &'_ SealedBlock<N::Block>) -> OpBlockExecutionCtx {
208 OpBlockExecutionCtx {
209 parent_hash: block.header().parent_hash(),
210 parent_beacon_block_root: block.header().parent_beacon_block_root(),
211 extra_data: block.header().extra_data().clone(),
212 }
213 }
214
215 fn context_for_next_block(
216 &self,
217 parent: &SealedHeader<N::BlockHeader>,
218 attributes: Self::NextBlockEnvCtx,
219 ) -> OpBlockExecutionCtx {
220 OpBlockExecutionCtx {
221 parent_hash: parent.hash(),
222 parent_beacon_block_root: attributes.parent_beacon_block_root,
223 extra_data: attributes.extra_data,
224 }
225 }
226}
227
228impl<ChainSpec, N, R> ConfigureEngineEvm<OpExecutionData> for OpEvmConfig<ChainSpec, N, R>
229where
230 ChainSpec: EthChainSpec<Header = Header> + OpHardforks,
231 N: NodePrimitives<
232 Receipt = R::Receipt,
233 SignedTx = R::Transaction,
234 BlockHeader = Header,
235 BlockBody = alloy_consensus::BlockBody<R::Transaction>,
236 Block = alloy_consensus::Block<R::Transaction>,
237 >,
238 OpTransaction<TxEnv>: FromRecoveredTx<N::SignedTx> + FromTxWithEncoded<N::SignedTx>,
239 R: OpReceiptBuilder<Receipt: DepositReceipt, Transaction: SignedTransaction>,
240 Self: Send + Sync + Unpin + Clone + 'static,
241{
242 fn evm_env_for_payload(&self, payload: &OpExecutionData) -> EvmEnvFor<Self> {
243 let timestamp = payload.payload.timestamp();
244 let block_number = payload.payload.block_number();
245
246 let spec = revm_spec_by_timestamp_after_bedrock(self.chain_spec(), timestamp);
247
248 let cfg_env = CfgEnv::new().with_chain_id(self.chain_spec().chain().id()).with_spec(spec);
249
250 let blob_excess_gas_and_price = spec
251 .into_eth_spec()
252 .is_enabled_in(SpecId::CANCUN)
253 .then_some(BlobExcessGasAndPrice { excess_blob_gas: 0, blob_gasprice: 1 });
254
255 let block_env = BlockEnv {
256 number: U256::from(block_number),
257 beneficiary: payload.payload.as_v1().fee_recipient,
258 timestamp: U256::from(timestamp),
259 difficulty: if spec.into_eth_spec() >= SpecId::MERGE {
260 U256::ZERO
261 } else {
262 payload.payload.as_v1().prev_randao.into()
263 },
264 prevrandao: (spec.into_eth_spec() >= SpecId::MERGE)
265 .then(|| payload.payload.as_v1().prev_randao),
266 gas_limit: payload.payload.as_v1().gas_limit,
267 basefee: payload.payload.as_v1().base_fee_per_gas.to(),
268 blob_excess_gas_and_price,
270 };
271
272 EvmEnv { cfg_env, block_env }
273 }
274
275 fn context_for_payload<'a>(&self, payload: &'a OpExecutionData) -> ExecutionCtxFor<'a, Self> {
276 OpBlockExecutionCtx {
277 parent_hash: payload.parent_hash(),
278 parent_beacon_block_root: payload.sidecar.parent_beacon_block_root(),
279 extra_data: payload.payload.as_v1().extra_data.clone(),
280 }
281 }
282
283 fn tx_iterator_for_payload(
284 &self,
285 payload: &OpExecutionData,
286 ) -> impl ExecutableTxIterator<Self> {
287 payload.payload.transactions().clone().into_iter().map(|encoded| {
288 let tx = TxTy::<Self::Primitives>::decode_2718_exact(encoded.as_ref())
289 .map_err(AnyError::new)?;
290 let signer = tx.try_recover().map_err(AnyError::new)?;
291 Ok::<_, AnyError>(WithEncoded::new(encoded, tx.with_signer(signer)))
292 })
293 }
294}
295
296#[cfg(test)]
297mod tests {
298 use super::*;
299 use alloy_consensus::{Header, Receipt};
300 use alloy_eips::eip7685::Requests;
301 use alloy_genesis::Genesis;
302 use alloy_primitives::{bytes, map::HashMap, Address, LogData, B256};
303 use op_revm::OpSpecId;
304 use reth_chainspec::ChainSpec;
305 use reth_evm::execute::ProviderError;
306 use reth_execution_types::{
307 AccountRevertInit, BundleStateInit, Chain, ExecutionOutcome, RevertsInit,
308 };
309 use reth_optimism_chainspec::{OpChainSpec, BASE_MAINNET};
310 use reth_optimism_primitives::{OpBlock, OpPrimitives, OpReceipt};
311 use reth_primitives_traits::{Account, RecoveredBlock};
312 use revm::{
313 database::{BundleState, CacheDB},
314 database_interface::EmptyDBTyped,
315 inspector::NoOpInspector,
316 primitives::Log,
317 state::AccountInfo,
318 };
319 use std::sync::Arc;
320
321 fn test_evm_config() -> OpEvmConfig {
322 OpEvmConfig::optimism(BASE_MAINNET.clone())
323 }
324
325 #[test]
326 fn test_fill_cfg_and_block_env() {
327 let header = Header::default();
329
330 let chain_spec = ChainSpec::builder()
333 .chain(0.into())
334 .genesis(Genesis::default())
335 .london_activated()
336 .paris_activated()
337 .shanghai_activated()
338 .build();
339
340 let EvmEnv { cfg_env, .. } =
343 OpEvmConfig::optimism(Arc::new(OpChainSpec { inner: chain_spec.clone() }))
344 .evm_env(&header);
345
346 assert_eq!(cfg_env.chain_id, chain_spec.chain().id());
349 }
350
351 #[test]
352 fn test_evm_with_env_default_spec() {
353 let evm_config = test_evm_config();
354
355 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
356
357 let evm_env = EvmEnv::default();
358
359 let evm = evm_config.evm_with_env(db, evm_env.clone());
360
361 assert_eq!(evm.cfg, evm_env.cfg_env);
363 }
364
365 #[test]
366 fn test_evm_with_env_custom_cfg() {
367 let evm_config = test_evm_config();
368
369 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
370
371 let cfg = CfgEnv::new().with_chain_id(111).with_spec(OpSpecId::default());
373
374 let evm_env = EvmEnv { cfg_env: cfg.clone(), ..Default::default() };
375
376 let evm = evm_config.evm_with_env(db, evm_env);
377
378 assert_eq!(evm.cfg, cfg);
380 }
381
382 #[test]
383 fn test_evm_with_env_custom_block_and_tx() {
384 let evm_config = test_evm_config();
385
386 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
387
388 let block = BlockEnv {
390 basefee: 1000,
391 gas_limit: 10_000_000,
392 number: U256::from(42),
393 ..Default::default()
394 };
395
396 let evm_env = EvmEnv { block_env: block, ..Default::default() };
397
398 let evm = evm_config.evm_with_env(db, evm_env.clone());
399
400 assert_eq!(evm.block, evm_env.block_env);
402 }
403
404 #[test]
405 fn test_evm_with_spec_id() {
406 let evm_config = test_evm_config();
407
408 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
409
410 let evm_env =
411 EvmEnv { cfg_env: CfgEnv::new().with_spec(OpSpecId::ECOTONE), ..Default::default() };
412
413 let evm = evm_config.evm_with_env(db, evm_env.clone());
414
415 assert_eq!(evm.cfg, evm_env.cfg_env);
416 }
417
418 #[test]
419 fn test_evm_with_env_and_default_inspector() {
420 let evm_config = test_evm_config();
421 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
422
423 let evm_env = EvmEnv { cfg_env: Default::default(), ..Default::default() };
424
425 let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
426
427 assert_eq!(evm.block, evm_env.block_env);
429 assert_eq!(evm.cfg, evm_env.cfg_env);
430 }
431
432 #[test]
433 fn test_evm_with_env_inspector_and_custom_cfg() {
434 let evm_config = test_evm_config();
435 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
436
437 let cfg = CfgEnv::new().with_chain_id(111).with_spec(OpSpecId::default());
438 let block = BlockEnv::default();
439 let evm_env = EvmEnv { block_env: block, cfg_env: cfg.clone() };
440
441 let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
442
443 assert_eq!(evm.cfg, cfg);
445 assert_eq!(evm.block, evm_env.block_env);
446 }
447
448 #[test]
449 fn test_evm_with_env_inspector_and_custom_block_tx() {
450 let evm_config = test_evm_config();
451 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
452
453 let block = BlockEnv {
455 basefee: 1000,
456 gas_limit: 10_000_000,
457 number: U256::from(42),
458 ..Default::default()
459 };
460 let evm_env = EvmEnv { block_env: block, ..Default::default() };
461
462 let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
463
464 assert_eq!(evm.block, evm_env.block_env);
466 }
467
468 #[test]
469 fn test_evm_with_env_inspector_and_spec_id() {
470 let evm_config = test_evm_config();
471 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
472
473 let evm_env =
474 EvmEnv { cfg_env: CfgEnv::new().with_spec(OpSpecId::ECOTONE), ..Default::default() };
475
476 let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
477
478 assert_eq!(evm.cfg, evm_env.cfg_env);
480 assert_eq!(evm.block, evm_env.block_env);
481 }
482
483 #[test]
484 fn receipts_by_block_hash() {
485 let block: RecoveredBlock<OpBlock> = Default::default();
487
488 let block1_hash = B256::new([0x01; 32]);
490 let block2_hash = B256::new([0x02; 32]);
491
492 let mut block1 = block.clone();
494 let mut block2 = block;
495
496 block1.set_block_number(10);
498 block1.set_hash(block1_hash);
499
500 block2.set_block_number(11);
501 block2.set_hash(block2_hash);
502
503 let receipt1 = OpReceipt::Legacy(Receipt {
505 cumulative_gas_used: 46913,
506 logs: vec![],
507 status: true.into(),
508 });
509
510 let receipt2 = OpReceipt::Legacy(Receipt {
512 cumulative_gas_used: 1325345,
513 logs: vec![],
514 status: true.into(),
515 });
516
517 let receipts = vec![vec![receipt1.clone()], vec![receipt2]];
519
520 let execution_outcome = ExecutionOutcome::<OpReceipt> {
523 bundle: Default::default(),
524 receipts,
525 requests: vec![],
526 first_block: 10,
527 };
528
529 let chain: Chain<OpPrimitives> =
532 Chain::new([block1, block2], execution_outcome.clone(), None);
533
534 assert_eq!(chain.receipts_by_block_hash(block1_hash), Some(vec![&receipt1]));
536
537 let execution_outcome1 = ExecutionOutcome {
539 bundle: Default::default(),
540 receipts: vec![vec![receipt1]],
541 requests: vec![],
542 first_block: 10,
543 };
544
545 assert_eq!(chain.execution_outcome_at_block(10), Some(execution_outcome1));
547
548 assert_eq!(chain.execution_outcome_at_block(11), Some(execution_outcome));
550 }
551
552 #[test]
553 fn test_initialization() {
554 let bundle = BundleState::new(
556 vec![(Address::new([2; 20]), None, Some(AccountInfo::default()), HashMap::default())],
557 vec![vec![(Address::new([2; 20]), None, vec![])]],
558 vec![],
559 );
560
561 let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt {
563 cumulative_gas_used: 46913,
564 logs: vec![],
565 status: true.into(),
566 }))]];
567
568 let requests = vec![Requests::new(vec![bytes!("dead"), bytes!("beef"), bytes!("beebee")])];
570
571 let first_block = 123;
573
574 let exec_res = ExecutionOutcome {
577 bundle: bundle.clone(),
578 receipts: receipts.clone(),
579 requests: requests.clone(),
580 first_block,
581 };
582
583 assert_eq!(
585 ExecutionOutcome::new(bundle, receipts.clone(), first_block, requests.clone()),
586 exec_res
587 );
588
589 let mut state_init: BundleStateInit = HashMap::default();
591 state_init
592 .insert(Address::new([2; 20]), (None, Some(Account::default()), HashMap::default()));
593
594 let mut revert_inner: HashMap<Address, AccountRevertInit> = HashMap::default();
596 revert_inner.insert(Address::new([2; 20]), (None, vec![]));
597
598 let mut revert_init: RevertsInit = HashMap::default();
600 revert_init.insert(123, revert_inner);
601
602 assert_eq!(
605 ExecutionOutcome::new_init(
606 state_init,
607 revert_init,
608 vec![],
609 receipts,
610 first_block,
611 requests,
612 ),
613 exec_res
614 );
615 }
616
617 #[test]
618 fn test_block_number_to_index() {
619 let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt {
621 cumulative_gas_used: 46913,
622 logs: vec![],
623 status: true.into(),
624 }))]];
625
626 let first_block = 123;
628
629 let exec_res = ExecutionOutcome {
632 bundle: Default::default(),
633 receipts,
634 requests: vec![],
635 first_block,
636 };
637
638 assert_eq!(exec_res.block_number_to_index(12), None);
640
641 assert_eq!(exec_res.block_number_to_index(133), None);
643
644 assert_eq!(exec_res.block_number_to_index(123), Some(0));
646 }
647
648 #[test]
649 fn test_get_logs() {
650 let receipts = vec![vec![OpReceipt::Legacy(Receipt {
652 cumulative_gas_used: 46913,
653 logs: vec![Log::<LogData>::default()],
654 status: true.into(),
655 })]];
656
657 let first_block = 123;
659
660 let exec_res = ExecutionOutcome {
663 bundle: Default::default(),
664 receipts,
665 requests: vec![],
666 first_block,
667 };
668
669 let logs: Vec<&Log> = exec_res.logs(123).unwrap().collect();
671
672 assert_eq!(logs, vec![&Log::<LogData>::default()]);
674 }
675
676 #[test]
677 fn test_receipts_by_block() {
678 let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt {
680 cumulative_gas_used: 46913,
681 logs: vec![Log::<LogData>::default()],
682 status: true.into(),
683 }))]];
684
685 let first_block = 123;
687
688 let exec_res = ExecutionOutcome {
691 bundle: Default::default(), receipts, requests: vec![], first_block, };
696
697 let receipts_by_block: Vec<_> = exec_res.receipts_by_block(123).iter().collect();
699
700 assert_eq!(
702 receipts_by_block,
703 vec![&Some(OpReceipt::Legacy(Receipt {
704 cumulative_gas_used: 46913,
705 logs: vec![Log::<LogData>::default()],
706 status: true.into(),
707 }))]
708 );
709 }
710
711 #[test]
712 fn test_receipts_len() {
713 let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt {
715 cumulative_gas_used: 46913,
716 logs: vec![Log::<LogData>::default()],
717 status: true.into(),
718 }))]];
719
720 let receipts_empty = vec![];
722
723 let first_block = 123;
725
726 let exec_res = ExecutionOutcome {
729 bundle: Default::default(), receipts, requests: vec![], first_block, };
734
735 assert_eq!(exec_res.len(), 1);
737
738 assert!(!exec_res.is_empty());
740
741 let exec_res_empty_receipts: ExecutionOutcome<OpReceipt> = ExecutionOutcome {
743 bundle: Default::default(), receipts: receipts_empty, requests: vec![], first_block, };
748
749 assert_eq!(exec_res_empty_receipts.len(), 0);
751
752 assert!(exec_res_empty_receipts.is_empty());
754 }
755
756 #[test]
757 fn test_revert_to() {
758 let receipt = OpReceipt::Legacy(Receipt {
760 cumulative_gas_used: 46913,
761 logs: vec![],
762 status: true.into(),
763 });
764
765 let receipts = vec![vec![Some(receipt.clone())], vec![Some(receipt.clone())]];
767
768 let first_block = 123;
770
771 let request = bytes!("deadbeef");
773
774 let requests =
776 vec![Requests::new(vec![request.clone()]), Requests::new(vec![request.clone()])];
777
778 let mut exec_res =
781 ExecutionOutcome { bundle: Default::default(), receipts, requests, first_block };
782
783 assert!(exec_res.revert_to(123));
785
786 assert_eq!(exec_res.receipts, vec![vec![Some(receipt)]]);
788
789 assert_eq!(exec_res.requests, vec![Requests::new(vec![request])]);
791
792 assert!(!exec_res.revert_to(133));
795
796 assert!(!exec_res.revert_to(10));
799 }
800
801 #[test]
802 fn test_extend_execution_outcome() {
803 let receipt = OpReceipt::Legacy(Receipt {
805 cumulative_gas_used: 46913,
806 logs: vec![],
807 status: true.into(),
808 });
809
810 let receipts = vec![vec![Some(receipt.clone())]];
812
813 let request = bytes!("deadbeef");
815
816 let requests = vec![Requests::new(vec![request.clone()])];
818
819 let first_block = 123;
821
822 let mut exec_res =
824 ExecutionOutcome { bundle: Default::default(), receipts, requests, first_block };
825
826 exec_res.extend(exec_res.clone());
828
829 assert_eq!(
831 exec_res,
832 ExecutionOutcome {
833 bundle: Default::default(),
834 receipts: vec![vec![Some(receipt.clone())], vec![Some(receipt)]],
835 requests: vec![Requests::new(vec![request.clone()]), Requests::new(vec![request])],
836 first_block: 123,
837 }
838 );
839 }
840
841 #[test]
842 fn test_split_at_execution_outcome() {
843 let receipt = OpReceipt::Legacy(Receipt {
845 cumulative_gas_used: 46913,
846 logs: vec![],
847 status: true.into(),
848 });
849
850 let receipts = vec![
852 vec![Some(receipt.clone())],
853 vec![Some(receipt.clone())],
854 vec![Some(receipt.clone())],
855 ];
856
857 let first_block = 123;
859
860 let request = bytes!("deadbeef");
862
863 let requests = vec![
865 Requests::new(vec![request.clone()]),
866 Requests::new(vec![request.clone()]),
867 Requests::new(vec![request.clone()]),
868 ];
869
870 let exec_res =
873 ExecutionOutcome { bundle: Default::default(), receipts, requests, first_block };
874
875 let result = exec_res.clone().split_at(124);
877
878 let lower_execution_outcome = ExecutionOutcome {
880 bundle: Default::default(),
881 receipts: vec![vec![Some(receipt.clone())]],
882 requests: vec![Requests::new(vec![request.clone()])],
883 first_block,
884 };
885
886 let higher_execution_outcome = ExecutionOutcome {
888 bundle: Default::default(),
889 receipts: vec![vec![Some(receipt.clone())], vec![Some(receipt)]],
890 requests: vec![Requests::new(vec![request.clone()]), Requests::new(vec![request])],
891 first_block: 124,
892 };
893
894 assert_eq!(result.0, Some(lower_execution_outcome));
896 assert_eq!(result.1, higher_execution_outcome);
897
898 assert_eq!(exec_res.clone().split_at(123), (None, exec_res));
900 }
901}