diff options
| author | Bitflash <btf01@proton.me> | 2026-08-01 17:53:17 -0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-08-01 17:53:17 -0300 |
| commit | ce59d8c72c667ce638bd12f37db26d8b3408a5d2 (patch) | |
| tree | 95c62a0d0cc0a21bc3349264ce0a3c949deb32c3 /src/key.h | |
| parent | 593cf346dbce0a8ce46a13a73a4f78ddd044ab52 (diff) | |
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.
Diffstat (limited to 'src/key.h')
| -rw-r--r-- | src/key.h | 34 |
1 files changed, 34 insertions, 0 deletions
| @@ -86,6 +86,40 @@ public: | |||
| 86 | return true; | 86 | return true; |
| 87 | } | 87 | } |
| 88 | 88 | ||
| 89 | // Load a raw 32-byte scalar, the form BIP32 derivation produces. | ||
| 90 | // | ||
| 91 | // SetPrivKey above takes DER and cannot be used for this: handing it a | ||
| 92 | // scalar makes d2i_ECPrivateKey fail, and if the caller ignores the return | ||
| 93 | // value -- as one attempt at this did -- the key is left with no material | ||
| 94 | // at all and the address it produces has no private half. Coins paid there | ||
| 95 | // are gone. Hence a separate entry point rather than a looser SetPrivKey. | ||
| 96 | // | ||
| 97 | // The public half is computed here rather than read from anywhere: | ||
| 98 | // pub = G * secret. | ||
| 99 | bool SetSecret(const std::vector<unsigned char>& vchSecret) | ||
| 100 | { | ||
| 101 | if (vchSecret.size() != 32) | ||
| 102 | return false; | ||
| 103 | |||
| 104 | bool fOk = false; | ||
| 105 | BIGNUM* bn = BN_bin2bn(&vchSecret[0], 32, NULL); | ||
| 106 | EC_POINT* pubkey = NULL; | ||
| 107 | if (bn && EC_KEY_set_private_key(pkey, bn)) | ||
| 108 | { | ||
| 109 | const EC_GROUP* group = EC_KEY_get0_group(pkey); | ||
| 110 | pubkey = EC_POINT_new(group); | ||
| 111 | if (pubkey && | ||
| 112 | EC_POINT_mul(group, pubkey, bn, NULL, NULL, NULL) && | ||
| 113 | EC_KEY_set_public_key(pkey, pubkey)) | ||
| 114 | fOk = true; | ||
| 115 | } | ||
| 116 | if (pubkey) | ||
| 117 | EC_POINT_free(pubkey); | ||
| 118 | if (bn) | ||
| 119 | BN_clear_free(bn); // it held the secret | ||
| 120 | return fOk; | ||
| 121 | } | ||
| 122 | |||
| 89 | CPrivKey GetPrivKey() const | 123 | CPrivKey GetPrivKey() const |
| 90 | { | 124 | { |
| 91 | unsigned int nSize = i2d_ECPrivateKey(pkey, NULL); | 125 | unsigned int nSize = i2d_ECPrivateKey(pkey, NULL); |