reth_transaction_pool/blobstore/
tracker.rs
1use 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 super::*;
92 use alloy_consensus::{Header, Signed};
93 use alloy_primitives::Signature;
94 use reth_ethereum_primitives::Transaction;
95 use reth_execution_types::Chain;
96 use reth_primitives_traits::{RecoveredBlock, SealedBlock, SealedHeader};
97
98 #[test]
99 fn test_finalized_tracker() {
100 let mut tracker = BlobStoreCanonTracker::default();
101
102 let block1 = vec![B256::random()];
103 let block2 = vec![B256::random()];
104 let block3 = vec![B256::random()];
105 tracker.add_block(1, block1.clone());
106 tracker.add_block(2, block2.clone());
107 tracker.add_block(3, block3.clone());
108
109 assert_eq!(tracker.on_finalized_block(0), BlobStoreUpdates::None);
110 assert_eq!(tracker.on_finalized_block(1), BlobStoreUpdates::Finalized(block1));
111 assert_eq!(
112 tracker.on_finalized_block(3),
113 BlobStoreUpdates::Finalized(block2.into_iter().chain(block3).collect::<Vec<_>>())
114 );
115 }
116
117 #[test]
118 fn test_add_new_chain_blocks() {
119 let mut tracker = BlobStoreCanonTracker::default();
120 let tx1_signed = Signed::new_unhashed(
122 Transaction::Eip4844(Default::default()),
123 Signature::test_signature(),
124 ); let tx2_signed = Signed::new_unhashed(
126 Transaction::Eip4844(Default::default()),
127 Signature::test_signature(),
128 ); let tx1_hash = *tx1_signed.hash();
131 let tx2_hash = *tx2_signed.hash();
132 let block1 = RecoveredBlock::new_sealed(
134 SealedBlock::from_sealed_parts(
135 SealedHeader::new(Header { number: 10, ..Default::default() }, B256::random()),
136 alloy_consensus::BlockBody {
137 transactions: vec![
138 tx1_signed.into(),
139 tx2_signed.into(),
140 Signed::new_unhashed(
142 Transaction::Eip7702(Default::default()),
143 Signature::test_signature(),
144 )
145 .into(),
146 ],
147 ..Default::default()
148 },
149 ),
150 Default::default(),
151 );
152
153 let block2 = RecoveredBlock::new_sealed(
156 SealedBlock::from_sealed_parts(
157 SealedHeader::new(Header { number: 11, ..Default::default() }, B256::random()),
158 alloy_consensus::BlockBody {
159 transactions: vec![
160 Signed::new_unhashed(
161 Transaction::Eip1559(Default::default()),
162 Signature::test_signature(),
163 )
164 .into(),
165 Signed::new_unhashed(
166 Transaction::Eip2930(Default::default()),
167 Signature::test_signature(),
168 )
169 .into(),
170 ],
171 ..Default::default()
172 },
173 ),
174 Default::default(),
175 );
176
177 let chain: Chain = Chain::new(vec![block1, block2], Default::default(), None);
179 let blocks = chain.into_inner().0;
180
181 tracker.add_new_chain_blocks(&blocks);
183
184 assert_eq!(tracker.blob_txs_in_blocks.get(&10).unwrap(), &vec![tx1_hash, tx2_hash]);
186 assert!(tracker.blob_txs_in_blocks.get(&11).unwrap().is_empty());
188 }
189}