1use crate::ExecutionOutcome;
4use alloc::{borrow::Cow, collections::BTreeMap, vec::Vec};
5use alloy_consensus::{transaction::Recovered, BlockHeader};
6use alloy_eips::{eip1898::ForkBlock, eip2718::Encodable2718, BlockNumHash};
7use alloy_primitives::{Address, BlockHash, BlockNumber, TxHash};
8use core::{fmt, ops::RangeInclusive};
9use reth_primitives_traits::{
10 transaction::signed::SignedTransaction, Block, BlockBody, NodePrimitives, RecoveredBlock,
11 SealedHeader,
12};
13use reth_trie_common::updates::TrieUpdates;
14use revm::database::BundleState;
15
16#[derive(Clone, Debug, PartialEq, Eq)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28pub struct Chain<N: NodePrimitives = reth_ethereum_primitives::EthPrimitives> {
29 blocks: BTreeMap<BlockNumber, RecoveredBlock<N::Block>>,
31 execution_outcome: ExecutionOutcome<N::Receipt>,
38 trie_updates: Option<TrieUpdates>,
42}
43
44impl<N: NodePrimitives> Default for Chain<N> {
45 fn default() -> Self {
46 Self {
47 blocks: Default::default(),
48 execution_outcome: Default::default(),
49 trie_updates: Default::default(),
50 }
51 }
52}
53
54impl<N: NodePrimitives> Chain<N> {
55 pub fn new(
61 blocks: impl IntoIterator<Item = RecoveredBlock<N::Block>>,
62 execution_outcome: ExecutionOutcome<N::Receipt>,
63 trie_updates: Option<TrieUpdates>,
64 ) -> Self {
65 let blocks =
66 blocks.into_iter().map(|b| (b.header().number(), b)).collect::<BTreeMap<_, _>>();
67 debug_assert!(!blocks.is_empty(), "Chain should have at least one block");
68
69 Self { blocks, execution_outcome, trie_updates }
70 }
71
72 pub fn from_block(
74 block: RecoveredBlock<N::Block>,
75 execution_outcome: ExecutionOutcome<N::Receipt>,
76 trie_updates: Option<TrieUpdates>,
77 ) -> Self {
78 Self::new([block], execution_outcome, trie_updates)
79 }
80
81 pub const fn blocks(&self) -> &BTreeMap<BlockNumber, RecoveredBlock<N::Block>> {
83 &self.blocks
84 }
85
86 pub fn into_blocks(self) -> BTreeMap<BlockNumber, RecoveredBlock<N::Block>> {
88 self.blocks
89 }
90
91 pub fn headers(&self) -> impl Iterator<Item = SealedHeader<N::BlockHeader>> + '_ {
93 self.blocks.values().map(|block| block.clone_sealed_header())
94 }
95
96 pub const fn trie_updates(&self) -> Option<&TrieUpdates> {
98 self.trie_updates.as_ref()
99 }
100
101 pub fn clear_trie_updates(&mut self) {
103 self.trie_updates.take();
104 }
105
106 pub const fn execution_outcome(&self) -> &ExecutionOutcome<N::Receipt> {
108 &self.execution_outcome
109 }
110
111 pub const fn execution_outcome_mut(&mut self) -> &mut ExecutionOutcome<N::Receipt> {
113 &mut self.execution_outcome
114 }
115
116 pub fn prepend_state(&mut self, state: BundleState) {
118 self.execution_outcome.prepend_state(state);
119 self.trie_updates.take(); }
121
122 pub fn is_empty(&self) -> bool {
124 self.blocks.is_empty()
125 }
126
127 pub fn block_number(&self, block_hash: BlockHash) -> Option<BlockNumber> {
129 self.blocks.iter().find_map(|(num, block)| (block.hash() == block_hash).then_some(*num))
130 }
131
132 pub fn recovered_block(&self, block_hash: BlockHash) -> Option<&RecoveredBlock<N::Block>> {
134 self.blocks.iter().find_map(|(_num, block)| (block.hash() == block_hash).then_some(block))
135 }
136
137 pub fn execution_outcome_at_block(
139 &self,
140 block_number: BlockNumber,
141 ) -> Option<ExecutionOutcome<N::Receipt>> {
142 if self.tip().number() == block_number {
143 return Some(self.execution_outcome.clone())
144 }
145
146 if self.blocks.contains_key(&block_number) {
147 let mut execution_outcome = self.execution_outcome.clone();
148 execution_outcome.revert_to(block_number);
149 return Some(execution_outcome)
150 }
151 None
152 }
153
154 pub fn into_inner(
159 self,
160 ) -> (ChainBlocks<'static, N::Block>, ExecutionOutcome<N::Receipt>, Option<TrieUpdates>) {
161 (ChainBlocks { blocks: Cow::Owned(self.blocks) }, self.execution_outcome, self.trie_updates)
162 }
163
164 pub const fn inner(&self) -> (ChainBlocks<'_, N::Block>, &ExecutionOutcome<N::Receipt>) {
168 (ChainBlocks { blocks: Cow::Borrowed(&self.blocks) }, &self.execution_outcome)
169 }
170
171 pub fn block_receipts_iter(&self) -> impl Iterator<Item = &Vec<N::Receipt>> + '_ {
173 self.execution_outcome.receipts().iter()
174 }
175
176 pub fn blocks_iter(&self) -> impl Iterator<Item = &RecoveredBlock<N::Block>> + '_ {
178 self.blocks().iter().map(|block| block.1)
179 }
180
181 pub fn blocks_and_receipts(
183 &self,
184 ) -> impl Iterator<Item = (&RecoveredBlock<N::Block>, &Vec<N::Receipt>)> + '_ {
185 self.blocks_iter().zip(self.block_receipts_iter())
186 }
187
188 pub fn fork_block(&self) -> ForkBlock {
190 let first = self.first();
191 ForkBlock {
192 number: first.header().number().saturating_sub(1),
193 hash: first.header().parent_hash(),
194 }
195 }
196
197 #[track_caller]
203 pub fn first(&self) -> &RecoveredBlock<N::Block> {
204 self.blocks.first_key_value().expect("Chain should have at least one block").1
205 }
206
207 #[track_caller]
213 pub fn tip(&self) -> &RecoveredBlock<N::Block> {
214 self.blocks.last_key_value().expect("Chain should have at least one block").1
215 }
216
217 pub fn len(&self) -> usize {
219 self.blocks.len()
220 }
221
222 pub fn range(&self) -> RangeInclusive<BlockNumber> {
228 self.first().header().number()..=self.tip().header().number()
229 }
230
231 pub fn receipts_by_block_hash(&self, block_hash: BlockHash) -> Option<Vec<&N::Receipt>> {
233 let num = self.block_number(block_hash)?;
234 Some(self.execution_outcome.receipts_by_block(num).iter().collect())
235 }
236
237 pub fn receipts_with_attachment(&self) -> Vec<BlockReceipts<N::Receipt>>
241 where
242 N::SignedTx: Encodable2718,
243 {
244 let mut receipt_attach = Vec::with_capacity(self.blocks().len());
245
246 self.blocks_and_receipts().for_each(|(block, receipts)| {
247 let block_num_hash = BlockNumHash::new(block.number(), block.hash());
248
249 let tx_receipts = block
250 .body()
251 .transactions()
252 .iter()
253 .zip(receipts)
254 .map(|(tx, receipt)| (tx.trie_hash(), receipt.clone()))
255 .collect();
256
257 receipt_attach.push(BlockReceipts {
258 block: block_num_hash,
259 tx_receipts,
260 timestamp: block.timestamp(),
261 });
262 });
263
264 receipt_attach
265 }
266
267 pub fn append_block(
270 &mut self,
271 block: RecoveredBlock<N::Block>,
272 execution_outcome: ExecutionOutcome<N::Receipt>,
273 ) {
274 self.blocks.insert(block.header().number(), block);
275 self.execution_outcome.extend(execution_outcome);
276 self.trie_updates.take(); }
278
279 pub fn append_chain(&mut self, other: Self) -> Result<(), Self> {
286 let chain_tip = self.tip();
287 let other_fork_block = other.fork_block();
288 if chain_tip.hash() != other_fork_block.hash {
289 return Err(other)
290 }
291
292 self.blocks.extend(other.blocks);
294 self.execution_outcome.extend(other.execution_outcome);
295 self.trie_updates.take(); Ok(())
298 }
299}
300
301#[derive(Debug)]
303pub struct DisplayBlocksChain<'a, B: reth_primitives_traits::Block>(
304 pub &'a BTreeMap<BlockNumber, RecoveredBlock<B>>,
305);
306
307impl<B: reth_primitives_traits::Block> fmt::Display for DisplayBlocksChain<'_, B> {
308 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
309 let mut list = f.debug_list();
310 let mut values = self.0.values().map(|block| block.num_hash());
311 if values.len() <= 3 {
312 list.entries(values);
313 } else {
314 list.entry(&values.next().unwrap());
315 list.entry(&format_args!("..."));
316 list.entry(&values.next_back().unwrap());
317 }
318 list.finish()
319 }
320}
321
322#[derive(Clone, Debug, Default, PartialEq, Eq)]
324pub struct ChainBlocks<'a, B: Block> {
325 blocks: Cow<'a, BTreeMap<BlockNumber, RecoveredBlock<B>>>,
326}
327
328impl<B: Block<Body: BlockBody<Transaction: SignedTransaction>>> ChainBlocks<'_, B> {
329 #[inline]
333 pub fn into_blocks(self) -> impl Iterator<Item = RecoveredBlock<B>> {
334 self.blocks.into_owned().into_values()
335 }
336
337 #[inline]
339 pub fn iter(&self) -> impl Iterator<Item = (&BlockNumber, &RecoveredBlock<B>)> {
340 self.blocks.iter()
341 }
342
343 #[inline]
349 pub fn tip(&self) -> &RecoveredBlock<B> {
350 self.blocks.last_key_value().expect("Chain should have at least one block").1
351 }
352
353 #[inline]
359 pub fn first(&self) -> &RecoveredBlock<B> {
360 self.blocks.first_key_value().expect("Chain should have at least one block").1
361 }
362
363 #[inline]
365 pub fn transactions(&self) -> impl Iterator<Item = &<B::Body as BlockBody>::Transaction> + '_ {
366 self.blocks.values().flat_map(|block| block.body().transactions_iter())
367 }
368
369 #[inline]
371 pub fn transactions_with_sender(
372 &self,
373 ) -> impl Iterator<Item = (&Address, &<B::Body as BlockBody>::Transaction)> + '_ {
374 self.blocks.values().flat_map(|block| block.transactions_with_sender())
375 }
376
377 #[inline]
381 pub fn transactions_ecrecovered(
382 &self,
383 ) -> impl Iterator<Item = Recovered<<B::Body as BlockBody>::Transaction>> + '_ {
384 self.transactions_with_sender().map(|(signer, tx)| tx.clone().with_signer(*signer))
385 }
386
387 #[inline]
389 pub fn transaction_hashes(&self) -> impl Iterator<Item = TxHash> + '_ {
390 self.blocks
391 .values()
392 .flat_map(|block| block.body().transactions_iter().map(|tx| tx.trie_hash()))
393 }
394}
395
396impl<B: Block> IntoIterator for ChainBlocks<'_, B> {
397 type Item = (BlockNumber, RecoveredBlock<B>);
398 type IntoIter = alloc::collections::btree_map::IntoIter<BlockNumber, RecoveredBlock<B>>;
399
400 fn into_iter(self) -> Self::IntoIter {
401 self.blocks.into_owned().into_iter()
402 }
403}
404
405#[derive(Default, Clone, Debug, PartialEq, Eq)]
407pub struct BlockReceipts<T = reth_ethereum_primitives::Receipt> {
408 pub block: BlockNumHash,
410 pub tx_receipts: Vec<(TxHash, T)>,
412 pub timestamp: u64,
414}
415
416#[cfg(feature = "serde-bincode-compat")]
418pub(super) mod serde_bincode_compat {
419 use crate::{serde_bincode_compat, ExecutionOutcome};
420 use alloc::{borrow::Cow, collections::BTreeMap};
421 use alloy_primitives::BlockNumber;
422 use reth_ethereum_primitives::EthPrimitives;
423 use reth_primitives_traits::{
424 serde_bincode_compat::{RecoveredBlock, SerdeBincodeCompat},
425 Block, NodePrimitives,
426 };
427 use reth_trie_common::serde_bincode_compat::updates::TrieUpdates;
428 use serde::{ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer};
429 use serde_with::{DeserializeAs, SerializeAs};
430
431 #[derive(Debug, Serialize, Deserialize)]
447 pub struct Chain<'a, N = EthPrimitives>
448 where
449 N: NodePrimitives<
450 Block: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static,
451 >,
452 {
453 blocks: RecoveredBlocks<'a, N::Block>,
454 execution_outcome: serde_bincode_compat::ExecutionOutcome<'a, N::Receipt>,
455 trie_updates: Option<TrieUpdates<'a>>,
456 }
457
458 #[derive(Debug)]
459 struct RecoveredBlocks<
460 'a,
461 B: reth_primitives_traits::Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat>
462 + 'static,
463 >(Cow<'a, BTreeMap<BlockNumber, reth_primitives_traits::RecoveredBlock<B>>>);
464
465 impl<B> Serialize for RecoveredBlocks<'_, B>
466 where
467 B: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static,
468 {
469 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
470 where
471 S: Serializer,
472 {
473 let mut state = serializer.serialize_map(Some(self.0.len()))?;
474
475 for (block_number, block) in self.0.iter() {
476 state.serialize_entry(block_number, &RecoveredBlock::<'_, B>::from(block))?;
477 }
478
479 state.end()
480 }
481 }
482
483 impl<'de, B> Deserialize<'de> for RecoveredBlocks<'_, B>
484 where
485 B: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static,
486 {
487 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
488 where
489 D: Deserializer<'de>,
490 {
491 Ok(Self(Cow::Owned(
492 BTreeMap::<BlockNumber, RecoveredBlock<'_, B>>::deserialize(deserializer)
493 .map(|blocks| blocks.into_iter().map(|(n, b)| (n, b.into())).collect())?,
494 )))
495 }
496 }
497
498 impl<'a, N> From<&'a super::Chain<N>> for Chain<'a, N>
499 where
500 N: NodePrimitives<
501 Block: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static,
502 >,
503 {
504 fn from(value: &'a super::Chain<N>) -> Self {
505 Self {
506 blocks: RecoveredBlocks(Cow::Borrowed(&value.blocks)),
507 execution_outcome: value.execution_outcome.as_repr(),
508 trie_updates: value.trie_updates.as_ref().map(Into::into),
509 }
510 }
511 }
512
513 impl<'a, N> From<Chain<'a, N>> for super::Chain<N>
514 where
515 N: NodePrimitives<
516 Block: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static,
517 >,
518 {
519 fn from(value: Chain<'a, N>) -> Self {
520 Self {
521 blocks: value.blocks.0.into_owned(),
522 execution_outcome: ExecutionOutcome::from_repr(value.execution_outcome),
523 trie_updates: value.trie_updates.map(Into::into),
524 }
525 }
526 }
527
528 impl<N> SerializeAs<super::Chain<N>> for Chain<'_, N>
529 where
530 N: NodePrimitives<
531 Block: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static,
532 >,
533 {
534 fn serialize_as<S>(source: &super::Chain<N>, serializer: S) -> Result<S::Ok, S::Error>
535 where
536 S: Serializer,
537 {
538 Chain::from(source).serialize(serializer)
539 }
540 }
541
542 impl<'de, N> DeserializeAs<'de, super::Chain<N>> for Chain<'de, N>
543 where
544 N: NodePrimitives<
545 Block: Block<Header: SerdeBincodeCompat, Body: SerdeBincodeCompat> + 'static,
546 >,
547 {
548 fn deserialize_as<D>(deserializer: D) -> Result<super::Chain<N>, D::Error>
549 where
550 D: Deserializer<'de>,
551 {
552 Chain::deserialize(deserializer).map(Into::into)
553 }
554 }
555
556 #[cfg(test)]
557 mod tests {
558 use super::super::{serde_bincode_compat, Chain};
559 use arbitrary::Arbitrary;
560 use rand::Rng;
561 use reth_primitives_traits::RecoveredBlock;
562 use serde::{Deserialize, Serialize};
563 use serde_with::serde_as;
564
565 #[test]
566 fn test_chain_bincode_roundtrip() {
567 #[serde_as]
568 #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
569 struct Data {
570 #[serde_as(as = "serde_bincode_compat::Chain")]
571 chain: Chain,
572 }
573
574 let mut bytes = [0u8; 1024];
575 rand::rng().fill(bytes.as_mut_slice());
576 let data = Data {
577 chain: Chain::new(
578 vec![RecoveredBlock::arbitrary(&mut arbitrary::Unstructured::new(&bytes))
579 .unwrap()],
580 Default::default(),
581 None,
582 ),
583 };
584
585 let encoded = bincode::serialize(&data).unwrap();
586 let decoded: Data = bincode::deserialize(&encoded).unwrap();
587 assert_eq!(decoded, data);
588 }
589 }
590}
591
592#[cfg(test)]
593mod tests {
594 use super::*;
595 use alloy_consensus::TxType;
596 use alloy_primitives::{Address, B256};
597 use reth_ethereum_primitives::Receipt;
598 use revm::{primitives::HashMap, state::AccountInfo};
599
600 #[test]
601 fn chain_append() {
602 let block: RecoveredBlock<reth_ethereum_primitives::Block> = Default::default();
603 let block1_hash = B256::new([0x01; 32]);
604 let block2_hash = B256::new([0x02; 32]);
605 let block3_hash = B256::new([0x03; 32]);
606 let block4_hash = B256::new([0x04; 32]);
607
608 let mut block1 = block.clone();
609 let mut block2 = block.clone();
610 let mut block3 = block.clone();
611 let mut block4 = block;
612
613 block1.set_hash(block1_hash);
614 block2.set_hash(block2_hash);
615 block3.set_hash(block3_hash);
616 block4.set_hash(block4_hash);
617
618 block3.set_parent_hash(block2_hash);
619
620 let mut chain1: Chain =
621 Chain { blocks: BTreeMap::from([(1, block1), (2, block2)]), ..Default::default() };
622
623 let chain2 =
624 Chain { blocks: BTreeMap::from([(3, block3), (4, block4)]), ..Default::default() };
625
626 assert!(chain1.append_chain(chain2.clone()).is_ok());
627
628 assert!(chain1.append_chain(chain2).is_err());
630 }
631
632 #[test]
633 fn test_number_split() {
634 let execution_outcome1: ExecutionOutcome = ExecutionOutcome::new(
635 BundleState::new(
636 vec![(
637 Address::new([2; 20]),
638 None,
639 Some(AccountInfo::default()),
640 HashMap::default(),
641 )],
642 vec![vec![(Address::new([2; 20]), None, vec![])]],
643 vec![],
644 ),
645 vec![vec![]],
646 1,
647 vec![],
648 );
649
650 let execution_outcome2 = ExecutionOutcome::new(
651 BundleState::new(
652 vec![(
653 Address::new([3; 20]),
654 None,
655 Some(AccountInfo::default()),
656 HashMap::default(),
657 )],
658 vec![vec![(Address::new([3; 20]), None, vec![])]],
659 vec![],
660 ),
661 vec![vec![]],
662 2,
663 vec![],
664 );
665
666 let mut block1: RecoveredBlock<reth_ethereum_primitives::Block> = Default::default();
667 let block1_hash = B256::new([15; 32]);
668 block1.set_block_number(1);
669 block1.set_hash(block1_hash);
670 block1.push_sender(Address::new([4; 20]));
671
672 let mut block2: RecoveredBlock<reth_ethereum_primitives::Block> = Default::default();
673 let block2_hash = B256::new([16; 32]);
674 block2.set_block_number(2);
675 block2.set_hash(block2_hash);
676 block2.push_sender(Address::new([4; 20]));
677
678 let mut block_state_extended = execution_outcome1;
679 block_state_extended.extend(execution_outcome2);
680
681 let chain: Chain =
682 Chain::new(vec![block1.clone(), block2.clone()], block_state_extended, None);
683
684 assert_eq!(
686 chain.execution_outcome_at_block(block2.number),
687 Some(chain.execution_outcome.clone())
688 );
689 assert_eq!(chain.execution_outcome_at_block(100), None);
691 }
692
693 #[test]
694 fn receipts_by_block_hash() {
695 let block: RecoveredBlock<reth_ethereum_primitives::Block> = Default::default();
697
698 let block1_hash = B256::new([0x01; 32]);
700 let block2_hash = B256::new([0x02; 32]);
701
702 let mut block1 = block.clone();
704 let mut block2 = block;
705
706 block1.set_hash(block1_hash);
708 block2.set_hash(block2_hash);
709
710 let receipt1 = Receipt {
712 tx_type: TxType::Legacy,
713 cumulative_gas_used: 46913,
714 logs: vec![],
715 success: true,
716 };
717
718 let receipt2 = Receipt {
720 tx_type: TxType::Legacy,
721 cumulative_gas_used: 1325345,
722 logs: vec![],
723 success: true,
724 };
725
726 let receipts = vec![vec![receipt1.clone()], vec![receipt2]];
728
729 let execution_outcome = ExecutionOutcome {
732 bundle: Default::default(),
733 receipts,
734 requests: vec![],
735 first_block: 10,
736 };
737
738 let chain: Chain = Chain {
741 blocks: BTreeMap::from([(10, block1), (11, block2)]),
742 execution_outcome: execution_outcome.clone(),
743 ..Default::default()
744 };
745
746 assert_eq!(chain.receipts_by_block_hash(block1_hash), Some(vec![&receipt1]));
748
749 let execution_outcome1 = ExecutionOutcome {
751 bundle: Default::default(),
752 receipts: vec![vec![receipt1]],
753 requests: vec![],
754 first_block: 10,
755 };
756
757 assert_eq!(chain.execution_outcome_at_block(10), Some(execution_outcome1));
759
760 assert_eq!(chain.execution_outcome_at_block(11), Some(execution_outcome));
762 }
763}