@@ -94,6 +94,30 @@ pub struct FilterSyncManager {
9494}
9595
9696impl FilterSyncManager {
97+ /// Calculate the start height of a CFHeaders batch.
98+ fn calculate_batch_start_height ( cf_headers : & CFHeaders , stop_height : u32 ) -> u32 {
99+ stop_height. saturating_sub ( cf_headers. filter_hashes . len ( ) as u32 - 1 )
100+ }
101+
102+ /// Get the height range for a CFHeaders batch.
103+ async fn get_batch_height_range (
104+ & self ,
105+ cf_headers : & CFHeaders ,
106+ storage : & dyn StorageManager ,
107+ ) -> SyncResult < ( u32 , u32 , u32 ) > {
108+ let header_tip_height = storage. get_tip_height ( ) . await
109+ . map_err ( |e| SyncError :: SyncFailed ( format ! ( "Failed to get header tip height: {}" , e) ) ) ?
110+ . unwrap_or ( 0 ) ;
111+
112+ let stop_height = self . find_height_for_block_hash ( & cf_headers. stop_hash , storage, 0 , header_tip_height) . await ?
113+ . ok_or_else ( || SyncError :: SyncFailed ( format ! (
114+ "Cannot find height for stop hash {} in CFHeaders" , cf_headers. stop_hash
115+ ) ) ) ?;
116+
117+ let start_height = Self :: calculate_batch_start_height ( cf_headers, stop_height) ;
118+ Ok ( ( start_height, stop_height, header_tip_height) )
119+ }
120+
97121 /// Create a new filter sync manager.
98122 pub fn new ( config : & ClientConfig , received_filter_heights : std:: sync:: Arc < std:: sync:: Mutex < std:: collections:: HashSet < u32 > > > ) -> Self {
99123 Self {
@@ -139,18 +163,8 @@ impl FilterSyncManager {
139163 return Ok ( false ) ;
140164 }
141165
142- // Get header tip height for validation
143- let header_tip_height = storage. get_tip_height ( ) . await
144- . map_err ( |e| SyncError :: SyncFailed ( format ! ( "Failed to get header tip height: {}" , e) ) ) ?
145- . unwrap_or ( 0 ) ;
146-
147- // Determine the actual start height of this batch
148- let stop_height = self . find_height_for_block_hash ( & cf_headers. stop_hash , storage, 0 , header_tip_height) . await ?
149- . ok_or_else ( || SyncError :: SyncFailed ( format ! (
150- "Cannot find height for stop hash {} in CFHeaders" , cf_headers. stop_hash
151- ) ) ) ?;
152-
153- let batch_start_height = stop_height. saturating_sub ( cf_headers. filter_hashes . len ( ) as u32 - 1 ) ;
166+ // Get the height range for this batch
167+ let ( batch_start_height, stop_height, header_tip_height) = self . get_batch_height_range ( & cf_headers, storage) . await ?;
154168
155169 tracing:: debug!( "Received CFHeaders batch: start={}, stop={}, count={} (expected start={})" ,
156170 batch_start_height, stop_height, cf_headers. filter_hashes. len( ) , self . current_sync_height) ;
@@ -161,10 +175,8 @@ impl FilterSyncManager {
161175 self . current_sync_height, batch_start_height) ;
162176
163177 // Handle overlapping headers using the helper method
164- let skip_count = ( self . current_sync_height - batch_start_height) as usize ;
165178 let ( new_headers_stored, new_current_height) = self . handle_overlapping_headers (
166179 & cf_headers,
167- skip_count,
168180 self . current_sync_height ,
169181 storage
170182 ) . await ?;
@@ -516,20 +528,20 @@ impl FilterSyncManager {
516528 async fn handle_overlapping_headers (
517529 & self ,
518530 cf_headers : & CFHeaders ,
519- skip_count : usize ,
520531 expected_start_height : u32 ,
521532 storage : & mut dyn StorageManager ,
522533 ) -> SyncResult < ( usize , u32 ) > {
534+ // Get the height range for this batch
535+ let ( batch_start_height, stop_height, _header_tip_height) = self . get_batch_height_range ( cf_headers, storage) . await ?;
536+ let skip_count = expected_start_height. saturating_sub ( batch_start_height) as usize ;
537+
538+ // Complete overlap case - all headers already processed
523539 if skip_count >= cf_headers. filter_hashes . len ( ) {
524- tracing:: info!( "✅ All {} headers in this batch already processed, skipping" , cf_headers. filter_hashes. len( ) ) ;
540+ tracing:: info!( "✅ All {} headers in batch already processed, skipping" , cf_headers. filter_hashes. len( ) ) ;
525541 return Ok ( ( 0 , expected_start_height) ) ;
526542 }
527543
528- // We need to compute the filter headers for the entire batch first,
529- // then extract only the new ones we need to store.
530- // This is because each filter header depends on the previous one.
531-
532- // First, find where in our chain the cf_headers.previous_filter_header connects
544+ // Find connection point in our chain
533545 let current_filter_tip = storage. get_filter_tip_height ( ) . await
534546 . map_err ( |e| SyncError :: SyncFailed ( format ! ( "Failed to get filter tip: {}" , e) ) ) ?
535547 . unwrap_or ( 0 ) ;
@@ -544,62 +556,28 @@ impl FilterSyncManager {
544556 }
545557 }
546558
547- // If we can't find a connection point, check if this is overlapping data we can safely ignore
548559 let connection_height = match connection_height {
549560 Some ( height) => height,
550561 None => {
551- // Calculate the height range this batch would cover
552- // Get header tip height since the stop hash might be beyond our current filter tip
553- let header_tip_height = storage. get_tip_height ( ) . await
554- . map_err ( |e| SyncError :: SyncFailed ( format ! ( "Failed to get header tip height: {}" , e) ) ) ?
555- . unwrap_or ( 0 ) ;
556-
557- let stop_height = self . find_height_for_block_hash ( & cf_headers. stop_hash , storage, 0 , header_tip_height) . await ?
558- . ok_or_else ( || SyncError :: SyncFailed ( format ! (
559- "Cannot find height for stop hash {} in overlapping headers" , cf_headers. stop_hash
560- ) ) ) ?;
561- let batch_start_height = stop_height. saturating_sub ( cf_headers. filter_hashes . len ( ) as u32 - 1 ) ;
562-
563- // Check if we already have valid data for the overlapping range
562+ // No connection found - check if this is overlapping data we can safely ignore
564563 let overlap_end = expected_start_height. saturating_sub ( 1 ) ;
565564 if batch_start_height <= overlap_end && overlap_end <= current_filter_tip {
566- // This is an overlapping batch from a different peer with different previous_filter_header
567- // We already have valid data for the overlapping range, so we can safely ignore this batch
568- tracing:: warn!( "📋 Cannot find connection point for overlapping headers from different peer." ) ;
569- tracing:: warn!( "📋 Batch range: {}-{}, our tip: {}, expected start: {}" ,
570- batch_start_height, stop_height, current_filter_tip, expected_start_height) ;
571- tracing:: warn!( "📋 This appears to be overlapping data from a different peer view - ignoring safely" ) ;
572-
573- // Calculate how many new headers we would have processed (for progress tracking)
574- let would_be_new_count = if stop_height > current_filter_tip {
575- ( stop_height - current_filter_tip) as usize
576- } else {
577- 0
578- } ;
579-
580- // Return success with the count of headers we would have added if this was valid
581- let new_current_height = if would_be_new_count > 0 {
582- current_filter_tip + would_be_new_count as u32 + 1
583- } else {
584- expected_start_height
585- } ;
586-
587- return Ok ( ( would_be_new_count, new_current_height) ) ;
565+ tracing:: warn!( "📋 Ignoring overlapping headers from different peer view (range {}-{})" ,
566+ batch_start_height, stop_height) ;
567+ return Ok ( ( 0 , expected_start_height) ) ;
588568 } else {
589- // This is a real problem - we can't connect and we don't have the data
590569 return Err ( SyncError :: SyncFailed ( "Cannot find connection point for overlapping headers" . to_string ( ) ) ) ;
591570 }
592571 }
593572 } ;
594573
595- // Process all filter headers starting from the connection point
574+ // Process all filter headers from the connection point
596575 let batch_start_height = connection_height + 1 ;
597576 let all_filter_headers = self . process_filter_headers ( cf_headers, batch_start_height, storage) . await ?;
598577
599- // Now extract only the new headers we need (skip the overlapping ones)
578+ // Extract only the new headers we need
600579 let headers_to_skip = expected_start_height. saturating_sub ( batch_start_height) as usize ;
601580 if headers_to_skip >= all_filter_headers. len ( ) {
602- tracing:: info!( "✅ All headers in overlapping batch already stored" ) ;
603581 return Ok ( ( 0 , expected_start_height) ) ;
604582 }
605583
@@ -620,6 +598,8 @@ impl FilterSyncManager {
620598 }
621599
622600 /// Verify filter header chain connects to our local chain.
601+ /// This is a simplified version focused only on cryptographic chain verification,
602+ /// with overlap detection handled by the dedicated overlap resolution system.
623603 async fn verify_filter_header_chain (
624604 & self ,
625605 cf_headers : & CFHeaders ,
@@ -650,36 +630,10 @@ impl FilterSyncManager {
650630 . map_err ( |e| SyncError :: SyncFailed ( format ! ( "Failed to get previous filter header at height {}: {}" , prev_height, e) ) ) ?
651631 . ok_or_else ( || SyncError :: SyncFailed ( format ! ( "Missing previous filter header at height {}" , prev_height) ) ) ?;
652632
653- // Always check if the previous_filter_header from the message exists anywhere in our chain
654- // This handles both normal continuation and overlapping ranges from recovery/multi-peer scenarios
655- let current_filter_tip = storage. get_filter_tip_height ( ) . await
656- . map_err ( |e| SyncError :: SyncFailed ( format ! ( "Failed to get filter tip during overlap check: {}" , e) ) ) ?
657- . unwrap_or ( 0 ) ;
658-
659- // Search through our stored headers to see if the received previous_filter_header
660- // matches any valid point in our chain
661- let mut found_valid_connection = false ;
662- let mut _connection_height = None ;
663-
664- for check_height in ( 0 ..=current_filter_tip) . rev ( ) {
665- if let Ok ( Some ( stored_header) ) = storage. get_filter_header ( check_height) . await {
666- if stored_header == cf_headers. previous_filter_header {
667- found_valid_connection = true ;
668- _connection_height = Some ( check_height) ;
669-
670- if cf_headers. previous_filter_header == expected_prev_header {
671- tracing:: debug!( "Filter headers connect normally at expected height {}" , check_height) ;
672- } else {
673- tracing:: info!( "Filter headers connect via overlap at height {} (expected at {})" , check_height, prev_height) ;
674- }
675- break ;
676- }
677- }
678- }
679-
680- if !found_valid_connection {
633+ // Simple chain continuity check - the received headers should connect to our expected previous header
634+ if cf_headers. previous_filter_header != expected_prev_header {
681635 tracing:: error!(
682- "Filter header chain verification failed: received previous_filter_header {:?} doesn't match any stored header (expected: {:?} at height {}) " ,
636+ "Filter header chain verification failed: received previous_filter_header {:?} doesn't match expected header {:?} at height {}" ,
683637 cf_headers. previous_filter_header,
684638 expected_prev_header,
685639 prev_height
@@ -1434,18 +1388,8 @@ impl FilterSyncManager {
14341388 return Ok ( ( ) ) ;
14351389 }
14361390
1437- // Get the block height for the stop hash
1438- let header_tip_height = storage. get_tip_height ( ) . await
1439- . map_err ( |e| SyncError :: SyncFailed ( format ! ( "Failed to get header tip height: {}" , e) ) ) ?
1440- . unwrap_or ( 0 ) ;
1441-
1442- let stop_height = self . find_height_for_block_hash ( & cfheaders. stop_hash , storage, 0 , header_tip_height) . await ?
1443- . ok_or_else ( || SyncError :: SyncFailed ( format ! (
1444- "Cannot find height for stop hash {} - header not found" , cfheaders. stop_hash
1445- ) ) ) ?;
1446-
1447- // Calculate the start height based on the number of filter hashes
1448- let start_height = stop_height. saturating_sub ( cfheaders. filter_hashes . len ( ) as u32 - 1 ) ;
1391+ // Get the height range for this batch
1392+ let ( start_height, stop_height, _header_tip_height) = self . get_batch_height_range ( & cfheaders, storage) . await ?;
14491393
14501394 tracing:: info!( "Received {} filter headers from height {} to {}" ,
14511395 cfheaders. filter_hashes. len( ) , start_height, stop_height) ;
@@ -1478,10 +1422,9 @@ impl FilterSyncManager {
14781422 current_filter_tip, start_height, stop_height) ;
14791423
14801424 // Use the handle_overlapping_headers method which properly handles the chain continuity
1481- let skip_count = ( current_filter_tip + 1 - start_height) as usize ;
14821425 let expected_start = current_filter_tip + 1 ;
14831426
1484- match self . handle_overlapping_headers ( & cfheaders, skip_count , expected_start, storage) . await {
1427+ match self . handle_overlapping_headers ( & cfheaders, expected_start, storage) . await {
14851428 Ok ( ( stored_count, _) ) => {
14861429 if stored_count > 0 {
14871430 tracing:: info!( "✅ Successfully handled overlapping filter headers" ) ;
0 commit comments