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