Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/wallet.cpp
Show First 20 Lines • Show All 1,289 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
void CWallet::MarkConflicted(const BlockHash &hashBlock, const TxId &txid) { | void CWallet::MarkConflicted(const BlockHash &hashBlock, const TxId &txid) { | ||||
auto locked_chain = chain().lock(); | auto locked_chain = chain().lock(); | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
int conflictconfirms = 0; | int conflictconfirms = 0; | ||||
CBlockIndex *pindex = LookupBlockIndex(hashBlock); | CBlockIndex *pindex = LookupBlockIndex(hashBlock); | ||||
if (pindex && chainActive.Contains(pindex)) { | if (pindex && ::ChainActive().Contains(pindex)) { | ||||
conflictconfirms = -(chainActive.Height() - pindex->nHeight + 1); | conflictconfirms = -(::ChainActive().Height() - pindex->nHeight + 1); | ||||
} | } | ||||
// If number of conflict confirms cannot be determined, this means that the | // If number of conflict confirms cannot be determined, this means that the | ||||
// block is still unknown or not yet part of the main chain, for example | // block is still unknown or not yet part of the main chain, for example | ||||
// when loading the wallet during a reindex. Do nothing in that case. | // when loading the wallet during a reindex. Do nothing in that case. | ||||
if (conflictconfirms >= 0) { | if (conflictconfirms >= 0) { | ||||
return; | return; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 108 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
void CWallet::BlockUntilSyncedToCurrentChain() { | void CWallet::BlockUntilSyncedToCurrentChain() { | ||||
AssertLockNotHeld(cs_main); | AssertLockNotHeld(cs_main); | ||||
AssertLockNotHeld(cs_wallet); | AssertLockNotHeld(cs_wallet); | ||||
{ | { | ||||
// Skip the queue-draining stuff if we know we're caught up with | // Skip the queue-draining stuff if we know we're caught up with | ||||
// chainActive.Tip()... | // ::ChainActive().Tip()... | ||||
// We could also take cs_wallet here, and call m_last_block_processed | // We could also take cs_wallet here, and call m_last_block_processed | ||||
// protected by cs_wallet instead of cs_main, but as long as we need | // protected by cs_wallet instead of cs_main, but as long as we need | ||||
// cs_main here anyway, it's easier to just call it cs_main-protected. | // cs_main here anyway, it's easier to just call it cs_main-protected. | ||||
auto locked_chain = chain().lock(); | auto locked_chain = chain().lock(); | ||||
const CBlockIndex *initialChainTip = chainActive.Tip(); | const CBlockIndex *initialChainTip = ::ChainActive().Tip(); | ||||
if (m_last_block_processed && | if (m_last_block_processed && | ||||
m_last_block_processed->GetAncestor(initialChainTip->nHeight) == | m_last_block_processed->GetAncestor(initialChainTip->nHeight) == | ||||
initialChainTip) { | initialChainTip) { | ||||
return; | return; | ||||
} | } | ||||
} | } | ||||
// ...otherwise put a callback in the validation interface queue and wait | // ...otherwise put a callback in the validation interface queue and wait | ||||
▲ Show 20 Lines • Show All 445 Lines • ▼ Show 20 Lines | int64_t CWallet::RescanFromTime(int64_t startTime, | ||||
bool update) { | bool update) { | ||||
// Find starting block. May be null if nCreateTime is greater than the | // Find starting block. May be null if nCreateTime is greater than the | ||||
// highest blockchain timestamp, in which case there is nothing that needs | // highest blockchain timestamp, in which case there is nothing that needs | ||||
// to be scanned. | // to be scanned. | ||||
CBlockIndex *startBlock = nullptr; | CBlockIndex *startBlock = nullptr; | ||||
{ | { | ||||
auto locked_chain = chain().lock(); | auto locked_chain = chain().lock(); | ||||
startBlock = | startBlock = | ||||
chainActive.FindEarliestAtLeast(startTime - TIMESTAMP_WINDOW); | ::ChainActive().FindEarliestAtLeast(startTime - TIMESTAMP_WINDOW); | ||||
WalletLogPrintf( | WalletLogPrintf("%s: Rescanning last %i blocks\n", __func__, | ||||
"%s: Rescanning last %i blocks\n", __func__, | startBlock | ||||
startBlock ? chainActive.Height() - startBlock->nHeight + 1 : 0); | ? ::ChainActive().Height() - startBlock->nHeight + 1 | ||||
: 0); | |||||
} | } | ||||
if (startBlock) { | if (startBlock) { | ||||
const CBlockIndex *const failedBlock = | const CBlockIndex *const failedBlock = | ||||
ScanForWalletTransactions(startBlock, nullptr, reserver, update); | ScanForWalletTransactions(startBlock, nullptr, reserver, update); | ||||
if (failedBlock) { | if (failedBlock) { | ||||
return failedBlock->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1; | return failedBlock->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | if (pindex) { | ||||
CBlockIndex *tip = nullptr; | CBlockIndex *tip = nullptr; | ||||
double progress_begin; | double progress_begin; | ||||
double progress_end; | double progress_end; | ||||
{ | { | ||||
auto locked_chain = chain().lock(); | auto locked_chain = chain().lock(); | ||||
progress_begin = | progress_begin = | ||||
GuessVerificationProgress(chainParams.TxData(), pindex); | GuessVerificationProgress(chainParams.TxData(), pindex); | ||||
if (pindexStop == nullptr) { | if (pindexStop == nullptr) { | ||||
tip = chainActive.Tip(); | tip = ::ChainActive().Tip(); | ||||
progress_end = | progress_end = | ||||
GuessVerificationProgress(chainParams.TxData(), tip); | GuessVerificationProgress(chainParams.TxData(), tip); | ||||
} else { | } else { | ||||
progress_end = | progress_end = | ||||
GuessVerificationProgress(chainParams.TxData(), pindexStop); | GuessVerificationProgress(chainParams.TxData(), pindexStop); | ||||
} | } | ||||
} | } | ||||
double progress_current = progress_begin; | double progress_current = progress_begin; | ||||
Show All 13 Lines | if (pindex) { | ||||
WalletLogPrintf("Still rescanning. At block %d. Progress=%f\n", | WalletLogPrintf("Still rescanning. At block %d. Progress=%f\n", | ||||
pindex->nHeight, progress_current); | pindex->nHeight, progress_current); | ||||
} | } | ||||
CBlock block; | CBlock block; | ||||
if (ReadBlockFromDisk(block, pindex, chainParams.GetConsensus())) { | if (ReadBlockFromDisk(block, pindex, chainParams.GetConsensus())) { | ||||
auto locked_chain = chain().lock(); | auto locked_chain = chain().lock(); | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
if (pindex && !chainActive.Contains(pindex)) { | if (pindex && !::ChainActive().Contains(pindex)) { | ||||
// Abort scan if current block is no longer active, to | // Abort scan if current block is no longer active, to | ||||
// prevent marking transactions as coming from the wrong | // prevent marking transactions as coming from the wrong | ||||
// block. | // block. | ||||
ret = pindex; | ret = pindex; | ||||
break; | break; | ||||
} | } | ||||
for (size_t posInBlock = 0; posInBlock < block.vtx.size(); | for (size_t posInBlock = 0; posInBlock < block.vtx.size(); | ||||
++posInBlock) { | ++posInBlock) { | ||||
SyncTransaction(block.vtx[posInBlock], pindex, posInBlock, | SyncTransaction(block.vtx[posInBlock], pindex, posInBlock, | ||||
fUpdate); | fUpdate); | ||||
} | } | ||||
} else { | } else { | ||||
ret = pindex; | ret = pindex; | ||||
} | } | ||||
if (pindex == pindexStop) { | if (pindex == pindexStop) { | ||||
break; | break; | ||||
} | } | ||||
{ | { | ||||
auto locked_chain = chain().lock(); | auto locked_chain = chain().lock(); | ||||
pindex = chainActive.Next(pindex); | pindex = ::ChainActive().Next(pindex); | ||||
progress_current = | progress_current = | ||||
GuessVerificationProgress(chainParams.TxData(), pindex); | GuessVerificationProgress(chainParams.TxData(), pindex); | ||||
if (pindexStop == nullptr && tip != chainActive.Tip()) { | if (pindexStop == nullptr && tip != ::ChainActive().Tip()) { | ||||
tip = chainActive.Tip(); | tip = ::ChainActive().Tip(); | ||||
// in case the tip has changed, update progress max | // in case the tip has changed, update progress max | ||||
progress_end = | progress_end = | ||||
GuessVerificationProgress(chainParams.TxData(), tip); | GuessVerificationProgress(chainParams.TxData(), tip); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if (pindex && fAbortRescan) { | if (pindex && fAbortRescan) { | ||||
▲ Show 20 Lines • Show All 1,056 Lines • ▼ Show 20 Lines | bool CWallet::CreateTransaction(interfaces::Chain::Lock &locked_chainIn, | ||||
// to appear in the next block; we don't want to potentially encourage | // to appear in the next block; we don't want to potentially encourage | ||||
// reorgs by allowing transactions to appear at lower heights than the next | // reorgs by allowing transactions to appear at lower heights than the next | ||||
// block in forks of the best chain. | // block in forks of the best chain. | ||||
// | // | ||||
// Of course, the subsidy is high enough, and transaction volume low enough, | // Of course, the subsidy is high enough, and transaction volume low enough, | ||||
// that fee sniping isn't a problem yet, but by implementing a fix now we | // that fee sniping isn't a problem yet, but by implementing a fix now we | ||||
// ensure code won't be written that makes assumptions about nLockTime that | // ensure code won't be written that makes assumptions about nLockTime that | ||||
// preclude a fix later. | // preclude a fix later. | ||||
txNew.nLockTime = chainActive.Height(); | txNew.nLockTime = ::ChainActive().Height(); | ||||
// Secondly occasionally randomly pick a nLockTime even further back, so | // Secondly occasionally randomly pick a nLockTime even further back, so | ||||
// that transactions that are delayed after signing for whatever reason, | // that transactions that are delayed after signing for whatever reason, | ||||
// e.g. high-latency mix networks and some CoinJoin implementations, have | // e.g. high-latency mix networks and some CoinJoin implementations, have | ||||
// better privacy. | // better privacy. | ||||
if (GetRandInt(10) == 0) { | if (GetRandInt(10) == 0) { | ||||
txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); | txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); | ||||
} | } | ||||
assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); | assert(txNew.nLockTime <= (unsigned int)::ChainActive().Height()); | ||||
assert(txNew.nLockTime < LOCKTIME_THRESHOLD); | assert(txNew.nLockTime < LOCKTIME_THRESHOLD); | ||||
{ | { | ||||
std::set<CInputCoin> setCoins; | std::set<CInputCoin> setCoins; | ||||
auto locked_chain = chain().lock(); | auto locked_chain = chain().lock(); | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
std::vector<COutput> vAvailableCoins; | std::vector<COutput> vAvailableCoins; | ||||
▲ Show 20 Lines • Show All 1,160 Lines • ▼ Show 20 Lines | for (const auto &entry : mapKeyMetadata) { | ||||
if (entry.second.nCreateTime) { | if (entry.second.nCreateTime) { | ||||
mapKeyBirth[entry.first] = entry.second.nCreateTime; | mapKeyBirth[entry.first] = entry.second.nCreateTime; | ||||
} | } | ||||
} | } | ||||
// Map in which we'll infer heights of other keys the tip can be | // Map in which we'll infer heights of other keys the tip can be | ||||
// reorganized; use a 144-block safety margin. | // reorganized; use a 144-block safety margin. | ||||
CBlockIndex *pindexMax = | CBlockIndex *pindexMax = | ||||
chainActive[std::max(0, chainActive.Height() - 144)]; | ::ChainActive()[std::max(0, ::ChainActive().Height() - 144)]; | ||||
std::map<CKeyID, CBlockIndex *> mapKeyFirstBlock; | std::map<CKeyID, CBlockIndex *> mapKeyFirstBlock; | ||||
for (const CKeyID &keyid : GetKeys()) { | for (const CKeyID &keyid : GetKeys()) { | ||||
if (mapKeyBirth.count(keyid) == 0) { | if (mapKeyBirth.count(keyid) == 0) { | ||||
mapKeyFirstBlock[keyid] = pindexMax; | mapKeyFirstBlock[keyid] = pindexMax; | ||||
} | } | ||||
} | } | ||||
// If there are no such keys, we're done. | // If there are no such keys, we're done. | ||||
if (mapKeyFirstBlock.empty()) { | if (mapKeyFirstBlock.empty()) { | ||||
return; | return; | ||||
} | } | ||||
// Find first block that affects those keys, if there are any left. | // Find first block that affects those keys, if there are any left. | ||||
std::vector<CKeyID> vAffected; | std::vector<CKeyID> vAffected; | ||||
for (const auto &entry : mapWallet) { | for (const auto &entry : mapWallet) { | ||||
// iterate over all wallet transactions... | // iterate over all wallet transactions... | ||||
const CWalletTx &wtx = entry.second; | const CWalletTx &wtx = entry.second; | ||||
CBlockIndex *pindex = LookupBlockIndex(wtx.hashBlock); | CBlockIndex *pindex = LookupBlockIndex(wtx.hashBlock); | ||||
if (pindex && chainActive.Contains(pindex)) { | if (pindex && ::ChainActive().Contains(pindex)) { | ||||
// ... which are already in a block | // ... which are already in a block | ||||
int nHeight = pindex->nHeight; | int nHeight = pindex->nHeight; | ||||
for (const CTxOut &txout : wtx.tx->vout) { | for (const CTxOut &txout : wtx.tx->vout) { | ||||
// Iterate over all their outputs... | // Iterate over all their outputs... | ||||
CAffectedKeysVisitor(*this, vAffected) | CAffectedKeysVisitor(*this, vAffected) | ||||
.Process(txout.scriptPubKey); | .Process(txout.scriptPubKey); | ||||
for (const CKeyID &keyid : vAffected) { | for (const CKeyID &keyid : vAffected) { | ||||
// ... and all their affected keys. | // ... and all their affected keys. | ||||
▲ Show 20 Lines • Show All 385 Lines • ▼ Show 20 Lines | if (fFirstRun) { | ||||
if (walletInstance->CanGenerateKeys() && | if (walletInstance->CanGenerateKeys() && | ||||
!walletInstance->TopUpKeyPool()) { | !walletInstance->TopUpKeyPool()) { | ||||
InitError(_("Unable to generate initial keys")); | InitError(_("Unable to generate initial keys")); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
// Temporary. Removed in upcoming lock cleanup | // Temporary. Removed in upcoming lock cleanup | ||||
auto locked_chain = chain.assumeLocked(); | auto locked_chain = chain.assumeLocked(); | ||||
walletInstance->ChainStateFlushed(chainActive.GetLocator()); | walletInstance->ChainStateFlushed(::ChainActive().GetLocator()); | ||||
} else if (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS) { | } else if (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS) { | ||||
// Make it impossible to disable private keys after creation | // Make it impossible to disable private keys after creation | ||||
InitError(strprintf(_("Error loading %s: Private keys can only be " | InitError(strprintf(_("Error loading %s: Private keys can only be " | ||||
"disabled during creation"), | "disabled during creation"), | ||||
walletFile)); | walletFile)); | ||||
return nullptr; | return nullptr; | ||||
} else if (walletInstance->IsWalletFlagSet( | } else if (walletInstance->IsWalletFlagSet( | ||||
WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { | WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { | ||||
▲ Show 20 Lines • Show All 87 Lines • ▼ Show 20 Lines | std::shared_ptr<CWallet> CWallet::CreateWalletFromFile( | ||||
// Try to top up keypool. No-op if the wallet is locked. | // Try to top up keypool. No-op if the wallet is locked. | ||||
walletInstance->TopUpKeyPool(); | walletInstance->TopUpKeyPool(); | ||||
// Temporary, for FindForkInGlobalIndex below. Removed in upcoming commit. | // Temporary, for FindForkInGlobalIndex below. Removed in upcoming commit. | ||||
LockAnnotation lock(::cs_main); | LockAnnotation lock(::cs_main); | ||||
auto locked_chain = chain.lock(); | auto locked_chain = chain.lock(); | ||||
LOCK(walletInstance->cs_wallet); | LOCK(walletInstance->cs_wallet); | ||||
CBlockIndex *pindexRescan = chainActive.Genesis(); | CBlockIndex *pindexRescan = ::ChainActive().Genesis(); | ||||
if (!gArgs.GetBoolArg("-rescan", false)) { | if (!gArgs.GetBoolArg("-rescan", false)) { | ||||
WalletBatch batch(*walletInstance->database); | WalletBatch batch(*walletInstance->database); | ||||
CBlockLocator locator; | CBlockLocator locator; | ||||
if (batch.ReadBestBlock(locator)) { | if (batch.ReadBestBlock(locator)) { | ||||
pindexRescan = FindForkInGlobalIndex(chainActive, locator); | pindexRescan = FindForkInGlobalIndex(::ChainActive(), locator); | ||||
} | } | ||||
} | } | ||||
walletInstance->m_last_block_processed = chainActive.Tip(); | walletInstance->m_last_block_processed = ::ChainActive().Tip(); | ||||
if (chainActive.Tip() && chainActive.Tip() != pindexRescan) { | if (::ChainActive().Tip() && ::ChainActive().Tip() != pindexRescan) { | ||||
// We can't rescan beyond non-pruned blocks, stop and throw an error. | // We can't rescan beyond non-pruned blocks, stop and throw an error. | ||||
// This might happen if a user uses an old wallet within a pruned node | // This might happen if a user uses an old wallet within a pruned node | ||||
// or if he ran -disablewallet for a longer time, then decided to | // or if he ran -disablewallet for a longer time, then decided to | ||||
// re-enable. | // re-enable. | ||||
if (fPruneMode) { | if (fPruneMode) { | ||||
CBlockIndex *block = chainActive.Tip(); | CBlockIndex *block = ::ChainActive().Tip(); | ||||
while (block && block->pprev && block->pprev->nStatus.hasData() && | while (block && block->pprev && block->pprev->nStatus.hasData() && | ||||
block->pprev->nTx > 0 && pindexRescan != block) { | block->pprev->nTx > 0 && pindexRescan != block) { | ||||
block = block->pprev; | block = block->pprev; | ||||
} | } | ||||
if (pindexRescan != block) { | if (pindexRescan != block) { | ||||
InitError(_("Prune: last wallet synchronisation goes beyond " | InitError(_("Prune: last wallet synchronisation goes beyond " | ||||
"pruned data. You need to -reindex (download the " | "pruned data. You need to -reindex (download the " | ||||
"whole blockchain again in case of pruned node)")); | "whole blockchain again in case of pruned node)")); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
} | } | ||||
uiInterface.InitMessage(_("Rescanning...")); | uiInterface.InitMessage(_("Rescanning...")); | ||||
walletInstance->WalletLogPrintf( | walletInstance->WalletLogPrintf( | ||||
"Rescanning last %i blocks (from block %i)...\n", | "Rescanning last %i blocks (from block %i)...\n", | ||||
chainActive.Height() - pindexRescan->nHeight, | ::ChainActive().Height() - pindexRescan->nHeight, | ||||
pindexRescan->nHeight); | pindexRescan->nHeight); | ||||
// No need to read and scan block if block was created before our wallet | // No need to read and scan block if block was created before our wallet | ||||
// birthday (as adjusted for block time variability) | // birthday (as adjusted for block time variability) | ||||
while (pindexRescan && walletInstance->nTimeFirstKey && | while (pindexRescan && walletInstance->nTimeFirstKey && | ||||
(pindexRescan->GetBlockTime() < | (pindexRescan->GetBlockTime() < | ||||
(walletInstance->nTimeFirstKey - TIMESTAMP_WINDOW))) { | (walletInstance->nTimeFirstKey - TIMESTAMP_WINDOW))) { | ||||
pindexRescan = chainActive.Next(pindexRescan); | pindexRescan = ::ChainActive().Next(pindexRescan); | ||||
} | } | ||||
nStart = GetTimeMillis(); | nStart = GetTimeMillis(); | ||||
{ | { | ||||
WalletRescanReserver reserver(walletInstance.get()); | WalletRescanReserver reserver(walletInstance.get()); | ||||
if (!reserver.reserve()) { | if (!reserver.reserve()) { | ||||
InitError( | InitError( | ||||
_("Failed to rescan the wallet during initialization")); | _("Failed to rescan the wallet during initialization")); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
walletInstance->ScanForWalletTransactions(pindexRescan, nullptr, | walletInstance->ScanForWalletTransactions(pindexRescan, nullptr, | ||||
reserver, true); | reserver, true); | ||||
} | } | ||||
walletInstance->WalletLogPrintf("Rescan completed in %15dms\n", | walletInstance->WalletLogPrintf("Rescan completed in %15dms\n", | ||||
GetTimeMillis() - nStart); | GetTimeMillis() - nStart); | ||||
walletInstance->ChainStateFlushed(chainActive.GetLocator()); | walletInstance->ChainStateFlushed(::ChainActive().GetLocator()); | ||||
walletInstance->database->IncrementUpdateCounter(); | walletInstance->database->IncrementUpdateCounter(); | ||||
// Restore wallet transaction metadata after -zapwallettxes=1 | // Restore wallet transaction metadata after -zapwallettxes=1 | ||||
if (gArgs.GetBoolArg("-zapwallettxes", false) && | if (gArgs.GetBoolArg("-zapwallettxes", false) && | ||||
gArgs.GetArg("-zapwallettxes", "1") != "2") { | gArgs.GetArg("-zapwallettxes", "1") != "2") { | ||||
WalletBatch batch(*walletInstance->database); | WalletBatch batch(*walletInstance->database); | ||||
for (const CWalletTx &wtxOld : vWtx) { | for (const CWalletTx &wtxOld : vWtx) { | ||||
▲ Show 20 Lines • Show All 76 Lines • ▼ Show 20 Lines | int CMerkleTx::GetDepthInMainChain( | ||||
if (hashUnset()) { | if (hashUnset()) { | ||||
return 0; | return 0; | ||||
} | } | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
// Find the block it claims to be in. | // Find the block it claims to be in. | ||||
CBlockIndex *pindex = LookupBlockIndex(hashBlock); | CBlockIndex *pindex = LookupBlockIndex(hashBlock); | ||||
if (!pindex || !chainActive.Contains(pindex)) { | if (!pindex || !::ChainActive().Contains(pindex)) { | ||||
return 0; | return 0; | ||||
} | } | ||||
return ((nIndex == -1) ? (-1) : 1) * | return ((nIndex == -1) ? (-1) : 1) * | ||||
(chainActive.Height() - pindex->nHeight + 1); | (::ChainActive().Height() - pindex->nHeight + 1); | ||||
} | } | ||||
int CMerkleTx::GetBlocksToMaturity( | int CMerkleTx::GetBlocksToMaturity( | ||||
interfaces::Chain::Lock &locked_chain) const { | interfaces::Chain::Lock &locked_chain) const { | ||||
if (!IsCoinBase()) { | if (!IsCoinBase()) { | ||||
return 0; | return 0; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 104 Lines • Show Last 20 Lines |