Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/wallet.cpp
// Copyright (c) 2009-2010 Satoshi Nakamoto | // Copyright (c) 2009-2010 Satoshi Nakamoto | ||||
// Copyright (c) 2009-2016 The Bitcoin Core developers | // Copyright (c) 2009-2016 The Bitcoin Core developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
#include "wallet/wallet.h" | #include "wallet/wallet.h" | ||||
#include "chain.h" | #include "chain.h" | ||||
#include "checkpoints.h" | #include "checkpoints.h" | ||||
#include "config.h" | #include "config.h" | ||||
#include "consensus/consensus.h" | #include "consensus/consensus.h" | ||||
#include "consensus/validation.h" | #include "consensus/validation.h" | ||||
#include "dstencode.h" | #include "dstencode.h" | ||||
#include "fs.h" | #include "fs.h" | ||||
#include "init.h" | |||||
#include "key.h" | #include "key.h" | ||||
#include "keystore.h" | #include "keystore.h" | ||||
#include "net.h" | #include "net.h" | ||||
#include "policy/policy.h" | #include "policy/policy.h" | ||||
#include "primitives/block.h" | #include "primitives/block.h" | ||||
#include "primitives/transaction.h" | #include "primitives/transaction.h" | ||||
#include "scheduler.h" | #include "scheduler.h" | ||||
#include "script/script.h" | #include "script/script.h" | ||||
▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | struct CompareValueOnly { | ||||
} | } | ||||
}; | }; | ||||
std::string COutput::ToString() const { | std::string COutput::ToString() const { | ||||
return strprintf("COutput(%s, %d, %d) [%s]", tx->GetId().ToString(), i, | return strprintf("COutput(%s, %d, %d) [%s]", tx->GetId().ToString(), i, | ||||
nDepth, FormatMoney(tx->tx->vout[i].nValue)); | 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 { | const CWalletTx *CWallet::GetWalletTx(const uint256 &hash) const { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(hash); | std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(hash); | ||||
if (it == mapWallet.end()) { | if (it == mapWallet.end()) { | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
return &(it->second); | return &(it->second); | ||||
▲ Show 20 Lines • Show All 1,013 Lines • ▼ Show 20 Lines | if (posInBlock != -1) { | ||||
} | } | ||||
} | } | ||||
bool fExisted = mapWallet.count(tx.GetId()) != 0; | bool fExisted = mapWallet.count(tx.GetId()) != 0; | ||||
if (fExisted && !fUpdate) { | if (fExisted && !fUpdate) { | ||||
return false; | return false; | ||||
} | } | ||||
if (fExisted || IsMine(tx) || IsFromMe(tx)) { | 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); | CWalletTx wtx(this, ptx); | ||||
// Get merkle branch if transaction was found in a block. | // Get merkle branch if transaction was found in a block. | ||||
if (posInBlock != -1) { | if (posInBlock != -1) { | ||||
wtx.SetMerkleBranch(pIndex, posInBlock); | wtx.SetMerkleBranch(pIndex, posInBlock); | ||||
} | } | ||||
return AddToWallet(wtx, false); | return AddToWallet(wtx, false); | ||||
▲ Show 20 Lines • Show All 1,977 Lines • ▼ Show 20 Lines | |||||
DBErrors CWallet::LoadWallet(bool &fFirstRunRet) { | DBErrors CWallet::LoadWallet(bool &fFirstRunRet) { | ||||
fFirstRunRet = false; | fFirstRunRet = false; | ||||
DBErrors nLoadWalletRet = CWalletDB(*dbw, "cr+").LoadWallet(this); | DBErrors nLoadWalletRet = CWalletDB(*dbw, "cr+").LoadWallet(this); | ||||
if (nLoadWalletRet == DB_NEED_REWRITE) { | if (nLoadWalletRet == DB_NEED_REWRITE) { | ||||
if (dbw->Rewrite("\x04pool")) { | if (dbw->Rewrite("\x04pool")) { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
setInternalKeyPool.clear(); | setInternalKeyPool.clear(); | ||||
setExternalKeyPool.clear(); | setExternalKeyPool.clear(); | ||||
m_pool_key_to_index.clear(); | |||||
// Note: can't top-up keypool here, because wallet is locked. | // Note: can't top-up keypool here, because wallet is locked. | ||||
// User will be prompted to unlock wallet the next operation | // User will be prompted to unlock wallet the next operation | ||||
// that requires a new key. | // that requires a new key. | ||||
} | } | ||||
} | } | ||||
if (nLoadWalletRet != DB_LOAD_OK) { | if (nLoadWalletRet != DB_LOAD_OK) { | ||||
return nLoadWalletRet; | return nLoadWalletRet; | ||||
Show All 15 Lines | DBErrors CWallet::ZapSelectTx(std::vector<uint256> &vHashIn, | ||||
for (uint256 hash : vHashOut) { | for (uint256 hash : vHashOut) { | ||||
mapWallet.erase(hash); | mapWallet.erase(hash); | ||||
} | } | ||||
if (nZapSelectTxRet == DB_NEED_REWRITE) { | if (nZapSelectTxRet == DB_NEED_REWRITE) { | ||||
if (dbw->Rewrite("\x04pool")) { | if (dbw->Rewrite("\x04pool")) { | ||||
setInternalKeyPool.clear(); | setInternalKeyPool.clear(); | ||||
setExternalKeyPool.clear(); | setExternalKeyPool.clear(); | ||||
m_pool_key_to_index.clear(); | |||||
// Note: can't top-up keypool here, because wallet is locked. | // Note: can't top-up keypool here, because wallet is locked. | ||||
// User will be prompted to unlock wallet the next operation | // User will be prompted to unlock wallet the next operation | ||||
// that requires a new key. | // that requires a new key. | ||||
} | } | ||||
} | } | ||||
if (nZapSelectTxRet != DB_LOAD_OK) { | if (nZapSelectTxRet != DB_LOAD_OK) { | ||||
return nZapSelectTxRet; | return nZapSelectTxRet; | ||||
} | } | ||||
MarkDirty(); | MarkDirty(); | ||||
return DB_LOAD_OK; | return DB_LOAD_OK; | ||||
} | } | ||||
DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx> &vWtx) { | DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx> &vWtx) { | ||||
vchDefaultKey = CPubKey(); | vchDefaultKey = CPubKey(); | ||||
DBErrors nZapWalletTxRet = CWalletDB(*dbw, "cr+").ZapWalletTx(vWtx); | DBErrors nZapWalletTxRet = CWalletDB(*dbw, "cr+").ZapWalletTx(vWtx); | ||||
if (nZapWalletTxRet == DB_NEED_REWRITE) { | if (nZapWalletTxRet == DB_NEED_REWRITE) { | ||||
if (dbw->Rewrite("\x04pool")) { | if (dbw->Rewrite("\x04pool")) { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
setInternalKeyPool.clear(); | setInternalKeyPool.clear(); | ||||
setExternalKeyPool.clear(); | setExternalKeyPool.clear(); | ||||
m_pool_key_to_index.clear(); | |||||
// Note: can't top-up keypool here, because wallet is locked. | // Note: can't top-up keypool here, because wallet is locked. | ||||
// User will be prompted to unlock wallet the next operation | // User will be prompted to unlock wallet the next operation | ||||
// that requires a new key. | // that requires a new key. | ||||
} | } | ||||
} | } | ||||
if (nZapWalletTxRet != DB_LOAD_OK) { | if (nZapWalletTxRet != DB_LOAD_OK) { | ||||
return nZapWalletTxRet; | return nZapWalletTxRet; | ||||
▲ Show 20 Lines • Show All 74 Lines • ▼ Show 20 Lines | bool CWallet::NewKeyPool() { | ||||
} | } | ||||
setInternalKeyPool.clear(); | setInternalKeyPool.clear(); | ||||
for (int64_t nIndex : setExternalKeyPool) { | for (int64_t nIndex : setExternalKeyPool) { | ||||
walletdb.ErasePool(nIndex); | walletdb.ErasePool(nIndex); | ||||
} | } | ||||
setExternalKeyPool.clear(); | setExternalKeyPool.clear(); | ||||
m_pool_key_to_index.clear(); | |||||
if (!TopUpKeyPool()) { | if (!TopUpKeyPool()) { | ||||
return false; | return false; | ||||
} | } | ||||
LogPrintf("CWallet::NewKeyPool rewrote keypool\n"); | LogPrintf("CWallet::NewKeyPool rewrote keypool\n"); | ||||
return true; | return true; | ||||
} | } | ||||
size_t CWallet::KeypoolCountExternalKeys() { | size_t CWallet::KeypoolCountExternalKeys() { | ||||
// setExternalKeyPool | // setExternalKeyPool | ||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
return setExternalKeyPool.size(); | 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) { | bool CWallet::TopUpKeyPool(unsigned int kpSize) { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
if (IsLocked()) { | if (IsLocked()) { | ||||
return false; | return false; | ||||
} | } | ||||
// Top up key pool | // Top up key pool | ||||
Show All 23 Lines | for (int64_t i = missingInternal + missingExternal; i--;) { | ||||
if (i < missingInternal) { | if (i < missingInternal) { | ||||
internal = true; | internal = true; | ||||
} | } | ||||
// How in the hell did you use so many keys? | // How in the hell did you use so many keys? | ||||
assert(m_max_keypool_index < std::numeric_limits<int64_t>::max()); | assert(m_max_keypool_index < std::numeric_limits<int64_t>::max()); | ||||
int64_t index = ++m_max_keypool_index; | int64_t index = ++m_max_keypool_index; | ||||
if (!walletdb.WritePool( | CPubKey pubkey(GenerateNewKey(walletdb, internal)); | ||||
index, | if (!walletdb.WritePool(index, CKeyPool(pubkey, internal))) { | ||||
CKeyPool(GenerateNewKey(walletdb, internal), internal))) { | |||||
throw std::runtime_error(std::string(__func__) + | throw std::runtime_error(std::string(__func__) + | ||||
": writing generated key failed"); | ": writing generated key failed"); | ||||
} | } | ||||
if (internal) { | if (internal) { | ||||
setInternalKeyPool.insert(index); | setInternalKeyPool.insert(index); | ||||
} else { | } else { | ||||
setExternalKeyPool.insert(index); | setExternalKeyPool.insert(index); | ||||
} | } | ||||
m_pool_key_to_index[pubkey.GetID()] = index; | |||||
} | } | ||||
if (missingInternal + missingExternal > 0) { | if (missingInternal + missingExternal > 0) { | ||||
LogPrintf( | LogPrintf( | ||||
"keypool added %d keys (%d internal), size=%u (%u internal)\n", | "keypool added %d keys (%d internal), size=%u (%u internal)\n", | ||||
missingInternal + missingExternal, missingInternal, | missingInternal + missingExternal, missingInternal, | ||||
setInternalKeyPool.size() + setExternalKeyPool.size(), | setInternalKeyPool.size() + setExternalKeyPool.size(), | ||||
setInternalKeyPool.size()); | setInternalKeyPool.size()); | ||||
} | } | ||||
Show All 36 Lines | if (!HaveKey(keypool.vchPubKey.GetID())) { | ||||
": unknown key in key pool"); | ": unknown key in key pool"); | ||||
} | } | ||||
if (keypool.fInternal != fReturningInternal) { | if (keypool.fInternal != fReturningInternal) { | ||||
throw std::runtime_error(std::string(__func__) + | throw std::runtime_error(std::string(__func__) + | ||||
": keypool entry misclassified"); | ": keypool entry misclassified"); | ||||
} | } | ||||
assert(keypool.vchPubKey.IsValid()); | assert(keypool.vchPubKey.IsValid()); | ||||
m_pool_key_to_index.erase(keypool.vchPubKey.GetID()); | |||||
LogPrintf("keypool reserve %d\n", nIndex); | LogPrintf("keypool reserve %d\n", nIndex); | ||||
} | } | ||||
void CWallet::KeepKey(int64_t nIndex) { | void CWallet::KeepKey(int64_t nIndex) { | ||||
// Remove from key pool. | // Remove from key pool. | ||||
CWalletDB walletdb(*dbw); | CWalletDB walletdb(*dbw); | ||||
walletdb.ErasePool(nIndex); | walletdb.ErasePool(nIndex); | ||||
LogPrintf("keypool keep %d\n", 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 | // Return to key pool | ||||
{ | { | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
if (fInternal) { | if (fInternal) { | ||||
setInternalKeyPool.insert(nIndex); | setInternalKeyPool.insert(nIndex); | ||||
} else { | } else { | ||||
setExternalKeyPool.insert(nIndex); | setExternalKeyPool.insert(nIndex); | ||||
} | } | ||||
m_pool_key_to_index[pubkey.GetID()] = nIndex; | |||||
} | } | ||||
LogPrintf("keypool return %d\n", nIndex); | LogPrintf("keypool return %d\n", nIndex); | ||||
} | } | ||||
bool CWallet::GetKeyFromPool(CPubKey &result, bool internal) { | bool CWallet::GetKeyFromPool(CPubKey &result, bool internal) { | ||||
CKeyPool keypool; | CKeyPool keypool; | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
▲ Show 20 Lines • Show All 267 Lines • ▼ Show 20 Lines | void CReserveKey::KeepKey() { | ||||
} | } | ||||
nIndex = -1; | nIndex = -1; | ||||
vchPubKey = CPubKey(); | vchPubKey = CPubKey(); | ||||
} | } | ||||
void CReserveKey::ReturnKey() { | void CReserveKey::ReturnKey() { | ||||
if (nIndex != -1) { | if (nIndex != -1) { | ||||
pwallet->ReturnKey(nIndex, fInternal); | pwallet->ReturnKey(nIndex, fInternal, vchPubKey); | ||||
} | } | ||||
nIndex = -1; | nIndex = -1; | ||||
vchPubKey = CPubKey(); | vchPubKey = CPubKey(); | ||||
} | } | ||||
static void LoadReserveKeysToSet(std::set<CKeyID> &setAddress, | void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id) { | ||||
const std::set<int64_t> &setKeyPool, | AssertLockHeld(cs_wallet); | ||||
CWalletDB &walletdb) { | bool internal = setInternalKeyPool.count(keypool_id); | ||||
for (const int64_t &id : setKeyPool) { | if (!internal) assert(setExternalKeyPool.count(keypool_id)); | ||||
CKeyPool keypool; | std::set<int64_t> *setKeyPool = | ||||
if (!walletdb.ReadPool(id, keypool)) { | internal ? &setInternalKeyPool : &setExternalKeyPool; | ||||
throw std::runtime_error(std::string(__func__) + ": read failed"); | auto it = setKeyPool->begin(); | ||||
} | |||||
assert(keypool.vchPubKey.IsValid()); | |||||
CKeyID keyID = keypool.vchPubKey.GetID(); | |||||
setAddress.insert(keyID); | |||||
} | |||||
} | |||||
void CWallet::GetAllReserveKeys(std::set<CKeyID> &setAddress) const { | |||||
setAddress.clear(); | |||||
CWalletDB walletdb(*dbw); | CWalletDB walletdb(*dbw); | ||||
while (it != std::end(*setKeyPool)) { | |||||
const int64_t &index = *(it); | |||||
if (index > keypool_id) { | |||||
// set*KeyPool is ordered | |||||
break; | |||||
} | |||||
LOCK2(cs_main, cs_wallet); | CKeyPool keypool; | ||||
LoadReserveKeysToSet(setAddress, setInternalKeyPool, walletdb); | if (walletdb.ReadPool(index, keypool)) { | ||||
LoadReserveKeysToSet(setAddress, setExternalKeyPool, walletdb); | // TODO: This should be unnecessary | ||||
m_pool_key_to_index.erase(keypool.vchPubKey.GetID()); | |||||
for (const CKeyID &keyID : setAddress) { | } | ||||
if (!HaveKey(keyID)) { | walletdb.ErasePool(index); | ||||
throw std::runtime_error(std::string(__func__) + | it = setKeyPool->erase(it); | ||||
": unknown key in key pool"); | |||||
} | } | ||||
} | } | ||||
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) { | void CWallet::GetScriptForMining(std::shared_ptr<CReserveScript> &script) { | ||||
std::shared_ptr<CReserveKey> rKey = std::make_shared<CReserveKey>(this); | std::shared_ptr<CReserveKey> rKey = std::make_shared<CReserveKey>(this); | ||||
CPubKey pubkey; | CPubKey pubkey; | ||||
if (!rKey->GetReservedKey(pubkey)) { | if (!rKey->GetReservedKey(pubkey)) { | ||||
return; | return; | ||||
} | } | ||||
Show All 35 Lines | for (std::set<COutPoint>::iterator it = setLockedCoins.begin(); | ||||
it != setLockedCoins.end(); it++) { | it != setLockedCoins.end(); it++) { | ||||
COutPoint outpt = (*it); | COutPoint outpt = (*it); | ||||
vOutpts.push_back(outpt); | vOutpts.push_back(outpt); | ||||
} | } | ||||
} | } | ||||
/** @} */ // end of Actions | /** @} */ // 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( | void CWallet::GetKeyBirthTimes( | ||||
std::map<CTxDestination, int64_t> &mapKeyBirth) const { | std::map<CTxDestination, int64_t> &mapKeyBirth) const { | ||||
// mapKeyMetadata | // mapKeyMetadata | ||||
AssertLockHeld(cs_wallet); | AssertLockHeld(cs_wallet); | ||||
mapKeyBirth.clear(); | mapKeyBirth.clear(); | ||||
// Get birth times for keys with metadata. | // Get birth times for keys with metadata. | ||||
for (const auto &entry : mapKeyMetadata) { | for (const auto &entry : mapKeyMetadata) { | ||||
▲ Show 20 Lines • Show All 389 Lines • ▼ Show 20 Lines | if (fFirstRun) { | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
} | } | ||||
LogPrintf(" wallet %15dms\n", GetTimeMillis() - nStart); | LogPrintf(" wallet %15dms\n", GetTimeMillis() - nStart); | ||||
RegisterValidationInterface(walletInstance); | RegisterValidationInterface(walletInstance); | ||||
// Try to top up keypool. No-op if the wallet is locked. | |||||
walletInstance->TopUpKeyPool(); | |||||
CBlockIndex *pindexRescan = chainActive.Genesis(); | CBlockIndex *pindexRescan = chainActive.Genesis(); | ||||
if (!gArgs.GetBoolArg("-rescan", false)) { | if (!gArgs.GetBoolArg("-rescan", false)) { | ||||
CWalletDB walletdb(*walletInstance->dbw); | CWalletDB walletdb(*walletInstance->dbw); | ||||
CBlockLocator locator; | CBlockLocator locator; | ||||
if (walletdb.ReadBestBlock(locator)) { | if (walletdb.ReadBestBlock(locator)) { | ||||
pindexRescan = FindForkInGlobalIndex(chainActive, locator); | pindexRescan = FindForkInGlobalIndex(chainActive, locator); | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 323 Lines • Show Last 20 Lines |