1use crate::engine::EngineApiKind;
4use alloy_eips::BlockNumHash;
5use alloy_primitives::{
6 map::{B256Map, B256Set},
7 BlockNumber, B256,
8};
9use reth_chain_state::{DeferredTrieData, 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>,
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 sealed_header_by_hash(&self, hash: &B256) -> Option<SealedHeader<N::BlockHeader>> {
79 self.blocks_by_hash.get(hash).map(|b| b.sealed_block().sealed_header().clone())
80 }
81
82 pub fn blocks_by_hash(&self, hash: B256) -> Option<(B256, Vec<ExecutedBlock<N>>)> {
88 let block = self.blocks_by_hash.get(&hash).cloned()?;
89 let mut parent_hash = block.recovered_block().parent_hash();
90 let mut blocks = vec![block];
91 while let Some(executed) = self.blocks_by_hash.get(&parent_hash) {
92 parent_hash = executed.recovered_block().parent_hash();
93 blocks.push(executed.clone());
94 }
95
96 Some((parent_hash, blocks))
97 }
98
99 pub(crate) fn prepare_canonical_overlay(&mut self) -> Option<LazyOverlay> {
108 let canonical_hash = self.current_canonical_head.hash;
109
110 let Some((anchor_hash, blocks)) = self.blocks_by_hash(canonical_hash) else {
112 self.cached_canonical_overlay = None;
114 return None;
115 };
116
117 let handles: Vec<DeferredTrieData> = blocks.iter().map(|b| b.trie_data_handle()).collect();
119
120 let overlay = LazyOverlay::new(anchor_hash, handles);
121 self.cached_canonical_overlay = Some(PreparedCanonicalOverlay {
122 parent_hash: canonical_hash,
123 overlay: overlay.clone(),
124 anchor_hash,
125 });
126
127 debug!(
128 target: "engine::tree",
129 %canonical_hash,
130 %anchor_hash,
131 num_blocks = blocks.len(),
132 "Prepared cached canonical overlay"
133 );
134
135 Some(overlay)
136 }
137
138 pub(crate) fn get_cached_overlay(
143 &self,
144 parent_hash: B256,
145 expected_anchor: B256,
146 ) -> Option<&PreparedCanonicalOverlay> {
147 self.cached_canonical_overlay.as_ref().filter(|cached| {
148 cached.parent_hash == parent_hash && cached.anchor_hash == expected_anchor
149 })
150 }
151
152 pub(crate) fn invalidate_cached_overlay(&mut self) {
156 self.cached_canonical_overlay = None;
157 }
158
159 pub fn insert_executed(&mut self, executed: ExecutedBlock<N>) {
161 let hash = executed.recovered_block().hash();
162 let parent_hash = executed.recovered_block().parent_hash();
163 let block_number = executed.recovered_block().number();
164
165 if self.blocks_by_hash.contains_key(&hash) {
166 return;
167 }
168
169 self.blocks_by_hash.insert(hash, executed.clone());
170
171 self.blocks_by_number.entry(block_number).or_default().push(executed);
172
173 self.parent_to_child.entry(parent_hash).or_default().insert(hash);
174 }
175
176 fn remove_by_hash(&mut self, hash: B256) -> Option<(ExecutedBlock<N>, B256Set)> {
182 let executed = self.blocks_by_hash.remove(&hash)?;
183
184 let parent_entry = self.parent_to_child.entry(executed.recovered_block().parent_hash());
186 if let hash_map::Entry::Occupied(mut entry) = parent_entry {
187 entry.get_mut().remove(&hash);
188
189 if entry.get().is_empty() {
190 entry.remove();
191 }
192 }
193
194 let children = self.parent_to_child.remove(&hash).unwrap_or_default();
196
197 let block_number_entry = self.blocks_by_number.entry(executed.recovered_block().number());
199 if let btree_map::Entry::Occupied(mut entry) = block_number_entry {
200 if let Some(index) = entry.get().iter().position(|b| b.recovered_block().hash() == hash)
202 {
203 entry.get_mut().swap_remove(index);
204
205 if entry.get().is_empty() {
207 entry.remove();
208 }
209 }
210 }
211
212 Some((executed, children))
213 }
214
215 pub fn is_canonical(&self, hash: B256) -> bool {
217 let mut current_block = self.current_canonical_head.hash;
218 if current_block == hash {
219 return true
220 }
221
222 while let Some(executed) = self.blocks_by_hash.get(¤t_block) {
223 current_block = executed.recovered_block().parent_hash();
224 if current_block == hash {
225 return true
226 }
227 }
228
229 false
230 }
231
232 pub fn remove_canonical_until(&mut self, upper_bound: BlockNumber, last_persisted_hash: B256) {
235 debug!(target: "engine::tree", ?upper_bound, ?last_persisted_hash, "Removing canonical blocks from the tree");
236
237 if !self.is_canonical(last_persisted_hash) {
240 return
241 }
242
243 let mut current_block = self.current_canonical_head.hash;
246 while let Some(executed) = self.blocks_by_hash.get(¤t_block) {
247 current_block = executed.recovered_block().parent_hash();
248 if executed.recovered_block().number() <= upper_bound {
249 let num_hash = executed.recovered_block().num_hash();
250 debug!(target: "engine::tree", ?num_hash, "Attempting to remove block walking back from the head");
251 self.remove_by_hash(executed.recovered_block().hash());
252 }
253 }
254 debug!(target: "engine::tree", ?upper_bound, ?last_persisted_hash, "Removed canonical blocks from the tree");
255 }
256
257 pub fn prune_finalized_sidechains(&mut self, finalized_num_hash: BlockNumHash) {
260 let BlockNumHash { number: finalized_num, hash: finalized_hash } = finalized_num_hash;
261
262 let blocks_to_remove = self
271 .blocks_by_number
272 .range((Bound::Unbounded, Bound::Excluded(finalized_num)))
273 .flat_map(|(_, blocks)| blocks.iter().map(|b| b.recovered_block().hash()))
274 .collect::<Vec<_>>();
275 for hash in blocks_to_remove {
276 if let Some((removed, _)) = self.remove_by_hash(hash) {
277 debug!(target: "engine::tree", num_hash=?removed.recovered_block().num_hash(), "Removed finalized sidechain block");
278 }
279 }
280
281 let mut blocks_to_remove = self.blocks_by_number.remove(&finalized_num).unwrap_or_default();
288
289 if let Some(position) =
291 blocks_to_remove.iter().position(|b| b.recovered_block().hash() == finalized_hash)
292 {
293 let finalized_block = blocks_to_remove.swap_remove(position);
294 self.blocks_by_number.insert(finalized_num, vec![finalized_block]);
295 }
296
297 let mut blocks_to_remove = blocks_to_remove
298 .into_iter()
299 .map(|e| e.recovered_block().hash())
300 .collect::<VecDeque<_>>();
301 while let Some(block) = blocks_to_remove.pop_front() {
302 if let Some((removed, children)) = self.remove_by_hash(block) {
303 debug!(target: "engine::tree", num_hash=?removed.recovered_block().num_hash(), "Removed finalized sidechain child block");
304 blocks_to_remove.extend(children);
305 }
306 }
307 }
308
309 pub fn remove_until(
320 &mut self,
321 upper_bound: BlockNumHash,
322 last_persisted_hash: B256,
323 finalized_num_hash: Option<BlockNumHash>,
324 ) {
325 debug!(target: "engine::tree", ?upper_bound, ?finalized_num_hash, "Removing blocks from the tree");
326
327 let finalized_num_hash = finalized_num_hash.map(|mut finalized| {
330 if upper_bound.number < finalized.number {
331 finalized = upper_bound;
332 debug!(target: "engine::tree", ?finalized, "Adjusted upper bound");
333 }
334 finalized
335 });
336
337 self.remove_canonical_until(upper_bound.number, last_persisted_hash);
345
346 if let Some(finalized_num_hash) = finalized_num_hash {
349 self.prune_finalized_sidechains(finalized_num_hash);
350 }
351
352 self.invalidate_cached_overlay();
354 }
355
356 pub const fn set_canonical_head(&mut self, new_head: BlockNumHash) {
358 self.current_canonical_head = new_head;
359 }
360
361 pub const fn canonical_head(&self) -> &BlockNumHash {
363 &self.current_canonical_head
364 }
365
366 pub const fn canonical_block_hash(&self) -> B256 {
368 self.canonical_head().hash
369 }
370
371 pub const fn canonical_block_number(&self) -> BlockNumber {
373 self.canonical_head().number
374 }
375}
376
377#[cfg(test)]
378impl<N: NodePrimitives> TreeState<N> {
379 pub fn is_descendant(
383 &self,
384 first: BlockNumHash,
385 second: alloy_eips::eip1898::BlockWithParent,
386 ) -> bool {
387 if second.parent == first.hash {
390 return true
391 }
392
393 if second.block.number <= first.number {
396 return false
397 }
398
399 let Some(mut current_block) = self.blocks_by_hash.get(&second.parent) else {
401 return false
403 };
404
405 while current_block.recovered_block().number() > first.number + 1 {
406 let Some(block) =
407 self.blocks_by_hash.get(¤t_block.recovered_block().parent_hash())
408 else {
409 return false
411 };
412
413 current_block = block;
414 }
415
416 current_block.recovered_block().parent_hash() == first.hash
418 }
419}
420
421#[derive(Debug, Clone)]
438pub struct PreparedCanonicalOverlay {
439 pub parent_hash: B256,
443 pub overlay: LazyOverlay,
448 pub anchor_hash: B256,
452}
453
454#[cfg(test)]
455mod tests {
456 use super::*;
457 use reth_chain_state::test_utils::TestBlockBuilder;
458
459 #[test]
460 fn test_tree_state_normal_descendant() {
461 let mut tree_state = TreeState::new(BlockNumHash::default(), EngineApiKind::Ethereum);
462 let blocks: Vec<_> = TestBlockBuilder::eth().get_executed_blocks(1..4).collect();
463
464 tree_state.insert_executed(blocks[0].clone());
465 assert!(tree_state.is_descendant(
466 blocks[0].recovered_block().num_hash(),
467 blocks[1].recovered_block().block_with_parent()
468 ));
469
470 tree_state.insert_executed(blocks[1].clone());
471
472 assert!(tree_state.is_descendant(
473 blocks[0].recovered_block().num_hash(),
474 blocks[2].recovered_block().block_with_parent()
475 ));
476 assert!(tree_state.is_descendant(
477 blocks[1].recovered_block().num_hash(),
478 blocks[2].recovered_block().block_with_parent()
479 ));
480 }
481
482 #[tokio::test]
483 async fn test_tree_state_insert_executed() {
484 let mut tree_state = TreeState::new(BlockNumHash::default(), EngineApiKind::Ethereum);
485 let blocks: Vec<_> = TestBlockBuilder::eth().get_executed_blocks(1..4).collect();
486
487 tree_state.insert_executed(blocks[0].clone());
488 tree_state.insert_executed(blocks[1].clone());
489
490 assert_eq!(
491 tree_state.parent_to_child.get(&blocks[0].recovered_block().hash()),
492 Some(&B256Set::from_iter([blocks[1].recovered_block().hash()]))
493 );
494
495 assert!(!tree_state.parent_to_child.contains_key(&blocks[1].recovered_block().hash()));
496
497 tree_state.insert_executed(blocks[2].clone());
498
499 assert_eq!(
500 tree_state.parent_to_child.get(&blocks[1].recovered_block().hash()),
501 Some(&B256Set::from_iter([blocks[2].recovered_block().hash()]))
502 );
503 assert!(tree_state.parent_to_child.contains_key(&blocks[1].recovered_block().hash()));
504
505 assert!(!tree_state.parent_to_child.contains_key(&blocks[2].recovered_block().hash()));
506 }
507
508 #[tokio::test]
509 async fn test_tree_state_insert_executed_with_reorg() {
510 let mut tree_state = TreeState::new(BlockNumHash::default(), EngineApiKind::Ethereum);
511 let mut test_block_builder = TestBlockBuilder::eth();
512 let blocks: Vec<_> = test_block_builder.get_executed_blocks(1..6).collect();
513
514 for block in &blocks {
515 tree_state.insert_executed(block.clone());
516 }
517 assert_eq!(tree_state.blocks_by_hash.len(), 5);
518
519 let fork_block_3 = test_block_builder
520 .get_executed_block_with_number(3, blocks[1].recovered_block().hash());
521 let fork_block_4 = test_block_builder
522 .get_executed_block_with_number(4, fork_block_3.recovered_block().hash());
523 let fork_block_5 = test_block_builder
524 .get_executed_block_with_number(5, fork_block_4.recovered_block().hash());
525
526 tree_state.insert_executed(fork_block_3.clone());
527 tree_state.insert_executed(fork_block_4.clone());
528 tree_state.insert_executed(fork_block_5.clone());
529
530 assert_eq!(tree_state.blocks_by_hash.len(), 8);
531 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());
536 assert_eq!(tree_state.blocks_by_hash.len(), 8);
537
538 assert!(tree_state.parent_to_child[&fork_block_3.recovered_block().hash()]
539 .contains(&fork_block_4.recovered_block().hash()));
540 assert!(tree_state.parent_to_child[&fork_block_4.recovered_block().hash()]
541 .contains(&fork_block_5.recovered_block().hash()));
542
543 assert_eq!(tree_state.blocks_by_number[&4].len(), 2);
544 assert_eq!(tree_state.blocks_by_number[&5].len(), 2);
545 }
546
547 #[tokio::test]
548 async fn test_tree_state_remove_before() {
549 let start_num_hash = BlockNumHash::default();
550 let mut tree_state = TreeState::new(start_num_hash, EngineApiKind::Ethereum);
551 let blocks: Vec<_> = TestBlockBuilder::eth().get_executed_blocks(1..6).collect();
552
553 for block in &blocks {
554 tree_state.insert_executed(block.clone());
555 }
556
557 let last = blocks.last().unwrap();
558
559 tree_state.set_canonical_head(last.recovered_block().num_hash());
561
562 tree_state.remove_until(
564 BlockNumHash::new(2, blocks[1].recovered_block().hash()),
565 start_num_hash.hash,
566 Some(blocks[1].recovered_block().num_hash()),
567 );
568
569 assert!(!tree_state.blocks_by_hash.contains_key(&blocks[0].recovered_block().hash()));
570 assert!(!tree_state.blocks_by_hash.contains_key(&blocks[1].recovered_block().hash()));
571 assert!(!tree_state.blocks_by_number.contains_key(&1));
572 assert!(!tree_state.blocks_by_number.contains_key(&2));
573
574 assert!(tree_state.blocks_by_hash.contains_key(&blocks[2].recovered_block().hash()));
575 assert!(tree_state.blocks_by_hash.contains_key(&blocks[3].recovered_block().hash()));
576 assert!(tree_state.blocks_by_hash.contains_key(&blocks[4].recovered_block().hash()));
577 assert!(tree_state.blocks_by_number.contains_key(&3));
578 assert!(tree_state.blocks_by_number.contains_key(&4));
579 assert!(tree_state.blocks_by_number.contains_key(&5));
580
581 assert!(!tree_state.parent_to_child.contains_key(&blocks[0].recovered_block().hash()));
582 assert!(!tree_state.parent_to_child.contains_key(&blocks[1].recovered_block().hash()));
583 assert!(tree_state.parent_to_child.contains_key(&blocks[2].recovered_block().hash()));
584 assert!(tree_state.parent_to_child.contains_key(&blocks[3].recovered_block().hash()));
585 assert!(!tree_state.parent_to_child.contains_key(&blocks[4].recovered_block().hash()));
586
587 assert_eq!(
588 tree_state.parent_to_child.get(&blocks[2].recovered_block().hash()),
589 Some(&B256Set::from_iter([blocks[3].recovered_block().hash()]))
590 );
591 assert_eq!(
592 tree_state.parent_to_child.get(&blocks[3].recovered_block().hash()),
593 Some(&B256Set::from_iter([blocks[4].recovered_block().hash()]))
594 );
595 }
596
597 #[tokio::test]
598 async fn test_tree_state_remove_before_finalized() {
599 let start_num_hash = BlockNumHash::default();
600 let mut tree_state = TreeState::new(start_num_hash, EngineApiKind::Ethereum);
601 let blocks: Vec<_> = TestBlockBuilder::eth().get_executed_blocks(1..6).collect();
602
603 for block in &blocks {
604 tree_state.insert_executed(block.clone());
605 }
606
607 let last = blocks.last().unwrap();
608
609 tree_state.set_canonical_head(last.recovered_block().num_hash());
611
612 tree_state.remove_until(
614 BlockNumHash::new(2, blocks[1].recovered_block().hash()),
615 start_num_hash.hash,
616 None,
617 );
618
619 assert!(!tree_state.blocks_by_hash.contains_key(&blocks[0].recovered_block().hash()));
620 assert!(!tree_state.blocks_by_hash.contains_key(&blocks[1].recovered_block().hash()));
621 assert!(!tree_state.blocks_by_number.contains_key(&1));
622 assert!(!tree_state.blocks_by_number.contains_key(&2));
623
624 assert!(tree_state.blocks_by_hash.contains_key(&blocks[2].recovered_block().hash()));
625 assert!(tree_state.blocks_by_hash.contains_key(&blocks[3].recovered_block().hash()));
626 assert!(tree_state.blocks_by_hash.contains_key(&blocks[4].recovered_block().hash()));
627 assert!(tree_state.blocks_by_number.contains_key(&3));
628 assert!(tree_state.blocks_by_number.contains_key(&4));
629 assert!(tree_state.blocks_by_number.contains_key(&5));
630
631 assert!(!tree_state.parent_to_child.contains_key(&blocks[0].recovered_block().hash()));
632 assert!(!tree_state.parent_to_child.contains_key(&blocks[1].recovered_block().hash()));
633 assert!(tree_state.parent_to_child.contains_key(&blocks[2].recovered_block().hash()));
634 assert!(tree_state.parent_to_child.contains_key(&blocks[3].recovered_block().hash()));
635 assert!(!tree_state.parent_to_child.contains_key(&blocks[4].recovered_block().hash()));
636
637 assert_eq!(
638 tree_state.parent_to_child.get(&blocks[2].recovered_block().hash()),
639 Some(&B256Set::from_iter([blocks[3].recovered_block().hash()]))
640 );
641 assert_eq!(
642 tree_state.parent_to_child.get(&blocks[3].recovered_block().hash()),
643 Some(&B256Set::from_iter([blocks[4].recovered_block().hash()]))
644 );
645 }
646
647 #[tokio::test]
648 async fn test_tree_state_remove_before_lower_finalized() {
649 let start_num_hash = BlockNumHash::default();
650 let mut tree_state = TreeState::new(start_num_hash, EngineApiKind::Ethereum);
651 let blocks: Vec<_> = TestBlockBuilder::eth().get_executed_blocks(1..6).collect();
652
653 for block in &blocks {
654 tree_state.insert_executed(block.clone());
655 }
656
657 let last = blocks.last().unwrap();
658
659 tree_state.set_canonical_head(last.recovered_block().num_hash());
661
662 tree_state.remove_until(
664 BlockNumHash::new(2, blocks[1].recovered_block().hash()),
665 start_num_hash.hash,
666 Some(blocks[0].recovered_block().num_hash()),
667 );
668
669 assert!(!tree_state.blocks_by_hash.contains_key(&blocks[0].recovered_block().hash()));
670 assert!(!tree_state.blocks_by_hash.contains_key(&blocks[1].recovered_block().hash()));
671 assert!(!tree_state.blocks_by_number.contains_key(&1));
672 assert!(!tree_state.blocks_by_number.contains_key(&2));
673
674 assert!(tree_state.blocks_by_hash.contains_key(&blocks[2].recovered_block().hash()));
675 assert!(tree_state.blocks_by_hash.contains_key(&blocks[3].recovered_block().hash()));
676 assert!(tree_state.blocks_by_hash.contains_key(&blocks[4].recovered_block().hash()));
677 assert!(tree_state.blocks_by_number.contains_key(&3));
678 assert!(tree_state.blocks_by_number.contains_key(&4));
679 assert!(tree_state.blocks_by_number.contains_key(&5));
680
681 assert!(!tree_state.parent_to_child.contains_key(&blocks[0].recovered_block().hash()));
682 assert!(!tree_state.parent_to_child.contains_key(&blocks[1].recovered_block().hash()));
683 assert!(tree_state.parent_to_child.contains_key(&blocks[2].recovered_block().hash()));
684 assert!(tree_state.parent_to_child.contains_key(&blocks[3].recovered_block().hash()));
685 assert!(!tree_state.parent_to_child.contains_key(&blocks[4].recovered_block().hash()));
686
687 assert_eq!(
688 tree_state.parent_to_child.get(&blocks[2].recovered_block().hash()),
689 Some(&B256Set::from_iter([blocks[3].recovered_block().hash()]))
690 );
691 assert_eq!(
692 tree_state.parent_to_child.get(&blocks[3].recovered_block().hash()),
693 Some(&B256Set::from_iter([blocks[4].recovered_block().hash()]))
694 );
695 }
696}