fix(wallets): stop a legacy wallet showing as a seed-phrase wallet when linked
The wallets list badged the active/linked row from the runtime seed status (activeWalletSeedBadge → wallet_seed_status_, reset only on disconnect) in preference to the offline on-disk probe. Two issues let a genuinely legacy wallet render as "seed phrase": - The offline probe's budget-fallback branch dropped the fMnemonicSeed flag: res.mnemonic was set only in the (parsed && complete) branch. The probe shares a 768 MB budget across all wallet files, so a large wallet (e.g. a 194 MB one) probed after the budget is spent falls into the fallback, loses its seed/legacy classification (mnemonic → 0), and the row defers to the runtime badge. - With mnemonic == 0, the code used the runtime badge, which can still carry a HasMnemonic from a previously-active mnemonic wallet — mislabelling the legacy wallet. Fix: - Carry the definitive positives (fMnemonicSeed/hdSeed/mkey) from a cap-truncated btree walk — a found marker is authoritative even when the scan didn't finish. - Make the on-disk fMnemonicSeed read take precedence: it's the SAME flag the daemon's IsMnemonicSeed()/z_exportmnemonic consult, so a definitive read wins; the runtime badge is used only when the probe genuinely couldn't decide, and never overrides a definitive on-disk classification. Verified the wallet in question is truly legacy (fMnemonicSeed=false on disk, matching the daemon's CHDChain serialization + IsMnemonicSeed). The flag reader (hdChainMnemonicFlag) is already unit-tested; suite green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user