From 0e02c74800e3dd7e6ad29d1cfbe116e39cf1077d Mon Sep 17 00:00:00 2001 From: lateminer Date: Tue, 2 Jan 2018 13:57:39 +0300 Subject: [PATCH] [0.13] Create a new HD seed after encrypting the wallet https://github.com/bitcoin/bitcoin/pull/8389/commits --- qa/rpc-tests/keypool.py | 8 +++++++ src/init.cpp | 4 ++-- src/wallet/rpcwallet.cpp | 2 +- src/wallet/wallet.cpp | 46 ++++++++++++++++++++++++++++++++++------ src/wallet/wallet.h | 7 ++++-- 5 files changed, 55 insertions(+), 12 deletions(-) diff --git a/qa/rpc-tests/keypool.py b/qa/rpc-tests/keypool.py index c300bbc57..a94f09f77 100755 --- a/qa/rpc-tests/keypool.py +++ b/qa/rpc-tests/keypool.py @@ -36,6 +36,10 @@ class KeyPoolTest(BitcoinTestFramework): def run_test(self): nodes = self.nodes + addr_before_encrypting = nodes[0].getnewaddress() + addr_before_encrypting_data = nodes[0].validateaddress(addr_before_encrypting) + wallet_info_old = nodes[0].getwalletinfo() + assert(addr_before_encrypting_data['hdmasterkeyid'] == wallet_info_old['hdmasterkeyid']) # Encrypt wallet and wait to terminate nodes[0].encryptwallet('test') bitcoind_processes[0].wait() @@ -43,6 +47,10 @@ class KeyPoolTest(BitcoinTestFramework): nodes[0] = start_node(0, self.options.tmpdir) # Keep creating keys addr = nodes[0].getnewaddress() + addr_data = nodes[0].validateaddress(addr) + wallet_info = nodes[0].getwalletinfo() + assert(addr_before_encrypting_data['hdmasterkeyid'] != wallet_info['hdmasterkeyid']) + assert(addr_data['hdmasterkeyid'] == wallet_info['hdmasterkeyid']) try: addr = nodes[0].getnewaddress() raise AssertionError('Keypool should be exhausted after one address') diff --git a/src/init.cpp b/src/init.cpp index d7c6b66c5..da953880c 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1543,8 +1543,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (GetBoolArg("-usehd", true) && pwalletMain->hdChain.masterKeyID.IsNull() ) { // generate a new master key CKey key; - key.MakeNewKey(true); - if (!pwalletMain->SetHDMasterKey(key)) + CPubKey masterPubKey = pwalletMain->GenerateNewHDMasterKey(); + if (!pwalletMain->SetHDMasterKey(masterPubKey)) throw std::runtime_error("CWallet::GenerateNewKey(): Storing master key failed"); } CPubKey newDefaultKey; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 55f0b5c97..24d287b17 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2127,7 +2127,7 @@ UniValue encryptwallet(const UniValue& params, bool fHelp) // slack space in .dat files; that is bad if the old data is // unencrypted private keys. So: StartShutdown(); - return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup."; + return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet. The keypool has been flushed and a new HD seed was generated (if you are using HD). You need to make a new backup."; } UniValue lockunspent(const UniValue& params, bool fHelp) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index f07b8c753..31428ccb8 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -946,6 +946,15 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) Lock(); Unlock(strWalletPassphrase); + + // if we are using HD, replace the HD master key with a new one + if (!hdChain.masterKeyID.IsNull()) { + CKey key; + key.MakeNewKey(true); + if (!SetHDMasterKey(key)) + return false; + } + NewKeyPool(); Lock(); @@ -1378,19 +1387,42 @@ CAmount CWallet::GetChange(const CTxOut& txout) const return (IsChange(txout) ? txout.nValue : 0); } +CPubKey CWallet::GenerateNewHDMasterKey() +{ + CKey key; + key.MakeNewKey(true); + + int64_t nCreationTime = GetTime(); + CKeyMetadata metadata(nCreationTime); + + // calculate the pubkey + CPubKey pubkey = key.GetPubKey(); + assert(key.VerifyPubKey(pubkey)); + + // set the hd keypath to "m" -> Master, refers the masterkeyid to itself + metadata.hdKeypath = "m"; + metadata.hdMasterKeyID = pubkey.GetID(); + + { + LOCK(cs_wallet); + + // mem store the metadata + mapKeyMetadata[pubkey.GetID()] = metadata; + + // write the key&metadata to the database + if (!AddKeyPubKey(key, pubkey)) + throw std::runtime_error("CWallet::GenerateNewKey(): AddKey failed"); + } + + return pubkey; +} + bool CWallet::SetHDMasterKey(const CKey& key) { LOCK(cs_wallet); // ensure this wallet.dat can only be opened by clients supporting HD SetMinVersion(FEATURE_HD); - - // store the key as normal "key"/"ckey" object - // in the database - // key metadata is not required - CPubKey pubkey = key.GetPubKey(); - if (!AddKeyPubKey(key, pubkey)) - throw std::runtime_error("CWallet::GenerateNewKey(): AddKey failed"); // store the keyid (hash160) together with // the child index counter in the database diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index decadf449..c15ad4076 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -819,10 +819,13 @@ public: uint64_t GetStakeWeight() const; /* Set the hd chain model (chain child index counters) */ bool SetHDChain(const CHDChain& chain, bool memonly); + const CHDChain& GetHDChain() { return hdChain; } + + /* Generates a new HD master key (will not be activated) */ + CPubKey GenerateNewHDMasterKey(); /* Set the current hd master key (will reset the chain child index counters) */ - bool SetHDMasterKey(const CKey& key); - const CHDChain& GetHDChain() { return hdChain; } + bool SetHDMasterKey(const CPubKey& key); }; /** A key allocated from the key pool. */