mirror of
https://github.com/kvazar-network/kevacoin.git
synced 2025-03-11 05:03:31 +00:00
Merge #8323: Add HD keypath to CKeyMetadata, report metadata in validateaddress
7945088 [Wallet] comsetic non-code changes for the HD feature (Jonas Schnelli) 68d7682 [Wallet] ensure CKeyMetadata.hdMasterKeyID will be cleared during SetNull() (Jonas Schnelli) f708085 [QA] extend wallet-hd test to cover HD metadata (Jonas Schnelli) 986c223 [Wallet] print hd masterkeyid in getwalletinfo (Jonas Schnelli) b1c7b24 [Wallet] report optional HDKeypath/HDMasterKeyId in validateaddress (Jonas Schnelli) 5b95dd2 [Wallet] extend CKeyMetadata with HD keypath (Jonas Schnelli)
This commit is contained in:
commit
238300b398
@ -30,6 +30,10 @@ class WalletHDTest(BitcoinTestFramework):
|
|||||||
def run_test (self):
|
def run_test (self):
|
||||||
tmpdir = self.options.tmpdir
|
tmpdir = self.options.tmpdir
|
||||||
|
|
||||||
|
# Make sure we use hd, keep masterkeyid
|
||||||
|
masterkeyid = self.nodes[1].getwalletinfo()['masterkeyid']
|
||||||
|
assert_equal(len(masterkeyid), 40)
|
||||||
|
|
||||||
# Import a non-HD private key in the HD wallet
|
# Import a non-HD private key in the HD wallet
|
||||||
non_hd_add = self.nodes[0].getnewaddress()
|
non_hd_add = self.nodes[0].getnewaddress()
|
||||||
self.nodes[1].importprivkey(self.nodes[0].dumpprivkey(non_hd_add))
|
self.nodes[1].importprivkey(self.nodes[0].dumpprivkey(non_hd_add))
|
||||||
@ -43,8 +47,11 @@ class WalletHDTest(BitcoinTestFramework):
|
|||||||
self.nodes[0].generate(101)
|
self.nodes[0].generate(101)
|
||||||
hd_add = None
|
hd_add = None
|
||||||
num_hd_adds = 300
|
num_hd_adds = 300
|
||||||
for _ in range(num_hd_adds):
|
for i in range(num_hd_adds):
|
||||||
hd_add = self.nodes[1].getnewaddress()
|
hd_add = self.nodes[1].getnewaddress()
|
||||||
|
hd_info = self.nodes[1].validateaddress(hd_add)
|
||||||
|
assert_equal(hd_info["hdkeypath"], "m/0'/0'/"+str(i+1)+"'")
|
||||||
|
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)
|
||||||
self.nodes[0].sendtoaddress(non_hd_add, 1)
|
self.nodes[0].sendtoaddress(non_hd_add, 1)
|
||||||
@ -64,6 +71,9 @@ class WalletHDTest(BitcoinTestFramework):
|
|||||||
hd_add_2 = None
|
hd_add_2 = None
|
||||||
for _ in range(num_hd_adds):
|
for _ in range(num_hd_adds):
|
||||||
hd_add_2 = self.nodes[1].getnewaddress()
|
hd_add_2 = self.nodes[1].getnewaddress()
|
||||||
|
hd_info_2 = self.nodes[1].validateaddress(hd_add_2)
|
||||||
|
assert_equal(hd_info_2["hdkeypath"], "m/0'/0'/"+str(_+1)+"'")
|
||||||
|
assert_equal(hd_info_2["hdmasterkeyid"], masterkeyid)
|
||||||
assert_equal(hd_add, hd_add_2)
|
assert_equal(hd_add, hd_add_2)
|
||||||
|
|
||||||
# Needs rescan
|
# Needs rescan
|
||||||
|
@ -166,6 +166,8 @@ UniValue validateaddress(const UniValue& params, bool fHelp)
|
|||||||
" \"pubkey\" : \"publickeyhex\", (string) The hex value of the raw public key\n"
|
" \"pubkey\" : \"publickeyhex\", (string) The hex value of the raw public key\n"
|
||||||
" \"iscompressed\" : true|false, (boolean) If the address is compressed\n"
|
" \"iscompressed\" : true|false, (boolean) If the address is compressed\n"
|
||||||
" \"account\" : \"account\" (string) DEPRECATED. The account associated with the address, \"\" is the default account\n"
|
" \"account\" : \"account\" (string) DEPRECATED. The account associated with the address, \"\" is the default account\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"
|
||||||
"}\n"
|
"}\n"
|
||||||
"\nExamples:\n"
|
"\nExamples:\n"
|
||||||
+ HelpExampleCli("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"")
|
+ HelpExampleCli("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"")
|
||||||
@ -200,6 +202,12 @@ UniValue validateaddress(const UniValue& params, bool fHelp)
|
|||||||
ret.pushKVs(detail);
|
ret.pushKVs(detail);
|
||||||
if (pwalletMain && pwalletMain->mapAddressBook.count(dest))
|
if (pwalletMain && pwalletMain->mapAddressBook.count(dest))
|
||||||
ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest].name));
|
ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest].name));
|
||||||
|
CKeyID keyID;
|
||||||
|
if (pwalletMain && address.GetKeyID(keyID) && pwalletMain->mapKeyMetadata.count(keyID) && !pwalletMain->mapKeyMetadata[keyID].hdKeypath.empty())
|
||||||
|
{
|
||||||
|
ret.push_back(Pair("hdkeypath", pwalletMain->mapKeyMetadata[keyID].hdKeypath));
|
||||||
|
ret.push_back(Pair("hdmasterkeyid", pwalletMain->mapKeyMetadata[keyID].hdMasterKeyID.GetHex()));
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -2269,6 +2269,7 @@ UniValue getwalletinfo(const UniValue& params, bool fHelp)
|
|||||||
" \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n"
|
" \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\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"
|
||||||
|
" \"masterkeyid\": \"<hash160>\", (string) the Hash160 of the HD master pubkey\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"\nExamples:\n"
|
"\nExamples:\n"
|
||||||
+ HelpExampleCli("getwalletinfo", "")
|
+ HelpExampleCli("getwalletinfo", "")
|
||||||
@ -2288,6 +2289,9 @@ UniValue getwalletinfo(const UniValue& params, bool fHelp)
|
|||||||
if (pwalletMain->IsCrypted())
|
if (pwalletMain->IsCrypted())
|
||||||
obj.push_back(Pair("unlocked_until", nWalletUnlockTime));
|
obj.push_back(Pair("unlocked_until", nWalletUnlockTime));
|
||||||
obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK())));
|
obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK())));
|
||||||
|
CKeyID masterKeyID = pwalletMain->GetHDChain().masterKeyID;
|
||||||
|
if (!masterKeyID.IsNull())
|
||||||
|
obj.push_back(Pair("masterkeyid", masterKeyID.GetHex()));
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,6 +126,8 @@ CPubKey CWallet::GenerateNewKey()
|
|||||||
// childIndex | BIP32_HARDENED_KEY_LIMIT = derive childIndex in hardened child-index-range
|
// childIndex | BIP32_HARDENED_KEY_LIMIT = derive childIndex in hardened child-index-range
|
||||||
// example: 1 | BIP32_HARDENED_KEY_LIMIT == 0x80000001 == 2147483649
|
// example: 1 | BIP32_HARDENED_KEY_LIMIT == 0x80000001 == 2147483649
|
||||||
externalChainChildKey.Derive(childKey, hdChain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
|
externalChainChildKey.Derive(childKey, hdChain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
|
||||||
|
metadata.hdKeypath = "m/0'/0'/"+std::to_string(hdChain.nExternalChainCounter)+"'";
|
||||||
|
metadata.hdMasterKeyID = hdChain.masterKeyID;
|
||||||
// increment childkey index
|
// increment childkey index
|
||||||
hdChain.nExternalChainCounter++;
|
hdChain.nExternalChainCounter++;
|
||||||
} while(HaveKey(childKey.key.GetPubKey().GetID()));
|
} while(HaveKey(childKey.key.GetPubKey().GetID()));
|
||||||
|
@ -577,7 +577,7 @@ private:
|
|||||||
|
|
||||||
void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>);
|
void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>);
|
||||||
|
|
||||||
/* the hd chain data model (external chain counters) */
|
/* the HD chain data model (external chain counters) */
|
||||||
CHDChain hdChain;
|
CHDChain hdChain;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -896,11 +896,12 @@ public:
|
|||||||
|
|
||||||
bool BackupWallet(const std::string& strDest);
|
bool BackupWallet(const std::string& strDest);
|
||||||
|
|
||||||
/* Set the hd chain model (chain child index counters) */
|
/* Set the HD chain model (chain child index counters) */
|
||||||
bool SetHDChain(const CHDChain& chain, bool memonly);
|
bool SetHDChain(const CHDChain& chain, bool memonly);
|
||||||
|
|
||||||
/* Set the current hd master key (will reset the chain child index counters) */
|
/* Set the current HD master key (will reset the chain child index counters) */
|
||||||
bool SetHDMasterKey(const CKey& key);
|
bool SetHDMasterKey(const CKey& key);
|
||||||
|
const CHDChain& GetHDChain() { return hdChain; }
|
||||||
};
|
};
|
||||||
|
|
||||||
/** A key allocated from the key pool. */
|
/** A key allocated from the key pool. */
|
||||||
|
@ -41,7 +41,7 @@ enum DBErrors
|
|||||||
DB_NEED_REWRITE
|
DB_NEED_REWRITE
|
||||||
};
|
};
|
||||||
|
|
||||||
/* simple hd chain data model */
|
/* simple HD chain data model */
|
||||||
class CHDChain
|
class CHDChain
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -73,9 +73,13 @@ public:
|
|||||||
class CKeyMetadata
|
class CKeyMetadata
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static const int CURRENT_VERSION=1;
|
static const int VERSION_BASIC=1;
|
||||||
|
static const int VERSION_WITH_HDDATA=10;
|
||||||
|
static const int CURRENT_VERSION=VERSION_WITH_HDDATA;
|
||||||
int nVersion;
|
int nVersion;
|
||||||
int64_t nCreateTime; // 0 means unknown
|
int64_t nCreateTime; // 0 means unknown
|
||||||
|
std::string hdKeypath; //optional HD/bip32 keypath
|
||||||
|
CKeyID hdMasterKeyID; //id of the HD masterkey used to derive this key
|
||||||
|
|
||||||
CKeyMetadata()
|
CKeyMetadata()
|
||||||
{
|
{
|
||||||
@ -83,7 +87,7 @@ public:
|
|||||||
}
|
}
|
||||||
CKeyMetadata(int64_t nCreateTime_)
|
CKeyMetadata(int64_t nCreateTime_)
|
||||||
{
|
{
|
||||||
nVersion = CKeyMetadata::CURRENT_VERSION;
|
SetNull();
|
||||||
nCreateTime = nCreateTime_;
|
nCreateTime = nCreateTime_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,12 +98,19 @@ public:
|
|||||||
READWRITE(this->nVersion);
|
READWRITE(this->nVersion);
|
||||||
nVersion = this->nVersion;
|
nVersion = this->nVersion;
|
||||||
READWRITE(nCreateTime);
|
READWRITE(nCreateTime);
|
||||||
|
if (this->nVersion >= VERSION_WITH_HDDATA)
|
||||||
|
{
|
||||||
|
READWRITE(hdKeypath);
|
||||||
|
READWRITE(hdMasterKeyID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetNull()
|
void SetNull()
|
||||||
{
|
{
|
||||||
nVersion = CKeyMetadata::CURRENT_VERSION;
|
nVersion = CKeyMetadata::CURRENT_VERSION;
|
||||||
nCreateTime = 0;
|
nCreateTime = 0;
|
||||||
|
hdKeypath.clear();
|
||||||
|
hdMasterKeyID.SetNull();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user