perf(history): scan shielded history in bounded passes, not every block

Every block re-ran z_listreceivedbyaddress for every z-address (O(wallet)
each, under cs_main) and a whole-wallet z_listsentbyaddress. On a 40-address
wallet that was ~80% of wall-clock. The history now advances in passes:

- an address is rescanned when it is stale by a number of blocks scaled to
  its measured cost (8 to 500), never-scanned and hinted addresses first
  (a hint: z_listunspent shows a note the history doesn't);
- z_listsentbyaddress covers only the blocks since the last good sweep
  (+100 reorg margin); the whole-wallet sweep runs once per connect, with
  a 10 min / 1 h / 6 h backoff after real failures;
- listtransactions pages are sized from last time, and an unchanged tip
  reuses the transparent rows;
- found-but-unenriched txids carry over as a backlog; only -5 is a
  definitive "not ours" for z_viewtransaction;
- between blocks, a cheap recent poll plus a one-address probe.

Duty model for a 40-address wallet at 10 s per scan: ~9% instead of ~80%.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-09 11:06:34 -05:00
co-authored by Claude Opus 5.5
parent 8b9a498eee
commit 879de37d18
10 changed files with 1645 additions and 233 deletions
+3 -3
View File
@@ -547,7 +547,7 @@ void App::lockWallet() {
if (ok) {
state_.locked = true;
state_.unlocked_until = 0;
resetTransactionHistoryCacheSession();
lockTransactionHistoryCacheSession(); // keeps the history's scan progress
lock_failure_warned_ = false;
DEBUG_LOGF("[App] Wallet locked\n");
} else {
@@ -642,7 +642,7 @@ void App::refreshWalletEncryptionState() {
settings_->save();
}
if (state_.locked) {
resetTransactionHistoryCacheSession();
lockTransactionHistoryCacheSession();
} else if (state_.transactions.empty()) {
loadTransactionHistoryCacheIfAvailable();
} else {
@@ -1799,7 +1799,7 @@ void App::renderDecryptWalletDialog() {
// Force address + peer refresh
invalidateAddressValidationCache();
addresses_dirty_ = true;
transactions_dirty_ = true;
markTransactionsDirty();
last_tx_block_height_ = -1;
refreshWalletEncryptionState();