1use crate::engine::EngineApiKind;
4use alloy_eips::{merge::EPOCH_SLOTS, BlockNumHash};
5use alloy_primitives::{
6 map::{HashMap, HashSet},
7 BlockNumber, B256,
8};
9use reth_chain_state::{EthPrimitives, ExecutedBlockWithTrieUpdates};
10use reth_primitives_traits::{AlloyBlockHeader, NodePrimitives, SealedBlock};
11use reth_trie::updates::TrieUpdates;
12use std::{
13 collections::{btree_map, hash_map, BTreeMap, VecDeque},
14 ops::Bound,
15 sync::Arc,
16};
17use tracing::debug;
18
19const DEFAULT_PERSISTED_TRIE_UPDATES_RETENTION: u64 = EPOCH_SLOTS * 2;
21
22const OPSTACK_PERSISTED_TRIE_UPDATES_RETENTION: u64 = EPOCH_SLOTS;
26
27#[derive(Debug, Default)]
34pub struct TreeState<N: NodePrimitives = EthPrimitives> {
35 pub(crate) blocks_by_hash: HashMap<B256, ExecutedBlockWithTrieUpdates<N>>,
39 pub(crate) blocks_by_number: BTreeMap<BlockNumber, Vec<ExecutedBlockWithTrieUpdates<N>>>,
45 pub(crate) parent_to_child: HashMap<B256, HashSet<B256>>,
47 pub(crate) persisted_trie_updates: HashMap<B256, (BlockNumber, Arc<TrieUpdates>)>,
51 pub(crate) current_canonical_head: BlockNumHash,
53 pub(crate) engine_kind: EngineApiKind,
55}
56
57impl<N: NodePrimitives> TreeState<N> {
58 pub(crate) fn new(current_canonical_head: BlockNumHash, engine_kind: EngineApiKind) -> Self {
60 Self {
61 blocks_by_hash: HashMap::default(),
62 blocks_by_number: BTreeMap::new(),
63 current_canonical_head,
64 parent_to_child: HashMap::default(),
65 persisted_trie_updates: HashMap::default(),
66 engine_kind,
67 }
68 }
69
70 pub(crate) fn reset(&mut self, current_canonical_head: BlockNumHash) {
72 *self = Self::new(current_canonical_head, self.engine_kind);
73 }
74
75 pub(crate) fn block_count(&self) -> usize {
77 self.blocks_by_hash.len()
78 }
79
80 pub(crate) fn executed_block_by_hash(
82 &self,
83 hash: B256,
84 ) -> Option<&ExecutedBlockWithTrieUpdates<N>> {
85 self.blocks_by_hash.get(&hash)
86 }
87
88 pub(crate) fn block_by_hash(&self, hash: B256) -> Option<Arc<SealedBlock<N::Block>>> {
90 self.blocks_by_hash.get(&hash).map(|b| Arc::new(b.recovered_block().sealed_block().clone()))
91 }
92
93 pub(crate) fn blocks_by_hash(
98 &self,
99 hash: B256,
100 ) -> Option<(B256, Vec<ExecutedBlockWithTrieUpdates<N>>)> {
101 let block = self.blocks_by_hash.get(&hash).cloned()?;
102 let mut parent_hash = block.recovered_block().parent_hash();
103 let mut blocks = vec![block];
104 while let Some(executed) = self.blocks_by_hash.get(&parent_hash) {
105 parent_hash = executed.recovered_block().parent_hash();
106 blocks.push(executed.clone());
107 }
108
109 Some((parent_hash, blocks))
110 }
111
112 pub(crate) fn insert_executed(&mut self, executed: ExecutedBlockWithTrieUpdates<N>) {
114 let hash = executed.recovered_block().hash();
115 let parent_hash = executed.recovered_block().parent_hash();
116 let block_number = executed.recovered_block().number();
117
118 if self.blocks_by_hash.contains_key(&hash) {
119 return;
120 }
121
122 self.blocks_by_hash.insert(hash, executed.clone());
123
124 self.blocks_by_number.entry(block_number).or_default().push(executed);
125
126 self.parent_to_child.entry(parent_hash).or_default().insert(hash);
127
128 for children in self.parent_to_child.values_mut() {
129 children.retain(|child| self.blocks_by_hash.contains_key(child));
130 }
131 }
132
133 fn remove_by_hash(
139 &mut self,
140 hash: B256,
141 ) -> Option<(ExecutedBlockWithTrieUpdates<N>, HashSet<B256>)> {
142 let executed = self.blocks_by_hash.remove(&hash)?;
143
144 let parent_entry = self.parent_to_child.entry(executed.recovered_block().parent_hash());
146 if let hash_map::Entry::Occupied(mut entry) = parent_entry {
147 entry.get_mut().remove(&hash);
148
149 if entry.get().is_empty() {
150 entry.remove();
151 }
152 }
153
154 let children = self.parent_to_child.remove(&hash).unwrap_or_default();
156
157 let block_number_entry = self.blocks_by_number.entry(executed.recovered_block().number());
159 if let btree_map::Entry::Occupied(mut entry) = block_number_entry {
160 if let Some(index) = entry.get().iter().position(|b| b.recovered_block().hash() == hash)
162 {
163 entry.get_mut().swap_remove(index);
164
165 if entry.get().is_empty() {
167 entry.remove();
168 }
169 }
170 }
171
172 Some((executed, children))
173 }
174
175 pub(crate) fn is_canonical(&self, hash: B256) -> bool {
177 let mut current_block = self.current_canonical_head.hash;
178 if current_block == hash {
179 return true
180 }
181
182 while let Some(executed) = self.blocks_by_hash.get(¤t_block) {
183 current_block = executed.recovered_block().parent_hash();
184 if current_block == hash {
185 return true
186 }
187 }
188
189 false
190 }
191
192 pub(crate) fn remove_canonical_until(
195 &mut self,
196 upper_bound: BlockNumber,
197 last_persisted_hash: B256,
198 ) {
199 debug!(target: "engine::tree", ?upper_bound, ?last_persisted_hash, "Removing canonical blocks from the tree");
200
201 if !self.is_canonical(last_persisted_hash) {
204 return
205 }
206
207 let mut current_block = self.current_canonical_head.hash;
210 while let Some(executed) = self.blocks_by_hash.get(¤t_block) {
211 current_block = executed.recovered_block().parent_hash();
212 if executed.recovered_block().number() <= upper_bound {
213 debug!(target: "engine::tree", num_hash=?executed.recovered_block().num_hash(), "Attempting to remove block walking back from the head");
214 if let Some((removed, _)) = self.remove_by_hash(executed.recovered_block().hash()) {
215 debug!(target: "engine::tree", num_hash=?removed.recovered_block().num_hash(), "Removed block walking back from the head");
216 self.persisted_trie_updates.insert(
218 removed.recovered_block().hash(),
219 (removed.recovered_block().number(), removed.trie),
220 );
221 }
222 }
223 }
224 debug!(target: "engine::tree", ?upper_bound, ?last_persisted_hash, "Removed canonical blocks from the tree");
225 }
226
227 pub(crate) fn prune_persisted_trie_updates(&mut self) {
230 let retention_blocks = if self.engine_kind.is_opstack() {
231 OPSTACK_PERSISTED_TRIE_UPDATES_RETENTION
232 } else {
233 DEFAULT_PERSISTED_TRIE_UPDATES_RETENTION
234 };
235
236 let earliest_block_to_retain =
237 self.current_canonical_head.number.saturating_sub(retention_blocks);
238
239 self.persisted_trie_updates
240 .retain(|_, (block_number, _)| *block_number > earliest_block_to_retain);
241 }
242
243 pub(crate) fn prune_finalized_sidechains(&mut self, finalized_num_hash: BlockNumHash) {
246 let BlockNumHash { number: finalized_num, hash: finalized_hash } = finalized_num_hash;
247
248 let blocks_to_remove = self
257 .blocks_by_number
258 .range((Bound::Unbounded, Bound::Excluded(finalized_num)))
259 .flat_map(|(_, blocks)| blocks.iter().map(|b| b.recovered_block().hash()))
260 .collect::<Vec<_>>();
261 for hash in blocks_to_remove {
262 if let Some((removed, _)) = self.remove_by_hash(hash) {
263 debug!(target: "engine::tree", num_hash=?removed.recovered_block().num_hash(), "Removed finalized sidechain block");
264 }
265 }
266
267 self.prune_persisted_trie_updates();
268
269 let mut blocks_to_remove = self.blocks_by_number.remove(&finalized_num).unwrap_or_default();
276
277 if let Some(position) =
279 blocks_to_remove.iter().position(|b| b.recovered_block().hash() == finalized_hash)
280 {
281 let finalized_block = blocks_to_remove.swap_remove(position);
282 self.blocks_by_number.insert(finalized_num, vec![finalized_block]);
283 }
284
285 let mut blocks_to_remove = blocks_to_remove
286 .into_iter()
287 .map(|e| e.recovered_block().hash())
288 .collect::<VecDeque<_>>();
289 while let Some(block) = blocks_to_remove.pop_front() {
290 if let Some((removed, children)) = self.remove_by_hash(block) {
291 debug!(target: "engine::tree", num_hash=?removed.recovered_block().num_hash(), "Removed finalized sidechain child block");
292 blocks_to_remove.extend(children);
293 }
294 }
295 }
296
297 pub(crate) fn remove_until(
308 &mut self,
309 upper_bound: BlockNumHash,
310 last_persisted_hash: B256,
311 finalized_num_hash: Option<BlockNumHash>,
312 ) {
313 debug!(target: "engine::tree", ?upper_bound, ?finalized_num_hash, "Removing blocks from the tree");
314
315 let finalized_num_hash = finalized_num_hash.map(|mut finalized| {
318 if upper_bound.number < finalized.number {
319 finalized = upper_bound;
320 debug!(target: "engine::tree", ?finalized, "Adjusted upper bound");
321 }
322 finalized
323 });
324
325 self.remove_canonical_until(upper_bound.number, last_persisted_hash);
333
334 if let Some(finalized_num_hash) = finalized_num_hash {
337 self.prune_finalized_sidechains(finalized_num_hash);
338 }
339 }
340
341 pub(crate) fn is_descendant(&self, first: BlockNumHash, second: &N::BlockHeader) -> bool {
345 if second.parent_hash() == first.hash {
348 return true
349 }
350
351 if second.number() <= first.number {
354 return false
355 }
356
357 let Some(mut current_block) = self.block_by_hash(second.parent_hash()) else {
359 return false
361 };
362
363 while current_block.number() > first.number + 1 {
364 let Some(block) = self.block_by_hash(current_block.header().parent_hash()) else {
365 return false
367 };
368
369 current_block = block;
370 }
371
372 current_block.parent_hash() == first.hash
374 }
375
376 pub(crate) const fn set_canonical_head(&mut self, new_head: BlockNumHash) {
378 self.current_canonical_head = new_head;
379 }
380
381 pub(crate) const fn canonical_head(&self) -> &BlockNumHash {
383 &self.current_canonical_head
384 }
385
386 pub(crate) const fn canonical_block_hash(&self) -> B256 {
388 self.canonical_head().hash
389 }
390
391 pub(crate) const fn canonical_block_number(&self) -> BlockNumber {
393 self.canonical_head().number
394 }
395}
396
397#[cfg(test)]
398mod tests {
399 use super::*;
400 use reth_chain_state::test_utils::TestBlockBuilder;
401
402 #[test]
403 fn test_tree_state_normal_descendant() {
404 let mut tree_state = TreeState::new(BlockNumHash::default(), EngineApiKind::Ethereum);
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().header()
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().header()
418 ));
419 assert!(tree_state.is_descendant(
420 blocks[1].recovered_block().num_hash(),
421 blocks[2].recovered_block().header()
422 ));
423 }
424
425 #[tokio::test]
426 async fn test_tree_state_insert_executed() {
427 let mut tree_state = TreeState::new(BlockNumHash::default(), EngineApiKind::Ethereum);
428 let blocks: Vec<_> = TestBlockBuilder::eth().get_executed_blocks(1..4).collect();
429
430 tree_state.insert_executed(blocks[0].clone());
431 tree_state.insert_executed(blocks[1].clone());
432
433 assert_eq!(
434 tree_state.parent_to_child.get(&blocks[0].recovered_block().hash()),
435 Some(&HashSet::from_iter([blocks[1].recovered_block().hash()]))
436 );
437
438 assert!(!tree_state.parent_to_child.contains_key(&blocks[1].recovered_block().hash()));
439
440 tree_state.insert_executed(blocks[2].clone());
441
442 assert_eq!(
443 tree_state.parent_to_child.get(&blocks[1].recovered_block().hash()),
444 Some(&HashSet::from_iter([blocks[2].recovered_block().hash()]))
445 );
446 assert!(tree_state.parent_to_child.contains_key(&blocks[1].recovered_block().hash()));
447
448 assert!(!tree_state.parent_to_child.contains_key(&blocks[2].recovered_block().hash()));
449 }
450
451 #[tokio::test]
452 async fn test_tree_state_insert_executed_with_reorg() {
453 let mut tree_state = TreeState::new(BlockNumHash::default(), EngineApiKind::Ethereum);
454 let mut test_block_builder = TestBlockBuilder::eth();
455 let blocks: Vec<_> = test_block_builder.get_executed_blocks(1..6).collect();
456
457 for block in &blocks {
458 tree_state.insert_executed(block.clone());
459 }
460 assert_eq!(tree_state.blocks_by_hash.len(), 5);
461
462 let fork_block_3 = test_block_builder
463 .get_executed_block_with_number(3, blocks[1].recovered_block().hash());
464 let fork_block_4 = test_block_builder
465 .get_executed_block_with_number(4, fork_block_3.recovered_block().hash());
466 let fork_block_5 = test_block_builder
467 .get_executed_block_with_number(5, fork_block_4.recovered_block().hash());
468
469 tree_state.insert_executed(fork_block_3.clone());
470 tree_state.insert_executed(fork_block_4.clone());
471 tree_state.insert_executed(fork_block_5.clone());
472
473 assert_eq!(tree_state.blocks_by_hash.len(), 8);
474 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());
479 assert_eq!(tree_state.blocks_by_hash.len(), 8);
480
481 assert!(tree_state.parent_to_child[&fork_block_3.recovered_block().hash()]
482 .contains(&fork_block_4.recovered_block().hash()));
483 assert!(tree_state.parent_to_child[&fork_block_4.recovered_block().hash()]
484 .contains(&fork_block_5.recovered_block().hash()));
485
486 assert_eq!(tree_state.blocks_by_number[&4].len(), 2);
487 assert_eq!(tree_state.blocks_by_number[&5].len(), 2);
488 }
489
490 #[tokio::test]
491 async fn test_tree_state_remove_before() {
492 let start_num_hash = BlockNumHash::default();
493 let mut tree_state = TreeState::new(start_num_hash, EngineApiKind::Ethereum);
494 let blocks: Vec<_> = TestBlockBuilder::eth().get_executed_blocks(1..6).collect();
495
496 for block in &blocks {
497 tree_state.insert_executed(block.clone());
498 }
499
500 let last = blocks.last().unwrap();
501
502 tree_state.set_canonical_head(last.recovered_block().num_hash());
504
505 tree_state.remove_until(
507 BlockNumHash::new(2, blocks[1].recovered_block().hash()),
508 start_num_hash.hash,
509 Some(blocks[1].recovered_block().num_hash()),
510 );
511
512 assert!(!tree_state.blocks_by_hash.contains_key(&blocks[0].recovered_block().hash()));
513 assert!(!tree_state.blocks_by_hash.contains_key(&blocks[1].recovered_block().hash()));
514 assert!(!tree_state.blocks_by_number.contains_key(&1));
515 assert!(!tree_state.blocks_by_number.contains_key(&2));
516
517 assert!(tree_state.blocks_by_hash.contains_key(&blocks[2].recovered_block().hash()));
518 assert!(tree_state.blocks_by_hash.contains_key(&blocks[3].recovered_block().hash()));
519 assert!(tree_state.blocks_by_hash.contains_key(&blocks[4].recovered_block().hash()));
520 assert!(tree_state.blocks_by_number.contains_key(&3));
521 assert!(tree_state.blocks_by_number.contains_key(&4));
522 assert!(tree_state.blocks_by_number.contains_key(&5));
523
524 assert!(!tree_state.parent_to_child.contains_key(&blocks[0].recovered_block().hash()));
525 assert!(!tree_state.parent_to_child.contains_key(&blocks[1].recovered_block().hash()));
526 assert!(tree_state.parent_to_child.contains_key(&blocks[2].recovered_block().hash()));
527 assert!(tree_state.parent_to_child.contains_key(&blocks[3].recovered_block().hash()));
528 assert!(!tree_state.parent_to_child.contains_key(&blocks[4].recovered_block().hash()));
529
530 assert_eq!(
531 tree_state.parent_to_child.get(&blocks[2].recovered_block().hash()),
532 Some(&HashSet::from_iter([blocks[3].recovered_block().hash()]))
533 );
534 assert_eq!(
535 tree_state.parent_to_child.get(&blocks[3].recovered_block().hash()),
536 Some(&HashSet::from_iter([blocks[4].recovered_block().hash()]))
537 );
538 }
539
540 #[tokio::test]
541 async fn test_tree_state_remove_before_finalized() {
542 let start_num_hash = BlockNumHash::default();
543 let mut tree_state = TreeState::new(start_num_hash, EngineApiKind::Ethereum);
544 let blocks: Vec<_> = TestBlockBuilder::eth().get_executed_blocks(1..6).collect();
545
546 for block in &blocks {
547 tree_state.insert_executed(block.clone());
548 }
549
550 let last = blocks.last().unwrap();
551
552 tree_state.set_canonical_head(last.recovered_block().num_hash());
554
555 tree_state.remove_until(
557 BlockNumHash::new(2, blocks[1].recovered_block().hash()),
558 start_num_hash.hash,
559 None,
560 );
561
562 assert!(!tree_state.blocks_by_hash.contains_key(&blocks[0].recovered_block().hash()));
563 assert!(!tree_state.blocks_by_hash.contains_key(&blocks[1].recovered_block().hash()));
564 assert!(!tree_state.blocks_by_number.contains_key(&1));
565 assert!(!tree_state.blocks_by_number.contains_key(&2));
566
567 assert!(tree_state.blocks_by_hash.contains_key(&blocks[2].recovered_block().hash()));
568 assert!(tree_state.blocks_by_hash.contains_key(&blocks[3].recovered_block().hash()));
569 assert!(tree_state.blocks_by_hash.contains_key(&blocks[4].recovered_block().hash()));
570 assert!(tree_state.blocks_by_number.contains_key(&3));
571 assert!(tree_state.blocks_by_number.contains_key(&4));
572 assert!(tree_state.blocks_by_number.contains_key(&5));
573
574 assert!(!tree_state.parent_to_child.contains_key(&blocks[0].recovered_block().hash()));
575 assert!(!tree_state.parent_to_child.contains_key(&blocks[1].recovered_block().hash()));
576 assert!(tree_state.parent_to_child.contains_key(&blocks[2].recovered_block().hash()));
577 assert!(tree_state.parent_to_child.contains_key(&blocks[3].recovered_block().hash()));
578 assert!(!tree_state.parent_to_child.contains_key(&blocks[4].recovered_block().hash()));
579
580 assert_eq!(
581 tree_state.parent_to_child.get(&blocks[2].recovered_block().hash()),
582 Some(&HashSet::from_iter([blocks[3].recovered_block().hash()]))
583 );
584 assert_eq!(
585 tree_state.parent_to_child.get(&blocks[3].recovered_block().hash()),
586 Some(&HashSet::from_iter([blocks[4].recovered_block().hash()]))
587 );
588 }
589
590 #[tokio::test]
591 async fn test_tree_state_remove_before_lower_finalized() {
592 let start_num_hash = BlockNumHash::default();
593 let mut tree_state = TreeState::new(start_num_hash, EngineApiKind::Ethereum);
594 let blocks: Vec<_> = TestBlockBuilder::eth().get_executed_blocks(1..6).collect();
595
596 for block in &blocks {
597 tree_state.insert_executed(block.clone());
598 }
599
600 let last = blocks.last().unwrap();
601
602 tree_state.set_canonical_head(last.recovered_block().num_hash());
604
605 tree_state.remove_until(
607 BlockNumHash::new(2, blocks[1].recovered_block().hash()),
608 start_num_hash.hash,
609 Some(blocks[0].recovered_block().num_hash()),
610 );
611
612 assert!(!tree_state.blocks_by_hash.contains_key(&blocks[0].recovered_block().hash()));
613 assert!(!tree_state.blocks_by_hash.contains_key(&blocks[1].recovered_block().hash()));
614 assert!(!tree_state.blocks_by_number.contains_key(&1));
615 assert!(!tree_state.blocks_by_number.contains_key(&2));
616
617 assert!(tree_state.blocks_by_hash.contains_key(&blocks[2].recovered_block().hash()));
618 assert!(tree_state.blocks_by_hash.contains_key(&blocks[3].recovered_block().hash()));
619 assert!(tree_state.blocks_by_hash.contains_key(&blocks[4].recovered_block().hash()));
620 assert!(tree_state.blocks_by_number.contains_key(&3));
621 assert!(tree_state.blocks_by_number.contains_key(&4));
622 assert!(tree_state.blocks_by_number.contains_key(&5));
623
624 assert!(!tree_state.parent_to_child.contains_key(&blocks[0].recovered_block().hash()));
625 assert!(!tree_state.parent_to_child.contains_key(&blocks[1].recovered_block().hash()));
626 assert!(tree_state.parent_to_child.contains_key(&blocks[2].recovered_block().hash()));
627 assert!(tree_state.parent_to_child.contains_key(&blocks[3].recovered_block().hash()));
628 assert!(!tree_state.parent_to_child.contains_key(&blocks[4].recovered_block().hash()));
629
630 assert_eq!(
631 tree_state.parent_to_child.get(&blocks[2].recovered_block().hash()),
632 Some(&HashSet::from_iter([blocks[3].recovered_block().hash()]))
633 );
634 assert_eq!(
635 tree_state.parent_to_child.get(&blocks[3].recovered_block().hash()),
636 Some(&HashSet::from_iter([blocks[4].recovered_block().hash()]))
637 );
638 }
639}