diff --git a/src/ui/windows/wallets_dialog.h b/src/ui/windows/wallets_dialog.h index 87e7768..b29daf6 100644 --- a/src/ui/windows/wallets_dialog.h +++ b/src/ui/windows/wallets_dialog.h @@ -288,17 +288,21 @@ 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; - // Seed-phrase vs legacy. Runtime status (z_exportmnemonic → activeWalletSeedBadge) is - // authoritative for the ACTIVE wallet; otherwise the offline probe reads the hdchain - // record's fMnemonicSeed flag directly (pres.mnemonic: 1 = BIP39 seed phrase, 2 = HD/legacy - // with no phrase, 0 = couldn't tell) — which, unlike bare HD-record presence, actually - // distinguishes the two. seed uses the same 1/2/0 encoding. - const int activeBadge = rowActive[i] ? app->activeWalletSeedBadge() : 0; - int seed = activeBadge; - if (seed == 0 && pres.probed) { - if (pres.mnemonic != 0) seed = pres.mnemonic; // read the flag off disk - else if (pres.complete && !pres.hdSeed) seed = 2; // no HD records at all → no phrase - } + // Seed-phrase vs legacy. The offline probe reads the hdchain record's fMnemonicSeed flag + // straight off disk (pres.mnemonic: 1 = BIP39 seed phrase, 2 = HD/legacy with no phrase, + // 0 = couldn't tell). That is the SAME flag the daemon's IsMnemonicSeed()/z_exportmnemonic + // consult, so a definitive read is authoritative and takes precedence. The runtime badge + // (activeWalletSeedBadge, reset only on disconnect) is used ONLY when the offline probe + // couldn't decide — it must NEVER override a definitive on-disk read, or a stale + // HasMnemonic carried from a previously-active mnemonic wallet mislabels a legacy wallet as + // a seed-phrase wallet. seed uses the same 1/2/0 encoding. + int seed = 0; + if (pres.probed && pres.mnemonic != 0) + seed = pres.mnemonic; // definitive on-disk flag wins + else if (rowActive[i] && app->activeWalletSeedBadge() != 0) + seed = app->activeWalletSeedBadge(); // runtime fallback (active row only) + else if (pres.probed && pres.complete && !pres.hdSeed) + seed = 2; // no HD records at all → no phrase const bool bSeed = (seed == 1); const bool bLegacy = (seed == 2); // seed==0 splits by what the probe DID learn: if it saw HD records we know it's an HD @@ -792,6 +796,14 @@ private: } else { const auto pr = util::probeWalletFile(t.first, std::min(budget, kPerFile)); res = ProbeResult{ pr.isBerkeleyDB, pr.scanComplete, pr.encrypted, pr.hdSeed }; + // A cap-truncated btree walk still yields DEFINITIVE positives (a found marker is + // authoritative even when the scan didn't finish), so carry what it read — notably + // the fMnemonicSeed flag. Otherwise a large wallet probed after the shared budget is + // spent loses its seed/legacy classification and the row falls back to the (possibly + // stale) runtime badge, mislabelling a legacy wallet as a seed-phrase wallet. + if (bt.mnemonicSeed != 0) res.mnemonic = bt.mnemonicSeed; + if (bt.hdSeed) res.hdSeed = true; + if (bt.encrypted) res.encrypted = true; budget -= std::min(budget, std::max(bt.bytesRead, pr.bytesRead)); } }