Skip to content

Commit 4a3fc35

Browse files
committed
Track keypool entries as internal vs external in memory
This resolves a super minor performance regressions in several keypool-handling functions
1 parent d083bd9 commit 4a3fc35

File tree

2 files changed

+102
-100
lines changed

2 files changed

+102
-100
lines changed

src/wallet/wallet.cpp

Lines changed: 89 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -2941,7 +2941,8 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
29412941
if (dbw->Rewrite("\x04pool"))
29422942
{
29432943
LOCK(cs_wallet);
2944-
setKeyPool.clear();
2944+
setInternalKeyPool.clear();
2945+
setExternalKeyPool.clear();
29452946
// Note: can't top-up keypool here, because wallet is locked.
29462947
// User will be prompted to unlock wallet the next operation
29472948
// that requires a new key.
@@ -2969,7 +2970,8 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256
29692970
{
29702971
if (dbw->Rewrite("\x04pool"))
29712972
{
2972-
setKeyPool.clear();
2973+
setInternalKeyPool.clear();
2974+
setExternalKeyPool.clear();
29732975
// Note: can't top-up keypool here, because wallet is locked.
29742976
// User will be prompted to unlock wallet the next operation
29752977
// that requires a new key.
@@ -2994,7 +2996,8 @@ DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx)
29942996
if (dbw->Rewrite("\x04pool"))
29952997
{
29962998
LOCK(cs_wallet);
2997-
setKeyPool.clear();
2999+
setInternalKeyPool.clear();
3000+
setExternalKeyPool.clear();
29983001
// Note: can't top-up keypool here, because wallet is locked.
29993002
// User will be prompted to unlock wallet the next operation
30003003
// that requires a new key.
@@ -3078,9 +3081,12 @@ bool CWallet::NewKeyPool()
30783081
{
30793082
LOCK(cs_wallet);
30803083
CWalletDB walletdb(*dbw);
3081-
for (int64_t nIndex : setKeyPool)
3084+
for (int64_t nIndex : setInternalKeyPool)
30823085
walletdb.ErasePool(nIndex);
3083-
setKeyPool.clear();
3086+
setInternalKeyPool.clear();
3087+
BOOST_FOREACH(int64_t nIndex, setExternalKeyPool)
3088+
walletdb.ErasePool(nIndex);
3089+
setExternalKeyPool.clear();
30843090

30853091
if (!TopUpKeyPool()) {
30863092
return false;
@@ -3092,25 +3098,8 @@ bool CWallet::NewKeyPool()
30923098

30933099
size_t CWallet::KeypoolCountExternalKeys()
30943100
{
3095-
AssertLockHeld(cs_wallet); // setKeyPool
3096-
3097-
// immediately return setKeyPool's size if HD or HD_SPLIT is disabled or not supported
3098-
if (!IsHDEnabled() || !CanSupportFeature(FEATURE_HD_SPLIT))
3099-
return setKeyPool.size();
3100-
3101-
CWalletDB walletdb(*dbw);
3102-
3103-
// count amount of external keys
3104-
size_t amountE = 0;
3105-
for(const int64_t& id : setKeyPool)
3106-
{
3107-
CKeyPool tmpKeypool;
3108-
if (!walletdb.ReadPool(id, tmpKeypool))
3109-
throw std::runtime_error(std::string(__func__) + ": read failed");
3110-
amountE += !tmpKeypool.fInternal;
3111-
}
3112-
3113-
return amountE;
3101+
AssertLockHeld(cs_wallet); // setExternalKeyPool
3102+
return setExternalKeyPool.size();
31143103
}
31153104

31163105
bool CWallet::TopUpKeyPool(unsigned int kpSize)
@@ -3130,10 +3119,8 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
31303119

31313120
// count amount of available keys (internal, external)
31323121
// make sure the keypool of external and internal keys fits the user selected target (-keypool)
3133-
int64_t amountExternal = KeypoolCountExternalKeys();
3134-
int64_t amountInternal = setKeyPool.size() - amountExternal;
3135-
int64_t missingExternal = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - amountExternal, (int64_t) 0);
3136-
int64_t missingInternal = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - amountInternal, (int64_t) 0);
3122+
int64_t missingExternal = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - (int64_t)setExternalKeyPool.size(), (int64_t) 0);
3123+
int64_t missingInternal = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - (int64_t)setInternalKeyPool.size(), (int64_t) 0);
31373124

31383125
if (!IsHDEnabled() || !CanSupportFeature(FEATURE_HD_SPLIT))
31393126
{
@@ -3147,18 +3134,26 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
31473134
int64_t nEnd = 1;
31483135
if (i < missingInternal)
31493136
internal = true;
3150-
if (!setKeyPool.empty())
3151-
nEnd = *(--setKeyPool.end()) + 1;
3137+
if (!setInternalKeyPool.empty())
3138+
nEnd = *(--setInternalKeyPool.end()) + 1;
3139+
if (!setExternalKeyPool.empty())
3140+
nEnd = std::max(nEnd, *(--setExternalKeyPool.end()) + 1);
3141+
31523142
if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey(internal), internal)))
31533143
throw std::runtime_error(std::string(__func__) + ": writing generated key failed");
3154-
setKeyPool.insert(nEnd);
3155-
LogPrintf("keypool added key %d, size=%u, internal=%d\n", nEnd, setKeyPool.size(), internal);
3144+
3145+
if (internal) {
3146+
setInternalKeyPool.insert(nEnd);
3147+
} else {
3148+
setExternalKeyPool.insert(nEnd);
3149+
}
3150+
LogPrintf("keypool added key %d, size=%u (%u internal), new key is %s\n", nEnd, setInternalKeyPool.size() + setExternalKeyPool.size(), setInternalKeyPool.size(), internal ? "internal" : "external");
31563151
}
31573152
}
31583153
return true;
31593154
}
31603155

3161-
void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool internal)
3156+
void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal)
31623157
{
31633158
nIndex = -1;
31643159
keypool.vchPubKey = CPubKey();
@@ -3168,30 +3163,30 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool int
31683163
if (!IsLocked())
31693164
TopUpKeyPool();
31703165

3166+
bool fReturningInternal = IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT) && fRequestedInternal;
3167+
std::set<int64_t>& setKeyPool = fReturningInternal ? setInternalKeyPool : setExternalKeyPool;
3168+
31713169
// Get the oldest key
31723170
if(setKeyPool.empty())
31733171
return;
31743172

31753173
CWalletDB walletdb(*dbw);
31763174

3177-
// try to find a key that matches the internal/external filter
3178-
for(const int64_t& id : setKeyPool)
3179-
{
3180-
CKeyPool tmpKeypool;
3181-
if (!walletdb.ReadPool(id, tmpKeypool))
3182-
throw std::runtime_error(std::string(__func__) + ": read failed");
3183-
if (!HaveKey(tmpKeypool.vchPubKey.GetID()))
3184-
throw std::runtime_error(std::string(__func__) + ": unknown key in key pool");
3185-
if (!IsHDEnabled() || !CanSupportFeature(FEATURE_HD_SPLIT) || tmpKeypool.fInternal == internal)
3186-
{
3187-
nIndex = id;
3188-
keypool = tmpKeypool;
3189-
setKeyPool.erase(id);
3190-
assert(keypool.vchPubKey.IsValid());
3191-
LogPrintf("keypool reserve %d\n", nIndex);
3192-
return;
3193-
}
3175+
auto it = setKeyPool.begin();
3176+
nIndex = *it;
3177+
setKeyPool.erase(it);
3178+
if (!walletdb.ReadPool(nIndex, keypool)) {
3179+
throw std::runtime_error(std::string(__func__) + ": read failed");
31943180
}
3181+
if (!HaveKey(keypool.vchPubKey.GetID())) {
3182+
throw std::runtime_error(std::string(__func__) + ": unknown key in key pool");
3183+
}
3184+
if (keypool.fInternal != fReturningInternal) {
3185+
throw std::runtime_error(std::string(__func__) + ": keypool entry misclassified");
3186+
}
3187+
3188+
assert(keypool.vchPubKey.IsValid());
3189+
LogPrintf("keypool reserve %d\n", nIndex);
31953190
}
31963191
}
31973192

@@ -3203,12 +3198,16 @@ void CWallet::KeepKey(int64_t nIndex)
32033198
LogPrintf("keypool keep %d\n", nIndex);
32043199
}
32053200

3206-
void CWallet::ReturnKey(int64_t nIndex)
3201+
void CWallet::ReturnKey(int64_t nIndex, bool fInternal)
32073202
{
32083203
// Return to key pool
32093204
{
32103205
LOCK(cs_wallet);
3211-
setKeyPool.insert(nIndex);
3206+
if (fInternal) {
3207+
setInternalKeyPool.insert(nIndex);
3208+
} else {
3209+
setExternalKeyPool.insert(nIndex);
3210+
}
32123211
}
32133212
LogPrintf("keypool return %d\n", nIndex);
32143213
}
@@ -3232,48 +3231,34 @@ bool CWallet::GetKeyFromPool(CPubKey& result, bool internal)
32323231
return true;
32333232
}
32343233

3235-
int64_t CWallet::GetOldestKeyPoolTime()
3236-
{
3237-
LOCK(cs_wallet);
3238-
3239-
// if the keypool is empty, return <NOW>
3240-
if (setKeyPool.empty())
3234+
static int64_t GetOldestKeyTimeInPool(const std::set<int64_t>& setKeyPool, CWalletDB& walletdb) {
3235+
if (setKeyPool.empty()) {
32413236
return GetTime();
3237+
}
32423238

32433239
CKeyPool keypool;
3244-
CWalletDB walletdb(*dbw);
3245-
3246-
if (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT))
3247-
{
3248-
// if HD & HD Chain Split is enabled, response max(oldest-internal-key, oldest-external-key)
3249-
int64_t now = GetTime();
3250-
int64_t oldest_external = now, oldest_internal = now;
3251-
3252-
for(const int64_t& id : setKeyPool)
3253-
{
3254-
if (!walletdb.ReadPool(id, keypool)) {
3255-
throw std::runtime_error(std::string(__func__) + ": read failed");
3256-
}
3257-
if (keypool.fInternal && keypool.nTime < oldest_internal) {
3258-
oldest_internal = keypool.nTime;
3259-
}
3260-
else if (!keypool.fInternal && keypool.nTime < oldest_external) {
3261-
oldest_external = keypool.nTime;
3262-
}
3263-
if (oldest_internal != now && oldest_external != now) {
3264-
break;
3265-
}
3266-
}
3267-
return std::max(oldest_internal, oldest_external);
3268-
}
3269-
// load oldest key from keypool, get time and return
32703240
int64_t nIndex = *(setKeyPool.begin());
32713241
if (!walletdb.ReadPool(nIndex, keypool))
32723242
throw std::runtime_error(std::string(__func__) + ": read oldest key in keypool failed");
32733243
assert(keypool.vchPubKey.IsValid());
32743244
return keypool.nTime;
32753245
}
32763246

3247+
int64_t CWallet::GetOldestKeyPoolTime()
3248+
{
3249+
LOCK(cs_wallet);
3250+
3251+
CWalletDB walletdb(*dbw);
3252+
3253+
// load oldest key from keypool, get time and return
3254+
int64_t oldestKey = GetOldestKeyTimeInPool(setExternalKeyPool, walletdb);
3255+
if (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT)) {
3256+
oldestKey = std::max(GetOldestKeyTimeInPool(setInternalKeyPool, walletdb), oldestKey);
3257+
}
3258+
3259+
return oldestKey;
3260+
}
3261+
32773262
std::map<CTxDestination, CAmount> CWallet::GetAddressBalances()
32783263
{
32793264
std::map<CTxDestination, CAmount> balances;
@@ -3432,6 +3417,7 @@ bool CReserveKey::GetReservedKey(CPubKey& pubkey, bool internal)
34323417
else {
34333418
return false;
34343419
}
3420+
fInternal = keypool.fInternal;
34353421
}
34363422
assert(vchPubKey.IsValid());
34373423
pubkey = vchPubKey;
@@ -3449,31 +3435,40 @@ void CReserveKey::KeepKey()
34493435
void CReserveKey::ReturnKey()
34503436
{
34513437
if (nIndex != -1)
3452-
pwallet->ReturnKey(nIndex);
3438+
pwallet->ReturnKey(nIndex, fInternal);
34533439
nIndex = -1;
34543440
vchPubKey = CPubKey();
34553441
}
34563442

3457-
void CWallet::GetAllReserveKeys(std::set<CKeyID>& setAddress) const
3458-
{
3459-
setAddress.clear();
3460-
3461-
CWalletDB walletdb(*dbw);
3462-
3463-
LOCK2(cs_main, cs_wallet);
3443+
static void LoadReserveKeysToSet(std::set<CKeyID>& setAddress, const std::set<int64_t>& setKeyPool, CWalletDB& walletdb) {
34643444
for (const int64_t& id : setKeyPool)
34653445
{
34663446
CKeyPool keypool;
34673447
if (!walletdb.ReadPool(id, keypool))
34683448
throw std::runtime_error(std::string(__func__) + ": read failed");
34693449
assert(keypool.vchPubKey.IsValid());
34703450
CKeyID keyID = keypool.vchPubKey.GetID();
3471-
if (!HaveKey(keyID))
3472-
throw std::runtime_error(std::string(__func__) + ": unknown key in key pool");
34733451
setAddress.insert(keyID);
34743452
}
34753453
}
34763454

3455+
void CWallet::GetAllReserveKeys(std::set<CKeyID>& setAddress) const
3456+
{
3457+
setAddress.clear();
3458+
3459+
CWalletDB walletdb(*dbw);
3460+
3461+
LOCK2(cs_main, cs_wallet);
3462+
LoadReserveKeysToSet(setAddress, setInternalKeyPool, walletdb);
3463+
LoadReserveKeysToSet(setAddress, setExternalKeyPool, walletdb);
3464+
3465+
for (const CKeyID& keyID : setAddress) {
3466+
if (!HaveKey(keyID)) {
3467+
throw std::runtime_error(std::string(__func__) + ": unknown key in key pool");
3468+
}
3469+
}
3470+
}
3471+
34773472
void CWallet::GetScriptForMining(std::shared_ptr<CReserveScript> &script)
34783473
{
34793474
std::shared_ptr<CReserveKey> rKey = std::make_shared<CReserveKey>(this);

src/wallet/wallet.h

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -696,7 +696,8 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
696696
/* HD derive new child key (on internal or external chain) */
697697
void DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret, bool internal = false);
698698

699-
std::set<int64_t> setKeyPool;
699+
std::set<int64_t> setInternalKeyPool;
700+
std::set<int64_t> setExternalKeyPool;
700701

701702
int64_t nTimeFirstKey;
702703

@@ -741,7 +742,11 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
741742

742743
void LoadKeyPool(int nIndex, const CKeyPool &keypool)
743744
{
744-
setKeyPool.insert(nIndex);
745+
if (keypool.fInternal) {
746+
setInternalKeyPool.insert(nIndex);
747+
} else {
748+
setExternalKeyPool.insert(nIndex);
749+
}
745750

746751
// If no metadata exists yet, create a default with the pool key's
747752
// creation time. Note that this may be overwritten by actually
@@ -970,9 +975,9 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
970975
bool NewKeyPool();
971976
size_t KeypoolCountExternalKeys();
972977
bool TopUpKeyPool(unsigned int kpSize = 0);
973-
void ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool internal);
978+
void ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal);
974979
void KeepKey(int64_t nIndex);
975-
void ReturnKey(int64_t nIndex);
980+
void ReturnKey(int64_t nIndex, bool fInternal);
976981
bool GetKeyFromPool(CPubKey &key, bool internal = false);
977982
int64_t GetOldestKeyPoolTime();
978983
void GetAllReserveKeys(std::set<CKeyID>& setAddress) const;
@@ -1026,8 +1031,8 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
10261031

10271032
unsigned int GetKeyPoolSize()
10281033
{
1029-
AssertLockHeld(cs_wallet); // setKeyPool
1030-
return setKeyPool.size();
1034+
AssertLockHeld(cs_wallet); // set{Ex,In}ternalKeyPool
1035+
return setInternalKeyPool.size() + setExternalKeyPool.size();
10311036
}
10321037

10331038
bool SetDefaultKey(const CPubKey &vchPubKey);
@@ -1131,11 +1136,13 @@ class CReserveKey : public CReserveScript
11311136
CWallet* pwallet;
11321137
int64_t nIndex;
11331138
CPubKey vchPubKey;
1139+
bool fInternal;
11341140
public:
11351141
CReserveKey(CWallet* pwalletIn)
11361142
{
11371143
nIndex = -1;
11381144
pwallet = pwalletIn;
1145+
fInternal = false;
11391146
}
11401147

11411148
CReserveKey() = default;

0 commit comments

Comments
 (0)