fix: FAQ CJK glyphs, outgoing-tx history, lite encryption, migrate & sync safety
Bug-report fixes plus adversarial-audit follow-ups. Bumps full 2.0.1->2.0.2 and lite 1.1.0->1.1.1. i18n / fonts: - Rebuild the NotoSansCJK subset with the glyphs the FAQ back-fill introduced (什/么/门/做); Chinese FAQ titles rendered as "??" on zh (and ja/ko). Transaction history (enumeration gaps): - Surface outgoing shielded z->z sends via z_listsentbyaddress (listtransactions and z_listreceivedbyaddress never report them). - Surface z->t deshields by parsing z_listsentbyaddress transparentSends (which z_viewtransaction does not expose); dedupe t->t against listtransactions. - Key send-row dedup on address so equal-value multi-output sends aren't dropped. Lite wallet (key safety): - The async create/restore/open path the UI uses now applies the passphrase: encrypt a new/restored wallet, unlock an existing one. It previously discarded the passphrase, storing the seed/keys in PLAINTEXT (create/restore) or leaving an encrypted wallet locked (open). Migrate-to-seed (fund safety): - Persist a sweep-submitted marker at broadcast so a resumed migration can't treat a ~0 balance (an unconfirmed in-flight sweep) as an empty wallet and adopt (swap wallet.dat) before the sweep confirms; best-effort locate routes to the confirm gate. Sync / threading: - Gate isSynced() on a peer-derived tip (tip_known) so a peerless node isn't reported synced (Send against a stale balance); add a "no peers" status banner. - Guard daemon_status_ with a mutex (monitor-thread write vs UI-thread read). Tests: extends tests/test_phase4.cpp across all of the above; suite green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BwycPDQSfEKtrTS2uC4JeT
This commit is contained in:
@@ -31,6 +31,7 @@ enum class NodeBannerReason {
|
||||
FullNodeOffline, // a reachable node was lost, or never came up; reconnect offered
|
||||
DaemonCrashed, // the embedded daemon crashed repeatedly and auto-restart stopped
|
||||
LiteOpenFailed, // lite build: the wallet failed to open
|
||||
NoPeers, // connected to the daemon but no peer-derived tip (0 peers) — stalled, stale
|
||||
};
|
||||
|
||||
struct NodeBannerState {
|
||||
@@ -50,6 +51,13 @@ struct NodeBannerInputs {
|
||||
bool daemon_initializing = false; // daemon launching / block index loading
|
||||
bool connection_in_progress = false; // a connect attempt is actively running
|
||||
|
||||
// Peer connectivity: a full node connected to the daemon but with NO peer-derived tip
|
||||
// (longestchain == 0 -> tip_known false) has no peers, so it silently stalls at a stale height
|
||||
// while balance/sync look "done". `blocks` gates the check so we don't flag the brief window
|
||||
// before the first chain-info refresh. (audit: no-peer-banner)
|
||||
bool tip_known = true; // sync.tip_known — a peer-derived network tip is known
|
||||
int blocks = 0; // sync.blocks — >0 once chain info has been fetched
|
||||
|
||||
// Full-node embedded-daemon crash signal.
|
||||
bool using_embedded_daemon = false;
|
||||
bool has_daemon_controller = false;
|
||||
@@ -81,13 +89,26 @@ inline NodeBannerState evaluateNodeStatusBanner(const NodeBannerInputs& in) {
|
||||
return s;
|
||||
}
|
||||
|
||||
// Full node. Connected, or in an expected startup phase → the loading/warmup overlay owns
|
||||
// the screen, so no banner. An active connect attempt likewise shows progress, not an
|
||||
// error — don't flicker a banner over it.
|
||||
if (in.connected) return s;
|
||||
// Full node. In an expected startup phase → the loading/warmup overlay owns the screen, so no
|
||||
// banner. An active connect attempt likewise shows progress, not an error — don't flicker a
|
||||
// banner over it. (These return even if `connected` has flipped early, matching prior behavior.)
|
||||
if (in.warming_up || in.daemon_initializing) return s;
|
||||
if (in.connection_in_progress) return s;
|
||||
|
||||
// Connected to the daemon (RPC works) but with no peer-derived tip → the node has no peers and
|
||||
// cannot learn about new blocks; it silently stalls at a stale height while everything looks
|
||||
// synced. Surface it as a recoverable warning (reconnect won't help, so no action button).
|
||||
if (in.connected) {
|
||||
if (!in.tip_known && in.blocks > 0) {
|
||||
s.show = true;
|
||||
s.severity = NodeBannerSeverity::Warning;
|
||||
s.reason = NodeBannerReason::NoPeers;
|
||||
s.action = NodeBannerAction::None;
|
||||
s.detail = in.connection_status;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// Genuinely offline. Distinguish "the embedded daemon crashed and we stopped retrying" (a
|
||||
// hard fault needing a manual restart) from an ordinary lost/failed connection (retryable).
|
||||
if (in.using_embedded_daemon && in.has_daemon_controller && !in.daemon_running &&
|
||||
|
||||
Reference in New Issue
Block a user