reth_transaction_pool/blobstore/
tracker.rs1use alloy_consensus::{transaction::TxHashRef, Typed2718};
4use alloy_primitives::{BlockNumber, B256};
5use reth_execution_types::ChainBlocks;
6use reth_primitives_traits::{Block, BlockBody, SignedTransaction};
7use std::collections::BTreeMap;
8
9#[derive(Debug, Default, Eq, PartialEq)]
11pub struct BlobStoreCanonTracker {
12 blob_txs_in_blocks: BTreeMap<BlockNumber, Vec<B256>>,
14}
15
16impl BlobStoreCanonTracker {
17 pub fn add_block(
19 &mut self,
20 block_number: BlockNumber,
21 blob_txs: impl IntoIterator<Item = B256>,
22 ) {
23 self.blob_txs_in_blocks.insert(block_number, blob_txs.into_iter().collect());
24 }
25
26 pub fn add_blocks(
30 &mut self,
31 blocks: impl IntoIterator<Item = (BlockNumber, impl IntoIterator<Item = B256>)>,
32 ) {
33 for (block_number, blob_txs) in blocks {
34 self.add_block(block_number, blob_txs);
35 }
36 }
37
38 pub fn add_new_chain_blocks<B>(&mut self, blocks: &ChainBlocks<'_, B>)
43 where
44 B: Block<Body: BlockBody<Transaction: SignedTransaction>>,
45 {
46 let blob_txs = blocks.iter().map(|(num, block)| {
47 let iter = block
48 .body()
49 .transactions()
50 .iter()
51 .filter(|tx| tx.is_eip4844())
52 .map(|tx| *tx.tx_hash());
53 (*num, iter)
54 });
55 self.add_blocks(blob_txs);
56 }
57
58 pub fn on_finalized_block(&mut self, finalized_block: BlockNumber) -> BlobStoreUpdates {
62 let mut finalized = Vec::new();
63 while let Some(entry) = self.blob_txs_in_blocks.first_entry() {
64 if *entry.key() <= finalized_block {
65 finalized.extend(entry.remove_entry().1);
66 } else {
67 break
68 }
69 }
70
71 if finalized.is_empty() {
72 BlobStoreUpdates::None
73 } else {
74 BlobStoreUpdates::Finalized(finalized)
75 }
76 }
77}
78
79#[derive(Debug, Eq, PartialEq)]
81pub enum BlobStoreUpdates {
82 None,
84 Finalized(Vec<B256>),
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91 use alloy_consensus::{Header, Signed};
92 use alloy_primitives::Signature;
93 use reth_ethereum_primitives::Transaction;
94 use reth_execution_types::Chain;
95 use reth_primitives_traits::{RecoveredBlock, SealedBlock, SealedHeader};
96
97 #[test]
98 fn test_finalized_tracker() {
99 let mut tracker = BlobStoreCanonTracker::default();
100
101 let block1 = vec![B256::random()];
102 let block2 = vec![B256::random()];
103 let block3 = vec![B256::random()];
104 tracker.add_block(1, block1.clone());
105 tracker.add_block(2, block2.clone());
106 tracker.add_block(3, block3.clone());
107
108 assert_eq!(tracker.on_finalized_block(0), BlobStoreUpdates::None);
109 assert_eq!(tracker.on_finalized_block(1), BlobStoreUpdates::Finalized(block1));
110 assert_eq!(
111 tracker.on_finalized_block(3),
112 BlobStoreUpdates::Finalized(block2.into_iter().chain(block3).collect::<Vec<_>>())
113 );
114 }
115
116 #[test]
117 fn test_add_new_chain_blocks() {
118 let mut tracker = BlobStoreCanonTracker::default();
119 let tx1_signed = Signed::new_unhashed(
121 Transaction::Eip4844(Default::default()),
122 Signature::test_signature(),
123 ); let tx2_signed = Signed::new_unhashed(
125 Transaction::Eip4844(Default::default()),
126 Signature::test_signature(),
127 ); let tx1_hash = *tx1_signed.hash();
130 let tx2_hash = *tx2_signed.hash();
131 let block1 = RecoveredBlock::new_sealed(
133 SealedBlock::from_sealed_parts(
134 SealedHeader::new(Header { number: 10, ..Default::default() }, B256::random()),
135 alloy_consensus::BlockBody {
136 transactions: vec![
137 tx1_signed.into(),
138 tx2_signed.into(),
139 Signed::new_unhashed(
141 Transaction::Eip7702(Default::default()),
142 Signature::test_signature(),
143 )
144 .into(),
145 ],
146 ..Default::default()
147 },
148 ),
149 Default::default(),
150 );
151
152 let block2 = RecoveredBlock::new_sealed(
155 SealedBlock::from_sealed_parts(
156 SealedHeader::new(Header { number: 11, ..Default::default() }, B256::random()),
157 alloy_consensus::BlockBody {
158 transactions: vec![
159 Signed::new_unhashed(
160 Transaction::Eip1559(Default::default()),
161 Signature::test_signature(),
162 )
163 .into(),
164 Signed::new_unhashed(
165 Transaction::Eip2930(Default::default()),
166 Signature::test_signature(),
167 )
168 .into(),
169 ],
170 ..Default::default()
171 },
172 ),
173 Default::default(),
174 );
175
176 let chain: Chain = Chain::new(vec![block1, block2], Default::default(), BTreeMap::new());
178 let blocks = chain.into_inner().0;
179
180 tracker.add_new_chain_blocks(&blocks);
182
183 assert_eq!(tracker.blob_txs_in_blocks.get(&10).unwrap(), &vec![tx1_hash, tx2_hash]);
185 assert!(tracker.blob_txs_in_blocks.get(&11).unwrap().is_empty());
187 }
188}