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
+2
View File
@@ -30,6 +30,8 @@ struct SentSweepState {
int fullFailures = 0;
long fullRetryDelaySec = 0;
std::string daemonId;
std::int64_t lastFullTime = 0; // when the last successful FULL sweep finished: drives the weekly
int lastFullHeight = -1; // safety-net re-sweep that catches changes the per-event guards miss
bool valid() const { return height >= 0; }
};