Summary
dash-spv compact-filter sync (BIP157/158) stalls indefinitely and silently when the only connected peer does not advertise the NODE_COMPACT_FILTERS service bit. Block-header sync (which doesn't need the capability) reaches the chain tip, but Filter Header / Filter sync freezes ~2,000 blocks short of tip, so the wallet never sees transactions/UTXOs in recent blocks. The request processor logs No peers support required capability and the download coordinator retries the same item 20+ times forever, never connecting to additional peers to find a capable one and never surfacing a terminal error.
Environment
dash-spv pinned rev 5c0113e7901551450f6063023eec4be95beeb6b9
- Network: testnet
- Consumer:
SwiftDashSDK / SwiftExampleApp (dashpay/platform) on an iOS Simulator, but the stall is entirely in dash-spv's peer-selection + filter download.
Symptom
Headers: 1,499,648/1,499,644 (100.0%) — reaches tip ✅
Filter Headers: 1,497,593/1,499,648 (99.9%) processed: 0, last_activity: 210s — frozen ~2,055 blocks behind tip, never advances (persists across client Pause/Start and Clear).
- Consequence: a freshly-funded address (faucet tx in a recent block) never appears as spendable, so anything needing recent UTXOs (asset-lock funding, receiving) is blocked, and the client looks hung with no error surfaced to the consumer.
Root cause (from logs)
Only one peer is connected, and it lacks compact-filter support:
INFO dash_spv::network::peer: Updated peer info for 91.92.199.202:15333: height=1499644, version=70240, services=ServiceFlags(7173)
INFO dash_spv::network::manager: Successfully connected to 91.92.199.202:15333
INFO dash_spv::sync::sync_coordinator: Spawning FilterHeader task, receiving message types: [CFHeaders]
ERROR dash_spv::network::manager: Request processor: failed to send message: Protocol error: No peers support required capability
ERROR dash_spv::network::manager: Request processor: failed to send message: Protocol error: No peers support required capability (x128)
WARN dash_spv::sync::download_coordinator: Retrying item (attempt 1) ... (attempt 20+)
ServiceFlags(7173) = 0x1C05 = bits 0,2,10,11,12 → NODE_NETWORK (1) | NODE_BLOOM (4) | NODE_NETWORK_LIMITED (1024) | 2048 | 4096. Bit 6 (NODE_COMPACT_FILTERS, value 64) is NOT set. So GetCFHeaders/GetCFilters can't be sent to this peer, every attempt fails with No peers support required capability, and the coordinator just keeps retrying the same item.
Why it's a bug (two parts)
- Peer selection. When compact-filter sync is enabled,
dash-spv connects to (and stays on) a single peer that doesn't advertise NODE_COMPACT_FILTERS, and never opens additional connections / rotates peers to find a filter-capable one. On testnet (where filter-serving nodes are sparse) this reliably wedges filter sync.
- Failure handling / surfacing. The download coordinator retries the same item indefinitely (20+ attempts observed) with no backoff escalation to a terminal, surfaced error and no trigger to re-select peers. To the consumer the client is indistinguishable from a hang — the progress bar simply freezes with no event/error.
Suggested fixes
- When
NODE_COMPACT_FILTERS is required for the active sync mode, require/prefer peers advertising it during peer selection, and proactively open additional connections / rotate peers until a capable one is found (instead of getting stuck on a single incapable peer).
- Bound the retry loop: after N failures with
No peers support required capability, either re-trigger peer discovery for a capable peer or emit a terminal error/event the consumer can react to.
- Surface a distinct
NoCompactFilterPeers (or similar) status/event so UIs can show "searching for a compact-filter peer…" / an actionable error rather than a frozen progress bar.
Repro
- testnet, compact-filter sync enabled.
- Connect such that the only reachable/selected peer advertises
services without bit 6 (NODE_COMPACT_FILTERS) — e.g. ServiceFlags(7173).
- Header sync reaches tip; Filter Header sync stalls ~2k blocks short; logs spam
No peers support required capability + Retrying item. No recovery across client restart/clear.
Summary
dash-spvcompact-filter sync (BIP157/158) stalls indefinitely and silently when the only connected peer does not advertise theNODE_COMPACT_FILTERSservice bit. Block-header sync (which doesn't need the capability) reaches the chain tip, but Filter Header / Filter sync freezes ~2,000 blocks short of tip, so the wallet never sees transactions/UTXOs in recent blocks. The request processor logsNo peers support required capabilityand the download coordinator retries the same item 20+ times forever, never connecting to additional peers to find a capable one and never surfacing a terminal error.Environment
dash-spvpinned rev5c0113e7901551450f6063023eec4be95beeb6b9SwiftDashSDK/ SwiftExampleApp (dashpay/platform) on an iOS Simulator, but the stall is entirely indash-spv's peer-selection + filter download.Symptom
Headers: 1,499,648/1,499,644 (100.0%)— reaches tip ✅Filter Headers: 1,497,593/1,499,648 (99.9%) processed: 0, last_activity: 210s— frozen ~2,055 blocks behind tip, never advances (persists across client Pause/Start and Clear).Root cause (from logs)
Only one peer is connected, and it lacks compact-filter support:
ServiceFlags(7173)=0x1C05= bits 0,2,10,11,12 →NODE_NETWORK (1) | NODE_BLOOM (4) | NODE_NETWORK_LIMITED (1024) | 2048 | 4096. Bit 6 (NODE_COMPACT_FILTERS, value 64) is NOT set. SoGetCFHeaders/GetCFilterscan't be sent to this peer, every attempt fails withNo peers support required capability, and the coordinator just keeps retrying the same item.Why it's a bug (two parts)
dash-spvconnects to (and stays on) a single peer that doesn't advertiseNODE_COMPACT_FILTERS, and never opens additional connections / rotates peers to find a filter-capable one. On testnet (where filter-serving nodes are sparse) this reliably wedges filter sync.Suggested fixes
NODE_COMPACT_FILTERSis required for the active sync mode, require/prefer peers advertising it during peer selection, and proactively open additional connections / rotate peers until a capable one is found (instead of getting stuck on a single incapable peer).No peers support required capability, either re-trigger peer discovery for a capable peer or emit a terminal error/event the consumer can react to.NoCompactFilterPeers(or similar) status/event so UIs can show "searching for a compact-filter peer…" / an actionable error rather than a frozen progress bar.Repro
serviceswithout bit 6 (NODE_COMPACT_FILTERS) — e.g.ServiceFlags(7173).No peers support required capability+Retrying item. No recovery across client restart/clear.