Merge #12924: Fix hdmaster-key / seed-key confusion (scripted diff)
6249021d1
[docs] Add release notes for HD master key -> HD seed rename (John Newbery)79053a5f2
[rpc] [wallet] Add 'hdmasterkeyid' alias return values. (John Newbery)c75c35141
[refactor] manually change remaining instances of master key to seed. (John Newbery)131d4450b
scripted-diff: Rename master key to seed (John Newbery) Pull request description: Addresses #12084 and #8684 This renames a couple of functions and members (no functional changes, expect log prints): - Rename CKey::SetMaster to CKey::SetSeed - Rename CHDChain::masterKeyId to CHDChain::seedID - Rename CHDChain::hdMasterKeyID to CHDChain::hdSeedID - Rename CWallet::GenerateNewHDMasterKey to CWallet::GenerateNewHDSeed - Rename CWallet::SetHDMasterKey to CWallet::SetHDSeed As well it introduces a tiny API change: - RPC API change: Rename "hdmasterkeyid" to "hdseedid", rename "hdmaster" in wallet-dump output to "hdseed" Fixes also a bug: - Bugfix: use "s" instead of the incorrect "m" for the seed-key hd-keypath key metadata Tree-SHA512: c913252636f213135a3b64df5de5d21844fb9c2d646567c1aad0ec65745188587de26119de99492c67e559bd49fdd9606b54276f00dddb84301785beba58f281
This commit is contained in:
commit
6738813bcb
12 changed files with 98 additions and 76 deletions
12
doc/release-notes-pr12924.md
Normal file
12
doc/release-notes-pr12924.md
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
RPC changes
|
||||||
|
------------
|
||||||
|
|
||||||
|
### Low-level changes
|
||||||
|
|
||||||
|
- The `getwalletinfo` RPC method now returns an `hdseedid` value, which is always the same as the incorrectly-named `hdmasterkeyid` value. `hdmasterkeyid` will be removed in V0.18.
|
||||||
|
- The `getaddressinfo` RPC method now returns an `hdseedid` value, which is always the same as the incorrectly-named `hdmasterkeyid` value. `hdmasterkeyid` will be removed in V0.18.
|
||||||
|
|
||||||
|
Other API changes
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
- The `inactivehdmaster` property in the `dumpwallet` output has been corrected to `inactivehdseed`
|
|
@ -273,7 +273,7 @@ bool CExtKey::Derive(CExtKey &out, unsigned int _nChild) const {
|
||||||
return key.Derive(out.key, out.chaincode, _nChild, chaincode);
|
return key.Derive(out.key, out.chaincode, _nChild, chaincode);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CExtKey::SetMaster(const unsigned char *seed, unsigned int nSeedLen) {
|
void CExtKey::SetSeed(const unsigned char *seed, unsigned int nSeedLen) {
|
||||||
static const unsigned char hashkey[] = {'B','i','t','c','o','i','n',' ','s','e','e','d'};
|
static const unsigned char hashkey[] = {'B','i','t','c','o','i','n',' ','s','e','e','d'};
|
||||||
std::vector<unsigned char, secure_allocator<unsigned char>> vout(64);
|
std::vector<unsigned char, secure_allocator<unsigned char>> vout(64);
|
||||||
CHMAC_SHA512(hashkey, sizeof(hashkey)).Write(seed, nSeedLen).Finalize(vout.data());
|
CHMAC_SHA512(hashkey, sizeof(hashkey)).Write(seed, nSeedLen).Finalize(vout.data());
|
||||||
|
|
|
@ -158,7 +158,7 @@ struct CExtKey {
|
||||||
void Decode(const unsigned char code[BIP32_EXTKEY_SIZE]);
|
void Decode(const unsigned char code[BIP32_EXTKEY_SIZE]);
|
||||||
bool Derive(CExtKey& out, unsigned int nChild) const;
|
bool Derive(CExtKey& out, unsigned int nChild) const;
|
||||||
CExtPubKey Neuter() const;
|
CExtPubKey Neuter() const;
|
||||||
void SetMaster(const unsigned char* seed, unsigned int nSeedLen);
|
void SetSeed(const unsigned char* seed, unsigned int nSeedLen);
|
||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
void Serialize(Stream& s) const
|
void Serialize(Stream& s) const
|
||||||
{
|
{
|
||||||
|
|
|
@ -91,7 +91,7 @@ static void RunTest(const TestVector &test) {
|
||||||
std::vector<unsigned char> seed = ParseHex(test.strHexMaster);
|
std::vector<unsigned char> seed = ParseHex(test.strHexMaster);
|
||||||
CExtKey key;
|
CExtKey key;
|
||||||
CExtPubKey pubkey;
|
CExtPubKey pubkey;
|
||||||
key.SetMaster(seed.data(), seed.size());
|
key.SetSeed(seed.data(), seed.size());
|
||||||
pubkey = key.Neuter();
|
pubkey = key.Neuter();
|
||||||
for (const TestDerivation &derive : test.vDerive) {
|
for (const TestDerivation &derive : test.vDerive) {
|
||||||
unsigned char data[74];
|
unsigned char data[74];
|
||||||
|
|
|
@ -752,13 +752,13 @@ UniValue dumpwallet(const JSONRPCRequest& request)
|
||||||
file << "\n";
|
file << "\n";
|
||||||
|
|
||||||
// add the base58check encoded extended master if the wallet uses HD
|
// add the base58check encoded extended master if the wallet uses HD
|
||||||
CKeyID masterKeyID = pwallet->GetHDChain().masterKeyID;
|
CKeyID seed_id = pwallet->GetHDChain().seed_id;
|
||||||
if (!masterKeyID.IsNull())
|
if (!seed_id.IsNull())
|
||||||
{
|
{
|
||||||
CKey key;
|
CKey seed;
|
||||||
if (pwallet->GetKey(masterKeyID, key)) {
|
if (pwallet->GetKey(seed_id, seed)) {
|
||||||
CExtKey masterKey;
|
CExtKey masterKey;
|
||||||
masterKey.SetMaster(key.begin(), key.size());
|
masterKey.SetSeed(seed.begin(), seed.size());
|
||||||
|
|
||||||
file << "# extended private masterkey: " << EncodeExtKey(masterKey) << "\n\n";
|
file << "# extended private masterkey: " << EncodeExtKey(masterKey) << "\n\n";
|
||||||
}
|
}
|
||||||
|
@ -773,12 +773,12 @@ UniValue dumpwallet(const JSONRPCRequest& request)
|
||||||
file << strprintf("%s %s ", EncodeSecret(key), strTime);
|
file << strprintf("%s %s ", EncodeSecret(key), strTime);
|
||||||
if (GetWalletAddressesForKey(pwallet, keyid, strAddr, strLabel)) {
|
if (GetWalletAddressesForKey(pwallet, keyid, strAddr, strLabel)) {
|
||||||
file << strprintf("label=%s", strLabel);
|
file << strprintf("label=%s", strLabel);
|
||||||
} else if (keyid == masterKeyID) {
|
} else if (keyid == seed_id) {
|
||||||
file << "hdmaster=1";
|
file << "hdseed=1";
|
||||||
} else if (mapKeyPool.count(keyid)) {
|
} else if (mapKeyPool.count(keyid)) {
|
||||||
file << "reserve=1";
|
file << "reserve=1";
|
||||||
} else if (pwallet->mapKeyMetadata[keyid].hdKeypath == "m") {
|
} else if (pwallet->mapKeyMetadata[keyid].hdKeypath == "s") {
|
||||||
file << "inactivehdmaster=1";
|
file << "inactivehdseed=1";
|
||||||
} else {
|
} else {
|
||||||
file << "change=1";
|
file << "change=1";
|
||||||
}
|
}
|
||||||
|
|
|
@ -2925,7 +2925,8 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
|
||||||
" \"keypoolsize_hd_internal\": xxxx, (numeric) how many new keys are pre-generated for internal use (used for change outputs, only appears if the wallet is using this feature, otherwise external keys are used)\n"
|
" \"keypoolsize_hd_internal\": xxxx, (numeric) how many new keys are pre-generated for internal use (used for change outputs, only appears if the wallet is using this feature, otherwise external keys are used)\n"
|
||||||
" \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n"
|
" \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n"
|
||||||
" \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n"
|
" \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n"
|
||||||
" \"hdmasterkeyid\": \"<hash160>\" (string, optional) the Hash160 of the HD master pubkey (only present when HD is enabled)\n"
|
" \"hdseedid\": \"<hash160>\" (string, optional) the Hash160 of the HD seed (only present when HD is enabled)\n"
|
||||||
|
" \"hdmasterkeyid\": \"<hash160>\" (string, optional) alias for hdseedid retained for backwards-compatibility. Will be removed in V0.18.\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"\nExamples:\n"
|
"\nExamples:\n"
|
||||||
+ HelpExampleCli("getwalletinfo", "")
|
+ HelpExampleCli("getwalletinfo", "")
|
||||||
|
@ -2949,16 +2950,18 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
|
||||||
obj.pushKV("txcount", (int)pwallet->mapWallet.size());
|
obj.pushKV("txcount", (int)pwallet->mapWallet.size());
|
||||||
obj.pushKV("keypoololdest", pwallet->GetOldestKeyPoolTime());
|
obj.pushKV("keypoololdest", pwallet->GetOldestKeyPoolTime());
|
||||||
obj.pushKV("keypoolsize", (int64_t)kpExternalSize);
|
obj.pushKV("keypoolsize", (int64_t)kpExternalSize);
|
||||||
CKeyID masterKeyID = pwallet->GetHDChain().masterKeyID;
|
CKeyID seed_id = pwallet->GetHDChain().seed_id;
|
||||||
if (!masterKeyID.IsNull() && pwallet->CanSupportFeature(FEATURE_HD_SPLIT)) {
|
if (!seed_id.IsNull() && pwallet->CanSupportFeature(FEATURE_HD_SPLIT)) {
|
||||||
obj.pushKV("keypoolsize_hd_internal", (int64_t)(pwallet->GetKeyPoolSize() - kpExternalSize));
|
obj.pushKV("keypoolsize_hd_internal", (int64_t)(pwallet->GetKeyPoolSize() - kpExternalSize));
|
||||||
}
|
}
|
||||||
if (pwallet->IsCrypted()) {
|
if (pwallet->IsCrypted()) {
|
||||||
obj.pushKV("unlocked_until", pwallet->nRelockTime);
|
obj.pushKV("unlocked_until", pwallet->nRelockTime);
|
||||||
}
|
}
|
||||||
obj.pushKV("paytxfee", ValueFromAmount(pwallet->m_pay_tx_fee.GetFeePerK()));
|
obj.pushKV("paytxfee", ValueFromAmount(pwallet->m_pay_tx_fee.GetFeePerK()));
|
||||||
if (!masterKeyID.IsNull())
|
if (!seed_id.IsNull()) {
|
||||||
obj.pushKV("hdmasterkeyid", masterKeyID.GetHex());
|
obj.pushKV("hdseedid", seed_id.GetHex());
|
||||||
|
obj.pushKV("hdmasterkeyid", seed_id.GetHex());
|
||||||
|
}
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3948,13 +3951,14 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
|
||||||
" ]\n"
|
" ]\n"
|
||||||
" \"sigsrequired\" : xxxxx (numeric, optional) Number of signatures required to spend multisig output (only if \"script\" is \"multisig\")\n"
|
" \"sigsrequired\" : xxxxx (numeric, optional) Number of signatures required to spend multisig output (only if \"script\" is \"multisig\")\n"
|
||||||
" \"pubkey\" : \"publickeyhex\", (string, optional) The hex value of the raw public key, for single-key addresses (possibly embedded in P2SH or P2WSH)\n"
|
" \"pubkey\" : \"publickeyhex\", (string, optional) The hex value of the raw public key, for single-key addresses (possibly embedded in P2SH or P2WSH)\n"
|
||||||
" \"embedded\" : {...}, (object, optional) Information about the address embedded in P2SH or P2WSH, if relevant and known. It includes all getaddressinfo output fields for the embedded address, excluding metadata (\"timestamp\", \"hdkeypath\", \"hdmasterkeyid\") and relation to the wallet (\"ismine\", \"iswatchonly\", \"account\").\n"
|
" \"embedded\" : {...}, (object, optional) Information about the address embedded in P2SH or P2WSH, if relevant and known. It includes all getaddressinfo output fields for the embedded address, excluding metadata (\"timestamp\", \"hdkeypath\", \"hdseedid\") and relation to the wallet (\"ismine\", \"iswatchonly\", \"account\").\n"
|
||||||
" \"iscompressed\" : true|false, (boolean) If the address is compressed\n"
|
" \"iscompressed\" : true|false, (boolean) If the address is compressed\n"
|
||||||
" \"label\" : \"label\" (string) The label associated with the address, \"\" is the default account\n"
|
" \"label\" : \"label\" (string) The label associated with the address, \"\" is the default account\n"
|
||||||
" \"account\" : \"account\" (string) DEPRECATED. This field will be removed in V0.18. To see this deprecated field, start bitcoind with -deprecatedrpc=accounts. The account associated with the address, \"\" is the default account\n"
|
" \"account\" : \"account\" (string) DEPRECATED. This field will be removed in V0.18. To see this deprecated field, start bitcoind with -deprecatedrpc=accounts. The account associated with the address, \"\" is the default account\n"
|
||||||
" \"timestamp\" : timestamp, (number, optional) The creation time of the key if available in seconds since epoch (Jan 1 1970 GMT)\n"
|
" \"timestamp\" : timestamp, (number, optional) The creation time of the key if available in seconds since epoch (Jan 1 1970 GMT)\n"
|
||||||
" \"hdkeypath\" : \"keypath\" (string, optional) The HD keypath if the key is HD and available\n"
|
" \"hdkeypath\" : \"keypath\" (string, optional) The HD keypath if the key is HD and available\n"
|
||||||
" \"hdmasterkeyid\" : \"<hash160>\" (string, optional) The Hash160 of the HD master pubkey\n"
|
" \"hdseedid\" : \"<hash160>\" (string, optional) The Hash160 of the HD seed\n"
|
||||||
|
" \"hdmasterkeyid\" : \"<hash160>\" (string, optional) alias for hdseedid maintained for backwards compatibility. Will be removed in V0.18.\n"
|
||||||
" \"labels\" (object) Array of labels associated with the address.\n"
|
" \"labels\" (object) Array of labels associated with the address.\n"
|
||||||
" [\n"
|
" [\n"
|
||||||
" { (json object of label data)\n"
|
" { (json object of label data)\n"
|
||||||
|
@ -4014,7 +4018,8 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
|
||||||
ret.pushKV("timestamp", meta->nCreateTime);
|
ret.pushKV("timestamp", meta->nCreateTime);
|
||||||
if (!meta->hdKeypath.empty()) {
|
if (!meta->hdKeypath.empty()) {
|
||||||
ret.pushKV("hdkeypath", meta->hdKeypath);
|
ret.pushKV("hdkeypath", meta->hdKeypath);
|
||||||
ret.pushKV("hdmasterkeyid", meta->hdMasterKeyID.GetHex());
|
ret.pushKV("hdseedid", meta->hd_seed_id.GetHex());
|
||||||
|
ret.pushKV("hdmasterkeyid", meta->hd_seed_id.GetHex());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4147,7 +4152,7 @@ UniValue sethdseed(const JSONRPCRequest& request)
|
||||||
" If false, addresses (including change addresses if the wallet already had HD Chain Split enabled) from the existing\n"
|
" If false, addresses (including change addresses if the wallet already had HD Chain Split enabled) from the existing\n"
|
||||||
" keypool will be used until it has been depleted.\n"
|
" keypool will be used until it has been depleted.\n"
|
||||||
"2. \"seed\" (string, optional) The WIF private key to use as the new HD seed; if not provided a random seed will be used.\n"
|
"2. \"seed\" (string, optional) The WIF private key to use as the new HD seed; if not provided a random seed will be used.\n"
|
||||||
" The seed value can be retrieved using the dumpwallet command. It is the private key marked hdmaster=1\n"
|
" The seed value can be retrieved using the dumpwallet command. It is the private key marked hdseed=1\n"
|
||||||
"\nExamples:\n"
|
"\nExamples:\n"
|
||||||
+ HelpExampleCli("sethdseed", "")
|
+ HelpExampleCli("sethdseed", "")
|
||||||
+ HelpExampleCli("sethdseed", "false")
|
+ HelpExampleCli("sethdseed", "false")
|
||||||
|
@ -4176,7 +4181,7 @@ UniValue sethdseed(const JSONRPCRequest& request)
|
||||||
|
|
||||||
CPubKey master_pub_key;
|
CPubKey master_pub_key;
|
||||||
if (request.params[1].isNull()) {
|
if (request.params[1].isNull()) {
|
||||||
master_pub_key = pwallet->GenerateNewHDMasterKey();
|
master_pub_key = pwallet->GenerateNewSeed();
|
||||||
} else {
|
} else {
|
||||||
CKey key = DecodeSecret(request.params[1].get_str());
|
CKey key = DecodeSecret(request.params[1].get_str());
|
||||||
if (!key.IsValid()) {
|
if (!key.IsValid()) {
|
||||||
|
@ -4187,10 +4192,10 @@ UniValue sethdseed(const JSONRPCRequest& request)
|
||||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key (either as an HD seed or as a loose private key)");
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key (either as an HD seed or as a loose private key)");
|
||||||
}
|
}
|
||||||
|
|
||||||
master_pub_key = pwallet->DeriveNewMasterHDKey(key);
|
master_pub_key = pwallet->DeriveNewSeed(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
pwallet->SetHDMasterKey(master_pub_key);
|
pwallet->SetHDSeed(master_pub_key);
|
||||||
if (flush_key_pool) pwallet->NewKeyPool();
|
if (flush_key_pool) pwallet->NewKeyPool();
|
||||||
|
|
||||||
return NullUniValue;
|
return NullUniValue;
|
||||||
|
|
|
@ -191,17 +191,17 @@ CPubKey CWallet::GenerateNewKey(WalletBatch &batch, bool internal)
|
||||||
void CWallet::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey& secret, bool internal)
|
void CWallet::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey& secret, bool internal)
|
||||||
{
|
{
|
||||||
// for now we use a fixed keypath scheme of m/0'/0'/k
|
// for now we use a fixed keypath scheme of m/0'/0'/k
|
||||||
CKey key; //master key seed (256bit)
|
CKey seed; //seed (256bit)
|
||||||
CExtKey masterKey; //hd master key
|
CExtKey masterKey; //hd master key
|
||||||
CExtKey accountKey; //key at m/0'
|
CExtKey accountKey; //key at m/0'
|
||||||
CExtKey chainChildKey; //key at m/0'/0' (external) or m/0'/1' (internal)
|
CExtKey chainChildKey; //key at m/0'/0' (external) or m/0'/1' (internal)
|
||||||
CExtKey childKey; //key at m/0'/0'/<n>'
|
CExtKey childKey; //key at m/0'/0'/<n>'
|
||||||
|
|
||||||
// try to get the master key
|
// try to get the seed
|
||||||
if (!GetKey(hdChain.masterKeyID, key))
|
if (!GetKey(hdChain.seed_id, seed))
|
||||||
throw std::runtime_error(std::string(__func__) + ": Master key not found");
|
throw std::runtime_error(std::string(__func__) + ": seed not found");
|
||||||
|
|
||||||
masterKey.SetMaster(key.begin(), key.size());
|
masterKey.SetSeed(seed.begin(), seed.size());
|
||||||
|
|
||||||
// derive m/0'
|
// derive m/0'
|
||||||
// use hardened derivation (child keys >= 0x80000000 are hardened after bip32)
|
// use hardened derivation (child keys >= 0x80000000 are hardened after bip32)
|
||||||
|
@ -228,7 +228,7 @@ void CWallet::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey
|
||||||
}
|
}
|
||||||
} while (HaveKey(childKey.key.GetPubKey().GetID()));
|
} while (HaveKey(childKey.key.GetPubKey().GetID()));
|
||||||
secret = childKey.key;
|
secret = childKey.key;
|
||||||
metadata.hdMasterKeyID = hdChain.masterKeyID;
|
metadata.hd_seed_id = hdChain.seed_id;
|
||||||
// update the chain model in the database
|
// update the chain model in the database
|
||||||
if (!batch.WriteHDChain(hdChain))
|
if (!batch.WriteHDChain(hdChain))
|
||||||
throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed");
|
throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed");
|
||||||
|
@ -689,9 +689,9 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
|
||||||
Lock();
|
Lock();
|
||||||
Unlock(strWalletPassphrase);
|
Unlock(strWalletPassphrase);
|
||||||
|
|
||||||
// if we are using HD, replace the HD master key (seed) with a new one
|
// if we are using HD, replace the HD seed with a new one
|
||||||
if (IsHDEnabled()) {
|
if (IsHDEnabled()) {
|
||||||
if (!SetHDMasterKey(GenerateNewHDMasterKey())) {
|
if (!SetHDSeed(GenerateNewSeed())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1450,41 +1450,41 @@ CAmount CWallet::GetChange(const CTransaction& tx) const
|
||||||
return nChange;
|
return nChange;
|
||||||
}
|
}
|
||||||
|
|
||||||
CPubKey CWallet::GenerateNewHDMasterKey()
|
CPubKey CWallet::GenerateNewSeed()
|
||||||
{
|
{
|
||||||
CKey key;
|
CKey key;
|
||||||
key.MakeNewKey(true);
|
key.MakeNewKey(true);
|
||||||
return DeriveNewMasterHDKey(key);
|
return DeriveNewSeed(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
CPubKey CWallet::DeriveNewMasterHDKey(const CKey& key)
|
CPubKey CWallet::DeriveNewSeed(const CKey& key)
|
||||||
{
|
{
|
||||||
int64_t nCreationTime = GetTime();
|
int64_t nCreationTime = GetTime();
|
||||||
CKeyMetadata metadata(nCreationTime);
|
CKeyMetadata metadata(nCreationTime);
|
||||||
|
|
||||||
// calculate the pubkey
|
// calculate the seed
|
||||||
CPubKey pubkey = key.GetPubKey();
|
CPubKey seed = key.GetPubKey();
|
||||||
assert(key.VerifyPubKey(pubkey));
|
assert(key.VerifyPubKey(seed));
|
||||||
|
|
||||||
// set the hd keypath to "m" -> Master, refers the masterkeyid to itself
|
// set the hd keypath to "s" -> Seed, refers the seed to itself
|
||||||
metadata.hdKeypath = "m";
|
metadata.hdKeypath = "s";
|
||||||
metadata.hdMasterKeyID = pubkey.GetID();
|
metadata.hd_seed_id = seed.GetID();
|
||||||
|
|
||||||
{
|
{
|
||||||
LOCK(cs_wallet);
|
LOCK(cs_wallet);
|
||||||
|
|
||||||
// mem store the metadata
|
// mem store the metadata
|
||||||
mapKeyMetadata[pubkey.GetID()] = metadata;
|
mapKeyMetadata[seed.GetID()] = metadata;
|
||||||
|
|
||||||
// write the key&metadata to the database
|
// write the key&metadata to the database
|
||||||
if (!AddKeyPubKey(key, pubkey))
|
if (!AddKeyPubKey(key, seed))
|
||||||
throw std::runtime_error(std::string(__func__) + ": AddKeyPubKey failed");
|
throw std::runtime_error(std::string(__func__) + ": AddKeyPubKey failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
return pubkey;
|
return seed;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CWallet::SetHDMasterKey(const CPubKey& pubkey)
|
bool CWallet::SetHDSeed(const CPubKey& seed)
|
||||||
{
|
{
|
||||||
LOCK(cs_wallet);
|
LOCK(cs_wallet);
|
||||||
// store the keyid (hash160) together with
|
// store the keyid (hash160) together with
|
||||||
|
@ -1492,7 +1492,7 @@ bool CWallet::SetHDMasterKey(const CPubKey& pubkey)
|
||||||
// as a hdchain object
|
// as a hdchain object
|
||||||
CHDChain newHdChain;
|
CHDChain newHdChain;
|
||||||
newHdChain.nVersion = CanSupportFeature(FEATURE_HD_SPLIT) ? CHDChain::VERSION_HD_CHAIN_SPLIT : CHDChain::VERSION_HD_BASE;
|
newHdChain.nVersion = CanSupportFeature(FEATURE_HD_SPLIT) ? CHDChain::VERSION_HD_CHAIN_SPLIT : CHDChain::VERSION_HD_BASE;
|
||||||
newHdChain.masterKeyID = pubkey.GetID();
|
newHdChain.seed_id = seed.GetID();
|
||||||
SetHDChain(newHdChain, false);
|
SetHDChain(newHdChain, false);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -1510,7 +1510,7 @@ bool CWallet::SetHDChain(const CHDChain& chain, bool memonly)
|
||||||
|
|
||||||
bool CWallet::IsHDEnabled() const
|
bool CWallet::IsHDEnabled() const
|
||||||
{
|
{
|
||||||
return !hdChain.masterKeyID.IsNull();
|
return !hdChain.seed_id.IsNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t CWalletTx::GetTxTime() const
|
int64_t CWalletTx::GetTxTime() const
|
||||||
|
@ -4130,8 +4130,8 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path&
|
||||||
walletInstance->SetMinVersion(FEATURE_HD);
|
walletInstance->SetMinVersion(FEATURE_HD);
|
||||||
|
|
||||||
// generate a new master key
|
// generate a new master key
|
||||||
CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey();
|
CPubKey masterPubKey = walletInstance->GenerateNewSeed();
|
||||||
if (!walletInstance->SetHDMasterKey(masterPubKey)) {
|
if (!walletInstance->SetHDSeed(masterPubKey)) {
|
||||||
throw std::runtime_error(std::string(__func__) + ": Storing master key failed");
|
throw std::runtime_error(std::string(__func__) + ": Storing master key failed");
|
||||||
}
|
}
|
||||||
hd_upgrade = true;
|
hd_upgrade = true;
|
||||||
|
@ -4164,10 +4164,10 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path&
|
||||||
}
|
}
|
||||||
walletInstance->SetMinVersion(FEATURE_LATEST);
|
walletInstance->SetMinVersion(FEATURE_LATEST);
|
||||||
|
|
||||||
// generate a new master key
|
// generate a new seed
|
||||||
CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey();
|
CPubKey seed = walletInstance->GenerateNewSeed();
|
||||||
if (!walletInstance->SetHDMasterKey(masterPubKey))
|
if (!walletInstance->SetHDSeed(seed))
|
||||||
throw std::runtime_error(std::string(__func__) + ": Storing master key failed");
|
throw std::runtime_error(std::string(__func__) + ": Storing HD seed failed");
|
||||||
|
|
||||||
// Top up the keypool
|
// Top up the keypool
|
||||||
if (!walletInstance->TopUpKeyPool()) {
|
if (!walletInstance->TopUpKeyPool()) {
|
||||||
|
|
|
@ -1139,17 +1139,17 @@ public:
|
||||||
/* Returns true if HD is enabled */
|
/* Returns true if HD is enabled */
|
||||||
bool IsHDEnabled() const;
|
bool IsHDEnabled() const;
|
||||||
|
|
||||||
/* Generates a new HD master key (will not be activated) */
|
/* Generates a new HD seed (will not be activated) */
|
||||||
CPubKey GenerateNewHDMasterKey();
|
CPubKey GenerateNewSeed();
|
||||||
|
|
||||||
/* Derives a new HD master key (will not be activated) */
|
/* Derives a new HD master key (will not be activated) */
|
||||||
CPubKey DeriveNewMasterHDKey(const CKey& key);
|
CPubKey DeriveNewSeed(const CKey& key);
|
||||||
|
|
||||||
/* Set the current HD master key (will reset the chain child index counters)
|
/* Set the current HD seed (will reset the chain child index counters)
|
||||||
Sets the master key's version based on the current wallet version (so the
|
Sets the seed's version based on the current wallet version (so the
|
||||||
caller must ensure the current wallet version is correct before calling
|
caller must ensure the current wallet version is correct before calling
|
||||||
this function). */
|
this function). */
|
||||||
bool SetHDMasterKey(const CPubKey& key);
|
bool SetHDSeed(const CPubKey& key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Blocks until the wallet state is up-to-date to /at least/ the current
|
* Blocks until the wallet state is up-to-date to /at least/ the current
|
||||||
|
|
|
@ -62,7 +62,7 @@ class CHDChain
|
||||||
public:
|
public:
|
||||||
uint32_t nExternalChainCounter;
|
uint32_t nExternalChainCounter;
|
||||||
uint32_t nInternalChainCounter;
|
uint32_t nInternalChainCounter;
|
||||||
CKeyID masterKeyID; //!< master key hash160
|
CKeyID seed_id; //!< seed hash160
|
||||||
|
|
||||||
static const int VERSION_HD_BASE = 1;
|
static const int VERSION_HD_BASE = 1;
|
||||||
static const int VERSION_HD_CHAIN_SPLIT = 2;
|
static const int VERSION_HD_CHAIN_SPLIT = 2;
|
||||||
|
@ -76,7 +76,7 @@ public:
|
||||||
{
|
{
|
||||||
READWRITE(this->nVersion);
|
READWRITE(this->nVersion);
|
||||||
READWRITE(nExternalChainCounter);
|
READWRITE(nExternalChainCounter);
|
||||||
READWRITE(masterKeyID);
|
READWRITE(seed_id);
|
||||||
if (this->nVersion >= VERSION_HD_CHAIN_SPLIT)
|
if (this->nVersion >= VERSION_HD_CHAIN_SPLIT)
|
||||||
READWRITE(nInternalChainCounter);
|
READWRITE(nInternalChainCounter);
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ public:
|
||||||
nVersion = CHDChain::CURRENT_VERSION;
|
nVersion = CHDChain::CURRENT_VERSION;
|
||||||
nExternalChainCounter = 0;
|
nExternalChainCounter = 0;
|
||||||
nInternalChainCounter = 0;
|
nInternalChainCounter = 0;
|
||||||
masterKeyID.SetNull();
|
seed_id.SetNull();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ public:
|
||||||
int nVersion;
|
int nVersion;
|
||||||
int64_t nCreateTime; // 0 means unknown
|
int64_t nCreateTime; // 0 means unknown
|
||||||
std::string hdKeypath; //optional HD/bip32 keypath
|
std::string hdKeypath; //optional HD/bip32 keypath
|
||||||
CKeyID hdMasterKeyID; //id of the HD masterkey used to derive this key
|
CKeyID hd_seed_id; //id of the HD seed used to derive this key
|
||||||
|
|
||||||
CKeyMetadata()
|
CKeyMetadata()
|
||||||
{
|
{
|
||||||
|
@ -120,7 +120,7 @@ public:
|
||||||
if (this->nVersion >= VERSION_WITH_HDDATA)
|
if (this->nVersion >= VERSION_WITH_HDDATA)
|
||||||
{
|
{
|
||||||
READWRITE(hdKeypath);
|
READWRITE(hdKeypath);
|
||||||
READWRITE(hdMasterKeyID);
|
READWRITE(hd_seed_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ public:
|
||||||
nVersion = CKeyMetadata::CURRENT_VERSION;
|
nVersion = CKeyMetadata::CURRENT_VERSION;
|
||||||
nCreateTime = 0;
|
nCreateTime = 0;
|
||||||
hdKeypath.clear();
|
hdKeypath.clear();
|
||||||
hdMasterKeyID.SetNull();
|
hd_seed_id.SetNull();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -36,10 +36,10 @@ def read_dump(file_name, addrs, script_addrs, hd_master_addr_old):
|
||||||
addr_keypath = comment.split(" addr=")[1]
|
addr_keypath = comment.split(" addr=")[1]
|
||||||
addr = addr_keypath.split(" ")[0]
|
addr = addr_keypath.split(" ")[0]
|
||||||
keypath = None
|
keypath = None
|
||||||
if keytype == "inactivehdmaster=1":
|
if keytype == "inactivehdseed=1":
|
||||||
# ensure the old master is still available
|
# ensure the old master is still available
|
||||||
assert(hd_master_addr_old == addr)
|
assert(hd_master_addr_old == addr)
|
||||||
elif keytype == "hdmaster=1":
|
elif keytype == "hdseed=1":
|
||||||
# ensure we have generated a new hd master key
|
# ensure we have generated a new hd master key
|
||||||
assert(hd_master_addr_old != addr)
|
assert(hd_master_addr_old != addr)
|
||||||
hd_master_addr_ret = addr
|
hd_master_addr_ret = addr
|
||||||
|
|
|
@ -29,7 +29,8 @@ class WalletHDTest(BitcoinTestFramework):
|
||||||
connect_nodes_bi(self.nodes, 0, 1)
|
connect_nodes_bi(self.nodes, 0, 1)
|
||||||
|
|
||||||
# Make sure we use hd, keep masterkeyid
|
# Make sure we use hd, keep masterkeyid
|
||||||
masterkeyid = self.nodes[1].getwalletinfo()['hdmasterkeyid']
|
masterkeyid = self.nodes[1].getwalletinfo()['hdseedid']
|
||||||
|
assert_equal(masterkeyid, self.nodes[1].getwalletinfo()['hdmasterkeyid'])
|
||||||
assert_equal(len(masterkeyid), 40)
|
assert_equal(len(masterkeyid), 40)
|
||||||
|
|
||||||
# create an internal key
|
# create an internal key
|
||||||
|
@ -54,6 +55,7 @@ class WalletHDTest(BitcoinTestFramework):
|
||||||
hd_add = self.nodes[1].getnewaddress()
|
hd_add = self.nodes[1].getnewaddress()
|
||||||
hd_info = self.nodes[1].getaddressinfo(hd_add)
|
hd_info = self.nodes[1].getaddressinfo(hd_add)
|
||||||
assert_equal(hd_info["hdkeypath"], "m/0'/0'/"+str(i)+"'")
|
assert_equal(hd_info["hdkeypath"], "m/0'/0'/"+str(i)+"'")
|
||||||
|
assert_equal(hd_info["hdseedid"], masterkeyid)
|
||||||
assert_equal(hd_info["hdmasterkeyid"], masterkeyid)
|
assert_equal(hd_info["hdmasterkeyid"], masterkeyid)
|
||||||
self.nodes[0].sendtoaddress(hd_add, 1)
|
self.nodes[0].sendtoaddress(hd_add, 1)
|
||||||
self.nodes[0].generate(1)
|
self.nodes[0].generate(1)
|
||||||
|
@ -83,6 +85,7 @@ class WalletHDTest(BitcoinTestFramework):
|
||||||
hd_add_2 = self.nodes[1].getnewaddress()
|
hd_add_2 = self.nodes[1].getnewaddress()
|
||||||
hd_info_2 = self.nodes[1].getaddressinfo(hd_add_2)
|
hd_info_2 = self.nodes[1].getaddressinfo(hd_add_2)
|
||||||
assert_equal(hd_info_2["hdkeypath"], "m/0'/0'/"+str(i)+"'")
|
assert_equal(hd_info_2["hdkeypath"], "m/0'/0'/"+str(i)+"'")
|
||||||
|
assert_equal(hd_info_2["hdseedid"], masterkeyid)
|
||||||
assert_equal(hd_info_2["hdmasterkeyid"], masterkeyid)
|
assert_equal(hd_info_2["hdmasterkeyid"], masterkeyid)
|
||||||
assert_equal(hd_add, hd_add_2)
|
assert_equal(hd_add, hd_add_2)
|
||||||
connect_nodes_bi(self.nodes, 0, 1)
|
connect_nodes_bi(self.nodes, 0, 1)
|
||||||
|
@ -122,9 +125,9 @@ class WalletHDTest(BitcoinTestFramework):
|
||||||
assert_equal(keypath[0:7], "m/0'/1'")
|
assert_equal(keypath[0:7], "m/0'/1'")
|
||||||
|
|
||||||
# Generate a new HD seed on node 1 and make sure it is set
|
# Generate a new HD seed on node 1 and make sure it is set
|
||||||
orig_masterkeyid = self.nodes[1].getwalletinfo()['hdmasterkeyid']
|
orig_masterkeyid = self.nodes[1].getwalletinfo()['hdseedid']
|
||||||
self.nodes[1].sethdseed()
|
self.nodes[1].sethdseed()
|
||||||
new_masterkeyid = self.nodes[1].getwalletinfo()['hdmasterkeyid']
|
new_masterkeyid = self.nodes[1].getwalletinfo()['hdseedid']
|
||||||
assert orig_masterkeyid != new_masterkeyid
|
assert orig_masterkeyid != new_masterkeyid
|
||||||
addr = self.nodes[1].getnewaddress()
|
addr = self.nodes[1].getnewaddress()
|
||||||
assert_equal(self.nodes[1].getaddressinfo(addr)['hdkeypath'], 'm/0\'/0\'/0\'') # Make sure the new address is the first from the keypool
|
assert_equal(self.nodes[1].getaddressinfo(addr)['hdkeypath'], 'm/0\'/0\'/0\'') # Make sure the new address is the first from the keypool
|
||||||
|
@ -134,16 +137,16 @@ class WalletHDTest(BitcoinTestFramework):
|
||||||
new_seed = self.nodes[0].dumpprivkey(self.nodes[0].getnewaddress())
|
new_seed = self.nodes[0].dumpprivkey(self.nodes[0].getnewaddress())
|
||||||
orig_masterkeyid = new_masterkeyid
|
orig_masterkeyid = new_masterkeyid
|
||||||
self.nodes[1].sethdseed(False, new_seed)
|
self.nodes[1].sethdseed(False, new_seed)
|
||||||
new_masterkeyid = self.nodes[1].getwalletinfo()['hdmasterkeyid']
|
new_masterkeyid = self.nodes[1].getwalletinfo()['hdseedid']
|
||||||
assert orig_masterkeyid != new_masterkeyid
|
assert orig_masterkeyid != new_masterkeyid
|
||||||
addr = self.nodes[1].getnewaddress()
|
addr = self.nodes[1].getnewaddress()
|
||||||
assert_equal(orig_masterkeyid, self.nodes[1].getaddressinfo(addr)['hdmasterkeyid'])
|
assert_equal(orig_masterkeyid, self.nodes[1].getaddressinfo(addr)['hdseedid'])
|
||||||
assert_equal(self.nodes[1].getaddressinfo(addr)['hdkeypath'], 'm/0\'/0\'/1\'') # Make sure the new address continues previous keypool
|
assert_equal(self.nodes[1].getaddressinfo(addr)['hdkeypath'], 'm/0\'/0\'/1\'') # Make sure the new address continues previous keypool
|
||||||
|
|
||||||
# Check that the next address is from the new seed
|
# Check that the next address is from the new seed
|
||||||
self.nodes[1].keypoolrefill(1)
|
self.nodes[1].keypoolrefill(1)
|
||||||
next_addr = self.nodes[1].getnewaddress()
|
next_addr = self.nodes[1].getnewaddress()
|
||||||
assert_equal(new_masterkeyid, self.nodes[1].getaddressinfo(next_addr)['hdmasterkeyid'])
|
assert_equal(new_masterkeyid, self.nodes[1].getaddressinfo(next_addr)['hdseedid'])
|
||||||
assert_equal(self.nodes[1].getaddressinfo(next_addr)['hdkeypath'], 'm/0\'/0\'/0\'') # Make sure the new address is not from previous keypool
|
assert_equal(self.nodes[1].getaddressinfo(next_addr)['hdkeypath'], 'm/0\'/0\'/0\'') # Make sure the new address is not from previous keypool
|
||||||
assert next_addr != addr
|
assert next_addr != addr
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,8 @@ class KeyPoolTest(BitcoinTestFramework):
|
||||||
addr_before_encrypting = nodes[0].getnewaddress()
|
addr_before_encrypting = nodes[0].getnewaddress()
|
||||||
addr_before_encrypting_data = nodes[0].getaddressinfo(addr_before_encrypting)
|
addr_before_encrypting_data = nodes[0].getaddressinfo(addr_before_encrypting)
|
||||||
wallet_info_old = nodes[0].getwalletinfo()
|
wallet_info_old = nodes[0].getwalletinfo()
|
||||||
assert(addr_before_encrypting_data['hdmasterkeyid'] == wallet_info_old['hdmasterkeyid'])
|
assert_equal(wallet_info_old['hdseedid'], wallet_info_old['hdmasterkeyid'])
|
||||||
|
assert(addr_before_encrypting_data['hdseedid'] == wallet_info_old['hdseedid'])
|
||||||
|
|
||||||
# Encrypt wallet and wait to terminate
|
# Encrypt wallet and wait to terminate
|
||||||
nodes[0].node_encrypt_wallet('test')
|
nodes[0].node_encrypt_wallet('test')
|
||||||
|
@ -26,8 +27,9 @@ class KeyPoolTest(BitcoinTestFramework):
|
||||||
addr = nodes[0].getnewaddress()
|
addr = nodes[0].getnewaddress()
|
||||||
addr_data = nodes[0].getaddressinfo(addr)
|
addr_data = nodes[0].getaddressinfo(addr)
|
||||||
wallet_info = nodes[0].getwalletinfo()
|
wallet_info = nodes[0].getwalletinfo()
|
||||||
assert(addr_before_encrypting_data['hdmasterkeyid'] != wallet_info['hdmasterkeyid'])
|
assert_equal(wallet_info['hdseedid'], wallet_info['hdmasterkeyid'])
|
||||||
assert(addr_data['hdmasterkeyid'] == wallet_info['hdmasterkeyid'])
|
assert(addr_before_encrypting_data['hdseedid'] != wallet_info['hdseedid'])
|
||||||
|
assert(addr_data['hdseedid'] == wallet_info['hdseedid'])
|
||||||
assert_raises_rpc_error(-12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress)
|
assert_raises_rpc_error(-12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress)
|
||||||
|
|
||||||
# put six (plus 2) new keys in the keypool (100% external-, +100% internal-keys, 1 in min)
|
# put six (plus 2) new keys in the keypool (100% external-, +100% internal-keys, 1 in min)
|
||||||
|
|
Loading…
Reference in a new issue