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