1use crate::BlockExecutionOutput;
2use alloy_eips::eip7685::Requests;
3use alloy_primitives::{logs_bloom, Address, BlockNumber, Bloom, Log, B256, U256};
4use reth_primitives::Receipts;
5use reth_primitives_traits::{Account, Bytecode, Receipt, StorageEntry};
6use reth_trie::{HashedPostState, KeyHasher};
7use revm::{
8 db::{states::BundleState, BundleAccount},
9 primitives::{AccountInfo, FlaggedStorage},
10};
11use std::collections::HashMap;
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15pub struct ChangedAccount {
16 pub address: Address,
18 pub nonce: u64,
20 pub balance: U256,
22}
23
24impl ChangedAccount {
25 pub const fn empty(address: Address) -> Self {
27 Self { address, nonce: 0, balance: U256::ZERO }
28 }
29}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
36#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
37pub struct ExecutionOutcome<T = reth_primitives::Receipt> {
38 pub bundle: BundleState,
40 pub receipts: Receipts<T>,
46 pub first_block: BlockNumber,
48 pub requests: Vec<Requests>,
55}
56
57impl<T> Default for ExecutionOutcome<T> {
58 fn default() -> Self {
59 Self {
60 bundle: Default::default(),
61 receipts: Default::default(),
62 first_block: Default::default(),
63 requests: Default::default(),
64 }
65 }
66}
67
68pub type BundleStateInit = HashMap<
70 Address,
71 (Option<Account>, Option<Account>, HashMap<B256, ((U256, bool), (U256, bool))>),
72>;
73
74pub type AccountRevertInit = (Option<Option<Account>>, Vec<StorageEntry>);
76
77pub type RevertsInit = HashMap<BlockNumber, HashMap<Address, AccountRevertInit>>;
79
80impl<T> ExecutionOutcome<T> {
81 pub const fn new(
86 bundle: BundleState,
87 receipts: Receipts<T>,
88 first_block: BlockNumber,
89 requests: Vec<Requests>,
90 ) -> Self {
91 Self { bundle, receipts, first_block, requests }
92 }
93
94 pub fn new_init(
99 state_init: BundleStateInit,
100 revert_init: RevertsInit,
101 contracts_init: impl IntoIterator<Item = (B256, Bytecode)>,
102 receipts: Receipts<T>,
103 first_block: BlockNumber,
104 requests: Vec<Requests>,
105 ) -> Self {
106 let mut reverts = revert_init.into_iter().collect::<Vec<_>>();
108 reverts.sort_unstable_by_key(|a| a.0);
109
110 let bundle = BundleState::new(
112 state_init.into_iter().map(|(address, (original, present, storage))| {
113 (
114 address,
115 original.map(Into::into),
116 present.map(Into::into),
117 storage
118 .into_iter()
119 .map(|(k, (orig_value, new_value))| {
120 (
121 k.into(),
122 (
123 FlaggedStorage::new_from_tuple(orig_value),
124 FlaggedStorage::new_from_tuple(new_value),
125 ),
126 )
127 })
128 .collect(),
129 )
130 }),
131 reverts.into_iter().map(|(_, reverts)| {
132 reverts.into_iter().map(|(address, (original, storage))| {
134 (
135 address,
136 original.map(|i| i.map(Into::into)),
137 storage.into_iter().map(|entry| {
138 (
139 entry.key.into(),
140 FlaggedStorage { value: entry.value, is_private: entry.is_private },
141 )
142 }),
143 )
144 })
145 }),
146 contracts_init.into_iter().map(|(code_hash, bytecode)| (code_hash, bytecode.0)),
147 );
148
149 Self { bundle, receipts, first_block, requests }
150 }
151
152 pub const fn state(&self) -> &BundleState {
154 &self.bundle
155 }
156
157 pub fn state_mut(&mut self) -> &mut BundleState {
159 &mut self.bundle
160 }
161
162 pub fn set_first_block(&mut self, first_block: BlockNumber) {
164 self.first_block = first_block;
165 }
166
167 pub fn accounts_iter(&self) -> impl Iterator<Item = (Address, Option<&AccountInfo>)> {
169 self.bundle.state().iter().map(|(a, acc)| (*a, acc.info.as_ref()))
170 }
171
172 pub fn bundle_accounts_iter(&self) -> impl Iterator<Item = (Address, &BundleAccount)> {
174 self.bundle.state().iter().map(|(a, acc)| (*a, acc))
175 }
176
177 pub fn account(&self, address: &Address) -> Option<Option<Account>> {
179 self.bundle.account(address).map(|a| a.info.as_ref().map(Into::into))
180 }
181
182 pub fn storage(&self, address: &Address, storage_key: U256) -> Option<FlaggedStorage> {
186 self.bundle.account(address).and_then(|a| a.storage_slot(storage_key))
187 }
188
189 pub fn bytecode(&self, code_hash: &B256) -> Option<Bytecode> {
191 self.bundle.bytecode(code_hash).map(Bytecode)
192 }
193
194 pub fn hash_state_slow<KH: KeyHasher>(&self) -> HashedPostState {
197 HashedPostState::from_bundle_state::<KH>(&self.bundle.state)
198 }
199
200 pub fn block_number_to_index(&self, block_number: BlockNumber) -> Option<usize> {
202 if self.first_block > block_number {
203 return None
204 }
205 let index = block_number - self.first_block;
206 if index >= self.receipts.len() as u64 {
207 return None
208 }
209 Some(index as usize)
210 }
211
212 pub fn generic_receipts_root_slow(
216 &self,
217 block_number: BlockNumber,
218 f: impl FnOnce(&[&T]) -> B256,
219 ) -> Option<B256> {
220 self.receipts.root_slow(self.block_number_to_index(block_number)?, f)
221 }
222
223 pub const fn receipts(&self) -> &Receipts<T> {
225 &self.receipts
226 }
227
228 pub fn receipts_mut(&mut self) -> &mut Receipts<T> {
230 &mut self.receipts
231 }
232
233 pub fn receipts_by_block(&self, block_number: BlockNumber) -> &[Option<T>] {
235 let Some(index) = self.block_number_to_index(block_number) else { return &[] };
236 &self.receipts[index]
237 }
238
239 pub fn is_empty(&self) -> bool {
241 self.len() == 0
242 }
243
244 pub fn len(&self) -> usize {
246 self.receipts.len()
247 }
248
249 pub const fn first_block(&self) -> BlockNumber {
251 self.first_block
252 }
253
254 pub fn revert_to(&mut self, block_number: BlockNumber) -> bool {
262 let Some(index) = self.block_number_to_index(block_number) else { return false };
263
264 let new_len = index + 1;
266 let rm_trx: usize = self.len() - new_len;
267
268 self.receipts.truncate(new_len);
270 self.requests.truncate(new_len);
272 self.bundle.revert(rm_trx);
274
275 true
276 }
277
278 pub fn split_at(self, at: BlockNumber) -> (Option<Self>, Self)
287 where
288 T: Clone,
289 {
290 if at == self.first_block {
291 return (None, self)
292 }
293
294 let (mut lower_state, mut higher_state) = (self.clone(), self);
295
296 lower_state.revert_to(at.checked_sub(1).unwrap());
298
299 let at_idx = higher_state.block_number_to_index(at).unwrap();
301 higher_state.receipts = higher_state.receipts.split_off(at_idx).into();
302 if at_idx < higher_state.requests.len() {
305 higher_state.requests = higher_state.requests.split_off(at_idx);
306 }
307 higher_state.bundle.take_n_reverts(at_idx);
308 higher_state.first_block = at;
309
310 (Some(lower_state), higher_state)
311 }
312
313 pub fn extend(&mut self, other: Self) {
319 self.bundle.extend(other.bundle);
320 self.receipts.extend(other.receipts.receipt_vec);
321 self.requests.extend(other.requests);
322 }
323
324 pub fn prepend_state(&mut self, mut other: BundleState) {
329 let other_len = other.reverts.len();
330 let this_bundle = std::mem::take(&mut self.bundle);
332 other.extend(this_bundle);
334 other.take_n_reverts(other_len);
336 std::mem::swap(&mut self.bundle, &mut other)
338 }
339
340 pub fn with_receipts(mut self, receipts: Receipts<T>) -> Self {
342 self.receipts = receipts;
343 self
344 }
345
346 pub fn with_requests(mut self, requests: Vec<Requests>) -> Self {
348 self.requests = requests;
349 self
350 }
351
352 pub fn changed_accounts(&self) -> impl Iterator<Item = ChangedAccount> + '_ {
358 self.accounts_iter().filter_map(|(addr, acc)| acc.map(|acc| (addr, acc))).map(
359 |(address, acc)| ChangedAccount { address, nonce: acc.nonce, balance: acc.balance },
360 )
361 }
362}
363
364impl<T: Receipt<Log = Log>> ExecutionOutcome<T> {
365 pub fn logs(&self, block_number: BlockNumber) -> Option<impl Iterator<Item = &Log>> {
367 let index = self.block_number_to_index(block_number)?;
368 Some(self.receipts[index].iter().filter_map(|r| Some(r.as_ref()?.logs().iter())).flatten())
369 }
370
371 pub fn block_logs_bloom(&self, block_number: BlockNumber) -> Option<Bloom> {
373 Some(logs_bloom(self.logs(block_number)?))
374 }
375}
376
377impl ExecutionOutcome {
378 pub fn ethereum_receipts_root(&self, _block_number: BlockNumber) -> Option<B256> {
383 self.receipts.root_slow(self.block_number_to_index(_block_number)?, |receipts| {
384 reth_primitives::proofs::calculate_receipt_root_no_memo(receipts)
385 })
386 }
387}
388
389impl<T> From<(BlockExecutionOutput<T>, BlockNumber)> for ExecutionOutcome<T> {
390 fn from(value: (BlockExecutionOutput<T>, BlockNumber)) -> Self {
391 Self {
392 bundle: value.0.state,
393 receipts: Receipts::from(value.0.receipts),
394 first_block: value.1,
395 requests: vec![value.0.requests],
396 }
397 }
398}
399
400#[cfg(test)]
401mod tests {
402 use super::*;
403 #[cfg(not(feature = "optimism"))]
404 use alloy_primitives::bytes;
405 #[cfg(not(feature = "optimism"))]
406 use alloy_primitives::LogData;
407 use alloy_primitives::{Address, B256};
408 use reth_primitives::Receipts;
409 #[cfg(not(feature = "optimism"))]
410 use reth_primitives::TxType;
411
412 #[test]
413 #[cfg(not(feature = "optimism"))]
414 fn test_initialisation() {
415 let bundle = BundleState::new(
417 vec![(Address::new([2; 20]), None, Some(AccountInfo::default()), HashMap::default())],
418 vec![vec![(Address::new([2; 20]), None, vec![])]],
419 vec![],
420 );
421
422 let receipts = Receipts {
424 receipt_vec: vec![vec![Some(reth_primitives::Receipt {
425 tx_type: TxType::Legacy,
426 cumulative_gas_used: 46913,
427 logs: vec![],
428 success: true,
429 })]],
430 };
431
432 let requests = vec![Requests::new(vec![bytes!("dead"), bytes!("beef"), bytes!("beebee")])];
434
435 let first_block = 123;
437
438 let exec_res = ExecutionOutcome {
441 bundle: bundle.clone(),
442 receipts: receipts.clone(),
443 requests: requests.clone(),
444 first_block,
445 };
446
447 assert_eq!(
449 ExecutionOutcome::new(bundle, receipts.clone(), first_block, requests.clone()),
450 exec_res
451 );
452
453 let mut state_init: BundleStateInit = HashMap::default();
455 state_init
456 .insert(Address::new([2; 20]), (None, Some(Account::default()), HashMap::default()));
457
458 let mut revert_inner: HashMap<Address, AccountRevertInit> = HashMap::default();
460 revert_inner.insert(Address::new([2; 20]), (None, vec![]));
461
462 let mut revert_init: RevertsInit = HashMap::default();
464 revert_init.insert(123, revert_inner);
465
466 assert_eq!(
469 ExecutionOutcome::new_init(
470 state_init,
471 revert_init,
472 vec![],
473 receipts,
474 first_block,
475 requests,
476 ),
477 exec_res
478 );
479 }
480
481 #[test]
482 #[cfg(not(feature = "optimism"))]
483 fn test_block_number_to_index() {
484 let receipts = Receipts {
486 receipt_vec: vec![vec![Some(reth_primitives::Receipt {
487 tx_type: TxType::Legacy,
488 cumulative_gas_used: 46913,
489 logs: vec![],
490 success: true,
491 })]],
492 };
493
494 let first_block = 123;
496
497 let exec_res = ExecutionOutcome {
500 bundle: Default::default(),
501 receipts,
502 requests: vec![],
503 first_block,
504 };
505
506 assert_eq!(exec_res.block_number_to_index(12), None);
508
509 assert_eq!(exec_res.block_number_to_index(133), None);
511
512 assert_eq!(exec_res.block_number_to_index(123), Some(0));
514 }
515
516 #[test]
517 #[cfg(not(feature = "optimism"))]
518 fn test_get_logs() {
519 let receipts = Receipts {
521 receipt_vec: vec![vec![Some(reth_primitives::Receipt {
522 tx_type: TxType::Legacy,
523 cumulative_gas_used: 46913,
524 logs: vec![Log::<LogData>::default()],
525 success: true,
526 })]],
527 };
528
529 let first_block = 123;
531
532 let exec_res = ExecutionOutcome {
535 bundle: Default::default(),
536 receipts,
537 requests: vec![],
538 first_block,
539 };
540
541 let logs: Vec<&Log> = exec_res.logs(123).unwrap().collect();
543
544 assert_eq!(logs, vec![&Log::<LogData>::default()]);
546 }
547
548 #[test]
549 #[cfg(not(feature = "optimism"))]
550 fn test_receipts_by_block() {
551 let receipts = Receipts {
553 receipt_vec: vec![vec![Some(reth_primitives::Receipt {
554 tx_type: TxType::Legacy,
555 cumulative_gas_used: 46913,
556 logs: vec![Log::<LogData>::default()],
557 success: true,
558 })]],
559 };
560
561 let first_block = 123;
563
564 let exec_res = ExecutionOutcome {
567 bundle: Default::default(), receipts, requests: vec![], first_block, };
572
573 let receipts_by_block: Vec<_> = exec_res.receipts_by_block(123).iter().collect();
575
576 assert_eq!(
578 receipts_by_block,
579 vec![&Some(reth_primitives::Receipt {
580 tx_type: TxType::Legacy,
581 cumulative_gas_used: 46913,
582 logs: vec![Log::<LogData>::default()],
583 success: true,
584 })]
585 );
586 }
587
588 #[test]
589 #[cfg(not(feature = "optimism"))]
590 fn test_receipts_len() {
591 let receipts = Receipts {
593 receipt_vec: vec![vec![Some(reth_primitives::Receipt {
594 tx_type: TxType::Legacy,
595 cumulative_gas_used: 46913,
596 logs: vec![Log::<LogData>::default()],
597 success: true,
598 })]],
599 };
600
601 let receipts_empty: Receipts = Receipts { receipt_vec: vec![] };
603
604 let first_block = 123;
606
607 let exec_res = ExecutionOutcome {
610 bundle: Default::default(), receipts, requests: vec![], first_block, };
615
616 assert_eq!(exec_res.len(), 1);
618
619 assert!(!exec_res.is_empty());
621
622 let exec_res_empty_receipts = ExecutionOutcome {
624 bundle: Default::default(), receipts: receipts_empty, requests: vec![], first_block, };
629
630 assert_eq!(exec_res_empty_receipts.len(), 0);
632
633 assert!(exec_res_empty_receipts.is_empty());
635 }
636
637 #[test]
638 #[cfg(not(feature = "optimism"))]
639 fn test_revert_to() {
640 let receipt = reth_primitives::Receipt {
642 tx_type: TxType::Legacy,
643 cumulative_gas_used: 46913,
644 logs: vec![],
645 success: true,
646 };
647
648 let receipts = Receipts {
650 receipt_vec: vec![vec![Some(receipt.clone())], vec![Some(receipt.clone())]],
651 };
652
653 let first_block = 123;
655
656 let request = bytes!("deadbeef");
658
659 let requests =
661 vec![Requests::new(vec![request.clone()]), Requests::new(vec![request.clone()])];
662
663 let mut exec_res =
666 ExecutionOutcome { bundle: Default::default(), receipts, requests, first_block };
667
668 assert!(exec_res.revert_to(123));
670
671 assert_eq!(exec_res.receipts, Receipts { receipt_vec: vec![vec![Some(receipt)]] });
673
674 assert_eq!(exec_res.requests, vec![Requests::new(vec![request])]);
676
677 assert!(!exec_res.revert_to(133));
680
681 assert!(!exec_res.revert_to(10));
684 }
685
686 #[test]
687 #[cfg(not(feature = "optimism"))]
688 fn test_extend_execution_outcome() {
689 let receipt = reth_primitives::Receipt {
691 tx_type: TxType::Legacy,
692 cumulative_gas_used: 46913,
693 logs: vec![],
694 success: true,
695 };
696
697 let receipts = Receipts { receipt_vec: vec![vec![Some(receipt.clone())]] };
699
700 let request = bytes!("deadbeef");
702
703 let requests = vec![Requests::new(vec![request.clone()])];
705
706 let first_block = 123;
708
709 let mut exec_res =
711 ExecutionOutcome { bundle: Default::default(), receipts, requests, first_block };
712
713 exec_res.extend(exec_res.clone());
715
716 assert_eq!(
718 exec_res,
719 ExecutionOutcome {
720 bundle: Default::default(),
721 receipts: Receipts {
722 receipt_vec: vec![vec![Some(receipt.clone())], vec![Some(receipt)]]
723 },
724 requests: vec![Requests::new(vec![request.clone()]), Requests::new(vec![request])],
725 first_block: 123,
726 }
727 );
728 }
729
730 #[test]
731 #[cfg(not(feature = "optimism"))]
732 fn test_split_at_execution_outcome() {
733 let receipt = reth_primitives::Receipt {
735 tx_type: TxType::Legacy,
736 cumulative_gas_used: 46913,
737 logs: vec![],
738 success: true,
739 };
740
741 let receipts = Receipts {
743 receipt_vec: vec![
744 vec![Some(receipt.clone())],
745 vec![Some(receipt.clone())],
746 vec![Some(receipt.clone())],
747 ],
748 };
749
750 let first_block = 123;
752
753 let request = bytes!("deadbeef");
755
756 let requests = vec![
758 Requests::new(vec![request.clone()]),
759 Requests::new(vec![request.clone()]),
760 Requests::new(vec![request.clone()]),
761 ];
762
763 let exec_res =
766 ExecutionOutcome { bundle: Default::default(), receipts, requests, first_block };
767
768 let result = exec_res.clone().split_at(124);
770
771 let lower_execution_outcome = ExecutionOutcome {
773 bundle: Default::default(),
774 receipts: Receipts { receipt_vec: vec![vec![Some(receipt.clone())]] },
775 requests: vec![Requests::new(vec![request.clone()])],
776 first_block,
777 };
778
779 let higher_execution_outcome = ExecutionOutcome {
781 bundle: Default::default(),
782 receipts: Receipts {
783 receipt_vec: vec![vec![Some(receipt.clone())], vec![Some(receipt)]],
784 },
785 requests: vec![Requests::new(vec![request.clone()]), Requests::new(vec![request])],
786 first_block: 124,
787 };
788
789 assert_eq!(result.0, Some(lower_execution_outcome));
791 assert_eq!(result.1, higher_execution_outcome);
792
793 assert_eq!(exec_res.clone().split_at(123), (None, exec_res));
795 }
796
797 #[test]
798 fn test_changed_accounts() {
799 let address1 = Address::random();
801 let address2 = Address::random();
802 let address3 = Address::random();
803
804 let account_info1 =
806 AccountInfo { nonce: 1, balance: U256::from(100), code_hash: B256::ZERO, code: None };
807 let account_info2 =
808 AccountInfo { nonce: 2, balance: U256::from(200), code_hash: B256::ZERO, code: None };
809
810 let mut bundle_state = BundleState::default();
812 bundle_state.state.insert(
813 address1,
814 BundleAccount {
815 info: Some(account_info1),
816 storage: Default::default(),
817 original_info: Default::default(),
818 status: Default::default(),
819 },
820 );
821 bundle_state.state.insert(
822 address2,
823 BundleAccount {
824 info: Some(account_info2),
825 storage: Default::default(),
826 original_info: Default::default(),
827 status: Default::default(),
828 },
829 );
830
831 bundle_state.state.insert(
833 address3,
834 BundleAccount {
835 info: None,
836 storage: Default::default(),
837 original_info: Default::default(),
838 status: Default::default(),
839 },
840 );
841
842 let execution_outcome: ExecutionOutcome = ExecutionOutcome {
843 bundle: bundle_state,
844 receipts: Receipts::default(),
845 first_block: 0,
846 requests: vec![],
847 };
848
849 let changed_accounts: Vec<ChangedAccount> = execution_outcome.changed_accounts().collect();
851
852 assert_eq!(changed_accounts.len(), 2);
854
855 assert!(changed_accounts.contains(&ChangedAccount {
856 address: address1,
857 nonce: 1,
858 balance: U256::from(100)
859 }));
860
861 assert!(changed_accounts.contains(&ChangedAccount {
862 address: address2,
863 nonce: 2,
864 balance: U256::from(200)
865 }));
866 }
867}