diff options
author | Pieter Wuille <pieter.wuille@gmail.com> | 2017-07-15 13:30:48 -0700 |
---|---|---|
committer | Pieter Wuille <pieter.wuille@gmail.com> | 2017-07-15 14:02:05 -0700 |
commit | 5cfdda2503c995cdd563b1a2a29162ac298d173d (patch) | |
tree | 8454d0ec86b4d8807eb17d411e38c24021723467 | |
parent | c5904e871479514b2e2e18b4fdbbe468c4e5ec8e (diff) | |
parent | d40a72ccbb71d61b43cbf4d222ca2ab5d3ca7510 (diff) |
Merge #10235: Track keypool entries as internal vs external in memory
d40a72ccb Clarify *(--.end()) iterator semantics in CWallet::TopUpKeyPool (Matt Corallo)
28301b978 Meet code style on lines changed in the previous commit (Matt Corallo)
4a3fc3562 Track keypool entries as internal vs external in memory (Matt Corallo)
Pull request description:
This is an alternative version of #10184. As @jonasschnelli points out there, the performance regressions are pretty minimal, but given that this is a pretty simple, mechanical change, its probably worth doing.
Tree-SHA512: e83f9ebf2998f8164d1b2eebe5e6dcdeadea8c30b7612861f830758c08bf4093cd6a67b3bcfa9cfcb139e5e0b106fc8898a975fc69f334981aefc756568ab613
-rw-r--r-- | src/wallet/wallet.cpp | 199 | ||||
-rw-r--r-- | src/wallet/wallet.h | 19 |
2 files changed, 115 insertions, 103 deletions
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 07b7f58a64..5689cc7b0c 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2977,7 +2977,8 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet) if (dbw->Rewrite("\x04pool")) { LOCK(cs_wallet); - setKeyPool.clear(); + setInternalKeyPool.clear(); + setExternalKeyPool.clear(); // Note: can't top-up keypool here, because wallet is locked. // User will be prompted to unlock wallet the next operation // that requires a new key. @@ -3005,7 +3006,8 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256 { if (dbw->Rewrite("\x04pool")) { - setKeyPool.clear(); + setInternalKeyPool.clear(); + setExternalKeyPool.clear(); // Note: can't top-up keypool here, because wallet is locked. // User will be prompted to unlock wallet the next operation // that requires a new key. @@ -3030,7 +3032,8 @@ DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx) if (dbw->Rewrite("\x04pool")) { LOCK(cs_wallet); - setKeyPool.clear(); + setInternalKeyPool.clear(); + setExternalKeyPool.clear(); // Note: can't top-up keypool here, because wallet is locked. // User will be prompted to unlock wallet the next operation // that requires a new key. @@ -3114,9 +3117,16 @@ bool CWallet::NewKeyPool() { LOCK(cs_wallet); CWalletDB walletdb(*dbw); - for (int64_t nIndex : setKeyPool) + + for (int64_t nIndex : setInternalKeyPool) { + walletdb.ErasePool(nIndex); + } + setInternalKeyPool.clear(); + + for (int64_t nIndex : setExternalKeyPool) { walletdb.ErasePool(nIndex); - setKeyPool.clear(); + } + setExternalKeyPool.clear(); if (!TopUpKeyPool()) { return false; @@ -3128,25 +3138,8 @@ bool CWallet::NewKeyPool() 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(*dbw); - - // 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; + AssertLockHeld(cs_wallet); // setExternalKeyPool + return setExternalKeyPool.size(); } bool CWallet::TopUpKeyPool(unsigned int kpSize) @@ -3166,10 +3159,8 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize) // count amount of available keys (internal, external) // 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 missingExternal = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - amountExternal, (int64_t) 0); - int64_t missingInternal = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - amountInternal, (int64_t) 0); + int64_t missingExternal = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - (int64_t)setExternalKeyPool.size(), (int64_t) 0); + int64_t missingInternal = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - (int64_t)setInternalKeyPool.size(), (int64_t) 0); if (!IsHDEnabled() || !CanSupportFeature(FEATURE_HD_SPLIT)) { @@ -3181,20 +3172,32 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize) for (int64_t i = missingInternal + missingExternal; i--;) { int64_t nEnd = 1; - if (i < missingInternal) + if (i < missingInternal) { internal = true; - if (!setKeyPool.empty()) - nEnd = *(--setKeyPool.end()) + 1; + } + + if (!setInternalKeyPool.empty()) { + nEnd = *(setInternalKeyPool.rbegin()) + 1; + } + if (!setExternalKeyPool.empty()) { + nEnd = std::max(nEnd, *(setExternalKeyPool.rbegin()) + 1); + } + 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, internal=%d\n", nEnd, setKeyPool.size(), internal); + + if (internal) { + setInternalKeyPool.insert(nEnd); + } else { + setExternalKeyPool.insert(nEnd); + } + LogPrintf("keypool added key %d, size=%u (%u internal), new key is %s\n", nEnd, setInternalKeyPool.size() + setExternalKeyPool.size(), setInternalKeyPool.size(), internal ? "internal" : "external"); } } return true; } -void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool internal) +void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal) { nIndex = -1; keypool.vchPubKey = CPubKey(); @@ -3204,30 +3207,30 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool int if (!IsLocked()) TopUpKeyPool(); + bool fReturningInternal = IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT) && fRequestedInternal; + std::set<int64_t>& setKeyPool = fReturningInternal ? setInternalKeyPool : setExternalKeyPool; + // Get the oldest key if(setKeyPool.empty()) return; CWalletDB walletdb(*dbw); - // 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() || !CanSupportFeature(FEATURE_HD_SPLIT) || tmpKeypool.fInternal == internal) - { - nIndex = id; - keypool = tmpKeypool; - setKeyPool.erase(id); - assert(keypool.vchPubKey.IsValid()); - LogPrintf("keypool reserve %d\n", nIndex); - return; - } + auto it = setKeyPool.begin(); + nIndex = *it; + setKeyPool.erase(it); + 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"); } + if (keypool.fInternal != fReturningInternal) { + throw std::runtime_error(std::string(__func__) + ": keypool entry misclassified"); + } + + assert(keypool.vchPubKey.IsValid()); + LogPrintf("keypool reserve %d\n", nIndex); } } @@ -3239,12 +3242,16 @@ void CWallet::KeepKey(int64_t nIndex) LogPrintf("keypool keep %d\n", nIndex); } -void CWallet::ReturnKey(int64_t nIndex) +void CWallet::ReturnKey(int64_t nIndex, bool fInternal) { // Return to key pool { LOCK(cs_wallet); - setKeyPool.insert(nIndex); + if (fInternal) { + setInternalKeyPool.insert(nIndex); + } else { + setExternalKeyPool.insert(nIndex); + } } LogPrintf("keypool return %d\n", nIndex); } @@ -3268,48 +3275,35 @@ bool CWallet::GetKeyFromPool(CPubKey& result, bool internal) return true; } -int64_t CWallet::GetOldestKeyPoolTime() -{ - LOCK(cs_wallet); - - // if the keypool is empty, return <NOW> - if (setKeyPool.empty()) +static int64_t GetOldestKeyTimeInPool(const std::set<int64_t>& setKeyPool, CWalletDB& walletdb) { + if (setKeyPool.empty()) { return GetTime(); + } CKeyPool keypool; - CWalletDB walletdb(*dbw); - - 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; - } - if (oldest_internal != now && oldest_external != now) { - break; - } - } - 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)) + if (!walletdb.ReadPool(nIndex, keypool)) { throw std::runtime_error(std::string(__func__) + ": read oldest key in keypool failed"); + } assert(keypool.vchPubKey.IsValid()); return keypool.nTime; } +int64_t CWallet::GetOldestKeyPoolTime() +{ + LOCK(cs_wallet); + + CWalletDB walletdb(*dbw); + + // load oldest key from keypool, get time and return + int64_t oldestKey = GetOldestKeyTimeInPool(setExternalKeyPool, walletdb); + if (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT)) { + oldestKey = std::max(GetOldestKeyTimeInPool(setInternalKeyPool, walletdb), oldestKey); + } + + return oldestKey; +} + std::map<CTxDestination, CAmount> CWallet::GetAddressBalances() { std::map<CTxDestination, CAmount> balances; @@ -3468,6 +3462,7 @@ bool CReserveKey::GetReservedKey(CPubKey& pubkey, bool internal) else { return false; } + fInternal = keypool.fInternal; } assert(vchPubKey.IsValid()); pubkey = vchPubKey; @@ -3484,19 +3479,14 @@ void CReserveKey::KeepKey() void CReserveKey::ReturnKey() { - if (nIndex != -1) - pwallet->ReturnKey(nIndex); + if (nIndex != -1) { + pwallet->ReturnKey(nIndex, fInternal); + } nIndex = -1; vchPubKey = CPubKey(); } -void CWallet::GetAllReserveKeys(std::set<CKeyID>& setAddress) const -{ - setAddress.clear(); - - CWalletDB walletdb(*dbw); - - LOCK2(cs_main, cs_wallet); +static void LoadReserveKeysToSet(std::set<CKeyID>& setAddress, const std::set<int64_t>& setKeyPool, CWalletDB& walletdb) { for (const int64_t& id : setKeyPool) { CKeyPool keypool; @@ -3504,12 +3494,27 @@ void CWallet::GetAllReserveKeys(std::set<CKeyID>& setAddress) const throw std::runtime_error(std::string(__func__) + ": read failed"); assert(keypool.vchPubKey.IsValid()); CKeyID keyID = keypool.vchPubKey.GetID(); - if (!HaveKey(keyID)) - throw std::runtime_error(std::string(__func__) + ": unknown key in key pool"); setAddress.insert(keyID); } } +void CWallet::GetAllReserveKeys(std::set<CKeyID>& setAddress) const +{ + setAddress.clear(); + + CWalletDB walletdb(*dbw); + + LOCK2(cs_main, cs_wallet); + LoadReserveKeysToSet(setAddress, setInternalKeyPool, walletdb); + LoadReserveKeysToSet(setAddress, setExternalKeyPool, walletdb); + + for (const CKeyID& keyID : setAddress) { + if (!HaveKey(keyID)) { + throw std::runtime_error(std::string(__func__) + ": unknown key in key pool"); + } + } +} + void CWallet::GetScriptForMining(std::shared_ptr<CReserveScript> &script) { std::shared_ptr<CReserveKey> rKey = std::make_shared<CReserveKey>(this); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index e3715cdf37..8848448682 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -699,7 +699,8 @@ private: /* HD derive new child key (on internal or external chain) */ void DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret, bool internal = false); - std::set<int64_t> setKeyPool; + std::set<int64_t> setInternalKeyPool; + std::set<int64_t> setExternalKeyPool; int64_t nTimeFirstKey; @@ -744,7 +745,11 @@ public: void LoadKeyPool(int nIndex, const CKeyPool &keypool) { - setKeyPool.insert(nIndex); + if (keypool.fInternal) { + setInternalKeyPool.insert(nIndex); + } else { + setExternalKeyPool.insert(nIndex); + } // If no metadata exists yet, create a default with the pool key's // creation time. Note that this may be overwritten by actually @@ -974,9 +979,9 @@ public: bool NewKeyPool(); size_t KeypoolCountExternalKeys(); bool TopUpKeyPool(unsigned int kpSize = 0); - void ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool internal); + void ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal); void KeepKey(int64_t nIndex); - void ReturnKey(int64_t nIndex); + void ReturnKey(int64_t nIndex, bool fInternal); bool GetKeyFromPool(CPubKey &key, bool internal = false); int64_t GetOldestKeyPoolTime(); void GetAllReserveKeys(std::set<CKeyID>& setAddress) const; @@ -1030,8 +1035,8 @@ public: unsigned int GetKeyPoolSize() { - AssertLockHeld(cs_wallet); // setKeyPool - return setKeyPool.size(); + AssertLockHeld(cs_wallet); // set{Ex,In}ternalKeyPool + return setInternalKeyPool.size() + setExternalKeyPool.size(); } bool SetDefaultKey(const CPubKey &vchPubKey); @@ -1135,11 +1140,13 @@ protected: CWallet* pwallet; int64_t nIndex; CPubKey vchPubKey; + bool fInternal; public: CReserveKey(CWallet* pwalletIn) { nIndex = -1; pwallet = pwalletIn; + fInternal = false; } CReserveKey() = default; |