fix(resilience,perf): gate send on a known tip, surface dropped/stale txs, advance cached confs, memoize explorer
Backlog mediums (display/gating/caching only — no send/sweep/encrypt call or args changed): Resilience: - Gate Send/Review on a KNOWN peer-derived tip (SyncInfo::sendTipReady): a fresh reconnect or peerless node has tip_known=false, so its local height isn't a trustworthy network tip — sending is disabled with a clear tooltip until it is. Lite is exempt (its own sync gate). The z_sendmany call is untouched. - History now shows "Dropped" for a conflicted tx (confirmations < 0) and "Expired?" for one unconfirmed > 1h, instead of "Pending" forever (new TransactionInfo::isConflicted/isStalePending; the tab inlines the check on its DisplayTx fields). - A cached z_viewtransaction row's confirmations now advance by the blocks mined since it was sampled (observed_height), instead of freezing at the cached value; never goes backwards; pending (0) isn't extrapolated. Performance: - Explorer block list uses loadRangeCached() (memoized on the cache revision) instead of loadRange(), so it only hits SQLite after the cache changes, not every frame. Tests: testResilienceMediums (tip gate, confirmations-advance, dropped/stale predicates) + loadRangeCached memoization in testExplorerBlockCache (isolated on its own cache). Held: Overview/Balance per-frame list cache, peers clippers, off-thread tx-history cache load (larger refactors). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1056,8 +1056,13 @@ NetworkRefreshService::TransactionViewCacheEntry NetworkRefreshService::parseVie
|
||||
|
||||
void NetworkRefreshService::appendViewTransactionOutputs(std::vector<TransactionInfo>& transactions,
|
||||
const std::string& txid,
|
||||
const TransactionViewCacheEntry& entry)
|
||||
const TransactionViewCacheEntry& entry,
|
||||
int currentHeight)
|
||||
{
|
||||
// A mined entry's confirmations grow by one per block since it was sampled; never go backwards.
|
||||
int entryConfirmations = entry.confirmations;
|
||||
if (entry.confirmations >= 1 && entry.observed_height >= 0 && currentHeight > entry.observed_height)
|
||||
entryConfirmations += currentHeight - entry.observed_height;
|
||||
for (const auto& out : entry.outgoing_outputs) {
|
||||
bool alreadyTracked = false;
|
||||
for (const auto& existing : transactions) {
|
||||
@@ -1089,7 +1094,7 @@ void NetworkRefreshService::appendViewTransactionOutputs(std::vector<Transaction
|
||||
}
|
||||
}
|
||||
if (!foundExistingMetadata) {
|
||||
info.confirmations = entry.confirmations;
|
||||
info.confirmations = entryConfirmations;
|
||||
info.timestamp = entry.timestamp;
|
||||
}
|
||||
transactions.push_back(std::move(info));
|
||||
@@ -1476,7 +1481,7 @@ NetworkRefreshService::TransactionRefreshResult NetworkRefreshService::collectTr
|
||||
auto cached = snapshot.viewTxCache.find(txid);
|
||||
if (cached != snapshot.viewTxCache.end()) {
|
||||
if (!trackedSend || !cached->second.outgoing_outputs.empty()) {
|
||||
appendViewTransactionOutputs(result.transactions, txid, cached->second);
|
||||
appendViewTransactionOutputs(result.transactions, txid, cached->second, currentBlockHeight);
|
||||
// NB: do NOT extract chat metadata from our OWN outgoing memos here — ingest marks
|
||||
// everything Incoming, so that duplicates every sent message as a phantom "from peer"
|
||||
// entry. Genuine incoming comes from the z_listreceivedbyaddress path; our sends are
|
||||
@@ -1509,6 +1514,7 @@ NetworkRefreshService::TransactionRefreshResult NetworkRefreshService::collectTr
|
||||
}
|
||||
|
||||
auto entry = parseViewTransactionCacheEntry(viewTransaction);
|
||||
entry.observed_height = currentBlockHeight; // confirmations below are sampled at this tip
|
||||
const bool storedRow = rowBacklog.count(txid) > 0;
|
||||
if (storedRow && entry.timestamp == 0) {
|
||||
const auto settled = settledStoredMeta.find(txid);
|
||||
|
||||
Reference in New Issue
Block a user