From 1b38e9c68bad1cf31ec0796cb432c9a5b0f07e90 Mon Sep 17 00:00:00 2001 From: pasta Date: Mon, 11 Aug 2025 18:10:09 -0500 Subject: [PATCH 1/2] fix(dash-spv): unify status display height calculation for genesis and checkpoint sync MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, the status display used different logic for calculating header heights: - Checkpoint sync: correctly used sync_base_height + storage_count - Genesis sync: incorrectly checked chain_state.headers which was kept minimal This caused status to show all zeros when syncing from genesis, while headers were actually being synced to disk successfully. The fix unifies both paths to use the same formula: - Genesis sync: 0 + storage_count = actual height - Checkpoint sync: checkpoint_height + storage_count = actual height This ensures accurate progress reporting regardless of sync method. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- dash-spv/src/client/status_display.rs | 87 +++++++-------------------- 1 file changed, 21 insertions(+), 66 deletions(-) diff --git a/dash-spv/src/client/status_display.rs b/dash-spv/src/client/status_display.rs index 8b5f022a6..94eca0dff 100644 --- a/dash-spv/src/client/status_display.rs +++ b/dash-spv/src/client/status_display.rs @@ -43,51 +43,21 @@ impl<'a> StatusDisplay<'a> { state: &ChainState, with_logging: bool, ) -> u32 { - if state.synced_from_checkpoint && state.sync_base_height > 0 { - // Get the actual number of headers in storage - if let Ok(Some(storage_tip)) = self.storage.get_tip_height().await { - // The blockchain height is sync_base_height + storage_tip - let blockchain_height = state.sync_base_height + storage_tip; - if with_logging { - tracing::debug!( - "Status display (checkpoint sync): storage_tip={}, sync_base={}, blockchain_height={}", - storage_tip, state.sync_base_height, blockchain_height - ); - } - blockchain_height - } else { - // No headers in storage yet, use the checkpoint height - state.sync_base_height + // Unified formula for both checkpoint and genesis sync: + // For genesis sync: sync_base_height = 0, so height = 0 + storage_count + // For checkpoint sync: height = checkpoint_height + storage_count + if let Ok(Some(storage_tip)) = self.storage.get_tip_height().await { + let blockchain_height = state.sync_base_height + storage_tip; + if with_logging { + tracing::debug!( + "Status display: storage_tip={}, sync_base={}, blockchain_height={}", + storage_tip, state.sync_base_height, blockchain_height + ); } + blockchain_height } else { - // Normal sync from genesis - // Check if headers are in storage but not loaded into memory yet - if state.headers.is_empty() { - // Headers might be in storage but not loaded into ChainState yet - if let Ok(Some(storage_tip)) = self.storage.get_tip_height().await { - if with_logging { - tracing::debug!( - "Status display (normal sync): ChainState empty but storage has {} headers", - storage_tip - ); - } - storage_tip - } else { - // No headers in storage or ChainState - 0 - } - } else { - // Headers are loaded in ChainState, use tip_height() - let tip = state.tip_height(); - if with_logging { - tracing::debug!( - "Status display (normal sync): chain state has {} headers, tip_height={}", - state.headers.len(), - tip - ); - } - tip - } + // No headers in storage yet + state.sync_base_height } } @@ -242,30 +212,15 @@ impl<'a> StatusDisplay<'a> { /// This helper method encapsulates the logic for determining the current filter header height, /// taking into account whether we're syncing from a checkpoint or from genesis. async fn calculate_filter_header_height(&self, state: &ChainState) -> u32 { - if state.synced_from_checkpoint && state.sync_base_height > 0 { - // Get the actual number of filter headers in storage - if let Ok(Some(storage_height)) = self.storage.get_filter_tip_height().await { - // The blockchain height is sync_base_height + storage_height - state.sync_base_height + storage_height - } else { - // No filter headers in storage yet, use the checkpoint height - state.sync_base_height - } + // Unified formula for both checkpoint and genesis sync: + // For genesis sync: sync_base_height = 0, so height = 0 + storage_count + // For checkpoint sync: height = checkpoint_height + storage_count + if let Ok(Some(storage_height)) = self.storage.get_filter_tip_height().await { + // The blockchain height is sync_base_height + storage_height + state.sync_base_height + storage_height } else { - // Normal sync from genesis - // Check if filter headers are in storage but not loaded into memory yet - if state.filter_headers.is_empty() { - // Filter headers might be in storage but not loaded into ChainState yet - if let Ok(Some(storage_height)) = self.storage.get_filter_tip_height().await { - storage_height - } else { - // No filter headers in storage or ChainState - 0 - } - } else { - // Filter headers are loaded in ChainState - state.filter_headers.len().saturating_sub(1) as u32 - } + // No filter headers in storage yet + state.sync_base_height } } } From 153fc699124e918c2e8ab59589bd01cf874a430e Mon Sep 17 00:00:00 2001 From: pasta Date: Mon, 11 Aug 2025 18:13:57 -0500 Subject: [PATCH 2/2] chore: run `cargo fmt` --- dash-spv/src/client/status_display.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dash-spv/src/client/status_display.rs b/dash-spv/src/client/status_display.rs index 94eca0dff..6483d1a96 100644 --- a/dash-spv/src/client/status_display.rs +++ b/dash-spv/src/client/status_display.rs @@ -51,7 +51,9 @@ impl<'a> StatusDisplay<'a> { if with_logging { tracing::debug!( "Status display: storage_tip={}, sync_base={}, blockchain_height={}", - storage_tip, state.sync_base_height, blockchain_height + storage_tip, + state.sync_base_height, + blockchain_height ); } blockchain_height