Add BIP39 seed phrases (SilentDragonXLite-compatible) and HD transparent keys

Derive transparent (t-addr) keys from the HD seed and add BIP39 mnemonic seed
phrases that are byte-for-byte compatible with SilentDragonXLite, so the same
24 words recover the same shielded and transparent addresses in either wallet.

HD transparent keys:
- Derive t-keys from the seed at m/44'/coin'/0'/0/i (were random CKeys).
- CHDChain gains a version-gated transparent counter; existing wallets load
  unchanged. GenerateNewKey routes through DeriveNewChildKey when enabled
  (-hdtransparent, default on).
- Restore from a seed hex via -hdseed with gap-limit pre-derivation; birthday
  pinned to genesis so the rescan is not clipped.

BIP39 seed phrases:
- Wire the vendored trezor BIP39 lib (src/crypto/bip39) into the build, fix its
  BIP39_WORDS guard, and disable the insecure mnemonic cache.
- Match SDXLite exactly: English wordlist, empty passphrase, PBKDF2 64-byte
  seed, coin type 141, ZIP-32 m/32'/141'/i' and BIP44 m/44'/141'/0'/0/i. Store
  the 32-byte entropy and expand to the 64-byte seed on demand.
- Restore via -mnemonic, create via -usemnemonic, reveal via z_exportmnemonic.

Verified by gtests including a known-answer BIP39 seed vector and z/t address
derivation checks (src/gtest/test_hdtransparent.cpp, test_mnemonic_compat.cpp).
Docs in doc/hd-transparent-keys.md and doc/seed-phrase.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-06 01:57:18 -05:00
parent 84aefb5475
commit 4caf2fc68f
23 changed files with 1044 additions and 22 deletions

View File

@@ -1063,6 +1063,9 @@ public:
* Generate a new key
*/
CPubKey GenerateNewKey();
//! Derive a new transparent key from the HD seed along the BIP44 external
//! chain m/44'/coin_type'/0'/0/i, advancing hdChain.transparentChildCounter.
void DeriveNewChildKey(CKeyMetadata& metadata, CKey& secretRet);
//! Adds a key to the store, and saves it to disk.
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey);
//! Adds a key to the store, without saving it to disk (used by LoadWallet)
@@ -1294,6 +1297,10 @@ public:
/* Returns true if HD is enabled for all address types, false if only for Sapling */
bool IsHDFullyEnabled() const;
/* Returns true if transparent keys should be HD-derived from the seed.
Requires an HD seed and the -hdtransparent option (default on). */
bool IsHDTransparentEnabled() const;
/* Generates a new HD seed (will reset the chain child index counters)
Sets the seed's version based on the current wallet version (so the
caller must ensure the current wallet version is correct before calling
@@ -1303,6 +1310,41 @@ public:
bool SetHDSeed(const HDSeed& seed);
bool SetCryptedHDSeed(const uint256& seedFp, const std::vector<unsigned char> &vchCryptedSecret);
/* Restore a wallet's HD seed from a hex string (as exported in the
z_exportwallet "# HDSeed=" comment): 32 bytes for a legacy raw seed, or
64 bytes for a BIP39-derived seed. Only succeeds on a wallet that has no
seed yet. Sets the chain birthday to genesis so a rescan finds all
historical (coinbase) funds. Returns false on bad input or existing seed. */
bool SetHDSeedFromHex(const std::string& seedHex);
/* Restore/create a wallet from a BIP39 mnemonic phrase, byte-compatible with
SilentDragonXLite: stores the 32-byte entropy, marks the chain mnemonic,
and derives the 64-byte BIP39 seed on demand. Only succeeds on a wallet
with no seed yet. Returns false on an invalid phrase or existing seed. */
bool SetHDSeedFromMnemonic(const std::string& phrase);
/* Return the wallet's 24-word BIP39 recovery phrase, if this is a mnemonic
wallet and the seed is available (unlocked). Returns false otherwise. */
bool GetMnemonicPhrase(std::string& phraseOut) const;
/* True if the HD seed was derived from a BIP39 mnemonic (stored as entropy). */
bool IsMnemonicSeed() const { return hdChain.fMnemonicSeed; }
/* Return the seed to feed into HD derivation. For mnemonic wallets this
expands the stored 32-byte entropy into the 64-byte BIP39 seed; for legacy
wallets it is the stored seed unchanged. Use this everywhere keys/OVKs are
derived so behaviour matches SilentDragonXLite. */
bool GetHDSeedForDerivation(HDSeed& seedOut) const;
/* Shared tail of the seed-install paths: stores `seed` and a fresh CHDChain
(mnemonic vs raw) with the given birthday. Caller must hold cs_wallet. */
bool InstallHDSeed(const HDSeed& seed, bool fMnemonic, int64_t nCreateTime);
/* Pre-derive `count` HD transparent keys (external chain) into the keystore,
stamped with creation time `nBirthday`, so a subsequent rescan can find
funds paid to them after a seed-only restore. */
void TopUpHDTransparentKeys(unsigned int count, int64_t nBirthday);
/* Set the HD chain model (chain child index counters) */
void SetHDChain(const CHDChain& chain, bool memonly);
const CHDChain& GetHDChain() const { return hdChain; }