perf(history): add a transactions revision counter to WalletState
WalletState had no change signal for its transaction list, so a UI cache had to fingerprint all N transactions every frame to notice an update. It now carries tx_revision, a counter that only increases; clear() on disconnect bumps it instead of resetting it, so a cache built before a reconnect or wallet switch can never match again. - Every writer of the list bumps it: the full-node refresh, loading the cached history, adding, completing and removing a pending send, a mining-address relabel, the lite refresh and the screenshot demo rows. - The full-node refresh and the lite refresh (every ~2 s) swap in the new list only when it differs field by field, the amount compared bit-exactly because -0.0 and +0.0 print differently. An unchanged refresh keeps the list and the revision; the compare costs O(N) per refresh, not per frame. Nothing reads the counter yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
#include <cstdint>
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
|
||||
#include "exchange_info.h"
|
||||
@@ -62,6 +63,22 @@ struct TransactionInfo {
|
||||
bool isMature() const { return confirmations >= 100; }
|
||||
};
|
||||
|
||||
// Exact field-wise equality: decides whether a refreshed list really differs. `amount` is compared
|
||||
// BIT-exactly (like the old History fingerprint): -0.0 and +0.0 render differently ("+-0.0" vs "+0.0").
|
||||
// Must list EVERY TransactionInfo field (tests mutate each one).
|
||||
inline bool sameTransactionInfo(const TransactionInfo& a, const TransactionInfo& b) {
|
||||
return a.timestamp == b.timestamp && a.confirmations == b.confirmations &&
|
||||
std::memcmp(&a.amount, &b.amount, sizeof(double)) == 0 &&
|
||||
a.txid == b.txid && a.type == b.type && a.address == b.address &&
|
||||
a.from_address == b.from_address && a.memo == b.memo;
|
||||
}
|
||||
inline bool sameTransactionList(const std::vector<TransactionInfo>& a, const std::vector<TransactionInfo>& b) {
|
||||
if (a.size() != b.size()) return false;
|
||||
for (std::size_t i = 0; i < a.size(); ++i)
|
||||
if (!sameTransactionInfo(a[i], b[i])) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Represents a connected peer
|
||||
*/
|
||||
@@ -327,6 +344,21 @@ struct WalletState {
|
||||
int wallet_tx_count = -1; // getwalletinfo's txcount; -1 = unknown
|
||||
int64_t last_address_update = 0; // set when an address-list refresh applies; 0 = never loaded yet
|
||||
int64_t last_tx_update = 0;
|
||||
|
||||
// Change counter for `transactions` (main thread only). Starts at 1 so a UI cache's 0 sentinel never matches;
|
||||
// only ever increases (clear() bumps, never resets) so a wallet switch can't alias an old cache key. EVERY
|
||||
// write to `transactions` (replace, insert/erase, in-place field edit) must use the helpers below or call
|
||||
// touchTransactions() afterwards. UI caches (History tab, ...) key on this, NOT on last_tx_update (a
|
||||
// wall-clock refresh stamp that lite never sets) nor App::tx_dirty_generation_ (a refresh-needed flag).
|
||||
std::uint64_t tx_revision = 1;
|
||||
void touchTransactions() { ++tx_revision; }
|
||||
void replaceTransactions(std::vector<TransactionInfo>&& next) { transactions = std::move(next); touchTransactions(); }
|
||||
// Replace only when the content differs; returns true (and bumps) if it did. Keeps the old list when identical.
|
||||
bool setTransactionsIfChanged(std::vector<TransactionInfo>&& next) {
|
||||
if (sameTransactionList(transactions, next)) return false;
|
||||
replaceTransactions(std::move(next));
|
||||
return true;
|
||||
}
|
||||
int64_t last_peer_update = 0;
|
||||
int64_t last_mining_update = 0;
|
||||
|
||||
@@ -358,6 +390,7 @@ struct WalletState {
|
||||
z_addresses.clear();
|
||||
t_addresses.clear();
|
||||
transactions.clear();
|
||||
touchTransactions(); // never reset: a post-switch revision must differ from every cached key
|
||||
peers.clear();
|
||||
bannedPeers.clear();
|
||||
// W6-1: reset node-level mining state too — the daemon restarts on a wallet switch (mining
|
||||
|
||||
Reference in New Issue
Block a user