aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWladimir J. van der Laan <laanwj@gmail.com>2016-07-18 07:54:46 +0200
committerWladimir J. van der Laan <laanwj@gmail.com>2016-07-18 07:58:33 +0200
commit238300b39894ef3a79540687d127a928d7b3c53c (patch)
tree3f3034bd48efda31c449a46e58598b3572b53661
parent37303934fe8f758f3a919c4a50ed3decbe99d059 (diff)
parent7945088d413819d8cf1772fd25e0f355c84c64d6 (diff)
downloadbitcoin-238300b39894ef3a79540687d127a928d7b3c53c.tar.xz
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)
-rwxr-xr-xqa/rpc-tests/wallet-hd.py12
-rw-r--r--src/rpc/misc.cpp8
-rw-r--r--src/wallet/rpcwallet.cpp4
-rw-r--r--src/wallet/wallet.cpp2
-rw-r--r--src/wallet/wallet.h7
-rw-r--r--src/wallet/walletdb.h17
6 files changed, 43 insertions, 7 deletions
diff --git a/qa/rpc-tests/wallet-hd.py b/qa/rpc-tests/wallet-hd.py
index 845eec027b..c738ee2207 100755
--- a/qa/rpc-tests/wallet-hd.py
+++ b/qa/rpc-tests/wallet-hd.py
@@ -30,6 +30,10 @@ class WalletHDTest(BitcoinTestFramework):
def run_test (self):
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
non_hd_add = self.nodes[0].getnewaddress()
self.nodes[1].importprivkey(self.nodes[0].dumpprivkey(non_hd_add))
@@ -43,8 +47,11 @@ class WalletHDTest(BitcoinTestFramework):
self.nodes[0].generate(101)
hd_add = None
num_hd_adds = 300
- for _ in range(num_hd_adds):
+ for i in range(num_hd_adds):
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].generate(1)
self.nodes[0].sendtoaddress(non_hd_add, 1)
@@ -64,6 +71,9 @@ class WalletHDTest(BitcoinTestFramework):
hd_add_2 = None
for _ in range(num_hd_adds):
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)
# Needs rescan
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index f2a29416e6..a8c5bcd177 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -166,6 +166,8 @@ UniValue validateaddress(const UniValue& params, bool fHelp)
" \"pubkey\" : \"publickeyhex\", (string) The hex value of the raw public key\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"
+ " \"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"
"\nExamples:\n"
+ HelpExampleCli("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"")
@@ -200,6 +202,12 @@ UniValue validateaddress(const UniValue& params, bool fHelp)
ret.pushKVs(detail);
if (pwalletMain && pwalletMain->mapAddressBook.count(dest))
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
}
return ret;
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 2747477fd9..b4831ad795 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -2269,6 +2269,7 @@ UniValue getwalletinfo(const UniValue& params, bool fHelp)
" \"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"
" \"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"
"\nExamples:\n"
+ HelpExampleCli("getwalletinfo", "")
@@ -2288,6 +2289,9 @@ UniValue getwalletinfo(const UniValue& params, bool fHelp)
if (pwalletMain->IsCrypted())
obj.push_back(Pair("unlocked_until", nWalletUnlockTime));
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;
}
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 7cb294bec1..46ed542158 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -126,6 +126,8 @@ CPubKey CWallet::GenerateNewKey()
// childIndex | BIP32_HARDENED_KEY_LIMIT = derive childIndex in hardened child-index-range
// example: 1 | BIP32_HARDENED_KEY_LIMIT == 0x80000001 == 2147483649
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
hdChain.nExternalChainCounter++;
} while(HaveKey(childKey.key.GetPubKey().GetID()));
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 7fc6ce5de5..e9d669a7d1 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -577,7 +577,7 @@ private:
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;
public:
@@ -896,11 +896,12 @@ public:
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);
- /* 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);
+ const CHDChain& GetHDChain() { return hdChain; }
};
/** A key allocated from the key pool. */
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index d083722dd2..5addd5c5c0 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -41,7 +41,7 @@ enum DBErrors
DB_NEED_REWRITE
};
-/* simple hd chain data model */
+/* simple HD chain data model */
class CHDChain
{
public:
@@ -73,9 +73,13 @@ public:
class CKeyMetadata
{
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;
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()
{
@@ -83,7 +87,7 @@ public:
}
CKeyMetadata(int64_t nCreateTime_)
{
- nVersion = CKeyMetadata::CURRENT_VERSION;
+ SetNull();
nCreateTime = nCreateTime_;
}
@@ -94,12 +98,19 @@ public:
READWRITE(this->nVersion);
nVersion = this->nVersion;
READWRITE(nCreateTime);
+ if (this->nVersion >= VERSION_WITH_HDDATA)
+ {
+ READWRITE(hdKeypath);
+ READWRITE(hdMasterKeyID);
+ }
}
void SetNull()
{
nVersion = CKeyMetadata::CURRENT_VERSION;
nCreateTime = 0;
+ hdKeypath.clear();
+ hdMasterKeyID.SetNull();
}
};