feat(spv): verify Merkle proofs progressively, gate spendability on it
Balance/history now render as soon as tx downloads finish instead of blocking on every historical Merkle proof, critical for mobile where proof-checking can take much longer than the download itself. Proofs continue to be checked in the background and each tx's Verified flag catches up progressively; header ranges are now fetched in batches (blockchain.block.headers) instead of one call per header to keep this fast over high-latency links. Coin selection (UtxoSpendability.IsSpendable) refuses to spend a UTXO until its Merkle proof is actually checked, regardless of confirmation count, so a server that fabricates a confirmed balance can get it displayed early but never spent before the forgery is caught. The disk cache only ever persists the fully-verified end state of a sync. UI surfaces the new PendingVerificationSats/SpendableSats split with a "verifying..." badge, and the sync save now runs off the UI thread to avoid freezing on slower hardware.
This commit is contained in:
+14
@@ -41,6 +41,20 @@ It cannot (given correct Merkle verification):
|
||||
- Fabricate a confirmed transaction with a valid Merkle proof
|
||||
- Forge a payment to a wrong address
|
||||
|
||||
**Progressive verification (mobile-friendly sync).** On a wallet with many historical
|
||||
transactions, `WalletSynchronizer` no longer blocks the whole sync on every Merkle proof:
|
||||
balance and history are shown as soon as transaction downloads finish (`PartialResult`,
|
||||
`Core/Spv/WalletSynchronizer.cs`), while proofs continue to be checked in the background and
|
||||
each transaction's `Verified` flag catches up progressively. This means the UI can display a
|
||||
server-reported balance/history that includes not-yet-verified entries — clearly marked with a
|
||||
"verifying…" badge and a separate non-spendable total (`PendingVerificationSats`). The
|
||||
security-critical invariant this depends on: coin selection (`TransactionFactory`, gated by
|
||||
`Wallet/UtxoSpendability.IsSpendable`) refuses to spend a UTXO whose `Verified` flag isn't true,
|
||||
regardless of confirmations — so a server that fabricates a fake confirmed balance can get it
|
||||
*displayed* early, but never *spent*, before the forged Merkle proof is caught and the sync
|
||||
fails outright. The disk cache (`SyncCache`) only ever persists the fully-verified end state of
|
||||
a sync, never a partial one, so no unverified data survives a restart.
|
||||
|
||||
---
|
||||
|
||||
## Key and seed management
|
||||
|
||||
Reference in New Issue
Block a user