@@ -18,7 +18,11 @@ use reth_primitives_traits::{
18
18
} ;
19
19
use reth_storage_api:: StateProviderBox ;
20
20
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
+ } ;
22
26
use tokio:: sync:: { broadcast, watch} ;
23
27
24
28
/// Size of the broadcast channel used to notify canonical state events.
@@ -365,6 +369,72 @@ impl<N: NodePrimitives> CanonicalInMemoryState<N> {
365
369
self . inner . in_memory_state . update_metrics ( ) ;
366
370
}
367
371
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
+
368
438
/// Returns in memory state corresponding the given hash.
369
439
pub fn state_by_hash ( & self , hash : B256 ) -> Option < Arc < BlockState < N > > > {
370
440
self . inner . in_memory_state . state_by_hash ( hash)
0 commit comments