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};
10use reth_primitives_traits::{AlloyBlockHeader, NodePrimitives, SealedHeader};
11use reth_storage_overlay::OverlayManager;
12use std::{
13 collections::{btree_map, hash_map, BTreeMap, VecDeque},
14 ops::Bound,
15};
16use tracing::debug;
17
18#[derive(Debug, Default)]
25pub struct TreeState<N: NodePrimitives = EthPrimitives> {
26 pub(crate) blocks_by_hash: B256Map<ExecutedBlock<N>>,
30 pub(crate) blocks_by_number: BTreeMap<BlockNumber, Vec<ExecutedBlock<N>>>,
36 pub(crate) parent_to_child: B256Map<B256Set>,
38 pub(crate) current_canonical_head: BlockNumHash,
40 pub(crate) engine_kind: EngineApiKind,
42 pub(crate) overlay_manager: OverlayManager<N>,
44}
45
46impl<N: NodePrimitives> TreeState<N> {
47 pub fn new(
49 current_canonical_head: BlockNumHash,
50 engine_kind: EngineApiKind,
51 overlay_manager: OverlayManager<N>,
52 ) -> Self {
53 Self {
54 blocks_by_hash: B256Map::default(),
55 blocks_by_number: BTreeMap::new(),
56 current_canonical_head,
57 parent_to_child: B256Map::default(),
58 engine_kind,
59 overlay_manager,
60 }
61 }
62
63 pub fn reset(&mut self, current_canonical_head: BlockNumHash) {
65 let engine_kind = self.engine_kind;
66 let removed_hashes = self.blocks_by_hash.keys().copied().collect::<Vec<_>>();
67 if !removed_hashes.is_empty() {
68 self.overlay_manager.remove_blocks(removed_hashes);
69 }
70 self.blocks_by_hash.clear();
71 self.blocks_by_number.clear();
72 self.parent_to_child.clear();
73 self.current_canonical_head = current_canonical_head;
74 self.engine_kind = engine_kind;
75 }
76
77 pub fn block_count(&self) -> usize {
79 self.blocks_by_hash.len()
80 }
81
82 pub fn executed_block_by_hash(&self, hash: B256) -> Option<&ExecutedBlock<N>> {
84 self.blocks_by_hash.get(&hash)
85 }
86
87 pub fn contains_hash(&self, hash: &B256) -> bool {
89 self.blocks_by_hash.contains_key(hash)
90 }
91
92 pub fn sealed_header_by_hash(&self, hash: &B256) -> Option<SealedHeader<N::BlockHeader>> {
94 self.blocks_by_hash.get(hash).map(|b| b.sealed_block().sealed_header().clone())
95 }
96
97 pub fn blocks_by_hash(&self, hash: B256) -> Option<(B256, Vec<ExecutedBlock<N>>)> {
103 let block = self.blocks_by_hash.get(&hash).cloned()?;
104 let mut parent_hash = block.recovered_block().parent_hash();
105 let mut blocks = vec![block];
106 while let Some(executed) = self.blocks_by_hash.get(&parent_hash) {
107 parent_hash = executed.recovered_block().parent_hash();
108 blocks.push(executed.clone());
109 }
110
111 Some((parent_hash, blocks))
112 }
113
114 pub fn insert_executed(&mut self, executed: ExecutedBlock<N>) {
116 let hash = executed.recovered_block().hash();
117 let parent_hash = executed.recovered_block().parent_hash();
118 let block_number = executed.recovered_block().number();
119
120 if self.blocks_by_hash.contains_key(&hash) {
121 return;
122 }
123
124 let overlay_block = executed.clone();
125 self.blocks_by_hash.insert(hash, executed.clone());
126
127 self.blocks_by_number.entry(block_number).or_default().push(executed);
128
129 self.parent_to_child.entry(parent_hash).or_default().insert(hash);
130 self.overlay_manager.insert_block(overlay_block);
131 }
132
133 fn remove_by_hash(&mut self, hash: B256) -> Option<(ExecutedBlock<N>, B256Set)> {
139 let executed = self.blocks_by_hash.remove(&hash)?;
140
141 let parent_entry = self.parent_to_child.entry(executed.recovered_block().parent_hash());
143 if let hash_map::Entry::Occupied(mut entry) = parent_entry {
144 entry.get_mut().remove(&hash);
145
146 if entry.get().is_empty() {
147 entry.remove();
148 }
149 }
150
151 let children = self.parent_to_child.remove(&hash).unwrap_or_default();
153
154 let block_number_entry = self.blocks_by_number.entry(executed.recovered_block().number());
156 if let btree_map::Entry::Occupied(mut entry) = block_number_entry {
157 if let Some(index) = entry.get().iter().position(|b| b.recovered_block().hash() == hash)
159 {
160 entry.get_mut().swap_remove(index);
161
162 if entry.get().is_empty() {
164 entry.remove();
165 }
166 }
167 }
168
169 Some((executed, children))
170 }
171
172 pub fn is_canonical(&self, hash: B256) -> bool {
174 let mut current_block = self.current_canonical_head.hash;
175 if current_block == hash {
176 return true
177 }
178
179 while let Some(executed) = self.blocks_by_hash.get(¤t_block) {
180 current_block = executed.recovered_block().parent_hash();
181 if current_block == hash {
182 return true
183 }
184 }
185
186 false
187 }
188
189 fn remove_canonical_until(
192 &mut self,
193 upper_bound: BlockNumber,
194 last_persisted_hash: B256,
195 removed_hashes: &mut Vec<B256>,
196 ) {
197 debug!(target: "engine::tree", ?upper_bound, ?last_persisted_hash, "Removing canonical blocks from the tree");
198
199 if !self.is_canonical(last_persisted_hash) {
202 return
203 }
204
205 let mut current_block = self.current_canonical_head.hash;
208 while let Some(executed) = self.blocks_by_hash.get(¤t_block) {
209 current_block = executed.recovered_block().parent_hash();
210 if executed.recovered_block().number() <= upper_bound {
211 let hash = executed.recovered_block().hash();
212 let num_hash = executed.recovered_block().num_hash();
213 debug!(target: "engine::tree", ?num_hash, "Attempting to remove block walking back from the head");
214 if self.remove_by_hash(hash).is_some() {
215 removed_hashes.push(hash);
216 }
217 }
218 }
219 debug!(target: "engine::tree", ?upper_bound, ?last_persisted_hash, "Removed canonical blocks from the tree");
220 }
221
222 fn prune_finalized_sidechains(
225 &mut self,
226 finalized_num_hash: BlockNumHash,
227 removed_hashes: &mut Vec<B256>,
228 ) {
229 let BlockNumHash { number: finalized_num, hash: finalized_hash } = finalized_num_hash;
230
231 let blocks_to_remove = self
240 .blocks_by_number
241 .range((Bound::Unbounded, Bound::Excluded(finalized_num)))
242 .flat_map(|(_, blocks)| blocks.iter().map(|b| b.recovered_block().hash()))
243 .collect::<Vec<_>>();
244 for hash in blocks_to_remove {
245 if let Some((removed, _)) = self.remove_by_hash(hash) {
246 debug!(target: "engine::tree", num_hash=?removed.recovered_block().num_hash(), "Removed finalized sidechain block");
247 removed_hashes.push(hash);
248 }
249 }
250
251 let mut blocks_to_remove = self.blocks_by_number.remove(&finalized_num).unwrap_or_default();
258
259 if let Some(position) =
261 blocks_to_remove.iter().position(|b| b.recovered_block().hash() == finalized_hash)
262 {
263 let finalized_block = blocks_to_remove.swap_remove(position);
264 self.blocks_by_number.insert(finalized_num, vec![finalized_block]);
265 }
266
267 let mut blocks_to_remove = blocks_to_remove
268 .into_iter()
269 .map(|e| e.recovered_block().hash())
270 .collect::<VecDeque<_>>();
271 while let Some(block) = blocks_to_remove.pop_front() {
272 if let Some((removed, children)) = self.remove_by_hash(block) {
273 debug!(target: "engine::tree", num_hash=?removed.recovered_block().num_hash(), "Removed finalized sidechain child block");
274 removed_hashes.push(block);
275 blocks_to_remove.extend(children);
276 }
277 }
278 }
279
280 pub fn remove_until(
291 &mut self,
292 upper_bound: BlockNumHash,
293 last_persisted_hash: B256,
294 finalized_num_hash: Option<BlockNumHash>,
295 ) {
296 debug!(target: "engine::tree", ?upper_bound, ?finalized_num_hash, "Removing blocks from the tree");
297
298 let finalized_num_hash = finalized_num_hash.map(|mut finalized| {
301 if upper_bound.number < finalized.number {
302 finalized = upper_bound;
303 debug!(target: "engine::tree", ?finalized, "Adjusted upper bound");
304 }
305 finalized
306 });
307
308 let mut removed_hashes = Vec::new();
316 self.remove_canonical_until(upper_bound.number, last_persisted_hash, &mut removed_hashes);
317
318 if let Some(finalized_num_hash) = finalized_num_hash {
321 self.prune_finalized_sidechains(finalized_num_hash, &mut removed_hashes);
322 }
323
324 if !removed_hashes.is_empty() {
325 self.overlay_manager.remove_blocks(removed_hashes);
326 }
327 }
328
329 pub const fn set_canonical_head(&mut self, new_head: BlockNumHash) {
331 self.current_canonical_head = new_head;
332 }
333
334 pub const fn canonical_head(&self) -> &BlockNumHash {
336 &self.current_canonical_head
337 }
338
339 pub const fn canonical_block_hash(&self) -> B256 {
341 self.canonical_head().hash
342 }
343
344 pub const fn canonical_block_number(&self) -> BlockNumber {
346 self.canonical_head().number
347 }
348}
349
350#[cfg(test)]
351impl<N: NodePrimitives> TreeState<N> {
352 pub fn is_descendant(
356 &self,
357 first: BlockNumHash,
358 second: alloy_eips::eip1898::BlockWithParent,
359 ) -> bool {
360 if second.parent == first.hash {
363 return true
364 }
365
366 if second.block.number <= first.number {
369 return false
370 }
371
372 let Some(mut current_block) = self.blocks_by_hash.get(&second.parent) else {
374 return false
376 };
377
378 while current_block.recovered_block().number() > first.number + 1 {
379 let Some(block) =
380 self.blocks_by_hash.get(¤t_block.recovered_block().parent_hash())
381 else {
382 return false
384 };
385
386 current_block = block;
387 }
388
389 current_block.recovered_block().parent_hash() == first.hash
391 }
392}
393
394#[cfg(test)]
395mod tests {
396 use super::*;
397 use reth_chain_state::test_utils::TestBlockBuilder;
398
399 #[test]
400 fn test_tree_state_normal_descendant() {
401 let mut tree_state = TreeState::new(
402 BlockNumHash::default(),
403 EngineApiKind::Ethereum,
404 OverlayManager::default(),
405 );
406 let blocks: Vec<_> = TestBlockBuilder::eth().get_executed_blocks(1..4).collect();
407
408 tree_state.insert_executed(blocks[0].clone());
409 assert!(tree_state.is_descendant(
410 blocks[0].recovered_block().num_hash(),
411 blocks[1].recovered_block().block_with_parent()
412 ));
413
414 tree_state.insert_executed(blocks[1].clone());
415
416 assert!(tree_state.is_descendant(
417 blocks[0].recovered_block().num_hash(),
418 blocks[2].recovered_block().block_with_parent()
419 ));
420 assert!(tree_state.is_descendant(
421 blocks[1].recovered_block().num_hash(),
422 blocks[2].recovered_block().block_with_parent()
423 ));
424 }
425
426 #[tokio::test]
427 async fn test_tree_state_insert_executed() {
428 let mut tree_state = TreeState::new(
429 BlockNumHash::default(),
430 EngineApiKind::Ethereum,
431 OverlayManager::default(),
432 );
433 let blocks: Vec<_> = TestBlockBuilder::eth().get_executed_blocks(1..4).collect();
434
435 tree_state.insert_executed(blocks[0].clone());
436 tree_state.insert_executed(blocks[1].clone());
437
438 assert_eq!(
439 tree_state.parent_to_child.get(&blocks[0].recovered_block().hash()),
440 Some(&B256Set::from_iter([blocks[1].recovered_block().hash()]))
441 );
442
443 assert!(!tree_state.parent_to_child.contains_key(&blocks[1].recovered_block().hash()));
444
445 tree_state.insert_executed(blocks[2].clone());
446
447 assert_eq!(
448 tree_state.parent_to_child.get(&blocks[1].recovered_block().hash()),
449 Some(&B256Set::from_iter([blocks[2].recovered_block().hash()]))
450 );
451 assert!(tree_state.parent_to_child.contains_key(&blocks[1].recovered_block().hash()));
452
453 assert!(!tree_state.parent_to_child.contains_key(&blocks[2].recovered_block().hash()));
454 }
455
456 #[tokio::test]
457 async fn test_tree_state_insert_executed_with_reorg() {
458 let mut tree_state = TreeState::new(
459 BlockNumHash::default(),
460 EngineApiKind::Ethereum,
461 OverlayManager::default(),
462 );
463 let mut test_block_builder = TestBlockBuilder::eth();
464 let blocks: Vec<_> = test_block_builder.get_executed_blocks(1..6).collect();
465
466 for block in &blocks {
467 tree_state.insert_executed(block.clone());
468 }
469 assert_eq!(tree_state.blocks_by_hash.len(), 5);
470
471 let fork_block_3 = test_block_builder
472 .get_executed_block_with_number(3, blocks[1].recovered_block().hash());
473 let fork_block_4 = test_block_builder
474 .get_executed_block_with_number(4, fork_block_3.recovered_block().hash());
475 let fork_block_5 = test_block_builder
476 .get_executed_block_with_number(5, fork_block_4.recovered_block().hash());
477
478 tree_state.insert_executed(fork_block_3.clone());
479 tree_state.insert_executed(fork_block_4.clone());
480 tree_state.insert_executed(fork_block_5.clone());
481
482 assert_eq!(tree_state.blocks_by_hash.len(), 8);
483 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());
488 assert_eq!(tree_state.blocks_by_hash.len(), 8);
489
490 assert!(tree_state.parent_to_child[&fork_block_3.recovered_block().hash()]
491 .contains(&fork_block_4.recovered_block().hash()));
492 assert!(tree_state.parent_to_child[&fork_block_4.recovered_block().hash()]
493 .contains(&fork_block_5.recovered_block().hash()));
494
495 assert_eq!(tree_state.blocks_by_number[&4].len(), 2);
496 assert_eq!(tree_state.blocks_by_number[&5].len(), 2);
497 }
498
499 #[tokio::test]
500 async fn test_tree_state_remove_before() {
501 let start_num_hash = BlockNumHash::default();
502 let mut tree_state =
503 TreeState::new(start_num_hash, EngineApiKind::Ethereum, OverlayManager::default());
504 let blocks: Vec<_> = TestBlockBuilder::eth().get_executed_blocks(1..6).collect();
505
506 for block in &blocks {
507 tree_state.insert_executed(block.clone());
508 }
509
510 let last = blocks.last().unwrap();
511
512 tree_state.set_canonical_head(last.recovered_block().num_hash());
514
515 tree_state.remove_until(
517 BlockNumHash::new(2, blocks[1].recovered_block().hash()),
518 start_num_hash.hash,
519 Some(blocks[1].recovered_block().num_hash()),
520 );
521
522 assert!(!tree_state.blocks_by_hash.contains_key(&blocks[0].recovered_block().hash()));
523 assert!(!tree_state.blocks_by_hash.contains_key(&blocks[1].recovered_block().hash()));
524 assert!(!tree_state.blocks_by_number.contains_key(&1));
525 assert!(!tree_state.blocks_by_number.contains_key(&2));
526
527 assert!(tree_state.blocks_by_hash.contains_key(&blocks[2].recovered_block().hash()));
528 assert!(tree_state.blocks_by_hash.contains_key(&blocks[3].recovered_block().hash()));
529 assert!(tree_state.blocks_by_hash.contains_key(&blocks[4].recovered_block().hash()));
530 assert!(tree_state.blocks_by_number.contains_key(&3));
531 assert!(tree_state.blocks_by_number.contains_key(&4));
532 assert!(tree_state.blocks_by_number.contains_key(&5));
533
534 assert!(!tree_state.parent_to_child.contains_key(&blocks[0].recovered_block().hash()));
535 assert!(!tree_state.parent_to_child.contains_key(&blocks[1].recovered_block().hash()));
536 assert!(tree_state.parent_to_child.contains_key(&blocks[2].recovered_block().hash()));
537 assert!(tree_state.parent_to_child.contains_key(&blocks[3].recovered_block().hash()));
538 assert!(!tree_state.parent_to_child.contains_key(&blocks[4].recovered_block().hash()));
539
540 assert_eq!(
541 tree_state.parent_to_child.get(&blocks[2].recovered_block().hash()),
542 Some(&B256Set::from_iter([blocks[3].recovered_block().hash()]))
543 );
544 assert_eq!(
545 tree_state.parent_to_child.get(&blocks[3].recovered_block().hash()),
546 Some(&B256Set::from_iter([blocks[4].recovered_block().hash()]))
547 );
548 }
549
550 #[tokio::test]
551 async fn test_tree_state_remove_before_finalized() {
552 let start_num_hash = BlockNumHash::default();
553 let mut tree_state =
554 TreeState::new(start_num_hash, EngineApiKind::Ethereum, OverlayManager::default());
555 let blocks: Vec<_> = TestBlockBuilder::eth().get_executed_blocks(1..6).collect();
556
557 for block in &blocks {
558 tree_state.insert_executed(block.clone());
559 }
560
561 let last = blocks.last().unwrap();
562
563 tree_state.set_canonical_head(last.recovered_block().num_hash());
565
566 tree_state.remove_until(
568 BlockNumHash::new(2, blocks[1].recovered_block().hash()),
569 start_num_hash.hash,
570 None,
571 );
572
573 assert!(!tree_state.blocks_by_hash.contains_key(&blocks[0].recovered_block().hash()));
574 assert!(!tree_state.blocks_by_hash.contains_key(&blocks[1].recovered_block().hash()));
575 assert!(!tree_state.blocks_by_number.contains_key(&1));
576 assert!(!tree_state.blocks_by_number.contains_key(&2));
577
578 assert!(tree_state.blocks_by_hash.contains_key(&blocks[2].recovered_block().hash()));
579 assert!(tree_state.blocks_by_hash.contains_key(&blocks[3].recovered_block().hash()));
580 assert!(tree_state.blocks_by_hash.contains_key(&blocks[4].recovered_block().hash()));
581 assert!(tree_state.blocks_by_number.contains_key(&3));
582 assert!(tree_state.blocks_by_number.contains_key(&4));
583 assert!(tree_state.blocks_by_number.contains_key(&5));
584
585 assert!(!tree_state.parent_to_child.contains_key(&blocks[0].recovered_block().hash()));
586 assert!(!tree_state.parent_to_child.contains_key(&blocks[1].recovered_block().hash()));
587 assert!(tree_state.parent_to_child.contains_key(&blocks[2].recovered_block().hash()));
588 assert!(tree_state.parent_to_child.contains_key(&blocks[3].recovered_block().hash()));
589 assert!(!tree_state.parent_to_child.contains_key(&blocks[4].recovered_block().hash()));
590
591 assert_eq!(
592 tree_state.parent_to_child.get(&blocks[2].recovered_block().hash()),
593 Some(&B256Set::from_iter([blocks[3].recovered_block().hash()]))
594 );
595 assert_eq!(
596 tree_state.parent_to_child.get(&blocks[3].recovered_block().hash()),
597 Some(&B256Set::from_iter([blocks[4].recovered_block().hash()]))
598 );
599 }
600
601 #[tokio::test]
602 async fn test_tree_state_remove_before_lower_finalized() {
603 let start_num_hash = BlockNumHash::default();
604 let mut tree_state =
605 TreeState::new(start_num_hash, EngineApiKind::Ethereum, OverlayManager::default());
606 let blocks: Vec<_> = TestBlockBuilder::eth().get_executed_blocks(1..6).collect();
607
608 for block in &blocks {
609 tree_state.insert_executed(block.clone());
610 }
611
612 let last = blocks.last().unwrap();
613
614 tree_state.set_canonical_head(last.recovered_block().num_hash());
616
617 tree_state.remove_until(
619 BlockNumHash::new(2, blocks[1].recovered_block().hash()),
620 start_num_hash.hash,
621 Some(blocks[0].recovered_block().num_hash()),
622 );
623
624 assert!(!tree_state.blocks_by_hash.contains_key(&blocks[0].recovered_block().hash()));
625 assert!(!tree_state.blocks_by_hash.contains_key(&blocks[1].recovered_block().hash()));
626 assert!(!tree_state.blocks_by_number.contains_key(&1));
627 assert!(!tree_state.blocks_by_number.contains_key(&2));
628
629 assert!(tree_state.blocks_by_hash.contains_key(&blocks[2].recovered_block().hash()));
630 assert!(tree_state.blocks_by_hash.contains_key(&blocks[3].recovered_block().hash()));
631 assert!(tree_state.blocks_by_hash.contains_key(&blocks[4].recovered_block().hash()));
632 assert!(tree_state.blocks_by_number.contains_key(&3));
633 assert!(tree_state.blocks_by_number.contains_key(&4));
634 assert!(tree_state.blocks_by_number.contains_key(&5));
635
636 assert!(!tree_state.parent_to_child.contains_key(&blocks[0].recovered_block().hash()));
637 assert!(!tree_state.parent_to_child.contains_key(&blocks[1].recovered_block().hash()));
638 assert!(tree_state.parent_to_child.contains_key(&blocks[2].recovered_block().hash()));
639 assert!(tree_state.parent_to_child.contains_key(&blocks[3].recovered_block().hash()));
640 assert!(!tree_state.parent_to_child.contains_key(&blocks[4].recovered_block().hash()));
641
642 assert_eq!(
643 tree_state.parent_to_child.get(&blocks[2].recovered_block().hash()),
644 Some(&B256Set::from_iter([blocks[3].recovered_block().hash()]))
645 );
646 assert_eq!(
647 tree_state.parent_to_child.get(&blocks[3].recovered_block().hash()),
648 Some(&B256Set::from_iter([blocks[4].recovered_block().hash()]))
649 );
650 }
651}