@@ -221,8 +221,8 @@ impl DashSpvClient {
221221 // Create shared data structures
222222 let watch_items = Arc :: new ( RwLock :: new ( HashSet :: new ( ) ) ) ;
223223
224- // Create sync manager
225- let sync_manager = SyncManager :: new ( & config) ;
224+ // Create sync manager with shared filter heights
225+ let sync_manager = SyncManager :: new ( & config, stats . read ( ) . await . received_filter_heights . clone ( ) ) ;
226226
227227 // Create validation manager
228228 let validation = ValidationManager :: new ( config. validation_mode ) ;
@@ -449,6 +449,10 @@ impl DashSpvClient {
449449 let mut last_consistency_check = Instant :: now ( ) ;
450450 let consistency_check_interval = std:: time:: Duration :: from_secs ( 300 ) ; // Every 5 minutes
451451
452+ // Timer for filter gap checking
453+ let mut last_filter_gap_check = Instant :: now ( ) ;
454+ let filter_gap_check_interval = std:: time:: Duration :: from_secs ( 10 ) ;
455+
452456 loop {
453457 // Check if we should stop
454458 let running = self . running . read ( ) . await ;
@@ -506,13 +510,40 @@ impl DashSpvClient {
506510 if last_status_update. elapsed ( ) >= status_update_interval {
507511 self . update_status_display ( ) . await ;
508512
509- // Report filter sync progress if active
510- let ( filters_requested, filters_received, progress , timeout) =
511- crate :: sync:: filters:: FilterSyncManager :: get_filter_sync_status ( & self . stats ) . await ;
513+ // Report enhanced filter sync progress if active
514+ let ( filters_requested, filters_received, basic_progress , timeout, total_missing , actual_coverage , missing_ranges ) =
515+ crate :: sync:: filters:: FilterSyncManager :: get_filter_sync_status_with_gaps ( & self . stats , self . sync_manager . filter_sync ( ) ) . await ;
512516
513517 if filters_requested > 0 {
514- tracing:: info!( "📊 Filter sync progress: {:.1}% ({}/{} filters received)" ,
515- progress, filters_received, filters_requested) ;
518+ // Check if sync is truly complete: both basic progress AND gap analysis must indicate completion
519+ // This fixes a bug where "Complete!" was shown when only gap analysis returned 0 missing filters
520+ // but basic progress (filters_received < filters_requested) indicated incomplete sync.
521+ let is_complete = filters_received >= filters_requested && total_missing == 0 ;
522+
523+ // Debug logging for completion detection
524+ if filters_received >= filters_requested && total_missing > 0 {
525+ tracing:: debug!( "🔍 Completion discrepancy detected: basic progress complete ({}/{}) but {} missing filters detected" ,
526+ filters_received, filters_requested, total_missing) ;
527+ }
528+
529+ if !is_complete {
530+ tracing:: info!( "📊 Filter sync: Basic {:.1}% ({}/{}), Actual coverage {:.1}%, Missing: {} filters in {} ranges" ,
531+ basic_progress, filters_received, filters_requested, actual_coverage, total_missing, missing_ranges. len( ) ) ;
532+
533+ // Show first few missing ranges for debugging
534+ if missing_ranges. len ( ) > 0 {
535+ let show_count = missing_ranges. len ( ) . min ( 3 ) ;
536+ for ( i, ( start, end) ) in missing_ranges. iter ( ) . enumerate ( ) . take ( show_count) {
537+ tracing:: warn!( " Gap {}: range {}-{} ({} filters)" , i + 1 , start, end, end - start + 1 ) ;
538+ }
539+ if missing_ranges. len ( ) > show_count {
540+ tracing:: warn!( " ... and {} more gaps" , missing_ranges. len( ) - show_count) ;
541+ }
542+ }
543+ } else {
544+ tracing:: info!( "📊 Filter sync progress: {:.1}% ({}/{} filters received) - Complete!" ,
545+ basic_progress, filters_received, filters_requested) ;
546+ }
516547
517548 if timeout {
518549 tracing:: warn!( "⚠️ Filter sync timeout: no filters received in 30+ seconds" ) ;
@@ -549,6 +580,17 @@ impl DashSpvClient {
549580 last_consistency_check = Instant :: now ( ) ;
550581 }
551582
583+ // Check for missing filters and retry periodically
584+ if last_filter_gap_check. elapsed ( ) >= filter_gap_check_interval {
585+ if self . config . enable_filters {
586+ if let Err ( e) = self . sync_manager . filter_sync_mut ( )
587+ . check_and_retry_missing_filters ( & mut * self . network , & * self . storage ) . await {
588+ tracing:: warn!( "Failed to check and retry missing filters: {}" , e) ;
589+ }
590+ }
591+ last_filter_gap_check = Instant :: now ( ) ;
592+ }
593+
552594 // Handle network messages
553595 match self . network . receive_message ( ) . await {
554596 Ok ( Some ( message) ) => {
0 commit comments