perf(spv): persist checkpoint-anchoring state across sync sessions

_anchoredUpTo tracked which heights were already proven to hash-chain
back to a checkpoint, but only in memory: every app restart re-walked
and re-verified the whole header chain from the checkpoint even when
the header bytes themselves were already cached on disk, dominating
reconnect time on large wallets. Round-trip it through SyncCache like
the other sync caches (RawTxHex/VerifiedAt/BlockHeaders).
This commit is contained in:
davide committed 2026-07-19 12:16:00 +02:00
1 parent b6440484c1
commit d9dd05aa52
5 files changed
+94 -15

No files matched your search

+11 -5
View File
@@ -271,7 +271,8 @@ public partial class MainWindowViewModel
_doc.Cache?.BlockHeaders,
_doc.Cache?.NextReceiveIndex ?? 0,
_doc.Cache?.NextChangeIndex ?? 0,
net);
net,
_doc.Cache?.AnchoredUpTo);
_synchronizer.Progress += msg => Dispatcher.UIThread.Post(() => StatusMessage = msg);
// Progressive verification (§7.4): the wallet becomes usable as soon as
// transaction downloads finish, without waiting for every historical Merkle
@@ -289,15 +290,19 @@ public partial class MainWindowViewModel
// Off the UI thread: sync does heavy CPU work (LINQ over thousands of
// cached txs/UTXOs) and blocking JSON/disk I/O between awaits, negligible
// on desktop but enough to freeze the UI (ANR) on slower mobile hardware.
var (result, rawHex, verifiedAt, blockHeaders) = await Task.Run(async () =>
var (result, rawHex, verifiedAt, blockHeaders, anchoredUpTo) = await Task.Run(async () =>
{
var r = await _synchronizer.SyncOnceAsync(ct);
var caches = _synchronizer.ExportCaches(PalladiumNetworks.For(_account.Profile.Kind));
return (r, caches.RawTxHex, caches.VerifiedAt, caches.BlockHeaders);
return (r, caches.RawTxHex, caches.VerifiedAt, caches.BlockHeaders, caches.AnchoredUpTo);
}, ct);
_lastTransactions = result.Transactions;
var cache = new SyncCache { RawTxHex = rawHex, VerifiedAt = verifiedAt, BlockHeaders = blockHeaders };
var cache = new SyncCache
{
RawTxHex = rawHex, VerifiedAt = verifiedAt, BlockHeaders = blockHeaders,
AnchoredUpTo = anchoredUpTo,
};
FillDisplayFields(cache, result);
_doc.Cache = cache;
await WalletStore.SaveAsync(_doc, _walletPath!, _password);
@@ -493,12 +498,13 @@ public partial class MainWindowViewModel
try
{
var net = PalladiumNetworks.For(_account.Profile.Kind);
var (rawHex, verifiedAt, blockHeaders) = _synchronizer.ExportCaches(net);
var (rawHex, verifiedAt, blockHeaders, anchoredUpTo) = _synchronizer.ExportCaches(net);
if (rawHex.Count == 0 && verifiedAt.Count == 0)
return;
(_doc.Cache ??= new SyncCache()).RawTxHex = rawHex;
_doc.Cache.VerifiedAt = verifiedAt;
_doc.Cache.BlockHeaders = blockHeaders;
_doc.Cache.AnchoredUpTo = anchoredUpTo;
WalletStore.Save(_doc, _walletPath, _password);
}
catch { /* non-fatal: the next full save will recover */ }