Track keypool entries as internal vs external in memory #10235
Merged
sipa
merged 3 commits into
bitcoin:master
from
TheBlueMatt:2017-04-wallet-more-keypool-cache
Jul 15, 2017
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
Jump to file or symbol
Failed to load files and symbols.
| @@ -2941,7 +2941,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. | ||
| @@ -2969,7 +2970,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. | ||
| @@ -2994,7 +2996,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. | ||
| @@ -3078,9 +3081,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; | ||
| @@ -3092,25 +3102,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) | ||
| @@ -3130,10 +3123,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)) | ||
| { | ||
| @@ -3145,20 +3136,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(); | ||
| @@ -3168,30 +3171,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); | ||
jonasschnelli
Member
|
||
| + 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); | ||
| } | ||
| } | ||
| @@ -3203,12 +3206,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); | ||
| } | ||
| @@ -3232,48 +3239,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; | ||
| @@ -3432,6 +3426,7 @@ bool CReserveKey::GetReservedKey(CPubKey& pubkey, bool internal) | ||
| else { | ||
| return false; | ||
| } | ||
| + fInternal = keypool.fInternal; | ||
| } | ||
| assert(vchPubKey.IsValid()); | ||
| pubkey = vchPubKey; | ||
| @@ -3448,32 +3443,42 @@ 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; | ||
| if (!walletdb.ReadPool(id, keypool)) | ||
| 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); | ||
I think you can also remove this one completely