1use crate::engine::EngineApiKind;
4use alloy_eips::BlockNumHash;
5use alloy_primitives::{
6 map::{B256Map, B256Set},
7 BlockNumber, B256,
8};
9use reth_chain_state::{EthPrimitives, ExecutedBlock, LazyOverlay};
10use reth_primitives_traits::{AlloyBlockHeader, NodePrimitives, SealedHeader};
11use std::{
12 collections::{btree_map, hash_map, BTreeMap, VecDeque},
13 ops::Bound,
14};
15use tracing::debug;
16
17#[derive(Debug, Default)]
24pub struct TreeState<N: NodePrimitives = EthPrimitives> {
25 pub(crate) blocks_by_hash: B256Map<ExecutedBlock<N>>,
29 pub(crate) blocks_by_number: BTreeMap<BlockNumber, Vec<ExecutedBlock<N>>>,
35 pub(crate) parent_to_child: B256Map<B256Set>,
37 pub(crate) current_canonical_head: BlockNumHash,
39 pub(crate) engine_kind: EngineApiKind,
41 pub(crate) cached_canonical_overlay: Option<PreparedCanonicalOverlay<N>>,
47}
48
49impl<N: NodePrimitives> TreeState<N> {
50 pub fn new(current_canonical_head: BlockNumHash, engine_kind: EngineApiKind) -> Self {
52 Self {
53 blocks_by_hash: B256Map::default(),
54 blocks_by_number: BTreeMap::new(),
55 current_canonical_head,
56 parent_to_child: B256Map::default(),
57 engine_kind,
58 cached_canonical_overlay: None,
59 }
60 }
61
62 pub fn reset(&mut self, current_canonical_head: BlockNumHash) {
64 *self = Self::new(current_canonical_head, self.engine_kind);
65 }
66
67 pub fn block_count(&self) -> usize {
69 self.blocks_by_hash.len()
70 }
71
72 pub fn executed_block_by_hash(&self, hash: B256) -> Option<&ExecutedBlock<N>> {
74 self.blocks_by_hash.get(&hash)
75 }
76
77 pub fn contains_hash(&self, hash: &B256) -> bool {
79 self.blocks_by_hash.contains_key(hash)
80 }
81
82 pub fn sealed_header_by_hash(&self, hash: &B256) -> Option<SealedHeader<N::BlockHeader>> {
84 self.blocks_by_hash.get(hash).map(|b| b.sealed_block().sealed_header().clone())
85 }
86
87 pub fn blocks_by_hash(&self, hash: B256) -> Option<(B256, Vec<ExecutedBlock<N>>)> {
93 let block = self.blocks_by_hash.get(&hash).cloned()?;
94 let mut parent_hash = block.recovered_block().parent_hash();
95 let mut blocks = vec![block];
96 while let Some(executed) = self.blocks_by_hash.get(&parent_hash) {
97 parent_hash = executed.recovered_block().parent_hash();
98 blocks.push(executed.clone());
99 }
100
101 Some((parent_hash, blocks))
102 }
103
104 pub(crate) fn prepare_canonical_overlay(&mut self) -> Option<PreparedCanonicalOverlay<N>> {
113 let canonical_hash = self.current_canonical_head.hash;
114
115 let Some((anchor_hash, blocks)) = self.blocks_by_hash(canonical_hash) else {
117 self.cached_canonical_overlay = None;
119 return None;
120 };
121
122 let num_blocks = blocks.len();
123 let prepared = PreparedCanonicalOverlay {
124 parent_hash: canonical_hash,
125 overlay: LazyOverlay::new(blocks),
126 anchor_hash,
127 };
128 self.cached_canonical_overlay = Some(prepared.clone());
129
130 debug!(
131 target: "engine::tree",
132 %canonical_hash,
133 %anchor_hash,
134 num_blocks,
135 "Prepared cached canonical overlay"
136 );
137
138 Some(prepared)
139 }
140
141 pub fn get_cached_overlay(
146 &self,
147 parent_hash: B256,
148 expected_anchor: B256,
149 ) -> Option<&PreparedCanonicalOverlay<N>> {
150 self.cached_canonical_overlay.as_ref().filter(|cached| {
151 cached.parent_hash == parent_hash && cached.anchor_hash == expected_anchor
152 })
153 }
154
155 pub(crate) fn invalidate_cached_overlay(&mut self) {
159 self.cached_canonical_overlay = None;
160 }
161
162 pub fn insert_executed(&mut self, executed: ExecutedBlock<N>) {
164 let hash = executed.recovered_block().hash();
165 let parent_hash = executed.recovered_block().parent_hash();
166 let block_number = executed.recovered_block().number();
167
168 if self.blocks_by_hash.contains_key(&hash) {
169 return;
170 }
171
172 self.blocks_by_hash.insert(hash, executed.clone());
173
174 self.blocks_by_number.entry(block_number).or_default().push(executed);
175
176 self.parent_to_child.entry(parent_hash).or_default().insert(hash);
177 }
178
179 fn remove_by_hash(&mut self, hash: B256) -> Option<(ExecutedBlock<N>, B256Set)> {
185 let executed = self.blocks_by_hash.remove(&hash)?;
186
187 let parent_entry = self.parent_to_child.entry(executed.recovered_block().parent_hash());
189 if let hash_map::Entry::Occupied(mut entry) = parent_entry {
190 entry.get_mut().remove(&hash);
191
192 if entry.get().is_empty() {
193 entry.remove();
194 }
195 }
196
197 let children = self.parent_to_child.remove(&hash).unwrap_or_default();
199
200 let block_number_entry = self.blocks_by_number.entry(executed.recovered_block().number());
202 if let btree_map::Entry::Occupied(mut entry) = block_number_entry {
203 if let Some(index) = entry.get().iter().position(|b| b.recovered_block().hash() == hash)
205 {
206 entry.get_mut().swap_remove(index);
207
208 if entry.get().is_empty() {
210 entry.remove();
211 }
212 }
213 }
214
215 Some((executed, children))
216 }
217
218 pub fn is_canonical(&self, hash: B256) -> bool {
220 let mut current_block = self.current_canonical_head.hash;
221 if current_block == hash {
222 return true
223 }
224
225 while let Some(executed) = self.blocks_by_hash.get(¤t_block) {
226 current_block = executed.recovered_block().parent_hash();
227 if current_block == hash {
228 return true
229 }
230 }
231
232 false
233 }
234
235 pub fn remove_canonical_until(&mut self, upper_bound: BlockNumber, last_persisted_hash: B256) {
238 debug!(target: "engine::tree", ?upper_bound, ?last_persisted_hash, "Removing canonical blocks from the tree");
239
240 if !self.is_canonical(last_persisted_hash) {
243 return
244 }
245
246 let mut current_block = self.current_canonical_head.hash;
249 while let Some(executed) = self.blocks_by_hash.get(¤t_block) {
250 current_block = executed.recovered_block().parent_hash();
251 if executed.recovered_block().number() <= upper_bound {
252 let num_hash = executed.recovered_block().num_hash();
253 debug!(target: "engine::tree", ?num_hash, "Attempting to remove block walking back from the head");
254 self.remove_by_hash(executed.recovered_block().hash());
255 }
256 }
257 debug!(target: "engine::tree", ?upper_bound, ?last_persisted_hash, "Removed canonical blocks from the tree");
258 }
259
260 pub fn prune_finalized_sidechains(&mut self, finalized_num_hash: BlockNumHash) {
263 let BlockNumHash { number: finalized_num, hash: finalized_hash } = finalized_num_hash;
264
265 let blocks_to_remove = self
274 .blocks_by_number
275 .range((Bound::Unbounded, Bound::Excluded(finalized_num)))
276 .flat_map(|(_, blocks)| blocks.iter().map(|b| b.recovered_block().hash()))
277 .collect::<Vec<_>>();
278 for hash in blocks_to_remove {
279 if let Some((removed, _)) = self.remove_by_hash(hash) {
280 debug!(target: "engine::tree", num_hash=?removed.recovered_block().num_hash(), "Removed finalized sidechain block");
281 }
282 }
283
284 let mut blocks_to_remove = self.blocks_by_number.remove(&finalized_num).unwrap_or_default();
291
292 if let Some(position) =
294 blocks_to_remove.iter().position(|b| b.recovered_block().hash() == finalized_hash)
295 {
296 let finalized_block = blocks_to_remove.swap_remove(position);
297 self.blocks_by_number.insert(finalized_num, vec![finalized_block]);
298 }
299
300 let mut blocks_to_remove = blocks_to_remove
301 .into_iter()
302 .map(|e| e.recovered_block().hash())
303 .collect::<VecDeque<_>>();
304 while let Some(block) = blocks_to_remove.pop_front() {
305 if let Some((removed, children)) = self.remove_by_hash(block) {
306 debug!(target: "engine::tree", num_hash=?removed.recovered_block().num_hash(), "Removed finalized sidechain child block");
307 blocks_to_remove.extend(children);
308 }
309 }
310 }
311
312 pub fn remove_until(
323 &mut self,
324 upper_bound: BlockNumHash,
325 last_persisted_hash: B256,
326 finalized_num_hash: Option<BlockNumHash>,
327 ) {
328 debug!(target: "engine::tree", ?upper_bound, ?finalized_num_hash, "Removing blocks from the tree");
329
330 let finalized_num_hash = finalized_num_hash.map(|mut finalized| {
333 if upper_bound.number < finalized.number {
334 finalized = upper_bound;
335 debug!(target: "engine::tree", ?finalized, "Adjusted upper bound");
336 }
337 finalized
338 });
339
340 self.remove_canonical_until(upper_bound.number, last_persisted_hash);
348
349 if let Some(finalized_num_hash) = finalized_num_hash {
352 self.prune_finalized_sidechains(finalized_num_hash);
353 }
354
355 self.invalidate_cached_overlay();
357 }
358
359 pub const fn set_canonical_head(&mut self, new_head: BlockNumHash) {
361 self.current_canonical_head = new_head;
362 }
363
364 pub const fn canonical_head(&self) -> &BlockNumHash {
366 &self.current_canonical_head
367 }
368
369 pub const fn canonical_block_hash(&self) -> B256 {
371 self.canonical_head().hash
372 }
373
374 pub const fn canonical_block_number(&self) -> BlockNumber {
376 self.canonical_head().number
377 }
378}
379
380#[cfg(test)]
381impl<N: NodePrimitives> TreeState<N> {
382 pub fn is_descendant(
386 &self,
387 first: BlockNumHash,
388 second: alloy_eips::eip1898::BlockWithParent,
389 ) -> bool {
390 if second.parent == first.hash {
393 return true
394 }
395
396 if second.block.number <= first.number {
399 return false
400 }
401
402 let Some(mut current_block) = self.blocks_by_hash.get(&second.parent) else {
404 return false
406 };
407
408 while current_block.recovered_block().number() > first.number + 1 {
409 let Some(block) =
410 self.blocks_by_hash.get(¤t_block.recovered_block().parent_hash())
411 else {
412 return false
414 };
415
416 current_block = block;
417 }
418
419 current_block.recovered_block().parent_hash() == first.hash
421 }
422}
423
424#[derive(Debug, Clone)]
441pub struct PreparedCanonicalOverlay<N: NodePrimitives = EthPrimitives> {
442 pub parent_hash: B256,
446 pub overlay: LazyOverlay<N>,
451 pub anchor_hash: B256,
455}
456
457#[cfg(test)]
458mod tests {
459 use super::*;
460 use reth_chain_state::test_utils::TestBlockBuilder;
461
462 #[test]
463 fn test_tree_state_normal_descendant() {
464 let mut tree_state = TreeState::new(BlockNumHash::default(), EngineApiKind::Ethereum);
465 let blocks: Vec<_> = TestBlockBuilder::eth().get_executed_blocks(1..4).collect();
466
467 tree_state.insert_executed(blocks[0].clone());
468 assert!(tree_state.is_descendant(
469 blocks[0].recovered_block().num_hash(),
470 blocks[1].recovered_block().block_with_parent()
471 ));
472
473 tree_state.insert_executed(blocks[1].clone());
474
475 assert!(tree_state.is_descendant(
476 blocks[0].recovered_block().num_hash(),
477 blocks[2].recovered_block().block_with_parent()
478 ));
479 assert!(tree_state.is_descendant(
480 blocks[1].recovered_block().num_hash(),
481 blocks[2].recovered_block().block_with_parent()
482 ));
483 }
484
485 #[tokio::test]
486 async fn test_tree_state_insert_executed() {
487 let mut tree_state = TreeState::new(BlockNumHash::default(), EngineApiKind::Ethereum);
488 let blocks: Vec<_> = TestBlockBuilder::eth().get_executed_blocks(1..4).collect();
489
490 tree_state.insert_executed(blocks[0].clone());
491 tree_state.insert_executed(blocks[1].clone());
492
493 assert_eq!(
494 tree_state.parent_to_child.get(&blocks[0].recovered_block().hash()),
495 Some(&B256Set::from_iter([blocks[1].recovered_block().hash()]))
496 );
497
498 assert!(!tree_state.parent_to_child.contains_key(&blocks[1].recovered_block().hash()));
499
500 tree_state.insert_executed(blocks[2].clone());
501
502 assert_eq!(
503 tree_state.parent_to_child.get(&blocks[1].recovered_block().hash()),
504 Some(&B256Set::from_iter([blocks[2].recovered_block().hash()]))
505 );
506 assert!(tree_state.parent_to_child.contains_key(&blocks[1].recovered_block().hash()));
507
508 assert!(!tree_state.parent_to_child.contains_key(&blocks[2].recovered_block().hash()));
509 }
510
511 #[tokio::test]
512 async fn test_tree_state_insert_executed_with_reorg() {
513 let mut tree_state = TreeState::new(BlockNumHash::default(), EngineApiKind::Ethereum);
514 let mut test_block_builder = TestBlockBuilder::eth();
515 let blocks: Vec<_> = test_block_builder.get_executed_blocks(1..6).collect();
516
517 for block in &blocks {
518 tree_state.insert_executed(block.clone());
519 }
520 assert_eq!(tree_state.blocks_by_hash.len(), 5);
521
522 let fork_block_3 = test_block_builder
523 .get_executed_block_with_number(3, blocks[1].recovered_block().hash());
524 let fork_block_4 = test_block_builder
525 .get_executed_block_with_number(4, fork_block_3.recovered_block().hash());
526 let fork_block_5 = test_block_builder
527 .get_executed_block_with_number(5, fork_block_4.recovered_block().hash());
528
529 tree_state.insert_executed(fork_block_3.clone());
530 tree_state.insert_executed(fork_block_4.clone());
531 tree_state.insert_executed(fork_block_5.clone());
532
533 assert_eq!(tree_state.blocks_by_hash.len(), 8);
534 assert_eq!(tree_state.blocks_by_number[&3].len(), 2); assert_eq!(tree_state.parent_to_child[&blocks[1].recovered_block().hash()].len(), 2); tree_state.insert_executed(fork_block_4.clone());
539 assert_eq!(tree_state.blocks_by_hash.len(), 8);
540
541 assert!(tree_state.parent_to_child[&fork_block_3.recovered_block().hash()]
542 .contains(&fork_block_4.recovered_block().hash()));
543 assert!(tree_state.parent_to_child[&fork_block_4.recovered_block().hash()]
544 .contains(&fork_block_5.recovered_block().hash()));
545
546 assert_eq!(tree_state.blocks_by_number[&4].len(), 2);
547 assert_eq!(tree_state.blocks_by_number[&5].len(), 2);
548 }
549
550 #[tokio::test]
551 async fn test_tree_state_remove_before() {
552 let start_num_hash = BlockNumHash::default();
553 let mut tree_state = TreeState::new(start_num_hash, EngineApiKind::Ethereum);
554 let blocks: Vec<_> = TestBlockBuilder::eth().get_executed_blocks(1..6).collect();
555
556 for block in &blocks {
557 tree_state.insert_executed(block.clone());
558 }
559
560 let last = blocks.last().unwrap();
561
562 tree_state.set_canonical_head(last.recovered_block().num_hash());
564
565 tree_state.remove_until(
567 BlockNumHash::new(2, blocks[1].recovered_block().hash()),
568 start_num_hash.hash,
569 Some(blocks[1].recovered_block().num_hash()),
570 );
571
572 assert!(!tree_state.blocks_by_hash.contains_key(&blocks[0].recovered_block().hash()));
573 assert!(!tree_state.blocks_by_hash.contains_key(&blocks[1].recovered_block().hash()));
574 assert!(!tree_state.blocks_by_number.contains_key(&1));
575 assert!(!tree_state.blocks_by_number.contains_key(&2));
576
577 assert!(tree_state.blocks_by_hash.contains_key(&blocks[2].recovered_block().hash()));
578 assert!(tree_state.blocks_by_hash.contains_key(&blocks[3].recovered_block().hash()));
579 assert!(tree_state.blocks_by_hash.contains_key(&blocks[4].recovered_block().hash()));
580 assert!(tree_state.blocks_by_number.contains_key(&3));
581 assert!(tree_state.blocks_by_number.contains_key(&4));
582 assert!(tree_state.blocks_by_number.contains_key(&5));
583
584 assert!(!tree_state.parent_to_child.contains_key(&blocks[0].recovered_block().hash()));
585 assert!(!tree_state.parent_to_child.contains_key(&blocks[1].recovered_block().hash()));
586 assert!(tree_state.parent_to_child.contains_key(&blocks[2].recovered_block().hash()));
587 assert!(tree_state.parent_to_child.contains_key(&blocks[3].recovered_block().hash()));
588 assert!(!tree_state.parent_to_child.contains_key(&blocks[4].recovered_block().hash()));
589
590 assert_eq!(
591 tree_state.parent_to_child.get(&blocks[2].recovered_block().hash()),
592 Some(&B256Set::from_iter([blocks[3].recovered_block().hash()]))
593 );
594 assert_eq!(
595 tree_state.parent_to_child.get(&blocks[3].recovered_block().hash()),
596 Some(&B256Set::from_iter([blocks[4].recovered_block().hash()]))
597 );
598 }
599
600 #[tokio::test]
601 async fn test_tree_state_remove_before_finalized() {
602 let start_num_hash = BlockNumHash::default();
603 let mut tree_state = TreeState::new(start_num_hash, EngineApiKind::Ethereum);
604 let blocks: Vec<_> = TestBlockBuilder::eth().get_executed_blocks(1..6).collect();
605
606 for block in &blocks {
607 tree_state.insert_executed(block.clone());
608 }
609
610 let last = blocks.last().unwrap();
611
612 tree_state.set_canonical_head(last.recovered_block().num_hash());
614
615 tree_state.remove_until(
617 BlockNumHash::new(2, blocks[1].recovered_block().hash()),
618 start_num_hash.hash,
619 None,
620 );
621
622 assert!(!tree_state.blocks_by_hash.contains_key(&blocks[0].recovered_block().hash()));
623 assert!(!tree_state.blocks_by_hash.contains_key(&blocks[1].recovered_block().hash()));
624 assert!(!tree_state.blocks_by_number.contains_key(&1));
625 assert!(!tree_state.blocks_by_number.contains_key(&2));
626
627 assert!(tree_state.blocks_by_hash.contains_key(&blocks[2].recovered_block().hash()));
628 assert!(tree_state.blocks_by_hash.contains_key(&blocks[3].recovered_block().hash()));
629 assert!(tree_state.blocks_by_hash.contains_key(&blocks[4].recovered_block().hash()));
630 assert!(tree_state.blocks_by_number.contains_key(&3));
631 assert!(tree_state.blocks_by_number.contains_key(&4));
632 assert!(tree_state.blocks_by_number.contains_key(&5));
633
634 assert!(!tree_state.parent_to_child.contains_key(&blocks[0].recovered_block().hash()));
635 assert!(!tree_state.parent_to_child.contains_key(&blocks[1].recovered_block().hash()));
636 assert!(tree_state.parent_to_child.contains_key(&blocks[2].recovered_block().hash()));
637 assert!(tree_state.parent_to_child.contains_key(&blocks[3].recovered_block().hash()));
638 assert!(!tree_state.parent_to_child.contains_key(&blocks[4].recovered_block().hash()));
639
640 assert_eq!(
641 tree_state.parent_to_child.get(&blocks[2].recovered_block().hash()),
642 Some(&B256Set::from_iter([blocks[3].recovered_block().hash()]))
643 );
644 assert_eq!(
645 tree_state.parent_to_child.get(&blocks[3].recovered_block().hash()),
646 Some(&B256Set::from_iter([blocks[4].recovered_block().hash()]))
647 );
648 }
649
650 #[tokio::test]
651 async fn test_tree_state_remove_before_lower_finalized() {
652 let start_num_hash = BlockNumHash::default();
653 let mut tree_state = TreeState::new(start_num_hash, EngineApiKind::Ethereum);
654 let blocks: Vec<_> = TestBlockBuilder::eth().get_executed_blocks(1..6).collect();
655
656 for block in &blocks {
657 tree_state.insert_executed(block.clone());
658 }
659
660 let last = blocks.last().unwrap();
661
662 tree_state.set_canonical_head(last.recovered_block().num_hash());
664
665 tree_state.remove_until(
667 BlockNumHash::new(2, blocks[1].recovered_block().hash()),
668 start_num_hash.hash,
669 Some(blocks[0].recovered_block().num_hash()),
670 );
671
672 assert!(!tree_state.blocks_by_hash.contains_key(&blocks[0].recovered_block().hash()));
673 assert!(!tree_state.blocks_by_hash.contains_key(&blocks[1].recovered_block().hash()));
674 assert!(!tree_state.blocks_by_number.contains_key(&1));
675 assert!(!tree_state.blocks_by_number.contains_key(&2));
676
677 assert!(tree_state.blocks_by_hash.contains_key(&blocks[2].recovered_block().hash()));
678 assert!(tree_state.blocks_by_hash.contains_key(&blocks[3].recovered_block().hash()));
679 assert!(tree_state.blocks_by_hash.contains_key(&blocks[4].recovered_block().hash()));
680 assert!(tree_state.blocks_by_number.contains_key(&3));
681 assert!(tree_state.blocks_by_number.contains_key(&4));
682 assert!(tree_state.blocks_by_number.contains_key(&5));
683
684 assert!(!tree_state.parent_to_child.contains_key(&blocks[0].recovered_block().hash()));
685 assert!(!tree_state.parent_to_child.contains_key(&blocks[1].recovered_block().hash()));
686 assert!(tree_state.parent_to_child.contains_key(&blocks[2].recovered_block().hash()));
687 assert!(tree_state.parent_to_child.contains_key(&blocks[3].recovered_block().hash()));
688 assert!(!tree_state.parent_to_child.contains_key(&blocks[4].recovered_block().hash()));
689
690 assert_eq!(
691 tree_state.parent_to_child.get(&blocks[2].recovered_block().hash()),
692 Some(&B256Set::from_iter([blocks[3].recovered_block().hash()]))
693 );
694 assert_eq!(
695 tree_state.parent_to_child.get(&blocks[3].recovered_block().hash()),
696 Some(&B256Set::from_iter([blocks[4].recovered_block().hash()]))
697 );
698 }
699}