Skip to content

Commit bb48156

Browse files
committed
fix: separate filter scan height from synced height
Introduces `filter_committed_height` on `WalletInterface`, separate from `synced_height`. `process_block` updates `synced_height` per-block (needed for balance/maturity calculations), but `FiltersManager` uses `filter_committed_height` for restart recovery. This prevents a bug where per-block `synced_height` advances past uncommitted filter batches, causing the rescan on restart to skip heights that need rescanning for newly discovered addresses. It's not the best solution I think, hence I added the TODO for now. I will look into this at some point later.
1 parent ea1c825 commit bb48156

5 files changed

Lines changed: 43 additions & 10 deletions

File tree

dash-spv/src/sync/filters/manager.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,11 @@ impl<H: BlockHeaderStorage, FH: FilterHeaderStorage, F: FilterStorage, W: Wallet
131131
requests: &RequestSender,
132132
) -> SyncResult<Vec<SyncEvent>> {
133133
self.set_state(SyncState::Syncing);
134-
// Get wallet state
135-
let (wallet_birth_height, wallet_synced_height) = {
134+
// Use filter_committed_height for restart recovery instead of
135+
// synced_height, which advances per-block and may exceed committed scan progress.
136+
let (wallet_birth_height, wallet_committed_height) = {
136137
let wallet = self.wallet.read().await;
137-
(wallet.earliest_required_height().await, wallet.synced_height())
138+
(wallet.earliest_required_height().await, wallet.filter_committed_height())
138139
};
139140

140141
// Get stored filters tip
@@ -146,8 +147,8 @@ impl<H: BlockHeaderStorage, FH: FilterHeaderStorage, F: FilterStorage, W: Wallet
146147

147148
// Calculate scan start (where we need to start processing)
148149
// Must be at least header_start_height for checkpoint-based sync
149-
let scan_start = if wallet_synced_height > 0 {
150-
wallet_birth_height.max(wallet_synced_height + 1)
150+
let scan_start = if wallet_committed_height > 0 {
151+
wallet_birth_height.max(wallet_committed_height + 1)
151152
} else {
152153
wallet_birth_height
153154
}
@@ -494,9 +495,12 @@ impl<H: BlockHeaderStorage, FH: FilterHeaderStorage, F: FilterStorage, W: Wallet
494495

495496
// Commit this batch
496497
let batch = self.active_batches.remove(&batch_start).unwrap();
497-
self.committed_height = batch.end_height();
498-
self.wallet.write().await.update_synced_height(batch.end_height());
499-
self.processing_height = batch.end_height() + 1;
498+
let end = batch.end_height();
499+
if end > self.committed_height {
500+
self.committed_height = end;
501+
self.wallet.write().await.update_filter_committed_height(end);
502+
}
503+
self.processing_height = end + 1;
500504

501505
tracing::info!(
502506
"Committed batch {}-{}, committed_height now {}",

dash-spv/src/sync/filters/sync_manager.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ impl<
3838

3939
async fn initialize(&mut self) -> SyncResult<()> {
4040
let wallet = self.wallet.read().await;
41-
let synced_height = wallet.synced_height();
41+
let committed_height = wallet.filter_committed_height();
4242
drop(wallet);
4343

44-
self.progress.update_current_height(synced_height);
44+
self.progress.update_current_height(committed_height);
4545
self.set_state(SyncState::WaitingForConnections);
4646

4747
tracing::info!(

key-wallet-manager/src/wallet_interface.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,21 @@ pub trait WalletInterface: Send + Sync + 'static {
7575
/// Update the wallet's synced height. This also triggers balance updates.
7676
fn update_synced_height(&mut self, height: CoreBlockHeight);
7777

78+
/// Return the height at which filter scanning was last committed.
79+
/// Defaults to `synced_height()` for implementations that don't separate these concepts.
80+
// TODO: This can probably somehow be combined with synced_height().
81+
fn filter_committed_height(&self) -> CoreBlockHeight {
82+
self.synced_height()
83+
}
84+
85+
/// Update the filter committed height. Call when a height is fully processed
86+
/// (including any rescans for newly discovered addresses).
87+
fn update_filter_committed_height(&mut self, height: CoreBlockHeight) {
88+
if height > self.synced_height() {
89+
self.update_synced_height(height);
90+
}
91+
}
92+
7893
/// Provide a human-readable description of the wallet implementation.
7994
///
8095
/// Implementations are encouraged to include high-level state such as the

key-wallet-manager/src/wallet_manager/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ pub struct WalletManager<T: WalletInfoInterface = ManagedWalletInfo> {
8080
network: Network,
8181
/// Last fully processed block height.
8282
synced_height: CoreBlockHeight,
83+
/// Height at which filter scanning was last committed.
84+
filter_committed_height: CoreBlockHeight,
8385
/// Immutable wallets indexed by wallet ID
8486
wallets: BTreeMap<WalletId, Wallet>,
8587
/// Mutable wallet info indexed by wallet ID
@@ -95,6 +97,7 @@ impl<T: WalletInfoInterface> WalletManager<T> {
9597
Self {
9698
network,
9799
synced_height: 0,
100+
filter_committed_height: 0,
98101
wallets: BTreeMap::new(),
99102
wallet_infos: BTreeMap::new(),
100103
#[cfg(feature = "std")]

key-wallet-manager/src/wallet_manager/process_block.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,17 @@ impl<T: WalletInfoInterface + Send + Sync + 'static> WalletInterface for WalletM
133133
}
134134
}
135135

136+
fn filter_committed_height(&self) -> CoreBlockHeight {
137+
self.filter_committed_height
138+
}
139+
140+
fn update_filter_committed_height(&mut self, height: CoreBlockHeight) {
141+
self.filter_committed_height = height;
142+
if height > self.synced_height {
143+
self.update_synced_height(height);
144+
}
145+
}
146+
136147
async fn describe(&self) -> String {
137148
let wallet_count = self.wallet_infos.len();
138149
if wallet_count == 0 {

0 commit comments

Comments
 (0)