aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/wallet.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet/wallet.cpp')
-rw-r--r--src/wallet/wallet.cpp166
1 files changed, 110 insertions, 56 deletions
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 18cc3bd028..8b03421691 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -12,6 +12,7 @@
#include "consensus/consensus.h"
#include "consensus/validation.h"
#include "fs.h"
+#include "init.h"
#include "key.h"
#include "keystore.h"
#include "validation.h"
@@ -80,6 +81,38 @@ std::string COutput::ToString() const
return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue));
}
+class CAffectedKeysVisitor : public boost::static_visitor<void> {
+private:
+ const CKeyStore &keystore;
+ std::vector<CKeyID> &vKeys;
+
+public:
+ CAffectedKeysVisitor(const CKeyStore &keystoreIn, std::vector<CKeyID> &vKeysIn) : keystore(keystoreIn), vKeys(vKeysIn) {}
+
+ void Process(const CScript &script) {
+ txnouttype type;
+ std::vector<CTxDestination> vDest;
+ int nRequired;
+ if (ExtractDestinations(script, type, vDest, nRequired)) {
+ for (const CTxDestination &dest : vDest)
+ boost::apply_visitor(*this, dest);
+ }
+ }
+
+ void operator()(const CKeyID &keyId) {
+ if (keystore.HaveKey(keyId))
+ vKeys.push_back(keyId);
+ }
+
+ void operator()(const CScriptID &scriptId) {
+ CScript script;
+ if (keystore.GetCScript(scriptId, script))
+ Process(script);
+ }
+
+ void operator()(const CNoDestination &none) {}
+};
+
const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
{
LOCK(cs_wallet);
@@ -1021,6 +1054,30 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const CBlockI
if (fExisted && !fUpdate) return false;
if (fExisted || IsMine(tx) || IsFromMe(tx))
{
+ /* Check if any keys in the wallet keypool that were supposed to be unused
+ * have appeared in a new transaction. If so, remove those keys from the keypool.
+ * This can happen when restoring an old wallet backup that does not contain
+ * the mostly recently created transactions from newer versions of the wallet.
+ */
+
+ // loop though all outputs
+ for (const CTxOut& txout: tx.vout) {
+ // extract addresses and check if they match with an unused keypool key
+ std::vector<CKeyID> vAffected;
+ CAffectedKeysVisitor(*this, vAffected).Process(txout.scriptPubKey);
+ for (const CKeyID &keyid : vAffected) {
+ std::map<CKeyID, int64_t>::const_iterator mi = m_pool_key_to_index.find(keyid);
+ if (mi != m_pool_key_to_index.end()) {
+ LogPrintf("%s: Detected a used keypool key, mark all keypool key up to this key as used\n", __func__);
+ MarkReserveKeysAsUsed(mi->second);
+
+ if (!TopUpKeyPool()) {
+ LogPrintf("%s: Topping up keypool failed (locked wallet)\n", __func__);
+ }
+ }
+ }
+ }
+
CWalletTx wtx(this, ptx);
// Get merkle branch if transaction was found in a block
@@ -3050,6 +3107,7 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
LOCK(cs_wallet);
setInternalKeyPool.clear();
setExternalKeyPool.clear();
+ m_pool_key_to_index.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.
@@ -3079,6 +3137,7 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256
{
setInternalKeyPool.clear();
setExternalKeyPool.clear();
+ m_pool_key_to_index.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.
@@ -3105,6 +3164,7 @@ DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx)
LOCK(cs_wallet);
setInternalKeyPool.clear();
setExternalKeyPool.clear();
+ m_pool_key_to_index.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.
@@ -3199,6 +3259,8 @@ bool CWallet::NewKeyPool()
}
setExternalKeyPool.clear();
+ m_pool_key_to_index.clear();
+
if (!TopUpKeyPool()) {
return false;
}
@@ -3213,6 +3275,25 @@ size_t CWallet::KeypoolCountExternalKeys()
return setExternalKeyPool.size();
}
+void CWallet::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool)
+{
+ AssertLockHeld(cs_wallet);
+ if (keypool.fInternal) {
+ setInternalKeyPool.insert(nIndex);
+ } else {
+ setExternalKeyPool.insert(nIndex);
+ }
+ m_max_keypool_index = std::max(m_max_keypool_index, nIndex);
+ m_pool_key_to_index[keypool.vchPubKey.GetID()] = nIndex;
+
+ // If no metadata exists yet, create a default with the pool key's
+ // creation time. Note that this may be overwritten by actually
+ // stored metadata for that key later, which is fine.
+ CKeyID keyid = keypool.vchPubKey.GetID();
+ if (mapKeyMetadata.count(keyid) == 0)
+ mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime);
+}
+
bool CWallet::TopUpKeyPool(unsigned int kpSize)
{
{
@@ -3249,7 +3330,8 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
assert(m_max_keypool_index < std::numeric_limits<int64_t>::max()); // How in the hell did you use so many keys?
int64_t index = ++m_max_keypool_index;
- if (!walletdb.WritePool(index, CKeyPool(GenerateNewKey(walletdb, internal), internal))) {
+ CPubKey pubkey(GenerateNewKey(walletdb, internal));
+ if (!walletdb.WritePool(index, CKeyPool(pubkey, internal))) {
throw std::runtime_error(std::string(__func__) + ": writing generated key failed");
}
@@ -3258,6 +3340,7 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
} else {
setExternalKeyPool.insert(index);
}
+ m_pool_key_to_index[pubkey.GetID()] = index;
}
if (missingInternal + missingExternal > 0) {
LogPrintf("keypool added %d keys (%d internal), size=%u (%u internal)\n", missingInternal + missingExternal, missingInternal, setInternalKeyPool.size() + setExternalKeyPool.size(), setInternalKeyPool.size());
@@ -3299,6 +3382,7 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRe
}
assert(keypool.vchPubKey.IsValid());
+ m_pool_key_to_index.erase(keypool.vchPubKey.GetID());
LogPrintf("keypool reserve %d\n", nIndex);
}
}
@@ -3311,7 +3395,7 @@ void CWallet::KeepKey(int64_t nIndex)
LogPrintf("keypool keep %d\n", nIndex);
}
-void CWallet::ReturnKey(int64_t nIndex, bool fInternal)
+void CWallet::ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey)
{
// Return to key pool
{
@@ -3321,6 +3405,7 @@ void CWallet::ReturnKey(int64_t nIndex, bool fInternal)
} else {
setExternalKeyPool.insert(nIndex);
}
+ m_pool_key_to_index[pubkey.GetID()] = nIndex;
}
LogPrintf("keypool return %d\n", nIndex);
}
@@ -3550,41 +3635,39 @@ void CReserveKey::KeepKey()
void CReserveKey::ReturnKey()
{
if (nIndex != -1) {
- pwallet->ReturnKey(nIndex, fInternal);
+ pwallet->ReturnKey(nIndex, fInternal, vchPubKey);
}
nIndex = -1;
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::MarkReserveKeysAsUsed(int64_t keypool_id)
{
- setAddress.clear();
+ AssertLockHeld(cs_wallet);
+ bool internal = setInternalKeyPool.count(keypool_id);
+ if (!internal) assert(setExternalKeyPool.count(keypool_id));
+ std::set<int64_t> *setKeyPool = internal ? &setInternalKeyPool : &setExternalKeyPool;
+ auto it = setKeyPool->begin();
CWalletDB walletdb(*dbw);
+ while (it != std::end(*setKeyPool)) {
+ const int64_t& index = *(it);
+ if (index > keypool_id) break; // set*KeyPool is ordered
- 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");
+ CKeyPool keypool;
+ if (walletdb.ReadPool(index, keypool)) { //TODO: This should be unnecessary
+ m_pool_key_to_index.erase(keypool.vchPubKey.GetID());
}
+ walletdb.ErasePool(index);
+ it = setKeyPool->erase(it);
}
}
+bool CWallet::HasUnusedKeys(int min_keys) const
+{
+ return setExternalKeyPool.size() >= min_keys && (setInternalKeyPool.size() >= min_keys || !CanSupportFeature(FEATURE_HD_SPLIT));
+}
+
void CWallet::GetScriptForMining(std::shared_ptr<CReserveScript> &script)
{
std::shared_ptr<CReserveKey> rKey = std::make_shared<CReserveKey>(this);
@@ -3634,38 +3717,6 @@ void CWallet::ListLockedCoins(std::vector<COutPoint>& vOutpts) const
/** @} */ // end of Actions
-class CAffectedKeysVisitor : public boost::static_visitor<void> {
-private:
- const CKeyStore &keystore;
- std::vector<CKeyID> &vKeys;
-
-public:
- CAffectedKeysVisitor(const CKeyStore &keystoreIn, std::vector<CKeyID> &vKeysIn) : keystore(keystoreIn), vKeys(vKeysIn) {}
-
- void Process(const CScript &script) {
- txnouttype type;
- std::vector<CTxDestination> vDest;
- int nRequired;
- if (ExtractDestinations(script, type, vDest, nRequired)) {
- for (const CTxDestination &dest : vDest)
- boost::apply_visitor(*this, dest);
- }
- }
-
- void operator()(const CKeyID &keyId) {
- if (keystore.HaveKey(keyId))
- vKeys.push_back(keyId);
- }
-
- void operator()(const CScriptID &scriptId) {
- CScript script;
- if (keystore.GetCScript(scriptId, script))
- Process(script);
- }
-
- void operator()(const CNoDestination &none) {}
-};
-
void CWallet::GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) const {
AssertLockHeld(cs_wallet); // mapKeyMetadata
mapKeyBirth.clear();
@@ -3990,6 +4041,9 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
RegisterValidationInterface(walletInstance);
+ // Try to top up keypool. No-op if the wallet is locked.
+ walletInstance->TopUpKeyPool();
+
CBlockIndex *pindexRescan = chainActive.Genesis();
if (!GetBoolArg("-rescan", false))
{