Skip to content

Commit 4b62bdf

Browse files
committed
Wallet: Refactor ReserveKeyFromKeyPool for safety
ReserveKeyFromKeyPool's previous behaviour is to set nIndex to -1 if the keypool is empty, OR throw an exception for technical failures. Instead, we now return false if the keypool is empty, true if the operation succeeded. This is to make failure more easily detectable by calling code.
1 parent 4cfe17c commit 4b62bdf

File tree

2 files changed

+31
-15
lines changed

2 files changed

+31
-15
lines changed

src/wallet/wallet.cpp

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3427,7 +3427,7 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
34273427
return true;
34283428
}
34293429

3430-
void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal)
3430+
bool CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal)
34313431
{
34323432
nIndex = -1;
34333433
keypool.vchPubKey = CPubKey();
@@ -3438,11 +3438,13 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRe
34383438
TopUpKeyPool();
34393439

34403440
bool fReturningInternal = IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT) && fRequestedInternal;
3441-
std::set<int64_t>& setKeyPool = set_pre_split_keypool.empty() ? (fReturningInternal ? setInternalKeyPool : setExternalKeyPool) : set_pre_split_keypool;
3441+
bool use_split_keypool = set_pre_split_keypool.empty();
3442+
std::set<int64_t>& setKeyPool = use_split_keypool ? (fReturningInternal ? setInternalKeyPool : setExternalKeyPool) : set_pre_split_keypool;
34423443

34433444
// Get the oldest key
3444-
if(setKeyPool.empty())
3445-
return;
3445+
if (setKeyPool.empty()) {
3446+
return false;
3447+
}
34463448

34473449
WalletBatch batch(*database);
34483450

@@ -3456,14 +3458,17 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRe
34563458
throw std::runtime_error(std::string(__func__) + ": unknown key in key pool");
34573459
}
34583460
// If the key was pre-split keypool, we don't care about what type it is
3459-
if (set_pre_split_keypool.size() == 0 && keypool.fInternal != fReturningInternal) {
3461+
if (use_split_keypool && keypool.fInternal != fReturningInternal) {
34603462
throw std::runtime_error(std::string(__func__) + ": keypool entry misclassified");
34613463
}
3464+
if (!keypool.vchPubKey.IsValid()) {
3465+
throw std::runtime_error(std::string(__func__) + ": keypool entry invalid");
3466+
}
34623467

3463-
assert(keypool.vchPubKey.IsValid());
34643468
m_pool_key_to_index.erase(keypool.vchPubKey.GetID());
34653469
LogPrintf("keypool reserve %d\n", nIndex);
34663470
}
3471+
return true;
34673472
}
34683473

34693474
void CWallet::KeepKey(int64_t nIndex)
@@ -3496,10 +3501,8 @@ bool CWallet::GetKeyFromPool(CPubKey& result, bool internal)
34963501
CKeyPool keypool;
34973502
{
34983503
LOCK(cs_wallet);
3499-
int64_t nIndex = 0;
3500-
ReserveKeyFromKeyPool(nIndex, keypool, internal);
3501-
if (nIndex == -1)
3502-
{
3504+
int64_t nIndex;
3505+
if (!ReserveKeyFromKeyPool(nIndex, keypool, internal)) {
35033506
if (IsLocked()) return false;
35043507
WalletBatch batch(*database);
35053508
result = GenerateNewKey(batch, internal);
@@ -3701,12 +3704,10 @@ bool CReserveKey::GetReservedKey(CPubKey& pubkey, bool internal)
37013704
if (nIndex == -1)
37023705
{
37033706
CKeyPool keypool;
3704-
pwallet->ReserveKeyFromKeyPool(nIndex, keypool, internal);
3705-
if (nIndex != -1)
3706-
vchPubKey = keypool.vchPubKey;
3707-
else {
3707+
if (!pwallet->ReserveKeyFromKeyPool(nIndex, keypool, internal)) {
37083708
return false;
37093709
}
3710+
vchPubKey = keypool.vchPubKey;
37103711
fInternal = keypool.fInternal;
37113712
}
37123713
assert(vchPubKey.IsValid());

src/wallet/wallet.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -999,7 +999,22 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface
999999
bool NewKeyPool();
10001000
size_t KeypoolCountExternalKeys() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
10011001
bool TopUpKeyPool(unsigned int kpSize = 0);
1002-
void ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal);
1002+
1003+
/**
1004+
* Reserves a key from the keypool and sets nIndex to its index
1005+
*
1006+
* @param[out] nIndex the index of the key in keypool
1007+
* @param[out] keypool the keypool the key was drawn from, which could be the
1008+
* the pre-split pool if present, or the internal or external pool
1009+
* @param fRequestedInternal true if the caller would like the key drawn
1010+
* from the internal keypool, false if external is preferred
1011+
*
1012+
* @return true if succeeded, false if failed due to empty keypool
1013+
* @throws std::runtime_error if keypool read failed, key was invalid,
1014+
* was not found in the wallet, or was misclassified in the internal
1015+
* or external keypool
1016+
*/
1017+
bool ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal);
10031018
void KeepKey(int64_t nIndex);
10041019
void ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey);
10051020
bool GetKeyFromPool(CPubKey &key, bool internal = false);

0 commit comments

Comments
 (0)