Skip to content

Commit 3b9c683

Browse files
committed
remove blocks from canonical in-memory state
1 parent aa1323c commit 3b9c683

File tree

2 files changed

+75
-3
lines changed

2 files changed

+75
-3
lines changed

crates/chain-state/src/in_memory.rs

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ use reth_primitives_traits::{
1818
};
1919
use reth_storage_api::StateProviderBox;
2020
use reth_trie::{updates::TrieUpdates, HashedPostState};
21-
use std::{collections::BTreeMap, sync::Arc, time::Instant};
21+
use std::{
22+
collections::{BTreeMap, VecDeque},
23+
sync::Arc,
24+
time::Instant,
25+
};
2226
use tokio::sync::{broadcast, watch};
2327

2428
/// Size of the broadcast channel used to notify canonical state events.
@@ -365,6 +369,72 @@ impl<N: NodePrimitives> CanonicalInMemoryState<N> {
365369
self.inner.in_memory_state.update_metrics();
366370
}
367371

372+
/// Removes an invalid block and all its descendants from the in-memory state.
373+
///
374+
/// This is called when a block is marked as invalid to immediately clean up
375+
/// the invalid fork from in-memory state, preventing it from being referenced
376+
/// during state provider construction.
377+
pub fn remove_invalid_block_and_descendants(&self, invalid_block_hash: B256) {
378+
// First, collect all blocks that need to be removed using BFS
379+
let mut blocks_to_remove = std::collections::HashSet::new();
380+
let mut queue = VecDeque::from([invalid_block_hash]);
381+
382+
// Find all descendants by traversing the parent-child relationships
383+
let blocks = self.inner.in_memory_state.blocks.read();
384+
while let Some(hash) = queue.pop_front() {
385+
// Skip if already processed
386+
let is_new = blocks_to_remove.insert(hash);
387+
if !is_new {
388+
continue;
389+
}
390+
391+
// Find all children of this block
392+
for (child_hash, child_state) in blocks.iter() {
393+
let Some(parent) = &child_state.parent else {
394+
continue;
395+
};
396+
if parent.hash() == hash {
397+
queue.push_back(*child_hash);
398+
}
399+
}
400+
}
401+
drop(blocks);
402+
403+
// Nothing to do if no blocks found
404+
if blocks_to_remove.is_empty() {
405+
return;
406+
}
407+
408+
// Remove all collected blocks, acquiring locks in order: numbers then blocks
409+
let mut numbers = self.inner.in_memory_state.numbers.write();
410+
let mut blocks = self.inner.in_memory_state.blocks.write();
411+
412+
for hash in &blocks_to_remove {
413+
let Some(block_state) = blocks.remove(hash) else {
414+
continue;
415+
};
416+
417+
let number = block_state.number();
418+
// Remove from numbers map if this block number maps to this hash
419+
if numbers.get(&number) == Some(hash) {
420+
numbers.remove(&number);
421+
}
422+
}
423+
424+
// Update pending state if its parent was removed
425+
self.inner.in_memory_state.pending.send_modify(|p| {
426+
let Some(p) = p.as_mut() else {
427+
return;
428+
};
429+
let parent_hash = p.block_ref().recovered_block().parent_hash();
430+
if blocks_to_remove.contains(&parent_hash) {
431+
p.parent = None;
432+
}
433+
});
434+
435+
self.inner.in_memory_state.update_metrics();
436+
}
437+
368438
/// Returns in memory state corresponding the given hash.
369439
pub fn state_by_hash(&self, hash: B256) -> Option<Arc<BlockState<N>>> {
370440
self.inner.in_memory_state.state_by_hash(hash)

crates/engine/tree/src/tree/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2737,9 +2737,11 @@ where
27372737
// keep track of the invalid header
27382738
self.state.invalid_headers.insert(block.block_with_parent());
27392739

2740-
// Remove the invalid block and all its descendants from the tree immediately
2741-
// to prevent them from being referenced during state provider construction
2740+
// Remove the invalid block and all its descendants from both the tree state and
2741+
// canonical in-memory state immediately to prevent them from being referenced
2742+
// during state provider construction
27422743
self.state.tree_state.remove_invalid_chain(block.hash());
2744+
self.canonical_in_memory_state.remove_invalid_block_and_descendants(block.hash());
27432745

27442746
self.emit_event(EngineApiEvent::BeaconConsensus(ConsensusEngineEvent::InvalidBlock(
27452747
Box::new(block),

0 commit comments

Comments
 (0)