From ce59d8c72c667ce638bd12f37db26d8b3408a5d2 Mon Sep 17 00:00:00 2001 From: Bitflash Date: Sat, 1 Aug 2026 17:53:17 -0300 Subject: wallet: derive the key pool from a seed, so a phrase could bring it back (#93) Adds BIP32 derivation behind the wallet key pool, with no user-facing way to create a seed yet. wallet.dat gains hdmaster/hdchaincode/hdnext; the mnemonic is never stored. TopUpKeyPool derives m/n' when a seed exists and falls back to a random key, with a log line, if derivation fails. New -selftest=wallet-hd, wired into make tests on both platforms. --- src/main.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'src/main.h') diff --git a/src/main.h b/src/main.h index 8d9b7e6..a04a4e3 100644 --- a/src/main.h +++ b/src/main.h @@ -141,6 +141,33 @@ static const int KEYPOOL_SIZE = 100; extern CCriticalSection cs_keyPool; extern map > mapKeyPool; +// --- Deterministic wallet (BIP32) ----------------------------------------- +// +// When a seed is present the key pool is derived from it -- m/0'/n, hardened, +// n increasing -- instead of being made of random keys. That is what lets a +// recovery phrase bring a wallet back: the same twelve words reproduce the same +// keys in the same order. +// +// A wallet without a seed keeps working exactly as before. Nothing here +// migrates an existing wallet on its own: the seed is created only when the +// user asks for a phrase and confirms they have written it down, because a +// phrase generated silently is a backup nobody has. +// +// Empty until a seed exists. +extern vector vchHDMaster; // 32-byte master private key (IL) +extern vector vchHDChainCode; // 32 bytes (IR) +extern unsigned int nHDNext; // next child index to derive +inline bool HaveHDSeed() { return vchHDMaster.size() == 32 && vchHDChainCode.size() == 32; } + +// Install a seed derived from a mnemonic, replacing any existing one, and reset +// the derivation counter. Writes to wallet.dat. Returns false and leaves the +// wallet untouched if the phrase is not valid. +bool SetHDSeedFromMnemonic(const string& strMnemonic, string& strErrorRet); + +// Derive the child at nIndex and return it as a key. Used by the key pool and +// by restore, which needs to run ahead of the pool. +bool DeriveHDKey(unsigned int nIndex, CKey& keyRet, string& strErrorRet); + // Fill the pool back up to KEYPOOL_SIZE. Every key it creates is written to // wallet.dat before it is offered to anybody. void TopUpKeyPool(); @@ -156,6 +183,7 @@ void ReacceptWalletTransactions(); // a key this wallet holds. Returns how many were added or updated. Needed by // anything that puts a key into the wallet after the fact -- an import, a // restored backup, and later a recovery phrase. +bool CanScanWalletTransactions(string& strErrorRet); int ScanForWalletTransactions(CBlockIndex* pindexStart); // Returns how many wallet transactions the chain corrected. int RescanSpentFlags(); -- cgit v1.2.3