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:
2026-10-19 19:12:32 -05:00
co-authored by Claude Opus 4.8
parent b404bb2aff
commit 3ee90993ed
8 changed files with 117 additions and 9 deletions
+3
View File
@@ -1511,6 +1511,8 @@ void I18n::loadBuiltinEnglish()
strings_["confirmations"] = "Confirmations";
strings_["confirmed"] = "Confirmed";
strings_["pending"] = "Pending";
strings_["tx_dropped"] = "Dropped";
strings_["tx_stale_pending"] = "Expired?";
strings_["baltab_pending_chip"] = "Pending +%.4f";
strings_["baltab_pending_tip"] = "Awaiting confirmations — not spendable yet; it moves to your available balance once confirmed.";
strings_["sent"] = "sent";
@@ -2703,6 +2705,7 @@ void I18n::loadBuiltinEnglish()
strings_["send_tooltip_not_connected"] = "Not connected to daemon";
strings_["send_tooltip_select_source"] = "Select a source address first";
strings_["send_tooltip_syncing"] = "Wait for blockchain to sync";
strings_["send_tooltip_no_tip"] = "Waiting for peers to report the network height. Sending is disabled until the wallet knows it is caught up.";
strings_["send_available_when_synced"] = "You can fill in the details now. Sending is available once the wallet is fully synced.";
strings_["send_tooltip_view_only"] = "View-only address — no spending key, cannot send";
strings_["send_total"] = "Total";