Browse files

Merge #8389: [0.13] Create a new HD seed after encrypting the wallet

de45c06 [Wallet] Add CKeyMetadata record for HDMasterKey(s), factor out HD key generation (Jonas Schnelli)
f142c11 [0.13] Create a new HD seed after encrypting the wallet (Jonas Schnelli)
  • Loading branch information...
laanwj committed Jul 28, 2016
2 parents 45eba4b + de45c06 commit c3c82c48d9d7b07e1c7db134afe086b31fea34c5
Showing with 59 additions and 12 deletions.
  1. +2 −0 doc/
  2. +10 −0 qa/rpc-tests/
  3. +1 −1 src/wallet/rpcwallet.cpp
  4. +42 −10 src/wallet/wallet.cpp
  5. +4 −1 src/wallet/wallet.h
@@ -135,6 +135,8 @@ Existing wallets will still use traditional key generation.
Backups of HD wallets, regardless of when they have been created, can
therefore be used to re-generate all possible private keys, even the
ones which haven't already been generated during the time of the backup.
**Attention:** Encrypting the wallet will create a new seed which requires
a new backup!
HD key generation for new wallets can be disabled by `-usehd=0`. Keep in
mind that this flag only has affect on newly created wallets.
@@ -12,13 +12,23 @@ 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
# Restart node 0
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'])
addr = nodes[0].getnewaddress()
raise AssertionError('Keypool should be exhausted after one address')
@@ -2081,7 +2081,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:
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)
@@ -626,6 +626,15 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
// if we are using HD, replace the HD master key (seed) with a new one
if (!hdChain.masterKeyID.IsNull()) {
CKey key;
CPubKey masterPubKey = GenerateNewHDMasterKey();
if (!SetHDMasterKey(masterPubKey))
return false;
@@ -1166,20 +1175,43 @@ CAmount CWallet::GetChange(const CTransaction& tx) const
return nChange;
bool CWallet::SetHDMasterKey(const CKey& key)
CPubKey CWallet::GenerateNewHDMasterKey()
CKey key;
int64_t nCreationTime = GetTime();
CKeyMetadata metadata(nCreationTime);
// calculate the pubkey
CPubKey pubkey = key.GetPubKey();
// set the hd keypath to "m" -> Master, refers the masterkeyid to itself
metadata.hdKeypath = "m";
metadata.hdMasterKeyID = pubkey.GetID();
// 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 CPubKey& pubkey)
// ensure this wallet.dat can only be opened by clients supporting 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
// as a hdchain object
@@ -3299,8 +3331,8 @@ bool CWallet::InitLoadWallet()
if (GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET) && walletInstance->hdChain.masterKeyID.IsNull()) {
// generate a new master key
CKey key;
if (!walletInstance->SetHDMasterKey(key))
CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey();
if (!walletInstance->SetHDMasterKey(masterPubKey))
throw std::runtime_error("CWallet::GenerateNewKey(): Storing master key failed");
CPubKey newDefaultKey;
@@ -901,8 +901,11 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
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);
bool SetHDMasterKey(const CPubKey& key);
/** A key allocated from the key pool. */

0 comments on commit c3c82c4

Please sign in to comment.