reth_blockchain_tree/
block_indices.rs

1//! Implementation of [`BlockIndices`] related to [`super::BlockchainTree`]
2
3use super::state::SidechainId;
4use crate::canonical_chain::CanonicalChain;
5use alloy_eips::BlockNumHash;
6use alloy_primitives::{BlockHash, BlockNumber};
7use linked_hash_set::LinkedHashSet;
8use reth_execution_types::Chain;
9use reth_primitives::SealedBlockWithSenders;
10use std::collections::{btree_map, hash_map, BTreeMap, BTreeSet, HashMap, HashSet};
11
12/// Internal indices of the blocks and chains.
13///
14/// This is main connection between blocks, chains and canonical chain.
15///
16/// It contains a list of canonical block hashes, forks to child blocks, and a mapping of block hash
17/// to chain ID.
18#[derive(Debug, Clone)]
19pub struct BlockIndices {
20    /// Last finalized block.
21    last_finalized_block: BlockNumber,
22    /// Non-finalized canonical chain. Contains N number (depends on `finalization_depth`) of
23    /// blocks. These blocks are found in `fork_to_child` but not inside `blocks_to_chain` or
24    /// `number_to_block` as those are sidechain specific indices.
25    canonical_chain: CanonicalChain,
26    /// Index needed when discarding the chain, so we can remove connected chains from tree.
27    ///
28    /// This maintains insertion order for all child blocks, so
29    /// [`BlockIndices::pending_block_num_hash`] returns always the same block: the first child
30    /// block we inserted.
31    ///
32    /// NOTE: It contains just blocks that are forks as a key and not all blocks.
33    fork_to_child: HashMap<BlockHash, LinkedHashSet<BlockHash>>,
34    /// Utility index for Block number to block hash(s).
35    ///
36    /// This maps all blocks with same block number to their hash.
37    ///
38    /// Can be used for RPC fetch block(s) in chain by its number.
39    ///
40    /// Note: This is a bijection: at all times `blocks_to_chain` and this map contain the block
41    /// hashes.
42    block_number_to_block_hashes: BTreeMap<BlockNumber, HashSet<BlockHash>>,
43    /// Block hashes to the sidechain IDs they belong to.
44    blocks_to_chain: HashMap<BlockHash, SidechainId>,
45}
46
47impl BlockIndices {
48    /// Create new block indices structure
49    pub fn new(
50        last_finalized_block: BlockNumber,
51        canonical_chain: BTreeMap<BlockNumber, BlockHash>,
52    ) -> Self {
53        Self {
54            last_finalized_block,
55            canonical_chain: CanonicalChain::new(canonical_chain),
56            fork_to_child: Default::default(),
57            blocks_to_chain: Default::default(),
58            block_number_to_block_hashes: Default::default(),
59        }
60    }
61
62    /// Return fork to child indices
63    pub const fn fork_to_child(&self) -> &HashMap<BlockHash, LinkedHashSet<BlockHash>> {
64        &self.fork_to_child
65    }
66
67    /// Return block to sidechain id
68    #[allow(dead_code)]
69    pub(crate) const fn blocks_to_chain(&self) -> &HashMap<BlockHash, SidechainId> {
70        &self.blocks_to_chain
71    }
72
73    /// Returns the hash and number of the pending block.
74    ///
75    /// It is possible that multiple child blocks for the canonical tip exist.
76    /// This will always return the _first_ child we recorded for the canonical tip.
77    pub(crate) fn pending_block_num_hash(&self) -> Option<BlockNumHash> {
78        let canonical_tip = self.canonical_tip();
79        let hash = self.fork_to_child.get(&canonical_tip.hash)?.front().copied()?;
80        Some(BlockNumHash { number: canonical_tip.number + 1, hash })
81    }
82
83    /// Returns all pending block hashes.
84    ///
85    /// Pending blocks are considered blocks that are extending the canonical tip by one block
86    /// number and have their parent hash set to the canonical tip.
87    pub fn pending_blocks(&self) -> (BlockNumber, Vec<BlockHash>) {
88        let canonical_tip = self.canonical_tip();
89        let pending_blocks = self
90            .fork_to_child
91            .get(&canonical_tip.hash)
92            .cloned()
93            .unwrap_or_default()
94            .into_iter()
95            .collect();
96        (canonical_tip.number + 1, pending_blocks)
97    }
98
99    /// Last finalized block
100    pub const fn last_finalized_block(&self) -> BlockNumber {
101        self.last_finalized_block
102    }
103
104    /// Insert non fork block.
105    pub(crate) fn insert_non_fork_block(
106        &mut self,
107        block_number: BlockNumber,
108        block_hash: BlockHash,
109        chain_id: SidechainId,
110    ) {
111        self.block_number_to_block_hashes.entry(block_number).or_default().insert(block_hash);
112        self.blocks_to_chain.insert(block_hash, chain_id);
113    }
114
115    /// Insert block to chain and fork child indices of the new chain
116    pub(crate) fn insert_chain(&mut self, chain_id: SidechainId, chain: &Chain) {
117        for (number, block) in chain.blocks() {
118            // add block -> chain_id index
119            self.blocks_to_chain.insert(block.hash(), chain_id);
120            // add number -> block
121            self.block_number_to_block_hashes.entry(*number).or_default().insert(block.hash());
122        }
123        let first = chain.first();
124        // add parent block -> block index
125        self.fork_to_child.entry(first.parent_hash).or_default().insert_if_absent(first.hash());
126    }
127
128    /// Get the [`SidechainId`] for the given block hash if it exists.
129    pub(crate) fn get_side_chain_id(&self, block: &BlockHash) -> Option<SidechainId> {
130        self.blocks_to_chain.get(block).copied()
131    }
132
133    /// Update all block hashes. iterate over present and new list of canonical hashes and compare
134    /// them. Remove all mismatches, disconnect them and return all chains that needs to be
135    /// removed.
136    pub(crate) fn update_block_hashes(
137        &mut self,
138        hashes: BTreeMap<u64, BlockHash>,
139    ) -> (BTreeSet<SidechainId>, Vec<BlockNumHash>) {
140        // set new canonical hashes.
141        self.canonical_chain.replace(hashes.clone());
142
143        let mut new_hashes = hashes.into_iter();
144        let mut old_hashes = self.canonical_chain().clone().into_iter();
145
146        let mut removed = Vec::new();
147        let mut added = Vec::new();
148
149        let mut new_hash = new_hashes.next();
150        let mut old_hash = old_hashes.next();
151
152        loop {
153            let Some(old_block_value) = old_hash else {
154                // end of old_hashes canonical chain. New chain has more blocks than old chain.
155                while let Some(new) = new_hash {
156                    // add new blocks to added list.
157                    added.push(new.into());
158                    new_hash = new_hashes.next();
159                }
160                break
161            };
162            let Some(new_block_value) = new_hash else {
163                // Old canonical chain had more block than new chain.
164                // remove all present block.
165                // this is mostly not going to happen as reorg should make new chain in Tree.
166                while let Some(rem) = old_hash {
167                    removed.push(rem);
168                    old_hash = old_hashes.next();
169                }
170                break
171            };
172            // compare old and new canonical block number
173            match new_block_value.0.cmp(&old_block_value.0) {
174                std::cmp::Ordering::Less => {
175                    // new chain has more past blocks than old chain
176                    added.push(new_block_value.into());
177                    new_hash = new_hashes.next();
178                }
179                std::cmp::Ordering::Equal => {
180                    if new_block_value.1 != old_block_value.1 {
181                        // remove block hash as it is different
182                        removed.push(old_block_value);
183                        added.push(new_block_value.into())
184                    }
185                    new_hash = new_hashes.next();
186                    old_hash = old_hashes.next();
187                }
188                std::cmp::Ordering::Greater => {
189                    // old chain has more past blocks than new chain
190                    removed.push(old_block_value);
191                    old_hash = old_hashes.next()
192                }
193            }
194        }
195
196        // remove children of removed blocks
197        (
198            removed.into_iter().fold(BTreeSet::new(), |mut fold, (number, hash)| {
199                fold.extend(self.remove_block(number, hash));
200                fold
201            }),
202            added,
203        )
204    }
205
206    /// Remove chain from indices and return dependent chains that need to be removed.
207    /// Does the cleaning of the tree and removing blocks from the chain.
208    pub(crate) fn remove_chain(&mut self, chain: &Chain) -> BTreeSet<SidechainId> {
209        chain
210            .blocks()
211            .iter()
212            .flat_map(|(block_number, block)| {
213                let block_hash = block.hash();
214                self.remove_block(*block_number, block_hash)
215            })
216            .collect()
217    }
218
219    /// Remove Blocks from indices.
220    fn remove_block(
221        &mut self,
222        block_number: BlockNumber,
223        block_hash: BlockHash,
224    ) -> BTreeSet<SidechainId> {
225        // rm number -> block
226        if let btree_map::Entry::Occupied(mut entry) =
227            self.block_number_to_block_hashes.entry(block_number)
228        {
229            let set = entry.get_mut();
230            set.remove(&block_hash);
231            // remove set if empty
232            if set.is_empty() {
233                entry.remove();
234            }
235        }
236
237        // rm block -> chain_id
238        self.blocks_to_chain.remove(&block_hash);
239
240        // rm fork -> child
241        let removed_fork = self.fork_to_child.remove(&block_hash);
242        removed_fork
243            .map(|fork_blocks| {
244                fork_blocks
245                    .into_iter()
246                    .filter_map(|fork_child| self.blocks_to_chain.remove(&fork_child))
247                    .collect()
248            })
249            .unwrap_or_default()
250    }
251
252    /// Remove all blocks from canonical list and insert new blocks to it.
253    ///
254    /// It is assumed that blocks are interconnected and that they connect to canonical chain
255    pub fn canonicalize_blocks(&mut self, blocks: &BTreeMap<BlockNumber, SealedBlockWithSenders>) {
256        if blocks.is_empty() {
257            return
258        }
259
260        // Remove all blocks from canonical chain
261        let first_number = *blocks.first_key_value().unwrap().0;
262
263        // this will remove all blocks numbers that are going to be replaced.
264        self.canonical_chain.retain(|&number, _| number < first_number);
265
266        // remove them from block to chain_id index
267        blocks.iter().map(|(_, b)| (b.number, b.hash(), b.parent_hash)).for_each(
268            |(number, hash, parent_hash)| {
269                // rm block -> chain_id
270                self.blocks_to_chain.remove(&hash);
271
272                // rm number -> block
273                if let btree_map::Entry::Occupied(mut entry) =
274                    self.block_number_to_block_hashes.entry(number)
275                {
276                    let set = entry.get_mut();
277                    set.remove(&hash);
278                    // remove set if empty
279                    if set.is_empty() {
280                        entry.remove();
281                    }
282                }
283                // rm fork block -> hash
284                if let hash_map::Entry::Occupied(mut entry) = self.fork_to_child.entry(parent_hash)
285                {
286                    let set = entry.get_mut();
287                    set.remove(&hash);
288                    // remove set if empty
289                    if set.is_empty() {
290                        entry.remove();
291                    }
292                }
293            },
294        );
295
296        // insert new canonical
297        self.canonical_chain.extend(blocks.iter().map(|(number, block)| (*number, block.hash())))
298    }
299
300    /// this is function that is going to remove N number of last canonical hashes.
301    ///
302    /// NOTE: This is not safe standalone, as it will not disconnect
303    /// blocks that depend on unwinded canonical chain. And should be
304    /// used when canonical chain is reinserted inside Tree.
305    pub(crate) fn unwind_canonical_chain(&mut self, unwind_to: BlockNumber) {
306        // this will remove all blocks numbers that are going to be replaced.
307        self.canonical_chain.retain(|num, _| *num <= unwind_to);
308    }
309
310    /// Used for finalization of block.
311    ///
312    /// Return list of chains for removal that depend on finalized canonical chain.
313    pub(crate) fn finalize_canonical_blocks(
314        &mut self,
315        finalized_block: BlockNumber,
316        num_of_additional_canonical_hashes_to_retain: u64,
317    ) -> BTreeSet<SidechainId> {
318        // get finalized chains. blocks between [self.last_finalized,finalized_block).
319        // Dont remove finalized_block, as sidechain can point to it.
320        let finalized_blocks: Vec<BlockHash> = self
321            .canonical_chain
322            .iter()
323            .filter(|(number, _)| *number >= self.last_finalized_block && *number < finalized_block)
324            .map(|(_, hash)| hash)
325            .collect();
326
327        // remove unneeded canonical hashes.
328        let remove_until =
329            finalized_block.saturating_sub(num_of_additional_canonical_hashes_to_retain);
330        self.canonical_chain.retain(|&number, _| number >= remove_until);
331
332        let mut lose_chains = BTreeSet::new();
333
334        for block_hash in finalized_blocks {
335            // there is a fork block.
336            if let Some(fork_blocks) = self.fork_to_child.remove(&block_hash) {
337                lose_chains = fork_blocks.into_iter().fold(lose_chains, |mut fold, fork_child| {
338                    if let Some(lose_chain) = self.blocks_to_chain.remove(&fork_child) {
339                        fold.insert(lose_chain);
340                    }
341                    fold
342                });
343            }
344        }
345
346        // set last finalized block.
347        self.last_finalized_block = finalized_block;
348
349        lose_chains
350    }
351
352    /// Returns the block hash of the canonical block with the given number.
353    #[inline]
354    pub fn canonical_hash(&self, block_number: &BlockNumber) -> Option<BlockHash> {
355        self.canonical_chain.canonical_hash(block_number)
356    }
357
358    /// Returns the block number of the canonical block with the given hash.
359    #[inline]
360    pub fn canonical_number(&self, block_hash: &BlockHash) -> Option<BlockNumber> {
361        self.canonical_chain.canonical_number(block_hash)
362    }
363
364    /// get canonical tip
365    #[inline]
366    pub fn canonical_tip(&self) -> BlockNumHash {
367        self.canonical_chain.tip()
368    }
369
370    /// Canonical chain needed for execution of EVM. It should contain last 256 block hashes.
371    #[inline]
372    pub(crate) const fn canonical_chain(&self) -> &CanonicalChain {
373        &self.canonical_chain
374    }
375}
376
377#[cfg(test)]
378mod tests {
379    use super::*;
380    use alloy_consensus::Header;
381    use alloy_primitives::B256;
382    use reth_primitives::{SealedBlock, SealedHeader};
383
384    #[test]
385    fn pending_block_num_hash_returns_none_if_no_fork() {
386        // Create a new canonical chain with a single block (represented by its number and hash).
387        let canonical_chain = BTreeMap::from([(0, B256::from_slice(&[1; 32]))]);
388
389        let block_indices = BlockIndices::new(0, canonical_chain);
390
391        // No fork to child blocks, so there is no pending block.
392        assert_eq!(block_indices.pending_block_num_hash(), None);
393    }
394
395    #[test]
396    fn pending_block_num_hash_works() {
397        // Create a canonical chain with multiple blocks at heights 1, 2, and 3.
398        let canonical_chain = BTreeMap::from([
399            (1, B256::from_slice(&[1; 32])),
400            (2, B256::from_slice(&[2; 32])),
401            (3, B256::from_slice(&[3; 32])),
402        ]);
403
404        let mut block_indices = BlockIndices::new(3, canonical_chain);
405
406        // Define the hash of the parent block (the block at height 3 in the canonical chain).
407        let parent_hash = B256::from_slice(&[3; 32]);
408
409        // Define the hashes of two child blocks that extend the canonical chain.
410        let child_hash_1 = B256::from_slice(&[2; 32]);
411        let child_hash_2 = B256::from_slice(&[3; 32]);
412
413        // Create a set to store both child block hashes.
414        let mut child_set = LinkedHashSet::new();
415        child_set.insert(child_hash_1);
416        child_set.insert(child_hash_2);
417
418        // Associate the parent block hash with its children in the fork_to_child mapping.
419        block_indices.fork_to_child.insert(parent_hash, child_set);
420
421        // Pending block should be the first child block.
422        assert_eq!(
423            block_indices.pending_block_num_hash(),
424            Some(BlockNumHash { number: 4, hash: child_hash_1 })
425        );
426    }
427
428    #[test]
429    fn pending_blocks_returns_empty_if_no_fork() {
430        // Create a canonical chain with a single block at height 10.
431        let canonical_chain = BTreeMap::from([(10, B256::from_slice(&[1; 32]))]);
432        let block_indices = BlockIndices::new(0, canonical_chain);
433
434        // No child blocks are associated with the canonical tip.
435        assert_eq!(block_indices.pending_blocks(), (11, Vec::new()));
436    }
437
438    #[test]
439    fn pending_blocks_returns_multiple_children() {
440        // Define the hash of the parent block (the block at height 5 in the canonical chain).
441        let parent_hash = B256::from_slice(&[3; 32]);
442
443        // Create a canonical chain with a block at height 5.
444        let canonical_chain = BTreeMap::from([(5, parent_hash)]);
445        let mut block_indices = BlockIndices::new(0, canonical_chain);
446
447        // Define the hashes of two child blocks.
448        let child_hash_1 = B256::from_slice(&[4; 32]);
449        let child_hash_2 = B256::from_slice(&[5; 32]);
450
451        // Create a set to store both child block hashes.
452        let mut child_set = LinkedHashSet::new();
453        child_set.insert(child_hash_1);
454        child_set.insert(child_hash_2);
455
456        // Associate the parent block hash with its children.
457        block_indices.fork_to_child.insert(parent_hash, child_set);
458
459        // Pending blocks should be the two child blocks.
460        assert_eq!(block_indices.pending_blocks(), (6, vec![child_hash_1, child_hash_2]));
461    }
462
463    #[test]
464    fn pending_blocks_with_multiple_forked_chains() {
465        // Define hashes for parent blocks and child blocks.
466        let parent_hash_1 = B256::from_slice(&[6; 32]);
467        let parent_hash_2 = B256::from_slice(&[7; 32]);
468
469        // Create a canonical chain with blocks at heights 1 and 2.
470        let canonical_chain = BTreeMap::from([(1, parent_hash_1), (2, parent_hash_2)]);
471
472        let mut block_indices = BlockIndices::new(2, canonical_chain);
473
474        // Define hashes for child blocks.
475        let child_hash_1 = B256::from_slice(&[8; 32]);
476        let child_hash_2 = B256::from_slice(&[9; 32]);
477
478        // Create sets to store child blocks for each parent block.
479        let mut child_set_1 = LinkedHashSet::new();
480        let mut child_set_2 = LinkedHashSet::new();
481        child_set_1.insert(child_hash_1);
482        child_set_2.insert(child_hash_2);
483
484        // Associate parent block hashes with their child blocks.
485        block_indices.fork_to_child.insert(parent_hash_1, child_set_1);
486        block_indices.fork_to_child.insert(parent_hash_2, child_set_2);
487
488        // Check that the pending blocks are only those extending the canonical tip.
489        assert_eq!(block_indices.pending_blocks(), (3, vec![child_hash_2]));
490    }
491
492    #[test]
493    fn insert_non_fork_block_adds_block_correctly() {
494        // Create a new BlockIndices instance with an empty state.
495        let mut block_indices = BlockIndices::new(0, BTreeMap::new());
496
497        // Define test parameters.
498        let block_number = 1;
499        let block_hash = B256::from_slice(&[1; 32]);
500        let chain_id = SidechainId::from(42);
501
502        // Insert the block into the BlockIndices instance.
503        block_indices.insert_non_fork_block(block_number, block_hash, chain_id);
504
505        // Check that the block number to block hashes mapping includes the new block hash.
506        assert_eq!(
507            block_indices.block_number_to_block_hashes.get(&block_number),
508            Some(&HashSet::from([block_hash]))
509        );
510
511        // Check that the block hash to chain ID mapping includes the new entry.
512        assert_eq!(block_indices.blocks_to_chain.get(&block_hash), Some(&chain_id));
513    }
514
515    #[test]
516    fn insert_non_fork_block_combined_tests() {
517        // Create a new BlockIndices instance with an empty state.
518        let mut block_indices = BlockIndices::new(0, BTreeMap::new());
519
520        // Define test parameters.
521        let block_number_1 = 2;
522        let block_hash_1 = B256::from_slice(&[1; 32]);
523        let block_hash_2 = B256::from_slice(&[2; 32]);
524        let chain_id_1 = SidechainId::from(84);
525
526        let block_number_2 = 4;
527        let block_hash_3 = B256::from_slice(&[3; 32]);
528        let chain_id_2 = SidechainId::from(200);
529
530        // Insert multiple hashes for the same block number.
531        block_indices.insert_non_fork_block(block_number_1, block_hash_1, chain_id_1);
532        block_indices.insert_non_fork_block(block_number_1, block_hash_2, chain_id_1);
533
534        // Insert blocks with different numbers.
535        block_indices.insert_non_fork_block(block_number_2, block_hash_3, chain_id_2);
536
537        // Block number 1 should have two block hashes associated with it.
538        let mut expected_hashes_for_block_1 = HashSet::default();
539        expected_hashes_for_block_1.insert(block_hash_1);
540        expected_hashes_for_block_1.insert(block_hash_2);
541        assert_eq!(
542            block_indices.block_number_to_block_hashes.get(&block_number_1),
543            Some(&expected_hashes_for_block_1)
544        );
545
546        // Check that the block hashes for block_number_1 are associated with the correct chain ID.
547        assert_eq!(block_indices.blocks_to_chain.get(&block_hash_1), Some(&chain_id_1));
548        assert_eq!(block_indices.blocks_to_chain.get(&block_hash_2), Some(&chain_id_1));
549
550        // Block number 2 should have a single block hash associated with it.
551        assert_eq!(
552            block_indices.block_number_to_block_hashes.get(&block_number_2),
553            Some(&HashSet::from([block_hash_3]))
554        );
555
556        // Block hash 3 should be associated with the correct chain ID.
557        assert_eq!(block_indices.blocks_to_chain.get(&block_hash_3), Some(&chain_id_2));
558    }
559
560    #[test]
561    fn insert_chain_validates_insertion() {
562        // Create a new BlockIndices instance with an empty state.
563        let mut block_indices = BlockIndices::new(0, BTreeMap::new());
564
565        // Define test parameters.
566        let chain_id = SidechainId::from(42);
567
568        // Define some example blocks and their hashes.
569        let block_hash_1 = B256::from_slice(&[1; 32]);
570        let block_hash_2 = B256::from_slice(&[2; 32]);
571        let parent_hash = B256::from_slice(&[0; 32]);
572
573        // Define blocks with their numbers and parent hashes.
574        let block_1 = SealedBlockWithSenders {
575            block: SealedBlock {
576                header: SealedHeader::new(
577                    Header { parent_hash, number: 1, ..Default::default() },
578                    block_hash_1,
579                ),
580                ..Default::default()
581            },
582            ..Default::default()
583        };
584        let block_2 = SealedBlockWithSenders {
585            block: SealedBlock {
586                header: SealedHeader::new(
587                    Header { parent_hash: block_hash_1, number: 2, ..Default::default() },
588                    block_hash_2,
589                ),
590                ..Default::default()
591            },
592            ..Default::default()
593        };
594
595        // Define a chain containing the blocks.
596        let chain = Chain::new(vec![block_1, block_2], Default::default(), Default::default());
597
598        // Insert the chain into the BlockIndices.
599        block_indices.insert_chain(chain_id, &chain);
600
601        // Check that the blocks are correctly mapped to the chain ID.
602        assert_eq!(block_indices.blocks_to_chain.get(&block_hash_1), Some(&chain_id));
603        assert_eq!(block_indices.blocks_to_chain.get(&block_hash_2), Some(&chain_id));
604
605        // Check that block numbers map to their respective hashes.
606        let mut expected_hashes_1 = HashSet::default();
607        expected_hashes_1.insert(block_hash_1);
608        assert_eq!(block_indices.block_number_to_block_hashes.get(&1), Some(&expected_hashes_1));
609
610        let mut expected_hashes_2 = HashSet::default();
611        expected_hashes_2.insert(block_hash_2);
612        assert_eq!(block_indices.block_number_to_block_hashes.get(&2), Some(&expected_hashes_2));
613
614        // Check that the fork_to_child mapping contains the correct parent-child relationship.
615        // We take the first block of the chain.
616        let mut expected_children = LinkedHashSet::new();
617        expected_children.insert(block_hash_1);
618        assert_eq!(block_indices.fork_to_child.get(&parent_hash), Some(&expected_children));
619    }
620}