feat(wallet): optional background auto-consolidation (Settings toggle)

A mining/pool wallet can accumulate tens of thousands of shielded notes, and the
manual "Consolidate" run — capped at 20 batches per address per click — needs
many re-runs to flatten a 10k+ note address. This adds a hands-off alternative:
a Settings toggle ("Auto-consolidate notes", full-node, off by default) that
trickle-merges notes in the background while the wallet is idle, so it sheds
notes over hours/days without the user ever opening the dialog.

Deliberately gentle, per the request: a small batch (15 inputs/tx vs 50 manual)
and only 3 batches per address per cycle, with cycles ~10 min apart — each cycle
is a handful of tiny merges that barely touch the node. It only acts when the
wallet is genuinely idle (walletScanReady: connected, synced, unlocked, node not
busy, not rescanning) and no send / shield / manual run is in progress, and it
goes dormant (re-scanning hourly) once every address is tidy.

Implementation reuses the existing paced services::ConsolidationRun engine in
"keep in each address" mode, so there is NO fund-path change and the run's
pacing / failure breakers / note-locking all apply:
- App::pumpAutoConsolidation (pumped from update() after pumpConsolidation):
  finalizes a finished cycle (schedules the cooldown + dismisses the run so the
  manual dialog never shows a stale auto result), else starts a new cycle when
  idle and the cooldown has elapsed.
- postAutoConsolidationScan: a quiet z_listunspent scan (the dialog's loadScope
  minus the UI state), session-guarded; on result picks candidates and starts.
- startConsolidationQuiet: the same guards as startConsolidation but silent, with
  a gentle RunConfig; finishConsolidation stays silent for auto runs (logs only).
- services::autoConsolidateCandidates (pure, unit-tested): addresses above the
  note floor that are worth merging; plus the gentle kAutoConsolidate* constants.
- The one-time large-wallet nudge is suppressed while auto-consolidation is on.

Settings plumbing (auto_consolidate, default false) + a daemon-gated toggle in
the Wallet tab's options, with a tooltip. Build + tests + hygiene green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-10-26 13:18:00 -05:00
co-authored by Claude Opus 4.8
parent 54f8359bd4
commit 64294d1518
10 changed files with 167 additions and 0 deletions
+2
View File
@@ -643,6 +643,7 @@ void I18n::loadBuiltinEnglish()
// Wallet settings
strings_["save_z_transactions"] = "Save shielded tx history";
strings_["auto_shield"] = "Auto-shield";
strings_["auto_consolidate"] = "Auto-consolidate notes";
strings_["use_tor"] = "Use Tor";
strings_["keep_daemon"] = "Keep daemon running";
strings_["stop_external"] = "Stop external daemon";
@@ -934,6 +935,7 @@ void I18n::loadBuiltinEnglish()
strings_["tt_clear_ztx"] = "Delete locally cached z-transaction history";
strings_["tt_save_ztx"] = "Store z-address transaction history locally for faster loading";
strings_["tt_auto_shield"] = "Automatically move transparent balance to shielded addresses for privacy";
strings_["tt_auto_consolidate"] = "Quietly merge small notes in the background while the wallet is idle, in tiny batches. Slower than a manual consolidation but barely noticeable, and each address's funds stay separate. Good for mining/pool wallets that accumulate thousands of notes.";
strings_["tt_tor"] = "Route daemon connections through the Tor network for anonymity";
strings_["tt_tor_unavailable"] = "Not available yet: this wallet does not route any traffic through Tor. For the node only, you can add proxy=127.0.0.1:9050 to DRAGONX.conf and restart the node. Tor must be running, or the node finds no peers. Price, update, bootstrap and pool requests still connect directly; use a system-wide VPN/Tor for those.";
strings_["tt_tor_unavailable_lite"] = "Not available yet: this wallet does not route any traffic through Tor. Use a system-wide VPN/Tor.";