|
|
|
@ -2941,7 +2941,8 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
@@ -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
@@ -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)
@@ -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,12 @@ bool CWallet::NewKeyPool()
@@ -3078,9 +3081,12 @@ bool CWallet::NewKeyPool()
|
|
|
|
|
{ |
|
|
|
|
LOCK(cs_wallet); |
|
|
|
|
CWalletDB walletdb(*dbw); |
|
|
|
|
for (int64_t nIndex : setKeyPool) |
|
|
|
|
for (int64_t nIndex : setInternalKeyPool) |
|
|
|
|
walletdb.ErasePool(nIndex); |
|
|
|
|
setKeyPool.clear(); |
|
|
|
|
setInternalKeyPool.clear(); |
|
|
|
|
BOOST_FOREACH(int64_t nIndex, setExternalKeyPool) |
|
|
|
|
walletdb.ErasePool(nIndex); |
|
|
|
|
setExternalKeyPool.clear(); |
|
|
|
|
|
|
|
|
|
if (!TopUpKeyPool()) { |
|
|
|
|
return false; |
|
|
|
@ -3092,25 +3098,8 @@ bool CWallet::NewKeyPool()
@@ -3092,25 +3098,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 +3119,8 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
@@ -3130,10 +3119,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)) |
|
|
|
|
{ |
|
|
|
@ -3147,18 +3134,26 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
@@ -3147,18 +3134,26 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
|
|
|
|
|
int64_t nEnd = 1; |
|
|
|
|
if (i < missingInternal) |
|
|
|
|
internal = true; |
|
|
|
|
if (!setKeyPool.empty()) |
|
|
|
|
nEnd = *(--setKeyPool.end()) + 1; |
|
|
|
|
if (!setInternalKeyPool.empty()) |
|
|
|
|
nEnd = *(--setInternalKeyPool.end()) + 1; |
|
|
|
|
if (!setExternalKeyPool.empty()) |
|
|
|
|
nEnd = std::max(nEnd, *(--setExternalKeyPool.end()) + 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 +3163,30 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool int
@@ -3168,30 +3163,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); |
|
|
|
|
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 +3198,16 @@ void CWallet::KeepKey(int64_t nIndex)
@@ -3203,12 +3198,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,41 +3231,12 @@ bool CWallet::GetKeyFromPool(CPubKey& result, bool internal)
@@ -3232,41 +3231,12 @@ 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)) |
|
|
|
|
throw std::runtime_error(std::string(__func__) + ": read oldest key in keypool failed"); |
|
|
|
@ -3274,6 +3244,21 @@ int64_t CWallet::GetOldestKeyPoolTime()
@@ -3274,6 +3244,21 @@ int64_t CWallet::GetOldestKeyPoolTime()
|
|
|
|
|
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 +3417,7 @@ bool CReserveKey::GetReservedKey(CPubKey& pubkey, bool internal)
@@ -3432,6 +3417,7 @@ bool CReserveKey::GetReservedKey(CPubKey& pubkey, bool internal)
|
|
|
|
|
else { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
fInternal = keypool.fInternal; |
|
|
|
|
} |
|
|
|
|
assert(vchPubKey.IsValid()); |
|
|
|
|
pubkey = vchPubKey; |
|
|
|
@ -3449,18 +3435,12 @@ void CReserveKey::KeepKey()
@@ -3449,18 +3435,12 @@ void CReserveKey::KeepKey()
|
|
|
|
|
void CReserveKey::ReturnKey() |
|
|
|
|
{ |
|
|
|
|
if (nIndex != -1) |
|
|
|
|
pwallet->ReturnKey(nIndex); |
|
|
|
|
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; |
|
|
|
@ -3468,12 +3448,27 @@ void CWallet::GetAllReserveKeys(std::set<CKeyID>& setAddress) const
@@ -3468,12 +3448,27 @@ void CWallet::GetAllReserveKeys(std::set<CKeyID>& setAddress) const
|
|
|
|
|
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); |
|
|
|
|