wallet: persist the mnemonic entropy in wallet.dat

Adds the record pair for the entropy, mirroring "hdseed"/"chdseed": a plaintext
form, an encrypted form that erases its plaintext counterpart the way
WriteCryptedKey does, and an erase.

Both new types are registered in IsKeyType so -salvagewallet preserves them.
Without that, salvage would silently drop the phrase while keeping the wallet
otherwise intact.

The records are defined but nothing writes them yet.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-23 06:30:18 +02:00
parent 2a7fdcc1db
commit 3caec548ae
2 changed files with 28 additions and 0 deletions

View File

@@ -871,6 +871,10 @@ static bool IsKeyType(string strType)
{
return (strType == "key" || strType == "wkey" ||
strType == "hdseed" || strType == "chdseed" ||
// The mnemonic entropy must survive a keys-only salvage: without it
// a recovered wallet keeps its seed (and stays fully spendable) but
// silently loses the ability to reprint its seed phrase.
strType == "mnementropy" || strType == "cmnementropy" ||
strType == "zkey" || strType == "czkey" ||
strType == "sapzkey" || strType == "csapzkey" ||
strType == "vkey" ||
@@ -1345,6 +1349,24 @@ bool CWalletDB::EraseHDSeed(const uint256& seedFp)
return Erase(std::make_pair(std::string("hdseed"), seedFp));
}
bool CWalletDB::WriteMnemonicEntropy(const uint256& entropyFp, const RawHDSeed& entropy)
{
nWalletDBUpdated++;
return Write(std::make_pair(std::string("mnementropy"), entropyFp), entropy);
}
bool CWalletDB::WriteCryptedMnemonicEntropy(const uint256& entropyFp, const std::vector<unsigned char>& vchCryptedSecret)
{
nWalletDBUpdated++;
return Write(std::make_pair(std::string("cmnementropy"), entropyFp), vchCryptedSecret);
}
bool CWalletDB::EraseMnemonicEntropy(const uint256& entropyFp)
{
nWalletDBUpdated++;
return Erase(std::make_pair(std::string("mnementropy"), entropyFp));
}
bool CWalletDB::WriteHDChain(const CHDChain& chain)
{
nWalletDBUpdated++;

View File

@@ -227,6 +227,12 @@ public:
//! CWallet::EncryptWallet) copies whatever records still exist into the new
//! file, so a leftover "hdseed" leaves the unencrypted seed on disk forever.
bool EraseHDSeed(const uint256& seedFp);
//! BIP39 entropy for a phrase-recoverable wallet. Display-only: derivation
//! never reads it. Record names are deliberately distinct prefixes from
//! "hdseed"/"chdseed" so they cannot collide.
bool WriteMnemonicEntropy(const uint256& entropyFp, const RawHDSeed& entropy);
bool WriteCryptedMnemonicEntropy(const uint256& entropyFp, const std::vector<unsigned char>& vchCryptedSecret);
bool EraseMnemonicEntropy(const uint256& entropyFp);
//! write the hdchain model (external chain child index counter)
bool WriteHDChain(const CHDChain& chain);