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