1use 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#[derive(Debug, Clone)]
19pub struct BlockIndices {
20 last_finalized_block: BlockNumber,
22 canonical_chain: CanonicalChain,
26 fork_to_child: HashMap<BlockHash, LinkedHashSet<BlockHash>>,
34 block_number_to_block_hashes: BTreeMap<BlockNumber, HashSet<BlockHash>>,
43 blocks_to_chain: HashMap<BlockHash, SidechainId>,
45}
46
47impl BlockIndices {
48 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 pub const fn fork_to_child(&self) -> &HashMap<BlockHash, LinkedHashSet<BlockHash>> {
64 &self.fork_to_child
65 }
66
67 #[allow(dead_code)]
69 pub(crate) const fn blocks_to_chain(&self) -> &HashMap<BlockHash, SidechainId> {
70 &self.blocks_to_chain
71 }
72
73 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 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 pub const fn last_finalized_block(&self) -> BlockNumber {
101 self.last_finalized_block
102 }
103
104 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 pub(crate) fn insert_chain(&mut self, chain_id: SidechainId, chain: &Chain) {
117 for (number, block) in chain.blocks() {
118 self.blocks_to_chain.insert(block.hash(), chain_id);
120 self.block_number_to_block_hashes.entry(*number).or_default().insert(block.hash());
122 }
123 let first = chain.first();
124 self.fork_to_child.entry(first.parent_hash).or_default().insert_if_absent(first.hash());
126 }
127
128 pub(crate) fn get_side_chain_id(&self, block: &BlockHash) -> Option<SidechainId> {
130 self.blocks_to_chain.get(block).copied()
131 }
132
133 pub(crate) fn update_block_hashes(
137 &mut self,
138 hashes: BTreeMap<u64, BlockHash>,
139 ) -> (BTreeSet<SidechainId>, Vec<BlockNumHash>) {
140 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 while let Some(new) = new_hash {
156 added.push(new.into());
158 new_hash = new_hashes.next();
159 }
160 break
161 };
162 let Some(new_block_value) = new_hash else {
163 while let Some(rem) = old_hash {
167 removed.push(rem);
168 old_hash = old_hashes.next();
169 }
170 break
171 };
172 match new_block_value.0.cmp(&old_block_value.0) {
174 std::cmp::Ordering::Less => {
175 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 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 removed.push(old_block_value);
191 old_hash = old_hashes.next()
192 }
193 }
194 }
195
196 (
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 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 fn remove_block(
221 &mut self,
222 block_number: BlockNumber,
223 block_hash: BlockHash,
224 ) -> BTreeSet<SidechainId> {
225 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 if set.is_empty() {
233 entry.remove();
234 }
235 }
236
237 self.blocks_to_chain.remove(&block_hash);
239
240 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 pub fn canonicalize_blocks(&mut self, blocks: &BTreeMap<BlockNumber, SealedBlockWithSenders>) {
256 if blocks.is_empty() {
257 return
258 }
259
260 let first_number = *blocks.first_key_value().unwrap().0;
262
263 self.canonical_chain.retain(|&number, _| number < first_number);
265
266 blocks.iter().map(|(_, b)| (b.number, b.hash(), b.parent_hash)).for_each(
268 |(number, hash, parent_hash)| {
269 self.blocks_to_chain.remove(&hash);
271
272 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 if set.is_empty() {
280 entry.remove();
281 }
282 }
283 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 if set.is_empty() {
290 entry.remove();
291 }
292 }
293 },
294 );
295
296 self.canonical_chain.extend(blocks.iter().map(|(number, block)| (*number, block.hash())))
298 }
299
300 pub(crate) fn unwind_canonical_chain(&mut self, unwind_to: BlockNumber) {
306 self.canonical_chain.retain(|num, _| *num <= unwind_to);
308 }
309
310 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 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 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 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 self.last_finalized_block = finalized_block;
348
349 lose_chains
350 }
351
352 #[inline]
354 pub fn canonical_hash(&self, block_number: &BlockNumber) -> Option<BlockHash> {
355 self.canonical_chain.canonical_hash(block_number)
356 }
357
358 #[inline]
360 pub fn canonical_number(&self, block_hash: &BlockHash) -> Option<BlockNumber> {
361 self.canonical_chain.canonical_number(block_hash)
362 }
363
364 #[inline]
366 pub fn canonical_tip(&self) -> BlockNumHash {
367 self.canonical_chain.tip()
368 }
369
370 #[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 let canonical_chain = BTreeMap::from([(0, B256::from_slice(&[1; 32]))]);
388
389 let block_indices = BlockIndices::new(0, canonical_chain);
390
391 assert_eq!(block_indices.pending_block_num_hash(), None);
393 }
394
395 #[test]
396 fn pending_block_num_hash_works() {
397 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 let parent_hash = B256::from_slice(&[3; 32]);
408
409 let child_hash_1 = B256::from_slice(&[2; 32]);
411 let child_hash_2 = B256::from_slice(&[3; 32]);
412
413 let mut child_set = LinkedHashSet::new();
415 child_set.insert(child_hash_1);
416 child_set.insert(child_hash_2);
417
418 block_indices.fork_to_child.insert(parent_hash, child_set);
420
421 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 let canonical_chain = BTreeMap::from([(10, B256::from_slice(&[1; 32]))]);
432 let block_indices = BlockIndices::new(0, canonical_chain);
433
434 assert_eq!(block_indices.pending_blocks(), (11, Vec::new()));
436 }
437
438 #[test]
439 fn pending_blocks_returns_multiple_children() {
440 let parent_hash = B256::from_slice(&[3; 32]);
442
443 let canonical_chain = BTreeMap::from([(5, parent_hash)]);
445 let mut block_indices = BlockIndices::new(0, canonical_chain);
446
447 let child_hash_1 = B256::from_slice(&[4; 32]);
449 let child_hash_2 = B256::from_slice(&[5; 32]);
450
451 let mut child_set = LinkedHashSet::new();
453 child_set.insert(child_hash_1);
454 child_set.insert(child_hash_2);
455
456 block_indices.fork_to_child.insert(parent_hash, child_set);
458
459 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 let parent_hash_1 = B256::from_slice(&[6; 32]);
467 let parent_hash_2 = B256::from_slice(&[7; 32]);
468
469 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 let child_hash_1 = B256::from_slice(&[8; 32]);
476 let child_hash_2 = B256::from_slice(&[9; 32]);
477
478 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 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 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 let mut block_indices = BlockIndices::new(0, BTreeMap::new());
496
497 let block_number = 1;
499 let block_hash = B256::from_slice(&[1; 32]);
500 let chain_id = SidechainId::from(42);
501
502 block_indices.insert_non_fork_block(block_number, block_hash, chain_id);
504
505 assert_eq!(
507 block_indices.block_number_to_block_hashes.get(&block_number),
508 Some(&HashSet::from([block_hash]))
509 );
510
511 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 let mut block_indices = BlockIndices::new(0, BTreeMap::new());
519
520 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 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 block_indices.insert_non_fork_block(block_number_2, block_hash_3, chain_id_2);
536
537 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 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 assert_eq!(
552 block_indices.block_number_to_block_hashes.get(&block_number_2),
553 Some(&HashSet::from([block_hash_3]))
554 );
555
556 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 let mut block_indices = BlockIndices::new(0, BTreeMap::new());
564
565 let chain_id = SidechainId::from(42);
567
568 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 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 let chain = Chain::new(vec![block_1, block_2], Default::default(), Default::default());
597
598 block_indices.insert_chain(chain_id, &chain);
600
601 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 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 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}