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/SeismicSystems/seismic-reth/issues/"
7)]
8#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
9#![cfg_attr(not(feature = "std"), no_std)]
10
11extern crate alloc;
12
13use alloc::{borrow::Cow, sync::Arc};
14use alloy_consensus::{BlockHeader, Header};
15use alloy_eips::eip1559::INITIAL_BASE_FEE;
16use alloy_evm::eth::EthBlockExecutionCtx;
17use alloy_primitives::{Bytes, U256};
18use build::SeismicBlockAssembler;
19use core::fmt::Debug;
20use reth_chainspec::{ChainSpec, EthChainSpec};
21use reth_ethereum_forks::EthereumHardfork;
22use reth_evm::{ConfigureEvm, EvmEnv, NextBlockEnvAttributes};
23use reth_primitives_traits::{SealedBlock, SealedHeader};
24use reth_seismic_primitives::{SeismicBlock, SeismicPrimitives};
25use revm::{
26 context::{BlockEnv, CfgEnv},
27 context_interface::block::BlobExcessGasAndPrice,
28};
29use seismic_enclave::rpc::SyncEnclaveApiClientBuilder;
30use seismic_revm::SeismicSpecId;
31use std::convert::Infallible;
32
33mod receipts;
34pub use receipts::*;
35mod build;
36
37pub mod config;
38use config::revm_spec;
39
40pub use alloy_seismic_evm::{block::SeismicBlockExecutorFactory, SeismicEvm, SeismicEvmFactory};
41
42#[derive(Debug, Clone)]
44pub struct SeismicEvmConfig<CB>
45where
46 CB: SyncEnclaveApiClientBuilder,
47{
48 pub executor_factory: SeismicBlockExecutorFactory<
50 CB,
51 SeismicRethReceiptBuilder,
52 Arc<ChainSpec>,
53 SeismicEvmFactory<CB>,
54 >,
55 pub block_assembler: SeismicBlockAssembler<ChainSpec>,
57}
58
59impl<CB> SeismicEvmConfig<CB>
60where
61 CB: SyncEnclaveApiClientBuilder,
62{
63 pub fn seismic(chain_spec: Arc<ChainSpec>, enclave_client: CB) -> Self {
65 SeismicEvmConfig::new_with_evm_factory(
66 chain_spec,
67 SeismicEvmFactory::<CB>::new(enclave_client.clone()),
68 enclave_client,
69 )
70 }
71}
72
73impl<CB> SeismicEvmConfig<CB>
74where
75 CB: SyncEnclaveApiClientBuilder,
76{
77 pub fn new(chain_spec: Arc<ChainSpec>, enclave_client: CB) -> Self {
79 Self::seismic(chain_spec, enclave_client)
80 }
81
82 pub fn new_with_evm_factory(
84 chain_spec: Arc<ChainSpec>,
85 evm_factory: SeismicEvmFactory<CB>,
86 client_builder: CB,
87 ) -> Self {
88 Self {
89 block_assembler: SeismicBlockAssembler::new(chain_spec.clone()),
90 executor_factory: SeismicBlockExecutorFactory::new(
91 SeismicRethReceiptBuilder::default(),
92 chain_spec,
93 evm_factory,
94 client_builder,
95 ),
96 }
97 }
98
99 pub const fn chain_spec(&self) -> &Arc<ChainSpec> {
101 self.executor_factory.spec()
102 }
103
104 pub fn with_extra_data(mut self, extra_data: Bytes) -> Self {
106 self.block_assembler.extra_data = extra_data;
107 self
108 }
109}
110
111impl<CB> ConfigureEvm for SeismicEvmConfig<CB>
112where
113 CB: SyncEnclaveApiClientBuilder + 'static,
114{
115 type Primitives = SeismicPrimitives;
116 type Error = Infallible;
117 type NextBlockEnvCtx = NextBlockEnvAttributes;
118 type BlockExecutorFactory = SeismicBlockExecutorFactory<
119 CB,
120 SeismicRethReceiptBuilder,
121 Arc<ChainSpec>,
122 SeismicEvmFactory<CB>,
123 >;
124 type BlockAssembler = SeismicBlockAssembler<ChainSpec>;
125
126 fn block_executor_factory(&self) -> &Self::BlockExecutorFactory {
127 &self.executor_factory
128 }
129
130 fn block_assembler(&self) -> &Self::BlockAssembler {
131 &self.block_assembler
132 }
133
134 fn evm_env(&self, header: &Header) -> EvmEnv<SeismicSpecId> {
135 let spec = SeismicSpecId::MERCURY;
137
138 let cfg_env = CfgEnv::new().with_chain_id(self.chain_spec().chain().id()).with_spec(spec);
140
141 let block_env = BlockEnv {
142 number: header.number(),
143 beneficiary: header.beneficiary(),
144 timestamp: header.timestamp(),
145 difficulty: U256::ZERO,
146 prevrandao: header.mix_hash(), gas_limit: header.gas_limit(),
149 basefee: header.base_fee_per_gas().unwrap_or_default(),
150 blob_excess_gas_and_price: header
152 .excess_blob_gas
153 .map(|excess_blob_gas| BlobExcessGasAndPrice::new(excess_blob_gas, true)),
154 };
155
156 EvmEnv { cfg_env, block_env }
157 }
158
159 fn next_evm_env(
160 &self,
161 parent: &Header,
162 attributes: &NextBlockEnvAttributes,
163 ) -> Result<EvmEnv<SeismicSpecId>, Self::Error> {
164 let spec_id = revm_spec(self.chain_spec(), parent);
165
166 let cfg = CfgEnv::new().with_chain_id(self.chain_spec().chain().id()).with_spec(spec_id);
168
169 let blob_excess_gas_and_price = parent
172 .maybe_next_block_excess_blob_gas(
173 self.chain_spec().blob_params_at_timestamp(attributes.timestamp),
174 )
175 .map(|gas| BlobExcessGasAndPrice::new(gas, spec_id >= SeismicSpecId::MERCURY));
176
177 let mut basefee = parent.next_block_base_fee(
178 self.chain_spec().base_fee_params_at_timestamp(attributes.timestamp),
179 );
180
181 let mut gas_limit = attributes.gas_limit;
182
183 if self.chain_spec().fork(EthereumHardfork::London).transitions_at_block(parent.number + 1)
186 {
187 let elasticity_multiplier = self
188 .chain_spec()
189 .base_fee_params_at_timestamp(attributes.timestamp)
190 .elasticity_multiplier;
191
192 gas_limit *= elasticity_multiplier as u64;
194
195 basefee = Some(INITIAL_BASE_FEE)
197 }
198
199 let block_env = BlockEnv {
200 number: parent.number + 1,
201 beneficiary: attributes.suggested_fee_recipient,
202 timestamp: attributes.timestamp,
203 difficulty: U256::ZERO,
204 prevrandao: Some(attributes.prev_randao),
205 gas_limit,
206 basefee: basefee.unwrap_or_default(),
208 blob_excess_gas_and_price,
210 };
211
212 Ok((cfg, block_env).into())
213 }
214
215 fn context_for_block<'a>(
216 &self,
217 block: &'a SealedBlock<SeismicBlock>,
218 ) -> EthBlockExecutionCtx<'a> {
219 EthBlockExecutionCtx {
220 parent_hash: block.header().parent_hash,
221 parent_beacon_block_root: block.header().parent_beacon_block_root,
222 ommers: &block.body().ommers,
223 withdrawals: block.body().withdrawals.as_ref().map(Cow::Borrowed),
224 }
225 }
226
227 fn context_for_next_block(
228 &self,
229 parent: &SealedHeader,
230 attributes: Self::NextBlockEnvCtx,
231 ) -> EthBlockExecutionCtx<'_> {
232 EthBlockExecutionCtx {
233 parent_hash: parent.hash(),
234 parent_beacon_block_root: attributes.parent_beacon_block_root,
235 ommers: &[],
236 withdrawals: attributes.withdrawals.map(Cow::Owned),
237 }
238 }
239}
240
241#[cfg(test)]
242mod tests {
243 use super::*;
244 use alloy_consensus::{Header, Receipt};
245 use alloy_eips::eip7685::Requests;
246 use alloy_evm::Evm;
247 use alloy_genesis::Genesis;
248 use alloy_primitives::{bytes, map::HashMap, Address, LogData, B256};
249 use reth_chainspec::ChainSpec;
250 use reth_evm::execute::ProviderError;
251 use reth_execution_types::{
252 AccountRevertInit, BundleStateInit, Chain, ExecutionOutcome, RevertsInit,
253 };
254 use reth_primitives_traits::{Account, RecoveredBlock};
255 use reth_seismic_chainspec::SEISMIC_MAINNET;
256 use reth_seismic_primitives::{SeismicBlock, SeismicPrimitives, SeismicReceipt};
257 use revm::{
258 database::{BundleState, CacheDB},
259 database_interface::EmptyDBTyped,
260 handler::PrecompileProvider,
261 inspector::NoOpInspector,
262 precompile::u64_to_address,
263 primitives::Log,
264 state::AccountInfo,
265 };
266 use seismic_enclave::MockEnclaveClientBuilder;
267 use std::sync::Arc;
268
269 fn test_evm_config() -> SeismicEvmConfig<MockEnclaveClientBuilder> {
270 SeismicEvmConfig::seismic(SEISMIC_MAINNET.clone(), MockEnclaveClientBuilder::new())
271 }
272
273 #[test]
274 fn test_fill_cfg_and_block_env() {
275 let header = Header::default();
277
278 let chain_spec = ChainSpec::builder()
281 .chain(0.into())
282 .genesis(Genesis::default())
283 .london_activated()
284 .paris_activated()
285 .shanghai_activated()
286 .build();
287
288 let EvmEnv { cfg_env, .. } = SeismicEvmConfig::seismic(
291 Arc::new(chain_spec.clone()),
292 MockEnclaveClientBuilder::new(),
293 )
294 .evm_env(&header);
295
296 assert_eq!(cfg_env.chain_id, chain_spec.chain().id());
299 }
300
301 #[test]
302 fn test_seismic_evm_with_env_default_spec() {
303 let evm_config = test_evm_config(); let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
306 let evm_env = EvmEnv::default();
307 let evm: SeismicEvm<_, NoOpInspector> = evm_config.evm_with_env(db, evm_env.clone());
308 let precompiles = evm.precompiles().clone();
309
310 assert_eq!(evm.cfg, evm_env.cfg_env);
312 assert_eq!(evm.cfg.spec, SeismicSpecId::MERCURY);
313
314 let precompile_addresses =
316 [u64_to_address(101), u64_to_address(102), u64_to_address(103), u64_to_address(104)];
317 for &addr in &precompile_addresses {
318 let is_contained = precompiles.contains(&addr);
319 assert!(
320 is_contained,
321 "Expected Precompile at address for RETH evm generation {addr:?}"
322 );
323 }
324 }
325
326 #[test]
327 fn test_evm_with_env_custom_cfg() {
328 let evm_config = test_evm_config();
329
330 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
331
332 let cfg = CfgEnv::new().with_chain_id(111).with_spec(SeismicSpecId::default());
334
335 let evm_env = EvmEnv { cfg_env: cfg.clone(), ..Default::default() };
336
337 let evm = evm_config.evm_with_env(db, evm_env);
338
339 assert_eq!(evm.cfg, cfg);
341 }
342
343 #[test]
344 fn test_evm_with_env_custom_block_and_tx() {
345 let evm_config = test_evm_config();
346
347 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
348
349 let block =
351 BlockEnv { basefee: 1000, gas_limit: 10_000_000, number: 42, ..Default::default() };
352
353 let evm_env = EvmEnv { block_env: block, ..Default::default() };
354
355 let evm = evm_config.evm_with_env(db, evm_env.clone());
356
357 assert_eq!(evm.block, evm_env.block_env);
359 }
360
361 #[test]
362 fn test_evm_with_spec_id() {
363 let evm_config = test_evm_config();
364
365 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
366
367 let evm_env = EvmEnv {
368 cfg_env: CfgEnv::new().with_spec(SeismicSpecId::MERCURY),
369 ..Default::default()
370 };
371
372 let evm = evm_config.evm_with_env(db, evm_env.clone());
373
374 assert_eq!(evm.cfg, evm_env.cfg_env);
375 }
376
377 #[test]
378 fn test_evm_with_env_and_default_inspector() {
379 let evm_config = test_evm_config();
380 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
381
382 let evm_env = EvmEnv { cfg_env: Default::default(), ..Default::default() };
383
384 let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
385
386 assert_eq!(*evm.block(), evm_env.block_env);
388 assert_eq!(evm.cfg, evm_env.cfg_env);
389 }
390
391 #[test]
392 fn test_evm_with_env_inspector_and_custom_cfg() {
393 let evm_config = test_evm_config();
394 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
395
396 let cfg = CfgEnv::new().with_chain_id(111).with_spec(SeismicSpecId::MERCURY);
397 let block = BlockEnv::default();
398 let evm_env = EvmEnv { block_env: block, cfg_env: cfg.clone() };
399
400 let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
401
402 assert_eq!(evm.cfg, cfg);
404 assert_eq!(evm.block, evm_env.block_env);
405 }
406
407 #[test]
408 fn test_evm_with_env_inspector_and_custom_block_tx() {
409 let evm_config = test_evm_config();
410 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
411
412 let block =
414 BlockEnv { basefee: 1000, gas_limit: 10_000_000, number: 42, ..Default::default() };
415 let evm_env = EvmEnv { block_env: block, ..Default::default() };
416
417 let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
418
419 assert_eq!(evm.block, evm_env.block_env);
421 }
422
423 #[test]
424 fn test_evm_with_env_inspector_and_spec_id() {
425 let evm_config = test_evm_config();
426 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
427
428 let evm_env = EvmEnv {
429 cfg_env: CfgEnv::new().with_spec(SeismicSpecId::MERCURY),
430 ..Default::default()
431 };
432
433 let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector {});
434
435 assert_eq!(evm.cfg, evm_env.cfg_env);
437 assert_eq!(evm.block, evm_env.block_env);
438 }
439
440 #[test]
441 fn receipts_by_block_hash() {
442 let block: RecoveredBlock<SeismicBlock> = Default::default();
444
445 let block1_hash = B256::new([0x01; 32]);
447 let block2_hash = B256::new([0x02; 32]);
448
449 let mut block1 = block.clone();
451 let mut block2 = block;
452
453 block1.set_block_number(10);
455 block1.set_hash(block1_hash);
456
457 block2.set_block_number(11);
458 block2.set_hash(block2_hash);
459
460 let receipt1 = SeismicReceipt::Legacy(Receipt {
462 cumulative_gas_used: 46913,
463 logs: vec![],
464 status: true.into(),
465 });
466
467 let receipt2 = SeismicReceipt::Legacy(Receipt {
469 cumulative_gas_used: 1325345,
470 logs: vec![],
471 status: true.into(),
472 });
473
474 let receipts = vec![vec![receipt1.clone()], vec![receipt2]];
476
477 let execution_outcome = ExecutionOutcome::<SeismicReceipt> {
480 bundle: Default::default(),
481 receipts,
482 requests: vec![],
483 first_block: 10,
484 };
485
486 let chain: Chain<SeismicPrimitives> =
489 Chain::new([block1, block2], execution_outcome.clone(), None);
490
491 assert_eq!(chain.receipts_by_block_hash(block1_hash), Some(vec![&receipt1]));
493
494 let execution_outcome1 = ExecutionOutcome {
496 bundle: Default::default(),
497 receipts: vec![vec![receipt1]],
498 requests: vec![],
499 first_block: 10,
500 };
501
502 assert_eq!(chain.execution_outcome_at_block(10), Some(execution_outcome1));
504
505 assert_eq!(chain.execution_outcome_at_block(11), Some(execution_outcome));
507 }
508
509 #[test]
510 fn test_initialisation() {
511 let bundle = BundleState::new(
513 vec![(Address::new([2; 20]), None, Some(AccountInfo::default()), HashMap::default())],
514 vec![vec![(Address::new([2; 20]), None, vec![])]],
515 vec![],
516 );
517
518 let receipts = vec![vec![Some(SeismicReceipt::Legacy(Receipt {
520 cumulative_gas_used: 46913,
521 logs: vec![],
522 status: true.into(),
523 }))]];
524
525 let requests = vec![Requests::new(vec![bytes!("dead"), bytes!("beef"), bytes!("beebee")])];
527
528 let first_block = 123;
530
531 let exec_res = ExecutionOutcome {
534 bundle: bundle.clone(),
535 receipts: receipts.clone(),
536 requests: requests.clone(),
537 first_block,
538 };
539
540 assert_eq!(
542 ExecutionOutcome::new(bundle, receipts.clone(), first_block, requests.clone()),
543 exec_res
544 );
545
546 let mut state_init: BundleStateInit = HashMap::default();
548 state_init
549 .insert(Address::new([2; 20]), (None, Some(Account::default()), HashMap::default()));
550
551 let mut revert_inner: HashMap<Address, AccountRevertInit> = HashMap::default();
553 revert_inner.insert(Address::new([2; 20]), (None, vec![]));
554
555 let mut revert_init: RevertsInit = HashMap::default();
557 revert_init.insert(123, revert_inner);
558
559 assert_eq!(
562 ExecutionOutcome::new_init(
563 state_init,
564 revert_init,
565 vec![],
566 receipts,
567 first_block,
568 requests,
569 ),
570 exec_res
571 );
572 }
573
574 #[test]
575 fn test_block_number_to_index() {
576 let receipts = vec![vec![Some(SeismicReceipt::Legacy(Receipt {
578 cumulative_gas_used: 46913,
579 logs: vec![],
580 status: true.into(),
581 }))]];
582
583 let first_block = 123;
585
586 let exec_res = ExecutionOutcome {
589 bundle: Default::default(),
590 receipts,
591 requests: vec![],
592 first_block,
593 };
594
595 assert_eq!(exec_res.block_number_to_index(12), None);
597
598 assert_eq!(exec_res.block_number_to_index(133), None);
600
601 assert_eq!(exec_res.block_number_to_index(123), Some(0));
603 }
604
605 #[test]
606 fn test_get_logs() {
607 let receipts = vec![vec![SeismicReceipt::Legacy(Receipt {
609 cumulative_gas_used: 46913,
610 logs: vec![Log::<LogData>::default()],
611 status: true.into(),
612 })]];
613
614 let first_block = 123;
616
617 let exec_res = ExecutionOutcome {
620 bundle: Default::default(),
621 receipts,
622 requests: vec![],
623 first_block,
624 };
625
626 let logs: Vec<&Log> = exec_res.logs(123).unwrap().collect();
628
629 assert_eq!(logs, vec![&Log::<LogData>::default()]);
631 }
632
633 #[test]
634 fn test_receipts_by_block() {
635 let receipts = vec![vec![Some(SeismicReceipt::Legacy(Receipt {
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(), receipts, requests: vec![], first_block, };
653
654 let receipts_by_block: Vec<_> = exec_res.receipts_by_block(123).iter().collect();
656
657 assert_eq!(
659 receipts_by_block,
660 vec![&Some(SeismicReceipt::Legacy(Receipt {
661 cumulative_gas_used: 46913,
662 logs: vec![Log::<LogData>::default()],
663 status: true.into(),
664 }))]
665 );
666 }
667
668 #[test]
669 fn test_receipts_len() {
670 let receipts = vec![vec![Some(SeismicReceipt::Legacy(Receipt {
672 cumulative_gas_used: 46913,
673 logs: vec![Log::<LogData>::default()],
674 status: true.into(),
675 }))]];
676
677 let receipts_empty = vec![];
679
680 let first_block = 123;
682
683 let exec_res = ExecutionOutcome {
686 bundle: Default::default(), receipts, requests: vec![], first_block, };
691
692 assert_eq!(exec_res.len(), 1);
694
695 assert!(!exec_res.is_empty());
697
698 let exec_res_empty_receipts: ExecutionOutcome<SeismicReceipt> = ExecutionOutcome {
700 bundle: Default::default(), receipts: receipts_empty, requests: vec![], first_block, };
705
706 assert_eq!(exec_res_empty_receipts.len(), 0);
708
709 assert!(exec_res_empty_receipts.is_empty());
711 }
712
713 #[test]
714 fn test_revert_to() {
715 let receipt = SeismicReceipt::Legacy(Receipt {
717 cumulative_gas_used: 46913,
718 logs: vec![],
719 status: true.into(),
720 });
721
722 let receipts = vec![vec![Some(receipt.clone())], vec![Some(receipt.clone())]];
724
725 let first_block = 123;
727
728 let request = bytes!("deadbeef");
730
731 let requests =
733 vec![Requests::new(vec![request.clone()]), Requests::new(vec![request.clone()])];
734
735 let mut exec_res =
738 ExecutionOutcome { bundle: Default::default(), receipts, requests, first_block };
739
740 assert!(exec_res.revert_to(123));
742
743 assert_eq!(exec_res.receipts, vec![vec![Some(receipt)]]);
745
746 assert_eq!(exec_res.requests, vec![Requests::new(vec![request])]);
748
749 assert!(!exec_res.revert_to(133));
752
753 assert!(!exec_res.revert_to(10));
756 }
757
758 #[test]
759 fn test_extend_execution_outcome() {
760 let receipt = SeismicReceipt::Legacy(Receipt {
762 cumulative_gas_used: 46913,
763 logs: vec![],
764 status: true.into(),
765 });
766
767 let receipts = vec![vec![Some(receipt.clone())]];
769
770 let request = bytes!("deadbeef");
772
773 let requests = vec![Requests::new(vec![request.clone()])];
775
776 let first_block = 123;
778
779 let mut exec_res =
781 ExecutionOutcome { bundle: Default::default(), receipts, requests, first_block };
782
783 exec_res.extend(exec_res.clone());
785
786 assert_eq!(
788 exec_res,
789 ExecutionOutcome {
790 bundle: Default::default(),
791 receipts: vec![vec![Some(receipt.clone())], vec![Some(receipt)]],
792 requests: vec![Requests::new(vec![request.clone()]), Requests::new(vec![request])],
793 first_block: 123,
794 }
795 );
796 }
797
798 #[test]
799 fn test_split_at_execution_outcome() {
800 let receipt = SeismicReceipt::Legacy(Receipt {
802 cumulative_gas_used: 46913,
803 logs: vec![],
804 status: true.into(),
805 });
806
807 let receipts = vec![
809 vec![Some(receipt.clone())],
810 vec![Some(receipt.clone())],
811 vec![Some(receipt.clone())],
812 ];
813
814 let first_block = 123;
816
817 let request = bytes!("deadbeef");
819
820 let requests = vec![
822 Requests::new(vec![request.clone()]),
823 Requests::new(vec![request.clone()]),
824 Requests::new(vec![request.clone()]),
825 ];
826
827 let exec_res =
830 ExecutionOutcome { bundle: Default::default(), receipts, requests, first_block };
831
832 let result = exec_res.clone().split_at(124);
834
835 let lower_execution_outcome = ExecutionOutcome {
837 bundle: Default::default(),
838 receipts: vec![vec![Some(receipt.clone())]],
839 requests: vec![Requests::new(vec![request.clone()])],
840 first_block,
841 };
842
843 let higher_execution_outcome = ExecutionOutcome {
845 bundle: Default::default(),
846 receipts: vec![vec![Some(receipt.clone())], vec![Some(receipt)]],
847 requests: vec![Requests::new(vec![request.clone()]), Requests::new(vec![request])],
848 first_block: 124,
849 };
850
851 assert_eq!(result.0, Some(lower_execution_outcome));
853 assert_eq!(result.1, higher_execution_outcome);
854
855 assert_eq!(exec_res.clone().split_at(123), (None, exec_res));
857 }
858}