reth_blockchain_tree/
state.rs

1//! Blockchain tree state.
2
3use crate::{AppendableChain, BlockBuffer, BlockIndices};
4use alloy_primitives::{BlockHash, BlockNumber};
5use reth_primitives::{Receipt, SealedBlock, SealedBlockWithSenders};
6use std::collections::{BTreeMap, HashMap};
7
8/// Container to hold the state of the blockchain tree.
9#[derive(Debug)]
10pub(crate) struct TreeState {
11    /// Keeps track of new unique identifiers for chains
12    block_chain_id_generator: u64,
13    /// The tracked chains and their current data.
14    pub(crate) chains: HashMap<SidechainId, AppendableChain>,
15    /// Indices to block and their connection to the canonical chain.
16    ///
17    /// This gets modified by the tree itself and is read from engine API/RPC to access the pending
18    /// block for example.
19    pub(crate) block_indices: BlockIndices,
20    /// Unconnected block buffer.
21    pub(crate) buffered_blocks: BlockBuffer,
22}
23
24impl TreeState {
25    /// Initializes the tree state with the given last finalized block number and last canonical
26    /// hashes.
27    pub(crate) fn new(
28        last_finalized_block_number: BlockNumber,
29        last_canonical_hashes: impl IntoIterator<Item = (BlockNumber, BlockHash)>,
30        buffer_limit: u32,
31    ) -> Self {
32        Self {
33            block_chain_id_generator: 0,
34            chains: Default::default(),
35            block_indices: BlockIndices::new(
36                last_finalized_block_number,
37                BTreeMap::from_iter(last_canonical_hashes),
38            ),
39            buffered_blocks: BlockBuffer::new(buffer_limit),
40        }
41    }
42
43    /// Issues a new unique identifier for a new sidechain.
44    #[inline]
45    fn next_id(&mut self) -> SidechainId {
46        let id = self.block_chain_id_generator;
47        self.block_chain_id_generator += 1;
48        SidechainId(id)
49    }
50
51    /// Expose internal indices of the `BlockchainTree`.
52    #[inline]
53    pub(crate) const fn block_indices(&self) -> &BlockIndices {
54        &self.block_indices
55    }
56
57    /// Returns the block with matching hash from any side-chain.
58    ///
59    /// Caution: This will not return blocks from the canonical chain.
60    #[inline]
61    pub(crate) fn block_by_hash(&self, block_hash: BlockHash) -> Option<&SealedBlock> {
62        self.block_with_senders_by_hash(block_hash).map(|block| &block.block)
63    }
64
65    /// Returns the block with matching hash from any side-chain.
66    ///
67    /// Caution: This will not return blocks from the canonical chain.
68    #[inline]
69    pub(crate) fn block_with_senders_by_hash(
70        &self,
71        block_hash: BlockHash,
72    ) -> Option<&SealedBlockWithSenders> {
73        let id = self.block_indices.get_side_chain_id(&block_hash)?;
74        let chain = self.chains.get(&id)?;
75        chain.block_with_senders(block_hash)
76    }
77
78    /// Returns the block's receipts with matching hash from any side-chain.
79    ///
80    /// Caution: This will not return blocks from the canonical chain.
81    pub(crate) fn receipts_by_block_hash(&self, block_hash: BlockHash) -> Option<Vec<&Receipt>> {
82        let id = self.block_indices.get_side_chain_id(&block_hash)?;
83        let chain = self.chains.get(&id)?;
84        chain.receipts_by_block_hash(block_hash)
85    }
86
87    /// Insert a chain into the tree.
88    ///
89    /// Inserts a chain into the tree and builds the block indices.
90    pub(crate) fn insert_chain(&mut self, chain: AppendableChain) -> Option<SidechainId> {
91        if chain.is_empty() {
92            return None
93        }
94        let chain_id = self.next_id();
95
96        self.block_indices.insert_chain(chain_id, &chain);
97        // add chain_id -> chain index
98        self.chains.insert(chain_id, chain);
99        Some(chain_id)
100    }
101
102    /// Checks the block buffer for the given block.
103    pub(crate) fn get_buffered_block(&self, hash: &BlockHash) -> Option<&SealedBlockWithSenders> {
104        self.buffered_blocks.block(hash)
105    }
106
107    /// Gets the lowest ancestor for the given block in the block buffer.
108    pub(crate) fn lowest_buffered_ancestor(
109        &self,
110        hash: &BlockHash,
111    ) -> Option<&SealedBlockWithSenders> {
112        self.buffered_blocks.lowest_ancestor(hash)
113    }
114}
115
116/// The ID of a sidechain internally in a [`BlockchainTree`][super::BlockchainTree].
117#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
118pub(crate) struct SidechainId(u64);
119
120impl From<SidechainId> for u64 {
121    fn from(value: SidechainId) -> Self {
122        value.0
123    }
124}
125
126#[cfg(test)]
127impl From<u64> for SidechainId {
128    fn from(value: u64) -> Self {
129        Self(value)
130    }
131}
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136    use crate::canonical_chain::CanonicalChain;
137    use alloy_primitives::B256;
138    use reth_execution_types::Chain;
139    use reth_provider::ExecutionOutcome;
140
141    #[test]
142    fn test_tree_state_initialization() {
143        // Set up some dummy data for initialization
144        let last_finalized_block_number = 10u64;
145        let last_canonical_hashes = vec![(9u64, B256::random()), (10u64, B256::random())];
146        let buffer_limit = 5;
147
148        // Initialize the tree state
149        let tree_state = TreeState::new(
150            last_finalized_block_number,
151            last_canonical_hashes.clone(),
152            buffer_limit,
153        );
154
155        // Verify the tree state after initialization
156        assert_eq!(tree_state.block_chain_id_generator, 0);
157        assert_eq!(tree_state.block_indices().last_finalized_block(), last_finalized_block_number);
158        assert_eq!(
159            *tree_state.block_indices.canonical_chain().inner(),
160            *CanonicalChain::new(last_canonical_hashes.into_iter().collect()).inner()
161        );
162        assert!(tree_state.chains.is_empty());
163        assert!(tree_state.buffered_blocks.lru.is_empty());
164    }
165
166    #[test]
167    fn test_tree_state_next_id() {
168        // Initialize the tree state
169        let mut tree_state = TreeState::new(0, vec![], 5);
170
171        // Generate a few sidechain IDs
172        let first_id = tree_state.next_id();
173        let second_id = tree_state.next_id();
174
175        // Verify the generated sidechain IDs and the updated generator state
176        assert_eq!(first_id, SidechainId(0));
177        assert_eq!(second_id, SidechainId(1));
178        assert_eq!(tree_state.block_chain_id_generator, 2);
179    }
180
181    #[test]
182    fn test_tree_state_insert_chain() {
183        // Initialize tree state
184        let mut tree_state = TreeState::new(0, vec![], 5);
185
186        // Create a chain with two blocks
187        let block: SealedBlockWithSenders = Default::default();
188        let block1_hash = B256::random();
189        let block2_hash = B256::random();
190
191        let mut block1 = block.clone();
192        let mut block2 = block;
193
194        block1.block.header.set_hash(block1_hash);
195        block1.block.header.set_block_number(9);
196        block2.block.header.set_hash(block2_hash);
197        block2.block.header.set_block_number(10);
198
199        let chain = AppendableChain::new(Chain::new(
200            [block1, block2],
201            Default::default(),
202            Default::default(),
203        ));
204
205        // Insert the chain into the TreeState
206        let chain_id = tree_state.insert_chain(chain).unwrap();
207
208        // Verify the chain ID and that it was added to the chains collection
209        assert_eq!(chain_id, SidechainId(0));
210        assert!(tree_state.chains.contains_key(&chain_id));
211
212        // Ensure that the block indices are updated
213        assert_eq!(
214            tree_state.block_indices.get_side_chain_id(&block1_hash).unwrap(),
215            SidechainId(0)
216        );
217        assert_eq!(
218            tree_state.block_indices.get_side_chain_id(&block2_hash).unwrap(),
219            SidechainId(0)
220        );
221
222        // Ensure that the block chain ID generator was updated
223        assert_eq!(tree_state.block_chain_id_generator, 1);
224
225        // Create an empty chain
226        let chain_empty = AppendableChain::new(Chain::default());
227
228        // Insert the empty chain into the tree state
229        let chain_id = tree_state.insert_chain(chain_empty);
230
231        // Ensure that the empty chain was not inserted
232        assert!(chain_id.is_none());
233
234        // Nothing should have changed and no new chain should have been added
235        assert!(tree_state.chains.contains_key(&SidechainId(0)));
236        assert!(!tree_state.chains.contains_key(&SidechainId(1)));
237        assert_eq!(
238            tree_state.block_indices.get_side_chain_id(&block1_hash).unwrap(),
239            SidechainId(0)
240        );
241        assert_eq!(
242            tree_state.block_indices.get_side_chain_id(&block2_hash).unwrap(),
243            SidechainId(0)
244        );
245        assert_eq!(tree_state.block_chain_id_generator, 1);
246    }
247
248    #[test]
249    fn test_block_by_hash_side_chain() {
250        // Initialize a tree state with some dummy data
251        let mut tree_state = TreeState::new(0, vec![], 5);
252
253        // Create two side-chain blocks with random hashes
254        let block1_hash = B256::random();
255        let block2_hash = B256::random();
256
257        let mut block1: SealedBlockWithSenders = Default::default();
258        let mut block2: SealedBlockWithSenders = Default::default();
259
260        block1.block.header.set_hash(block1_hash);
261        block1.block.header.set_block_number(9);
262        block2.block.header.set_hash(block2_hash);
263        block2.block.header.set_block_number(10);
264
265        // Create an chain with these blocks
266        let chain = AppendableChain::new(Chain::new(
267            vec![block1.clone(), block2.clone()],
268            Default::default(),
269            Default::default(),
270        ));
271
272        // Insert the side chain into the TreeState
273        tree_state.insert_chain(chain).unwrap();
274
275        // Retrieve the blocks by their hashes
276        let retrieved_block1 = tree_state.block_by_hash(block1_hash);
277        assert_eq!(*retrieved_block1.unwrap(), block1.block);
278
279        let retrieved_block2 = tree_state.block_by_hash(block2_hash);
280        assert_eq!(*retrieved_block2.unwrap(), block2.block);
281
282        // Test block_by_hash with a random hash that doesn't exist
283        let non_existent_hash = B256::random();
284        let result = tree_state.block_by_hash(non_existent_hash);
285
286        // Ensure that no block is found
287        assert!(result.is_none());
288    }
289
290    #[test]
291    fn test_block_with_senders_by_hash() {
292        // Initialize a tree state with some dummy data
293        let mut tree_state = TreeState::new(0, vec![], 5);
294
295        // Create two side-chain blocks with random hashes
296        let block1_hash = B256::random();
297        let block2_hash = B256::random();
298
299        let mut block1: SealedBlockWithSenders = Default::default();
300        let mut block2: SealedBlockWithSenders = Default::default();
301
302        block1.block.header.set_hash(block1_hash);
303        block1.block.header.set_block_number(9);
304        block2.block.header.set_hash(block2_hash);
305        block2.block.header.set_block_number(10);
306
307        // Create a chain with these blocks
308        let chain = AppendableChain::new(Chain::new(
309            vec![block1.clone(), block2.clone()],
310            Default::default(),
311            Default::default(),
312        ));
313
314        // Insert the side chain into the TreeState
315        tree_state.insert_chain(chain).unwrap();
316
317        // Test to retrieve the blocks with senders by their hashes
318        let retrieved_block1 = tree_state.block_with_senders_by_hash(block1_hash);
319        assert_eq!(*retrieved_block1.unwrap(), block1);
320
321        let retrieved_block2 = tree_state.block_with_senders_by_hash(block2_hash);
322        assert_eq!(*retrieved_block2.unwrap(), block2);
323
324        // Test block_with_senders_by_hash with a random hash that doesn't exist
325        let non_existent_hash = B256::random();
326        let result = tree_state.block_with_senders_by_hash(non_existent_hash);
327
328        // Ensure that no block is found
329        assert!(result.is_none());
330    }
331
332    #[test]
333    fn test_get_buffered_block() {
334        // Initialize a tree state with some dummy data
335        let mut tree_state = TreeState::new(0, vec![], 5);
336
337        // Create a block with a random hash and add it to the buffer
338        let block_hash = B256::random();
339        let mut block: SealedBlockWithSenders = Default::default();
340        block.block.header.set_hash(block_hash);
341
342        // Add the block to the buffered blocks in the TreeState
343        tree_state.buffered_blocks.insert_block(block.clone());
344
345        // Test get_buffered_block to retrieve the block by its hash
346        let retrieved_block = tree_state.get_buffered_block(&block_hash);
347        assert_eq!(*retrieved_block.unwrap(), block);
348
349        // Test get_buffered_block with a non-existent hash
350        let non_existent_hash = B256::random();
351        let result = tree_state.get_buffered_block(&non_existent_hash);
352
353        // Ensure that no block is found
354        assert!(result.is_none());
355    }
356
357    #[test]
358    fn test_lowest_buffered_ancestor() {
359        // Initialize a tree state with some dummy data
360        let mut tree_state = TreeState::new(0, vec![], 5);
361
362        // Create blocks with random hashes and set up parent-child relationships
363        let ancestor_hash = B256::random();
364        let descendant_hash = B256::random();
365
366        let mut ancestor_block: SealedBlockWithSenders = Default::default();
367        let mut descendant_block: SealedBlockWithSenders = Default::default();
368
369        ancestor_block.block.header.set_hash(ancestor_hash);
370        descendant_block.block.header.set_hash(descendant_hash);
371        descendant_block.block.header.set_parent_hash(ancestor_hash);
372
373        // Insert the blocks into the buffer
374        tree_state.buffered_blocks.insert_block(ancestor_block.clone());
375        tree_state.buffered_blocks.insert_block(descendant_block.clone());
376
377        // Test lowest_buffered_ancestor for the descendant block
378        let lowest_ancestor = tree_state.lowest_buffered_ancestor(&descendant_hash);
379        assert!(lowest_ancestor.is_some());
380        assert_eq!(lowest_ancestor.unwrap().block.header.hash(), ancestor_hash);
381
382        // Test lowest_buffered_ancestor with a non-existent hash
383        let non_existent_hash = B256::random();
384        let result = tree_state.lowest_buffered_ancestor(&non_existent_hash);
385
386        // Ensure that no ancestor is found
387        assert!(result.is_none());
388    }
389
390    #[test]
391    fn test_receipts_by_block_hash() {
392        // Initialize a tree state with some dummy data
393        let mut tree_state = TreeState::new(0, vec![], 5);
394
395        // Create a block with a random hash and receipts
396        let block_hash = B256::random();
397        let receipt1 = Receipt::default();
398        let receipt2 = Receipt::default();
399
400        let mut block: SealedBlockWithSenders = Default::default();
401        block.block.header.set_hash(block_hash);
402
403        let receipts = vec![receipt1, receipt2];
404
405        // Create a chain with the block and its receipts
406        let chain = AppendableChain::new(Chain::new(
407            vec![block.clone()],
408            ExecutionOutcome { receipts: receipts.clone().into(), ..Default::default() },
409            Default::default(),
410        ));
411
412        // Insert the chain into the TreeState
413        tree_state.insert_chain(chain).unwrap();
414
415        // Test receipts_by_block_hash for the inserted block
416        let retrieved_receipts = tree_state.receipts_by_block_hash(block_hash);
417        assert!(retrieved_receipts.is_some());
418
419        // Check if the correct receipts are returned
420        let receipts_ref: Vec<&Receipt> = receipts.iter().collect();
421        assert_eq!(retrieved_receipts.unwrap(), receipts_ref);
422
423        // Test receipts_by_block_hash with a non-existent block hash
424        let non_existent_hash = B256::random();
425        let result = tree_state.receipts_by_block_hash(non_existent_hash);
426
427        // Ensure that no receipts are found
428        assert!(result.is_none());
429    }
430}