feat(spv): cross-check recent headers with a witness server

Blocks above the newest checkpoint cannot be anchored, so the header from
the primary server is now compared with the one from a second server taken
from the hardcoded bootstrap list. A disagreement at 3+ blocks of depth
aborts the sync; a shallower one leaves the proof unverified until the next
sync; with no reachable witness behaviour is unchanged. A bounded
proof-of-work check was evaluated and rejected. See ADR 0042.
This commit is contained in:
davide committed 2026-11-03 15:29:29 +01:00
1 parent 305f22ed0e
commit 9010ebce59
11 files changed
+430 -4

No files matched your search

+14 -1
View File
@@ -159,6 +159,13 @@ static async Task<int> Sync(string[] o)
await using var client = await Connect(o, account.Profile);
var sync = new WalletSynchronizer(account, client, doc.GapLimit);
IReadOnlyList<ElectrumClient> witnesses = [];
sync.WitnessProvider = async ct =>
{
var pins = new CertificatePinStore(AppPaths.CertificatePinsPath(account.Profile.Kind));
return witnesses = await WitnessConnector.ConnectAsync(account.Profile,
account.Profile.BootstrapServers, client.Host, client.Port, client.UseSsl, pins, ct: ct);
};
sync.Progress += msg => Console.WriteLine($" {msg}");
var net = PalladiumNetworks.For(account.Profile.Kind);
sync.PreloadCaches(
@@ -169,7 +176,13 @@ static async Task<int> Sync(string[] o)
doc.Cache?.NextChangeIndex ?? 0,
net,
doc.Cache?.AnchoredDownTo);
var result = await sync.SyncOnceAsync();
SyncResult result;
try { result = await sync.SyncOnceAsync(); }
finally
{
foreach (var w in witnesses)
await w.DisposeAsync();
}
var (rawHex, verifiedAt, blockHeaders, anchoredDownTo) = sync.ExportCaches(net);
doc.Cache = new SyncCache