From 3caec548ae1e4c3a173411da9dc74f038f5792ec Mon Sep 17 00:00:00 2001 From: DanS Date: Sun, 23 Aug 2026 06:30:18 +0200 Subject: [PATCH] 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) --- src/wallet/walletdb.cpp | 22 ++++++++++++++++++++++ src/wallet/walletdb.h | 6 ++++++ 2 files changed, 28 insertions(+) diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 4f159f9a1..eb144bd25 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -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& 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++; diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 6f08c6018..a219ec855 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -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& vchCryptedSecret); + bool EraseMnemonicEntropy(const uint256& entropyFp); //! write the hdchain model (external chain child index counter) bool WriteHDChain(const CHDChain& chain);