From 5a5478669c487963e99491112abc065c59973966 Mon Sep 17 00:00:00 2001 From: mtg0001 <106211915+mtg0001@users.noreply.github.com> Date: Sat, 1 Aug 2026 21:43:45 -0300 Subject: Keep new phrase addresses recoverable (#102) -newphrase installed a seed but left the wallet's visible address as the pre-seed random key and the old random pool intact, so the next address handed out -- a mining payout included -- could be one the twelve words cannot reproduce, while the window said a recovery phrase existed. Installing a phrase now clears the stale pool records (private keys stay in wallet.dat), reserves a derived default receiving key, tolerates already-stored derived keys so repeated installs do not stall the pool, and -restorephrase reports what it recovered rather than the wallet's total transaction count. Self-test covers installing a phrase over a pre-existing random pool. --- src/selftest.cpp | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) (limited to 'src/selftest.cpp') diff --git a/src/selftest.cpp b/src/selftest.cpp index b733c60..bc9fa5a 100644 --- a/src/selftest.cpp +++ b/src/selftest.cpp @@ -327,10 +327,35 @@ static int RunWalletHDSelfTest() throw std::runtime_error("LoadWallet failed"); std::string strError; + TopUpKeyPool(); + int nRandomPoolSize = 0; + CRITICAL_BLOCK(cs_keyPool) + nRandomPoolSize = (int)mapKeyPool.size(); + nFail += Check(nRandomPoolSize == KEYPOOL_SIZE, + "a wallet can start with a random key pool") ? 0 : 1; + nFail += Check(SetHDSeedFromMnemonic(strPhraseA, strError), "a valid phrase installs a seed") ? 0 : 1; nFail += Check(HaveHDSeed(), "the wallet reports having a seed") ? 0 : 1; + int nPoolAfterSeed = 0; + CRITICAL_BLOCK(cs_keyPool) + nPoolAfterSeed = (int)mapKeyPool.size(); + nFail += Check(nPoolAfterSeed == 0, + "installing a phrase clears the old random key pool") ? 0 : 1; + nFail += Check(nHDNext == 1, + "installing a phrase reserves one derived default key") ? 0 : 1; + + CKey keyFirstDerived; + if (!DeriveHDKey(0, keyFirstDerived, strError)) + throw std::runtime_error("default derivation failed: " + strError); + std::vector vchDefaultKey; + bool fDefaultRead = CWalletDB("r").ReadDefaultKey(vchDefaultKey); + nFail += Check(fDefaultRead && vchDefaultKey == keyFirstDerived.GetPubKey(), + "the default receiving key is derived from the phrase") ? 0 : 1; + nFail += Check(WalletHasPrivateKey(vchDefaultKey), + "the derived default key is stored in wallet.dat") ? 0 : 1; + std::vector vchBefore = vchHDMaster; nFail += Check(!SetHDSeedFromMnemonic("not a mnemonic at all", strError), "an invalid phrase is refused") ? 0 : 1; @@ -378,8 +403,6 @@ static int RunWalletHDSelfTest() // With a seed installed the pool must be derived from it, and the // counter must move exactly once per key. SetHDSeedFromMnemonic(strPhraseA, strError); - CRITICAL_BLOCK(cs_keyPool) - mapKeyPool.clear(); unsigned int nNextBefore = nHDNext; TopUpKeyPool(); -- cgit v1.2.3