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:
@@ -18,7 +18,13 @@ public static class UtxoSpendability
|
||||
public static int Confirmations(this CachedUtxo utxo, int tipHeight) =>
|
||||
utxo.Height <= 0 ? 0 : tipHeight - utxo.Height + 1;
|
||||
|
||||
/// <summary>True when the UTXO has met its confirmation threshold and is not frozen.</summary>
|
||||
/// <summary>
|
||||
/// True when the UTXO has met its confirmation threshold, is not frozen, and its Merkle
|
||||
/// proof has actually been checked. Without the <see cref="CachedUtxo.Verified"/> gate a
|
||||
/// malicious server could report a fake confirmed UTXO and have it spent before background
|
||||
/// verification ever caught the forgery.
|
||||
/// </summary>
|
||||
public static bool IsSpendable(this CachedUtxo utxo, ChainProfile profile, int tipHeight) =>
|
||||
!utxo.Frozen && utxo.Height > 0 && utxo.Confirmations(tipHeight) >= utxo.RequiredConfirmations(profile);
|
||||
!utxo.Frozen && utxo.Height > 0 && utxo.Verified
|
||||
&& utxo.Confirmations(tipHeight) >= utxo.RequiredConfirmations(profile);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user