From 58741421866355f29d67054b83dadf46e87f9874 Mon Sep 17 00:00:00 2001 From: DanS Date: Wed, 15 Jul 2026 16:02:50 -0500 Subject: [PATCH] fix(wallets): don't mislabel a legacy HD wallet as "Seed phrase" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Wallets modal's "Seed phrase" vs "Legacy" badge came from the offline wallet.dat probe, which flags hdseed/chdseed/hdchain. But hdchain is present in BOTH a BIP39-mnemonic wallet AND a legacy HD wallet (DragonX has no separate mnemonic DB record — the mnemonic is derived from the HD seed), so a legacy HD wallet was shown as "Seed phrase". For the ACTIVE wallet the app already knows the truth at runtime via z_exportmnemonic (wallet_seed_status_). The active row's badge now uses that authoritative status (activeWalletSeedBadge: seed-phrase / legacy / undecided) and only falls back to the probe for non-active or not-yet-decided wallets; the "unknown" seed badge is suppressed once the runtime status is authoritative. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/app.h | 8 ++++++++ src/ui/windows/wallets_dialog.h | 13 ++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/app.h b/src/app.h index 57734d4..95f4f37 100644 --- a/src/app.h +++ b/src/app.h @@ -389,6 +389,14 @@ public: // True when the current full-node wallet is a legacy, pre-seed-phrase wallet (no BIP39 mnemonic) // that a capable daemon could migrate — the Migrate-to-seed button glows to nudge the user. bool isPreSeedWallet() const { return wallet_seed_status_ == WalletSeedStatus::NoMnemonic; } + // Authoritative BIP39-mnemonic status of the ACTIVE wallet, decided at runtime by z_exportmnemonic + // (not the offline file probe, which can't tell an HD-but-no-mnemonic wallet from a seed-phrase one). + // 0 = unknown/undecidable, 1 = has a seed phrase, 2 = legacy (no mnemonic). + int activeWalletSeedBadge() const { + if (wallet_seed_status_ == WalletSeedStatus::HasMnemonic) return 1; + if (wallet_seed_status_ == WalletSeedStatus::NoMnemonic) return 2; + return 0; + } void showAboutDialog() { show_about_ = true; } // Legacy tab compat — maps int to NavPage diff --git a/src/ui/windows/wallets_dialog.h b/src/ui/windows/wallets_dialog.h index 90e8bde..ca3155c 100644 --- a/src/ui/windows/wallets_dialog.h +++ b/src/ui/windows/wallets_dialog.h @@ -288,9 +288,16 @@ public: // encryption — so absence of a lock never falsely reads as "unencrypted" on a huge wallet. const ProbeResult pres = probeAt(i); // from the frame-consistent snapshot above const bool bLock = pres.probed && pres.encrypted; - const bool bSeed = pres.probed && pres.hdSeed; - const bool bLegacy = pres.probed && pres.complete && !pres.hdSeed; - const bool bUnknown = pres.probed && !pres.complete && !pres.encrypted; + // Seed-phrase vs legacy: the offline probe only sees HD records (hdseed/hdchain), which + // are present in BOTH mnemonic and legacy-HD wallets — so it can't tell them apart. For + // the ACTIVE wallet the app knows the truth (z_exportmnemonic → activeWalletSeedBadge); + // prefer that and fall back to the probe only for non-active or not-yet-decided wallets. + const int activeBadge = rowActive[i] ? app->activeWalletSeedBadge() : 0; + const bool bSeed = (activeBadge == 1) || (activeBadge == 0 && pres.probed && pres.hdSeed); + const bool bLegacy = (activeBadge == 2) || + (activeBadge == 0 && pres.probed && pres.complete && !pres.hdSeed); + // No "unknown" seed badge once the runtime status is authoritative for the active wallet. + const bool bUnknown = (activeBadge == 0) && pres.probed && !pres.complete && !pres.encrypted; struct Badge { const char* glyph; ImU32 col; const char* label; const char* tip; }; Badge bl[4]; int nb = 0; if (bSeed) bl[nb++] = { ICON_MD_ECO, WithAlpha(Success(), 235), TR("wallets_badge_seed_short"), TR("wallets_badge_seed") };