docs/docs-as-code.md defines the workflow; docs/adr holds 31 ADRs (retroactive, reconstructed from code and changelog) with a template and index. docs/check-docs.sh validates ADR structure, index, cited code paths, relative links and CLAUDE.md/AGENTS.md sync.
1.3 KiB
1.3 KiB
0008. Progressive verification: show early, spend only after proof
- Status: Accepted
- Area: SPV
- Recorded: 2026-11-03 (retroactive: reconstructed from code, comments and changelog)
Context
On wallets with thousands of historical transactions, blocking the whole sync on every Merkle proof made the app unusable, especially on mobile hardware.
Decision
WalletSynchronizer raises PartialResult as soon as transaction downloads finish, then keeps verifying proofs in the background and refreshes the result periodically. Each cached transaction and UTXO carries a Verified flag. The UI marks unverified entries ("verifying…") and shows PendingVerificationSats separately. The invariant: UtxoSpendability.IsSpendable requires Verified, so unverified coins can be displayed but never spent. SyncOnceAsync still completes only when verification is finished, and the disk cache stores only the fully verified end state.
Consequences
- Fast time-to-usable-balance.
- A forged balance can be displayed briefly but never spent before the proof failure aborts the sync.
- Balances have overlapping subsets (immature vs. pending verification); consumers must use
SpendableSats, never subtract the subsets.
References
src/Core/Spv/WalletSynchronizer.cssrc/Core/Wallet/UtxoSpendability.csSECURITY.md