perf(history): toggle mining address without a full chain re-scan

Marking/unmarking a mining address triggered a long history reload: it called
invalidateShieldedHistoryScanProgress() + forced a transaction refresh, which
re-scans every z-address over many RPC cycles. But "mined" vs "receive" is a
pure function of the LOCAL mining-address set — the daemon knows nothing about
it — so a chain re-scan is pointless.

Relabel the affected rows in the in-memory history directly and persist just
those to the encrypted SQLite history cache. The History tab updates instantly
(its display cache rebuilds on the type change), with no daemon round-trip and
no reload. Only re-save when something actually changed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-13 09:37:56 -05:00
parent 555f541c84
commit b71f8ae0a8

View File

@@ -2067,28 +2067,25 @@ bool App::isMiningAddress(const std::string& addr) const
void App::setMiningAddress(const std::string& addr, bool mining) void App::setMiningAddress(const std::string& addr, bool mining)
{ {
if (settings_) { if (!settings_) return;
settings_->setMiningAddress(addr, mining); settings_->setMiningAddress(addr, mining);
settings_->save(); settings_->save();
// Re-label the in-memory history right away. "mined" vs "receive" is purely a function of // "mined" vs "receive" is a pure function of the LOCAL mining-address set — the daemon knows
// whether the receiving address is flagged for mining, so flip the affected rows now: the // nothing about it, so there is NO need to re-scan the chain. Relabel the affected rows in the
// History tab updates immediately, and — importantly — the next refresh's carry-over of // in-memory history directly and persist them to the (SQLite) history cache. This is instant,
// not-yet-rescanned transactions then matches the fresh scan. (Without this, the refresh // with no daemon round-trip; the History tab's display cache rebuilds on the type change.
// re-scans a tx as "receive" but appendMissingPreviousTransactions, which dedupes by const auto miningAddrs = settings_->getMiningAddresses();
// txid+type, still carries the stale "mined" copy over, so the change never showed.) bool changed = false;
const auto miningAddrs = settings_->getMiningAddresses(); for (auto& tx : state_.transactions) {
for (auto& tx : state_.transactions) { if (tx.address.empty() || (tx.type != "receive" && tx.type != "mined")) continue;
if ((tx.type == "receive" || tx.type == "mined") && !tx.address.empty()) { std::string newType = miningAddrs.count(tx.address) ? "mined" : "receive";
tx.type = miningAddrs.count(tx.address) ? "mined" : "receive"; if (tx.type != newType) {
} tx.type = std::move(newType);
changed = true;
} }
invalidateShieldedHistoryScanProgress(true);
transactions_dirty_ = true;
last_tx_block_height_ = -1;
network_refresh_.markDue(services::NetworkRefreshService::Timer::Transactions);
} }
if (changed) storeTransactionHistoryCacheIfAvailable();
} }
void App::invalidateAddressValidationCache() void App::invalidateAddressValidationCache()