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>
88 lines
3.8 KiB
Markdown
88 lines
3.8 KiB
Markdown
# HD transparent keys
|
|
|
|
DragonX derives **transparent** (t-address) keys deterministically from the
|
|
wallet's HD seed, so they can be recovered from the seed alone — the same way
|
|
Sapling (shielded) keys already are.
|
|
|
|
## Derivation
|
|
|
|
Transparent keys are derived over secp256k1 using BIP32/BIP44:
|
|
|
|
```
|
|
m / 44' / coin_type' / 0' / 0 / i
|
|
```
|
|
|
|
* `coin_type` is `Params().BIP44CoinType()` — **141** on mainnet, **1** on
|
|
test/regtest.
|
|
* Account is fixed at `0'` and the chain at `0` (external). The internal/change
|
|
chain (`1`) is **not** used: on this `ac_private=1` chain a non-coinbase
|
|
transparent output is consensus-invalid, so transparent change can never carry
|
|
value.
|
|
* `i` is `CHDChain.transparentChildCounter`, a monotonic index persisted in the
|
|
wallet so the same addresses regenerate after a seed-only restore.
|
|
|
|
Each derived key records its `hdKeypath` and the seed fingerprint (`seedFp`) in
|
|
its `CKeyMetadata`, matching the Sapling scheme.
|
|
|
|
## Why this matters on a private chain
|
|
|
|
On DragonX (`ac_private=1` from genesis) a normal user can never *receive* to a
|
|
transparent address — inbound t-payments are rejected by consensus. The only
|
|
thing that legitimately lands spendable value on a t-address is a **mining
|
|
coinbase** (plus notary/burn special cases). There is no "coinbase must be
|
|
shielded" rule, so mature coinbase is directly spendable.
|
|
|
|
So HD transparent keys exist to let a **miner recover coinbase rewards** that
|
|
were paid to wallet-derived t-addresses, using only the seed.
|
|
|
|
## Enabling / disabling
|
|
|
|
Controlled by `-hdtransparent` (default **on**). When on and the wallet has an
|
|
HD seed, every newly generated transparent key (receive address, change,
|
|
coinbase payout drawn from the keypool) is HD-derived.
|
|
|
|
```
|
|
-hdtransparent=0 # keep the legacy behaviour (random transparent keys)
|
|
```
|
|
|
|
## Backing up and restoring
|
|
|
|
* **Back up the seed.** `z_exportwallet <file>` writes the 32-byte HD seed as a
|
|
`# HDSeed=<hex>` line. Guard this value like a private key.
|
|
* **Restore into a fresh/empty wallet** by starting the node with:
|
|
|
|
```
|
|
-hdseed=<64-hex-character seed>
|
|
-hdtransparentgaplimit=<n> # HD transparent keys to pre-derive (default 1000)
|
|
```
|
|
|
|
On restore the node injects the seed, pre-derives `n` transparent keys with a
|
|
genesis birthday, and the normal startup rescan finds any coinbase paid to
|
|
them. Raise `-hdtransparentgaplimit` if the wallet minted more than `n`
|
|
distinct coinbase addresses.
|
|
|
|
> **Warning:** passing `-hdseed` on the command line exposes the seed to your
|
|
> shell history and the process list. Prefer putting it in `DRAGONX.conf` with
|
|
> tight file permissions, and remove it after the restore completes.
|
|
|
|
## Limitations (read before relying on recovery)
|
|
|
|
* **Legacy random keys are not recoverable.** Any transparent key created before
|
|
this feature (or with `-hdtransparent=0`) came from the CSPRNG, not the seed,
|
|
and the phrase/seed will **not** regenerate it. Keep `wallet.dat` /
|
|
`dumpwallet` backups for those. A wallet that predates the feature and then
|
|
enables it becomes a *mix* of random (old) and HD (new) keys.
|
|
* **Gap limit.** A rescan only discovers keys already present in the wallet.
|
|
Restore pre-derives `-hdtransparentgaplimit` keys; coinbase paid to an index
|
|
beyond that window is not found until you derive further and rescan again.
|
|
* **Scope.** Recovers transparent **coinbase** value only, per the consensus
|
|
rules above. Shielded funds are recovered separately via the Sapling HD keys.
|
|
|
|
## On-disk compatibility
|
|
|
|
The transparent counter is stored in `CHDChain` under a new serialization
|
|
version (`VERSION_HD_TRANSPARENT = 2`). Existing v1 `wallet.dat` records load
|
|
unchanged (the counter defaults to 0); the record is rewritten as v2 the first
|
|
time an HD transparent key is derived. Downgrading a v2 wallet to an older
|
|
binary is not supported.
|