Merge #13252: Wallet: Refactor ReserveKeyFromKeyPool for safety

4b62bdf513 Wallet: Refactor ReserveKeyFromKeyPool for safety (Ben Woosley)

Pull request description:

  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.

Tree-SHA512: 753f057ad13bd4c28d121f426bf0967ed72b827d97fb24582f9326ec60072abc5482e3db69ccada7c5fc66de9957fc59098432dd223fc4116991cab44c6d7aef
This commit is contained in:
Wladimir J. van der Laan 2018-05-30 19:38:48 +02:00
commit c4cc8d9930
No known key found for this signature in database
GPG Key ID: 1E4AED62986CD25D
2 changed files with 31 additions and 15 deletions

View File

@ -3427,7 +3427,7 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
return true; return true;
} }
void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal) bool CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal)
{ {
nIndex = -1; nIndex = -1;
keypool.vchPubKey = CPubKey(); keypool.vchPubKey = CPubKey();
@ -3438,11 +3438,13 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRe
TopUpKeyPool(); TopUpKeyPool();
bool fReturningInternal = IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT) && fRequestedInternal; bool fReturningInternal = IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT) && fRequestedInternal;
std::set<int64_t>& setKeyPool = set_pre_split_keypool.empty() ? (fReturningInternal ? setInternalKeyPool : setExternalKeyPool) : set_pre_split_keypool; bool use_split_keypool = set_pre_split_keypool.empty();
std::set<int64_t>& setKeyPool = use_split_keypool ? (fReturningInternal ? setInternalKeyPool : setExternalKeyPool) : set_pre_split_keypool;
// Get the oldest key // Get the oldest key
if(setKeyPool.empty()) if (setKeyPool.empty()) {
return; return false;
}
WalletBatch batch(*database); WalletBatch batch(*database);
@ -3456,14 +3458,17 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRe
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 the key was pre-split keypool, we don't care about what type it is // If the key was pre-split keypool, we don't care about what type it is
if (set_pre_split_keypool.size() == 0 && keypool.fInternal != fReturningInternal) { if (use_split_keypool && keypool.fInternal != fReturningInternal) {
throw std::runtime_error(std::string(__func__) + ": keypool entry misclassified"); throw std::runtime_error(std::string(__func__) + ": keypool entry misclassified");
} }
if (!keypool.vchPubKey.IsValid()) {
throw std::runtime_error(std::string(__func__) + ": keypool entry invalid");
}
assert(keypool.vchPubKey.IsValid());
m_pool_key_to_index.erase(keypool.vchPubKey.GetID()); m_pool_key_to_index.erase(keypool.vchPubKey.GetID());
LogPrintf("keypool reserve %d\n", nIndex); LogPrintf("keypool reserve %d\n", nIndex);
} }
return true;
} }
void CWallet::KeepKey(int64_t nIndex) void CWallet::KeepKey(int64_t nIndex)
@ -3496,10 +3501,8 @@ bool CWallet::GetKeyFromPool(CPubKey& result, bool internal)
CKeyPool keypool; CKeyPool keypool;
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
int64_t nIndex = 0; int64_t nIndex;
ReserveKeyFromKeyPool(nIndex, keypool, internal); if (!ReserveKeyFromKeyPool(nIndex, keypool, internal)) {
if (nIndex == -1)
{
if (IsLocked()) return false; if (IsLocked()) return false;
WalletBatch batch(*database); WalletBatch batch(*database);
result = GenerateNewKey(batch, internal); result = GenerateNewKey(batch, internal);
@ -3701,12 +3704,10 @@ bool CReserveKey::GetReservedKey(CPubKey& pubkey, bool internal)
if (nIndex == -1) if (nIndex == -1)
{ {
CKeyPool keypool; CKeyPool keypool;
pwallet->ReserveKeyFromKeyPool(nIndex, keypool, internal); if (!pwallet->ReserveKeyFromKeyPool(nIndex, keypool, internal)) {
if (nIndex != -1)
vchPubKey = keypool.vchPubKey;
else {
return false; return false;
} }
vchPubKey = keypool.vchPubKey;
fInternal = keypool.fInternal; fInternal = keypool.fInternal;
} }
assert(vchPubKey.IsValid()); assert(vchPubKey.IsValid());

View File

@ -999,7 +999,22 @@ public:
bool NewKeyPool(); bool NewKeyPool();
size_t KeypoolCountExternalKeys() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); size_t KeypoolCountExternalKeys() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool TopUpKeyPool(unsigned int kpSize = 0); bool TopUpKeyPool(unsigned int kpSize = 0);
void ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal);
/**
* Reserves a key from the keypool and sets nIndex to its index
*
* @param[out] nIndex the index of the key in keypool
* @param[out] keypool the keypool the key was drawn from, which could be the
* the pre-split pool if present, or the internal or external pool
* @param fRequestedInternal true if the caller would like the key drawn
* from the internal keypool, false if external is preferred
*
* @return true if succeeded, false if failed due to empty keypool
* @throws std::runtime_error if keypool read failed, key was invalid,
* was not found in the wallet, or was misclassified in the internal
* or external keypool
*/
bool ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal);
void KeepKey(int64_t nIndex); void KeepKey(int64_t nIndex);
void ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey); void ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey);
bool GetKeyFromPool(CPubKey &key, bool internal = false); bool GetKeyFromPool(CPubKey &key, bool internal = false);