perf(history): weekly safety-net full re-sweep for sent history

Persisting the sent-sweep watermark means a relaunch runs a windowed sweep,
which can't see a change made outside the GUI (a Console z_importviewingkey,
an external same-seed restore) that adds an old send below the window. This
adds a periodic full re-sweep so that case is still caught within ~7 days.

- sentSweepSafetyNetDue(now, lastFull) = lastFull > 0 && now - lastFull > 7d
  (a wallet that has never full-swept doesn't trigger one; the seeding sweep
  records the time). lastFullTime / lastFullHeight ride in the sent_sweep
  snapshot; any successful full sweep (seed, verify-mismatch or safety-net)
  resets the clock. On load, a valid watermark without a clock starts it now,
  so even watermarks seeded before this field eventually re-sweep.
- When due, the snapshot forces a full sweep for that cycle. Gated on: a
  watermark exists, a routine (non-syncing) cycle, not the Mining page, and
  not in full-sweep backoff -- so a full sweep that can't finish on a slow
  daemon backs off instead of retrying every cycle.

Degrades gracefully: ~0.4 s on a note-cache daemon; callLong(120 s) with
backoff on an old one (the deferred banding would keep each cs_main hold
short so it fits there too). Helper + persistence covered by tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-10-16 17:56:30 -05:00
co-authored by Claude Opus 4.8
parent e28cacbe8e
commit 07437fb447
7 changed files with 49 additions and 3 deletions
+5
View File
@@ -65,6 +65,11 @@ bool sentSweepNeedsLongCall(int tip, int fromHeight)
return tip > 0 && fromHeight >= 0 && (tip - fromHeight) > kSentSweepLongCallBlocks;
}
bool sentSweepSafetyNetDue(std::int64_t nowSec, std::int64_t lastFullSec)
{
return lastFullSec > 0 && (nowSec - lastFullSec) > kSentSweepSafetyNetSec;
}
long fullSentSweepRetryDelaySec(int consecutiveFailures)
{
if (consecutiveFailures <= 0) return 0;