From 02592f4c5e6d9416165deef96398ac7760f457d1 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Tue, 10 Jan 2017 16:45:30 +0100 Subject: [Wallet] split the keypool in an internal and external part --- src/wallet/wallet.cpp | 127 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 85 insertions(+), 42 deletions(-) (limited to 'src/wallet/wallet.cpp') diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 445e40b04..71c97a4e8 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -85,7 +85,7 @@ const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const return &(it->second); } -CPubKey CWallet::GenerateNewKey() +CPubKey CWallet::GenerateNewKey(bool internal) { AssertLockHeld(cs_wallet); // mapKeyMetadata bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets @@ -98,7 +98,7 @@ CPubKey CWallet::GenerateNewKey() // use HD key derivation if HD was enabled during wallet creation if (IsHDEnabled()) { - DeriveNewChildKey(metadata, secret); + DeriveNewChildKey(metadata, secret, (CanSupportFeature(FEATURE_HD_SPLIT) ? internal : false)); } else { secret.MakeNewKey(fCompressed); } @@ -118,13 +118,13 @@ CPubKey CWallet::GenerateNewKey() return pubkey; } -void CWallet::DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret) +void CWallet::DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret, bool internal) { // for now we use a fixed keypath scheme of m/0'/0'/k CKey key; //master key seed (256bit) CExtKey masterKey; //hd master key CExtKey accountKey; //key at m/0' - CExtKey externalChainChildKey; //key at m/0'/0' + CExtKey chainChildKey; //key at m/0'/0' (external) or m/0'/1' (internal) CExtKey childKey; //key at m/0'/0'/' // try to get the master key @@ -137,19 +137,22 @@ void CWallet::DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret) // use hardened derivation (child keys >= 0x80000000 are hardened after bip32) masterKey.Derive(accountKey, BIP32_HARDENED_KEY_LIMIT); - // derive m/0'/0' - accountKey.Derive(externalChainChildKey, BIP32_HARDENED_KEY_LIMIT); + // derive m/0'/0' (external chain) OR m/0'/1' (internal chain) + accountKey.Derive(chainChildKey, BIP32_HARDENED_KEY_LIMIT+(internal ? 1 : 0)); // derive child key at next index, skip keys already known to the wallet do { // always derive hardened keys // 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) + "'"; + chainChildKey.Derive(childKey, (internal ? hdChain.nInternalChainCounter : hdChain.nExternalChainCounter) | BIP32_HARDENED_KEY_LIMIT); + metadata.hdKeypath = "m/0'/" + std::string(internal ? "1'/"+ std::to_string(hdChain.nInternalChainCounter) : "0'/" + std::to_string(hdChain.nExternalChainCounter)) + "'"; metadata.hdMasterKeyID = hdChain.masterKeyID; // increment childkey index - hdChain.nExternalChainCounter++; + if (internal) + hdChain.nInternalChainCounter++; + else + hdChain.nExternalChainCounter++; } while (HaveKey(childKey.key.GetPubKey().GetID())); secret = childKey.key; @@ -799,7 +802,7 @@ bool CWallet::GetAccountPubkey(CPubKey &pubKey, std::string strAccount, bool bFo // Generate a new key if (bForceNew) { - if (!GetKeyFromPool(account.vchPubKey)) + if (!GetKeyFromPool(account.vchPubKey, false)) return false; SetAddressBook(account.vchPubKey.GetID(), strAccount, "receive"); @@ -1304,8 +1307,8 @@ bool CWallet::SetHDMasterKey(const CPubKey& pubkey) { LOCK(cs_wallet); - // ensure this wallet.dat can only be opened by clients supporting HD - SetMinVersion(FEATURE_HD); + // ensure this wallet.dat can only be opened by clients supporting HD with chain split + SetMinVersion(FEATURE_HD_SPLIT); // store the keyid (hash160) together with // the child index counter in the database @@ -2445,7 +2448,7 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, CWalletT // Reserve a new key pair from key pool CPubKey vchPubKey; bool ret; - ret = reservekey.GetReservedKey(vchPubKey); + ret = reservekey.GetReservedKey(vchPubKey, true); if (!ret) { strFailReason = _("Keypool ran out, please call keypoolrefill first"); @@ -2899,18 +2902,31 @@ bool CWallet::NewKeyPool() if (IsLocked()) return false; - int64_t nKeys = std::max(GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t)0); - for (int i = 0; i < nKeys; i++) - { - int64_t nIndex = i+1; - walletdb.WritePool(nIndex, CKeyPool(GenerateNewKey())); - setKeyPool.insert(nIndex); - } - LogPrintf("CWallet::NewKeyPool wrote %d new keys\n", nKeys); + TopUpKeyPool(); + LogPrintf("CWallet::NewKeyPool rewrote keypool\n"); } return true; } +size_t CWallet::KeypoolCountExternalKeys() +{ + AssertLockHeld(cs_wallet); // setKeyPool + + CWalletDB walletdb(strWalletFile); + + // count amount of external keys + size_t amountE = 0; + for(const int64_t& id : setKeyPool) + { + CKeyPool tmpKeypool; + if (!walletdb.ReadPool(id, tmpKeypool)) + throw std::runtime_error(std::string(__func__) + ": read failed"); + amountE += !tmpKeypool.fInternal; + } + + return amountE; +} + bool CWallet::TopUpKeyPool(unsigned int kpSize) { { @@ -2919,8 +2935,6 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize) if (IsLocked()) return false; - CWalletDB walletdb(strWalletFile); - // Top up key pool unsigned int nTargetSize; if (kpSize > 0) @@ -2928,21 +2942,39 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize) else nTargetSize = std::max(GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 0); - while (setKeyPool.size() < (nTargetSize + 1)) + // count amount of available keys (internal, external) + // make sure the keypool of external keys fits the user selected target (-keypool) + // generate +20% internal keys (minimum 2 keys) + int64_t amountExternal = KeypoolCountExternalKeys(); + int64_t amountInternal = setKeyPool.size() - amountExternal; + int64_t targetInternal = max((int64_t)ceil(nTargetSize * 0.2), (int64_t) 2); + int64_t missingExternal = max( (int64_t)(nTargetSize - amountExternal), (int64_t) 0); + int64_t missingInternal = max(targetInternal - amountInternal, (int64_t) 0); + + if (!IsHDEnabled() || !CanSupportFeature(FEATURE_HD_SPLIT)) + { + // don't create extra internal keys + missingInternal = 0; + } + bool internal = false; + CWalletDB walletdb(strWalletFile); + for (int64_t i = missingInternal + missingExternal; i--;) { int64_t nEnd = 1; + if (i < missingInternal) + internal = true; if (!setKeyPool.empty()) nEnd = *(--setKeyPool.end()) + 1; - if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey()))) + if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey(internal), internal))) throw std::runtime_error(std::string(__func__) + ": writing generated key failed"); setKeyPool.insert(nEnd); - LogPrintf("keypool added key %d, size=%u\n", nEnd, setKeyPool.size()); + LogPrintf("keypool added key %d, size=%u, internal=%d\n", nEnd, setKeyPool.size(), internal); } } return true; } -void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool) +void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool internal) { nIndex = -1; keypool.vchPubKey = CPubKey(); @@ -2958,14 +2990,24 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool) CWalletDB walletdb(strWalletFile); - nIndex = *(setKeyPool.begin()); - setKeyPool.erase(setKeyPool.begin()); - if (!walletdb.ReadPool(nIndex, keypool)) - throw std::runtime_error(std::string(__func__) + ": read failed"); - if (!HaveKey(keypool.vchPubKey.GetID())) - throw std::runtime_error(std::string(__func__) + ": unknown key in key pool"); - assert(keypool.vchPubKey.IsValid()); - LogPrintf("keypool reserve %d\n", nIndex); + // try to find a key that matches the internal/external filter + for(const int64_t& id : setKeyPool) + { + CKeyPool tmpKeypool; + if (!walletdb.ReadPool(id, tmpKeypool)) + throw std::runtime_error(std::string(__func__) + ": read failed"); + if (!HaveKey(tmpKeypool.vchPubKey.GetID())) + throw std::runtime_error(std::string(__func__) + ": unknown key in key pool"); + if (!IsHDEnabled() || tmpKeypool.fInternal == internal) + { + nIndex = id; + keypool = tmpKeypool; + setKeyPool.erase(id); + assert(keypool.vchPubKey.IsValid()); + LogPrintf("keypool reserve %d\n", nIndex); + return; + } + } } } @@ -2990,17 +3032,17 @@ void CWallet::ReturnKey(int64_t nIndex) LogPrintf("keypool return %d\n", nIndex); } -bool CWallet::GetKeyFromPool(CPubKey& result) +bool CWallet::GetKeyFromPool(CPubKey& result, bool internal) { int64_t nIndex = 0; CKeyPool keypool; { LOCK(cs_wallet); - ReserveKeyFromKeyPool(nIndex, keypool); + ReserveKeyFromKeyPool(nIndex, keypool, internal); if (nIndex == -1) { if (IsLocked()) return false; - result = GenerateNewKey(); + result = GenerateNewKey(internal); return true; } KeepKey(nIndex); @@ -3205,12 +3247,12 @@ std::set CWallet::GetAccountAddresses(const std::string& strAcco return result; } -bool CReserveKey::GetReservedKey(CPubKey& pubkey) +bool CReserveKey::GetReservedKey(CPubKey& pubkey, bool internal) { if (nIndex == -1) { CKeyPool keypool; - pwallet->ReserveKeyFromKeyPool(nIndex, keypool); + pwallet->ReserveKeyFromKeyPool(nIndex, keypool, internal); if (nIndex != -1) vchPubKey = keypool.vchPubKey; else { @@ -3629,7 +3671,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) throw std::runtime_error(std::string(__func__) + ": Storing master key failed"); } CPubKey newDefaultKey; - if (walletInstance->GetKeyFromPool(newDefaultKey)) { + if (walletInstance->GetKeyFromPool(newDefaultKey, false)) { walletInstance->SetDefaultKey(newDefaultKey); if (!walletInstance->SetAddressBook(walletInstance->vchDefaultKey.GetID(), "", "receive")) { InitError(_("Cannot write default address") += "\n"); @@ -3890,10 +3932,11 @@ CKeyPool::CKeyPool() nTime = GetTime(); } -CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn) +CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn) { nTime = GetTime(); vchPubKey = vchPubKeyIn; + fInternal = internalIn; } CWalletKey::CWalletKey(int64_t nExpires) -- cgit v1.2.3 From d59531ddfc09f705c2ef3a33c41d6d910756da18 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Mon, 16 Jan 2017 08:56:37 +0100 Subject: Immediately return setKeyPool's size if HD or HD_SPLIT is disabled or not supported --- src/wallet/wallet.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/wallet/wallet.cpp') diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 71c97a4e8..349dff4f2 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2912,6 +2912,10 @@ size_t CWallet::KeypoolCountExternalKeys() { AssertLockHeld(cs_wallet); // setKeyPool + // immediately return setKeyPool's size if HD or HD_SPLIT is disabled or not supported + if (!IsHDEnabled() || !CanSupportFeature(FEATURE_HD_SPLIT)) + return setKeyPool.size(); + CWalletDB walletdb(strWalletFile); // count amount of external keys -- cgit v1.2.3 From 01de822c8dbc92d8039dd17de9193d72eb7fadd2 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Mon, 16 Jan 2017 08:57:31 +0100 Subject: Removed redundant IsLocked() check in NewKeyPool() --- src/wallet/wallet.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/wallet/wallet.cpp') diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 349dff4f2..15dabad7f 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2899,10 +2899,9 @@ bool CWallet::NewKeyPool() walletdb.ErasePool(nIndex); setKeyPool.clear(); - if (IsLocked()) + if (!TopUpKeyPool()) { return false; - - TopUpKeyPool(); + } LogPrintf("CWallet::NewKeyPool rewrote keypool\n"); } return true; -- cgit v1.2.3 From 469a47b7601bddeba052415efe95d0e8b0a7b05e Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Mon, 16 Jan 2017 09:05:27 +0100 Subject: Make sure ReserveKeyFromKeyPool only hands out internal keys if HD_SPLIT is supported --- src/wallet/wallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/wallet/wallet.cpp') diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 15dabad7f..2707567e8 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3001,7 +3001,7 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool int throw std::runtime_error(std::string(__func__) + ": read failed"); if (!HaveKey(tmpKeypool.vchPubKey.GetID())) throw std::runtime_error(std::string(__func__) + ": unknown key in key pool"); - if (!IsHDEnabled() || tmpKeypool.fInternal == internal) + if (!IsHDEnabled() || (tmpKeypool.fInternal == internal && CanSupportFeature(FEATURE_HD_SPLIT))) { nIndex = id; keypool = tmpKeypool; -- cgit v1.2.3 From 9af8f00a7530934d4c9c64eb6c2676ac80b24ace Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Mon, 16 Jan 2017 11:08:00 +0100 Subject: Make sure we hand out keypool keys if HD_SPLIT is not enabled --- src/wallet/wallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/wallet/wallet.cpp') diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 2707567e8..9c5172366 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3001,7 +3001,7 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool int throw std::runtime_error(std::string(__func__) + ": read failed"); if (!HaveKey(tmpKeypool.vchPubKey.GetID())) throw std::runtime_error(std::string(__func__) + ": unknown key in key pool"); - if (!IsHDEnabled() || (tmpKeypool.fInternal == internal && CanSupportFeature(FEATURE_HD_SPLIT))) + if (!IsHDEnabled() || !CanSupportFeature(FEATURE_HD_SPLIT) || tmpKeypool.fInternal == internal) { nIndex = id; keypool = tmpKeypool; -- cgit v1.2.3 From d0a627a53aa8cc5cd190f54c95b888dff88a4d37 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Mon, 16 Jan 2017 11:10:12 +0100 Subject: Fix issue where CDataStream->nVersion was taken a CKeyPool record version --- src/wallet/wallet.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/wallet/wallet.cpp') diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 9c5172366..488e6690c 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3933,6 +3933,7 @@ bool CWallet::BackupWallet(const std::string& strDest) CKeyPool::CKeyPool() { nTime = GetTime(); + fInternal = false; } CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn) -- cgit v1.2.3 From bcafca1077cf789ba79d16501a8cbb5d692bf8e6 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Mon, 16 Jan 2017 11:22:30 +0100 Subject: Make sure we always generate one keypool key at minimum --- src/wallet/wallet.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/wallet/wallet.cpp') diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 488e6690c..e7ac918b2 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2950,9 +2950,9 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize) // generate +20% internal keys (minimum 2 keys) int64_t amountExternal = KeypoolCountExternalKeys(); int64_t amountInternal = setKeyPool.size() - amountExternal; - int64_t targetInternal = max((int64_t)ceil(nTargetSize * 0.2), (int64_t) 2); - int64_t missingExternal = max( (int64_t)(nTargetSize - amountExternal), (int64_t) 0); - int64_t missingInternal = max(targetInternal - amountInternal, (int64_t) 0); + int64_t targetInternal = std::max((int64_t)ceil(nTargetSize * 0.2), (int64_t) 2); + int64_t missingExternal = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - amountExternal, (int64_t) 0); + int64_t missingInternal = std::max(targetInternal - amountInternal, (int64_t) 0); if (!IsHDEnabled() || !CanSupportFeature(FEATURE_HD_SPLIT)) { -- cgit v1.2.3 From 79df9df3482d71391dee1dd59054aed8f7bbfa6b Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Tue, 17 Jan 2017 08:55:30 +0100 Subject: Switch to 100% for the HD internal keypool size --- src/wallet/wallet.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src/wallet/wallet.cpp') diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index e7ac918b2..7ad48e37b 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2946,13 +2946,11 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize) nTargetSize = std::max(GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 0); // count amount of available keys (internal, external) - // make sure the keypool of external keys fits the user selected target (-keypool) - // generate +20% internal keys (minimum 2 keys) + // make sure the keypool of external and internal keys fits the user selected target (-keypool) int64_t amountExternal = KeypoolCountExternalKeys(); int64_t amountInternal = setKeyPool.size() - amountExternal; - int64_t targetInternal = std::max((int64_t)ceil(nTargetSize * 0.2), (int64_t) 2); int64_t missingExternal = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - amountExternal, (int64_t) 0); - int64_t missingInternal = std::max(targetInternal - amountInternal, (int64_t) 0); + int64_t missingInternal = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - amountInternal, (int64_t) 0); if (!IsHDEnabled() || !CanSupportFeature(FEATURE_HD_SPLIT)) { -- cgit v1.2.3 From dd526c2a2d2f0f7427047891a5e94b4e6c18b190 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Tue, 17 Jan 2017 09:07:48 +0100 Subject: Don't switch to HD-chain-split during wallet encryption of non HD-chain-split wallets --- src/wallet/wallet.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src/wallet/wallet.cpp') diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 7ad48e37b..768781079 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1306,10 +1306,6 @@ CPubKey CWallet::GenerateNewHDMasterKey() bool CWallet::SetHDMasterKey(const CPubKey& pubkey) { LOCK(cs_wallet); - - // ensure this wallet.dat can only be opened by clients supporting HD with chain split - SetMinVersion(FEATURE_HD_SPLIT); - // store the keyid (hash160) together with // the child index counter in the database // as a hdchain object @@ -3670,6 +3666,9 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey(); if (!walletInstance->SetHDMasterKey(masterPubKey)) throw std::runtime_error(std::string(__func__) + ": Storing master key failed"); + + // ensure this wallet.dat can only be opened by clients supporting HD with chain split + walletInstance->SetMinVersion(FEATURE_HD_SPLIT); } CPubKey newDefaultKey; if (walletInstance->GetKeyFromPool(newDefaultKey, false)) { -- cgit v1.2.3 From add38d9b83fe570a16ccda8e3c2642cb3b0b1f2f Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Tue, 17 Jan 2017 09:43:12 +0100 Subject: GetOldestKeyPoolTime: if HD & HD Chain Split is enabled, response max(oldest-internal-key, oldest-external-key) --- src/wallet/wallet.cpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'src/wallet/wallet.cpp') diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 768781079..7da45587b 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3056,9 +3056,27 @@ int64_t CWallet::GetOldestKeyPoolTime() if (setKeyPool.empty()) return GetTime(); - // load oldest key from keypool, get time and return CKeyPool keypool; CWalletDB walletdb(strWalletFile); + + if (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT)) + { + // if HD & HD Chain Split is enabled, response max(oldest-internal-key, oldest-external-key) + int64_t now = GetTime(); + int64_t oldest_external = now, oldest_internal = now; + + for(const int64_t& id : setKeyPool) + { + if (!walletdb.ReadPool(id, keypool)) + throw std::runtime_error(std::string(__func__) + ": read failed"); + if (keypool.fInternal && keypool.nTime < oldest_internal) + oldest_internal = keypool.nTime; + else if (!keypool.fInternal && keypool.nTime < oldest_external) + oldest_external = keypool.nTime; + } + return std::max(oldest_internal, oldest_external); + } + // load oldest key from keypool, get time and return int64_t nIndex = *(setKeyPool.begin()); if (!walletdb.ReadPool(nIndex, keypool)) throw std::runtime_error(std::string(__func__) + ": read oldest key in keypool failed"); -- cgit v1.2.3 From d9638e5aa46cd7e5fce4392f5333bbc556014160 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Thu, 26 Jan 2017 21:02:55 +0100 Subject: Overhaul the internal/external key derive switch --- src/wallet/wallet.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'src/wallet/wallet.cpp') diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 7da45587b..733afd49c 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -145,17 +145,19 @@ void CWallet::DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret, bool inter // always derive hardened keys // childIndex | BIP32_HARDENED_KEY_LIMIT = derive childIndex in hardened child-index-range // example: 1 | BIP32_HARDENED_KEY_LIMIT == 0x80000001 == 2147483649 - chainChildKey.Derive(childKey, (internal ? hdChain.nInternalChainCounter : hdChain.nExternalChainCounter) | BIP32_HARDENED_KEY_LIMIT); - metadata.hdKeypath = "m/0'/" + std::string(internal ? "1'/"+ std::to_string(hdChain.nInternalChainCounter) : "0'/" + std::to_string(hdChain.nExternalChainCounter)) + "'"; - metadata.hdMasterKeyID = hdChain.masterKeyID; - // increment childkey index - if (internal) + if (internal) { + chainChildKey.Derive(childKey, hdChain.nInternalChainCounter | BIP32_HARDENED_KEY_LIMIT); + metadata.hdKeypath = "m/0'/1'/" + std::to_string(hdChain.nInternalChainCounter) + "'"; hdChain.nInternalChainCounter++; - else + } + else { + chainChildKey.Derive(childKey, hdChain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT); + metadata.hdKeypath = "m/0'/0'/" + std::to_string(hdChain.nExternalChainCounter) + "'"; hdChain.nExternalChainCounter++; + } } while (HaveKey(childKey.key.GetPubKey().GetID())); secret = childKey.key; - + metadata.hdMasterKeyID = hdChain.masterKeyID; // update the chain model in the database if (!CWalletDB(strWalletFile).WriteHDChain(hdChain)) throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed"); -- cgit v1.2.3 From 771a304ffe3c074a8ca1cdfb83d08379a5516e88 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Fri, 24 Mar 2017 10:53:35 +0100 Subject: Make sure we set the wallets min version to FEATURE_HD_SPLIT at the very first point --- src/wallet/wallet.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/wallet/wallet.cpp') diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 733afd49c..0a0687975 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3682,13 +3682,14 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) { // Create new keyUser and set as default key if (GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET) && !walletInstance->IsHDEnabled()) { + + // ensure this wallet.dat can only be opened by clients supporting HD with chain split + walletInstance->SetMinVersion(FEATURE_HD_SPLIT); + // generate a new master key CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey(); if (!walletInstance->SetHDMasterKey(masterPubKey)) throw std::runtime_error(std::string(__func__) + ": Storing master key failed"); - - // ensure this wallet.dat can only be opened by clients supporting HD with chain split - walletInstance->SetMinVersion(FEATURE_HD_SPLIT); } CPubKey newDefaultKey; if (walletInstance->GetKeyFromPool(newDefaultKey, false)) { -- cgit v1.2.3 From ed79e4f497a9a7747b006a5cb04d4173a6bbc1c5 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Fri, 24 Mar 2017 10:54:48 +0100 Subject: Optimize GetOldestKeyPoolTime(), return as soon as we have both oldest keys --- src/wallet/wallet.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'src/wallet/wallet.cpp') diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 0a0687975..d7d2927ba 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3069,12 +3069,18 @@ int64_t CWallet::GetOldestKeyPoolTime() for(const int64_t& id : setKeyPool) { - if (!walletdb.ReadPool(id, keypool)) + if (!walletdb.ReadPool(id, keypool)) { throw std::runtime_error(std::string(__func__) + ": read failed"); - if (keypool.fInternal && keypool.nTime < oldest_internal) + } + if (keypool.fInternal && keypool.nTime < oldest_internal) { oldest_internal = keypool.nTime; - else if (!keypool.fInternal && keypool.nTime < oldest_external) + } + else if (!keypool.fInternal && keypool.nTime < oldest_external) { oldest_external = keypool.nTime; + } + if (oldest_internal != now && oldest_external != now) { + break; + } } return std::max(oldest_internal, oldest_external); } -- cgit v1.2.3 From 1df08d1580fbab9e58017cf6c1ecb73550bf6ed7 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Fri, 24 Mar 2017 10:57:55 +0100 Subject: Add assertion for CanSupportFeature(FEATURE_HD_SPLIT) --- src/wallet/wallet.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/wallet/wallet.cpp') diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index d7d2927ba..4e625f64d 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -138,6 +138,7 @@ void CWallet::DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret, bool inter masterKey.Derive(accountKey, BIP32_HARDENED_KEY_LIMIT); // derive m/0'/0' (external chain) OR m/0'/1' (internal chain) + assert(internal ? CanSupportFeature(FEATURE_HD_SPLIT) : true); accountKey.Derive(chainChildKey, BIP32_HARDENED_KEY_LIMIT+(internal ? 1 : 0)); // derive child key at next index, skip keys already known to the wallet -- cgit v1.2.3 From 9382f0425e87b30e4621e0e23a99d6e880ec2200 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Mon, 27 Mar 2017 09:51:55 +0200 Subject: Do not break backward compatibility during wallet encryption --- src/wallet/wallet.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src/wallet/wallet.cpp') diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 4e625f64d..a99817958 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -639,7 +639,9 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) if (IsHDEnabled()) { CKey key; CPubKey masterPubKey = GenerateNewHDMasterKey(); - if (!SetHDMasterKey(masterPubKey)) + // preserve the old chains version to not break backward compatibility + CHDChain oldChain = GetHDChain(); + if (!SetHDMasterKey(masterPubKey, &oldChain)) return false; } @@ -1306,13 +1308,17 @@ CPubKey CWallet::GenerateNewHDMasterKey() return pubkey; } -bool CWallet::SetHDMasterKey(const CPubKey& pubkey) +bool CWallet::SetHDMasterKey(const CPubKey& pubkey, CHDChain *possibleOldChain) { LOCK(cs_wallet); // store the keyid (hash160) together with // the child index counter in the database // as a hdchain object CHDChain newHdChain; + if (possibleOldChain) { + // preserve the old chains version + newHdChain.nVersion = possibleOldChain->nVersion; + } newHdChain.masterKeyID = pubkey.GetID(); SetHDChain(newHdChain, false); -- cgit v1.2.3