Track keypool entries as internal vs external in memory

This resolves a super minor performance regressions in several
keypool-handling functions
This commit is contained in:
Matt Corallo 2017-04-19 12:55:32 -04:00
parent d083bd9b9c
commit 4a3fc35629
2 changed files with 102 additions and 100 deletions

View file

@ -2941,7 +2941,8 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
if (dbw->Rewrite("\x04pool")) if (dbw->Rewrite("\x04pool"))
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
setKeyPool.clear(); setInternalKeyPool.clear();
setExternalKeyPool.clear();
// Note: can't top-up keypool here, because wallet is locked. // Note: can't top-up keypool here, because wallet is locked.
// User will be prompted to unlock wallet the next operation // User will be prompted to unlock wallet the next operation
// that requires a new key. // that requires a new key.
@ -2969,7 +2970,8 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256
{ {
if (dbw->Rewrite("\x04pool")) if (dbw->Rewrite("\x04pool"))
{ {
setKeyPool.clear(); setInternalKeyPool.clear();
setExternalKeyPool.clear();
// Note: can't top-up keypool here, because wallet is locked. // Note: can't top-up keypool here, because wallet is locked.
// User will be prompted to unlock wallet the next operation // User will be prompted to unlock wallet the next operation
// that requires a new key. // that requires a new key.
@ -2994,7 +2996,8 @@ DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx)
if (dbw->Rewrite("\x04pool")) if (dbw->Rewrite("\x04pool"))
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
setKeyPool.clear(); setInternalKeyPool.clear();
setExternalKeyPool.clear();
// Note: can't top-up keypool here, because wallet is locked. // Note: can't top-up keypool here, because wallet is locked.
// User will be prompted to unlock wallet the next operation // User will be prompted to unlock wallet the next operation
// that requires a new key. // that requires a new key.
@ -3078,9 +3081,12 @@ bool CWallet::NewKeyPool()
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
CWalletDB walletdb(*dbw); CWalletDB walletdb(*dbw);
for (int64_t nIndex : setKeyPool) for (int64_t nIndex : setInternalKeyPool)
walletdb.ErasePool(nIndex); walletdb.ErasePool(nIndex);
setKeyPool.clear(); setInternalKeyPool.clear();
BOOST_FOREACH(int64_t nIndex, setExternalKeyPool)
walletdb.ErasePool(nIndex);
setExternalKeyPool.clear();
if (!TopUpKeyPool()) { if (!TopUpKeyPool()) {
return false; return false;
@ -3092,25 +3098,8 @@ bool CWallet::NewKeyPool()
size_t CWallet::KeypoolCountExternalKeys() size_t CWallet::KeypoolCountExternalKeys()
{ {
AssertLockHeld(cs_wallet); // setKeyPool AssertLockHeld(cs_wallet); // setExternalKeyPool
return setExternalKeyPool.size();
// 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;
} }
bool CWallet::TopUpKeyPool(unsigned int kpSize) bool CWallet::TopUpKeyPool(unsigned int kpSize)
@ -3130,10 +3119,8 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
// count amount of available keys (internal, external) // count amount of available keys (internal, external)
// make sure the keypool of external and internal keys fits the user selected target (-keypool) // make sure the keypool of external and internal keys fits the user selected target (-keypool)
int64_t amountExternal = KeypoolCountExternalKeys(); int64_t missingExternal = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - (int64_t)setExternalKeyPool.size(), (int64_t) 0);
int64_t amountInternal = setKeyPool.size() - amountExternal; int64_t missingInternal = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - (int64_t)setInternalKeyPool.size(), (int64_t) 0);
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);
if (!IsHDEnabled() || !CanSupportFeature(FEATURE_HD_SPLIT)) if (!IsHDEnabled() || !CanSupportFeature(FEATURE_HD_SPLIT))
{ {
@ -3147,18 +3134,26 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
int64_t nEnd = 1; int64_t nEnd = 1;
if (i < missingInternal) if (i < missingInternal)
internal = true; internal = true;
if (!setKeyPool.empty()) if (!setInternalKeyPool.empty())
nEnd = *(--setKeyPool.end()) + 1; nEnd = *(--setInternalKeyPool.end()) + 1;
if (!setExternalKeyPool.empty())
nEnd = std::max(nEnd, *(--setExternalKeyPool.end()) + 1);
if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey(internal), internal))) if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey(internal), internal)))
throw std::runtime_error(std::string(__func__) + ": writing generated key failed"); 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; return true;
} }
void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool internal) void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal)
{ {
nIndex = -1; nIndex = -1;
keypool.vchPubKey = CPubKey(); keypool.vchPubKey = CPubKey();
@ -3168,30 +3163,30 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool int
if (!IsLocked()) if (!IsLocked())
TopUpKeyPool(); TopUpKeyPool();
bool fReturningInternal = IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT) && fRequestedInternal;
std::set<int64_t>& setKeyPool = fReturningInternal ? setInternalKeyPool : setExternalKeyPool;
// Get the oldest key // Get the oldest key
if(setKeyPool.empty()) if(setKeyPool.empty())
return; return;
CWalletDB walletdb(*dbw); CWalletDB walletdb(*dbw);
// try to find a key that matches the internal/external filter auto it = setKeyPool.begin();
for(const int64_t& id : setKeyPool) nIndex = *it;
{ setKeyPool.erase(it);
CKeyPool tmpKeypool; if (!walletdb.ReadPool(nIndex, keypool)) {
if (!walletdb.ReadPool(id, tmpKeypool))
throw std::runtime_error(std::string(__func__) + ": read failed"); throw std::runtime_error(std::string(__func__) + ": read failed");
if (!HaveKey(tmpKeypool.vchPubKey.GetID())) }
if (!HaveKey(keypool.vchPubKey.GetID())) {
throw std::runtime_error(std::string(__func__) + ": unknown key in key pool"); throw std::runtime_error(std::string(__func__) + ": unknown key in key pool");
if (!IsHDEnabled() || !CanSupportFeature(FEATURE_HD_SPLIT) || tmpKeypool.fInternal == internal) }
{ if (keypool.fInternal != fReturningInternal) {
nIndex = id; throw std::runtime_error(std::string(__func__) + ": keypool entry misclassified");
keypool = tmpKeypool; }
setKeyPool.erase(id);
assert(keypool.vchPubKey.IsValid()); assert(keypool.vchPubKey.IsValid());
LogPrintf("keypool reserve %d\n", nIndex); LogPrintf("keypool reserve %d\n", nIndex);
return;
}
}
} }
} }
@ -3203,12 +3198,16 @@ void CWallet::KeepKey(int64_t nIndex)
LogPrintf("keypool keep %d\n", 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 // Return to key pool
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
setKeyPool.insert(nIndex); if (fInternal) {
setInternalKeyPool.insert(nIndex);
} else {
setExternalKeyPool.insert(nIndex);
}
} }
LogPrintf("keypool return %d\n", nIndex); LogPrintf("keypool return %d\n", nIndex);
} }
@ -3232,41 +3231,12 @@ bool CWallet::GetKeyFromPool(CPubKey& result, bool internal)
return true; return true;
} }
int64_t CWallet::GetOldestKeyPoolTime() static int64_t GetOldestKeyTimeInPool(const std::set<int64_t>& setKeyPool, CWalletDB& walletdb) {
{ if (setKeyPool.empty()) {
LOCK(cs_wallet);
// if the keypool is empty, return <NOW>
if (setKeyPool.empty())
return GetTime(); return GetTime();
}
CKeyPool keypool; 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()); 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"); throw std::runtime_error(std::string(__func__) + ": read oldest key in keypool failed");
@ -3274,6 +3244,21 @@ int64_t CWallet::GetOldestKeyPoolTime()
return keypool.nTime; 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> CWallet::GetAddressBalances()
{ {
std::map<CTxDestination, CAmount> balances; std::map<CTxDestination, CAmount> balances;
@ -3432,6 +3417,7 @@ bool CReserveKey::GetReservedKey(CPubKey& pubkey, bool internal)
else { else {
return false; return false;
} }
fInternal = keypool.fInternal;
} }
assert(vchPubKey.IsValid()); assert(vchPubKey.IsValid());
pubkey = vchPubKey; pubkey = vchPubKey;
@ -3449,11 +3435,23 @@ void CReserveKey::KeepKey()
void CReserveKey::ReturnKey() void CReserveKey::ReturnKey()
{ {
if (nIndex != -1) if (nIndex != -1)
pwallet->ReturnKey(nIndex); pwallet->ReturnKey(nIndex, fInternal);
nIndex = -1; nIndex = -1;
vchPubKey = CPubKey(); vchPubKey = CPubKey();
} }
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();
setAddress.insert(keyID);
}
}
void CWallet::GetAllReserveKeys(std::set<CKeyID>& setAddress) const void CWallet::GetAllReserveKeys(std::set<CKeyID>& setAddress) const
{ {
setAddress.clear(); setAddress.clear();
@ -3461,16 +3459,13 @@ void CWallet::GetAllReserveKeys(std::set<CKeyID>& setAddress) const
CWalletDB walletdb(*dbw); CWalletDB walletdb(*dbw);
LOCK2(cs_main, cs_wallet); LOCK2(cs_main, cs_wallet);
for (const int64_t& id : setKeyPool) LoadReserveKeysToSet(setAddress, setInternalKeyPool, walletdb);
{ LoadReserveKeysToSet(setAddress, setExternalKeyPool, walletdb);
CKeyPool keypool;
if (!walletdb.ReadPool(id, keypool)) for (const CKeyID& keyID : setAddress) {
throw std::runtime_error(std::string(__func__) + ": read failed"); if (!HaveKey(keyID)) {
assert(keypool.vchPubKey.IsValid());
CKeyID keyID = keypool.vchPubKey.GetID();
if (!HaveKey(keyID))
throw std::runtime_error(std::string(__func__) + ": unknown key in key pool"); throw std::runtime_error(std::string(__func__) + ": unknown key in key pool");
setAddress.insert(keyID); }
} }
} }

View file

@ -696,7 +696,8 @@ private:
/* HD derive new child key (on internal or external chain) */ /* HD derive new child key (on internal or external chain) */
void DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret, bool internal = false); 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; int64_t nTimeFirstKey;
@ -741,7 +742,11 @@ public:
void LoadKeyPool(int nIndex, const CKeyPool &keypool) 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 // If no metadata exists yet, create a default with the pool key's
// creation time. Note that this may be overwritten by actually // creation time. Note that this may be overwritten by actually
@ -970,9 +975,9 @@ public:
bool NewKeyPool(); bool NewKeyPool();
size_t KeypoolCountExternalKeys(); size_t KeypoolCountExternalKeys();
bool TopUpKeyPool(unsigned int kpSize = 0); 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 KeepKey(int64_t nIndex);
void ReturnKey(int64_t nIndex); void ReturnKey(int64_t nIndex, bool fInternal);
bool GetKeyFromPool(CPubKey &key, bool internal = false); bool GetKeyFromPool(CPubKey &key, bool internal = false);
int64_t GetOldestKeyPoolTime(); int64_t GetOldestKeyPoolTime();
void GetAllReserveKeys(std::set<CKeyID>& setAddress) const; void GetAllReserveKeys(std::set<CKeyID>& setAddress) const;
@ -1026,8 +1031,8 @@ public:
unsigned int GetKeyPoolSize() unsigned int GetKeyPoolSize()
{ {
AssertLockHeld(cs_wallet); // setKeyPool AssertLockHeld(cs_wallet); // set{Ex,In}ternalKeyPool
return setKeyPool.size(); return setInternalKeyPool.size() + setExternalKeyPool.size();
} }
bool SetDefaultKey(const CPubKey &vchPubKey); bool SetDefaultKey(const CPubKey &vchPubKey);
@ -1131,11 +1136,13 @@ protected:
CWallet* pwallet; CWallet* pwallet;
int64_t nIndex; int64_t nIndex;
CPubKey vchPubKey; CPubKey vchPubKey;
bool fInternal;
public: public:
CReserveKey(CWallet* pwalletIn) CReserveKey(CWallet* pwalletIn)
{ {
nIndex = -1; nIndex = -1;
pwallet = pwalletIn; pwallet = pwalletIn;
fInternal = false;
} }
CReserveKey() = default; CReserveKey() = default;