Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/wallet.cpp
Show First 20 Lines • Show All 1,715 Lines • ▼ Show 20 Lines | int64_t CWallet::RescanFromTime(int64_t startTime, | ||||
WalletLogPrintf("%s: Rescanning last %i blocks\n", __func__, | WalletLogPrintf("%s: Rescanning last %i blocks\n", __func__, | ||||
start ? WITH_LOCK(cs_wallet, return GetLastBlockHeight()) - | start ? WITH_LOCK(cs_wallet, return GetLastBlockHeight()) - | ||||
start_height + 1 | start_height + 1 | ||||
: 0); | : 0); | ||||
if (start) { | if (start) { | ||||
// TODO: this should take into account failure by ScanResult::USER_ABORT | // TODO: this should take into account failure by ScanResult::USER_ABORT | ||||
ScanResult result = ScanForWalletTransactions( | ScanResult result = ScanForWalletTransactions( | ||||
start_block, {} /* max_height */, reserver, update); | start_block, start_height, {} /* max_height */, reserver, update); | ||||
if (result.status == ScanResult::FAILURE) { | if (result.status == ScanResult::FAILURE) { | ||||
int64_t time_max; | int64_t time_max; | ||||
CHECK_NONFATAL(chain().findBlock(result.last_failed_block, | CHECK_NONFATAL(chain().findBlock(result.last_failed_block, | ||||
FoundBlock().maxTime(time_max))); | FoundBlock().maxTime(time_max))); | ||||
return time_max + TIMESTAMP_WINDOW + 1; | return time_max + TIMESTAMP_WINDOW + 1; | ||||
} | } | ||||
} | } | ||||
return startTime; | return startTime; | ||||
} | } | ||||
/** | /** | ||||
* Scan the block chain (starting in start_block) for transactions from or to | * Scan the block chain (starting in start_block) for transactions from or to | ||||
* us. If fUpdate is true, found transactions that already exist in the wallet | * us. If fUpdate is true, found transactions that already exist in the wallet | ||||
* will be updated. | * will be updated. | ||||
* | * | ||||
* @param[in] start_block Scan starting block. If block is not on the active | * @param[in] start_block Scan starting block. If block is not on the active | ||||
* chain, the scan will return SUCCESS immediately. | * chain, the scan will return SUCCESS immediately. | ||||
* @param[in] start_height Height of start_block | |||||
* @param[in] max_height Optional max scanning height. If unset there is | * @param[in] max_height Optional max scanning height. If unset there is | ||||
* no maximum and scanning can continue to the tip | * no maximum and scanning can continue to the tip | ||||
* | * | ||||
* @return ScanResult returning scan information and indicating success or | * @return ScanResult returning scan information and indicating success or | ||||
* failure. Return status will be set to SUCCESS if scan was | * failure. Return status will be set to SUCCESS if scan was | ||||
* successful. FAILURE if a complete rescan was not possible (due to | * successful. FAILURE if a complete rescan was not possible (due to | ||||
* pruning or corruption). USER_ABORT if the rescan was aborted before | * pruning or corruption). USER_ABORT if the rescan was aborted before | ||||
* it could complete. | * it could complete. | ||||
* | * | ||||
* @pre Caller needs to make sure start_block (and the optional stop_block) are | * @pre Caller needs to make sure start_block (and the optional stop_block) are | ||||
* on the main chain after to the addition of any new keys you want to detect | * on the main chain after to the addition of any new keys you want to detect | ||||
* transactions for. | * transactions for. | ||||
*/ | */ | ||||
CWallet::ScanResult CWallet::ScanForWalletTransactions( | CWallet::ScanResult CWallet::ScanForWalletTransactions( | ||||
const BlockHash &start_block, Optional<int> max_height, | const BlockHash &start_block, int start_height, Optional<int> max_height, | ||||
const WalletRescanReserver &reserver, bool fUpdate) { | const WalletRescanReserver &reserver, bool fUpdate) { | ||||
int64_t nNow = GetTime(); | int64_t nNow = GetTime(); | ||||
int64_t start_time = GetTimeMillis(); | int64_t start_time = GetTimeMillis(); | ||||
assert(reserver.isReserved()); | assert(reserver.isReserved()); | ||||
BlockHash block_hash = start_block; | BlockHash block_hash = start_block; | ||||
ScanResult result; | ScanResult result; | ||||
WalletLogPrintf("Rescan started from block %s...\n", | WalletLogPrintf("Rescan started from block %s...\n", | ||||
start_block.ToString()); | start_block.ToString()); | ||||
fAbortRescan = false; | fAbortRescan = false; | ||||
// Show rescan progress in GUI as dialog or on splashscreen, if -rescan on | |||||
// Show rescan progress in GUI as dialog or on splashscreen, if -rescan | // startup. | ||||
// on startup. | |||||
ShowProgress( | ShowProgress( | ||||
strprintf("%s " + _("Rescanning...").translated, GetDisplayName()), 0); | strprintf("%s " + _("Rescanning...").translated, GetDisplayName()), 0); | ||||
BlockHash tip_hash; | BlockHash tip_hash = WITH_LOCK(cs_wallet, return GetLastBlockHash()); | ||||
// The way the 'block_height' is initialized is just a workaround for | |||||
// the gcc bug #47679 since version 4.6.0. | |||||
Optional<int> block_height = MakeOptional(false, int()); | |||||
double progress_begin; | |||||
double progress_end; | |||||
{ | |||||
auto locked_chain = chain().lock(); | |||||
if (Optional<int> tip_height = locked_chain->getHeight()) { | |||||
tip_hash = locked_chain->getBlockHash(*tip_height); | |||||
} | |||||
block_height = locked_chain->getBlockHeight(block_hash); | |||||
BlockHash end_hash = tip_hash; | BlockHash end_hash = tip_hash; | ||||
if (max_height) { | if (max_height) { | ||||
chain().findAncestorByHeight(tip_hash, *max_height, | chain().findAncestorByHeight(tip_hash, *max_height, | ||||
FoundBlock().hash(end_hash)); | FoundBlock().hash(end_hash)); | ||||
} | } | ||||
progress_begin = chain().guessVerificationProgress(block_hash); | double progress_begin = chain().guessVerificationProgress(block_hash); | ||||
progress_end = chain().guessVerificationProgress(end_hash); | double progress_end = chain().guessVerificationProgress(end_hash); | ||||
} | |||||
double progress_current = progress_begin; | double progress_current = progress_begin; | ||||
while (block_height && !fAbortRescan && !chain().shutdownRequested()) { | int block_height = start_height; | ||||
while (!fAbortRescan && !chain().shutdownRequested()) { | |||||
m_scanning_progress = (progress_current - progress_begin) / | m_scanning_progress = (progress_current - progress_begin) / | ||||
(progress_end - progress_begin); | (progress_end - progress_begin); | ||||
if (*block_height % 100 == 0 && progress_end - progress_begin > 0.0) { | if (block_height % 100 == 0 && progress_end - progress_begin > 0.0) { | ||||
ShowProgress( | ShowProgress( | ||||
strprintf("%s " + _("Rescanning...").translated, | strprintf("%s " + _("Rescanning...").translated, | ||||
GetDisplayName()), | GetDisplayName()), | ||||
std::max(1, std::min(99, int(m_scanning_progress * 100)))); | std::max(1, std::min(99, (int)(m_scanning_progress * 100)))); | ||||
} | } | ||||
if (GetTime() >= nNow + 60) { | if (GetTime() >= nNow + 60) { | ||||
nNow = GetTime(); | nNow = GetTime(); | ||||
WalletLogPrintf("Still rescanning. At block %d. Progress=%f\n", | WalletLogPrintf("Still rescanning. At block %d. Progress=%f\n", | ||||
*block_height, progress_current); | block_height, progress_current); | ||||
} | } | ||||
CBlock block; | CBlock block; | ||||
bool next_block; | |||||
BlockHash next_block_hash; | |||||
bool reorg = false; | |||||
if (chain().findBlock(block_hash, FoundBlock().data(block)) && | if (chain().findBlock(block_hash, FoundBlock().data(block)) && | ||||
!block.IsNull()) { | !block.IsNull()) { | ||||
auto locked_chain = chain().lock(); | auto locked_chain = chain().lock(); | ||||
LOCK(cs_wallet); | LOCK(cs_wallet); | ||||
if (!locked_chain->getBlockHeight(block_hash)) { | next_block = chain().findNextBlock( | ||||
// Abort scan if current block is no longer active, to | block_hash, block_height, FoundBlock().hash(next_block_hash), | ||||
// prevent marking transactions as coming from the wrong | &reorg); | ||||
// block. | if (reorg) { | ||||
// Abort scan if current block is no longer active, to prevent | |||||
// marking transactions as coming from the wrong block. | |||||
// TODO: This should return success instead of failure, see | // TODO: This should return success instead of failure, see | ||||
// https://github.com/bitcoin/bitcoin/pull/14711#issuecomment-458342518 | // https://github.com/bitcoin/bitcoin/pull/14711#issuecomment-458342518 | ||||
result.last_failed_block = block_hash; | result.last_failed_block = block_hash; | ||||
result.status = ScanResult::FAILURE; | result.status = ScanResult::FAILURE; | ||||
break; | break; | ||||
} | } | ||||
for (size_t posInBlock = 0; posInBlock < block.vtx.size(); | for (size_t posInBlock = 0; posInBlock < block.vtx.size(); | ||||
++posInBlock) { | ++posInBlock) { | ||||
CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, | CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, | ||||
*block_height, block_hash, | block_height, block_hash, | ||||
posInBlock); | posInBlock); | ||||
SyncTransaction(block.vtx[posInBlock], confirm, fUpdate); | SyncTransaction(block.vtx[posInBlock], confirm, fUpdate); | ||||
} | } | ||||
// scan succeeded, record block as most recent successfully | // scan succeeded, record block as most recent successfully | ||||
// scanned | // scanned | ||||
result.last_scanned_block = block_hash; | result.last_scanned_block = block_hash; | ||||
result.last_scanned_height = *block_height; | result.last_scanned_height = block_height; | ||||
} else { | } else { | ||||
// could not scan block, keep scanning but record this block as | // could not scan block, keep scanning but record this block as | ||||
// the most recent failure | // the most recent failure | ||||
result.last_failed_block = block_hash; | result.last_failed_block = block_hash; | ||||
result.status = ScanResult::FAILURE; | result.status = ScanResult::FAILURE; | ||||
next_block = chain().findNextBlock( | |||||
block_hash, block_height, FoundBlock().hash(next_block_hash), | |||||
&reorg); | |||||
} | } | ||||
if (max_height && *block_height >= *max_height) { | if (max_height && block_height >= *max_height) { | ||||
break; | break; | ||||
} | } | ||||
{ | { | ||||
auto locked_chain = chain().lock(); | auto locked_chain = chain().lock(); | ||||
Optional<int> tip_height = locked_chain->getHeight(); | if (!next_block || reorg) { | ||||
if (!tip_height || *tip_height <= block_height || | |||||
!locked_chain->getBlockHeight(block_hash)) { | |||||
// break successfully when rescan has reached the tip, or | // break successfully when rescan has reached the tip, or | ||||
// previous block is no longer on the chain due to a reorg | // previous block is no longer on the chain due to a reorg | ||||
break; | break; | ||||
} | } | ||||
// increment block and verification progress | // increment block and verification progress | ||||
block_hash = locked_chain->getBlockHash(++*block_height); | block_hash = next_block_hash; | ||||
++block_height; | |||||
progress_current = chain().guessVerificationProgress(block_hash); | progress_current = chain().guessVerificationProgress(block_hash); | ||||
// handle updated tip hash | // handle updated tip hash | ||||
const BlockHash prev_tip_hash = tip_hash; | const BlockHash prev_tip_hash = tip_hash; | ||||
tip_hash = locked_chain->getBlockHash(*tip_height); | tip_hash = WITH_LOCK(cs_wallet, return GetLastBlockHash()); | ||||
if (!max_height && prev_tip_hash != tip_hash) { | if (!max_height && prev_tip_hash != tip_hash) { | ||||
// in case the tip has changed, update progress max | // in case the tip has changed, update progress max | ||||
progress_end = chain().guessVerificationProgress(tip_hash); | progress_end = chain().guessVerificationProgress(tip_hash); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// Hide progress dialog in GUI. | // Hide progress dialog in GUI. | ||||
ShowProgress( | ShowProgress( | ||||
strprintf("%s " + _("Rescanning...").translated, GetDisplayName()), | strprintf("%s " + _("Rescanning...").translated, GetDisplayName()), | ||||
100); | 100); | ||||
if (block_height && fAbortRescan) { | if (block_height && fAbortRescan) { | ||||
WalletLogPrintf("Rescan aborted at block %d. Progress=%f\n", | WalletLogPrintf("Rescan aborted at block %d. Progress=%f\n", | ||||
*block_height, progress_current); | block_height, progress_current); | ||||
result.status = ScanResult::USER_ABORT; | result.status = ScanResult::USER_ABORT; | ||||
} else if (block_height && chain().shutdownRequested()) { | } else if (block_height && chain().shutdownRequested()) { | ||||
WalletLogPrintf("Rescan interrupted by shutdown request at block " | WalletLogPrintf( | ||||
"%d. Progress=%f\n", | "Rescan interrupted by shutdown request at block %d. Progress=%f\n", | ||||
*block_height, progress_current); | block_height, progress_current); | ||||
result.status = ScanResult::USER_ABORT; | result.status = ScanResult::USER_ABORT; | ||||
} else { | } else { | ||||
WalletLogPrintf("Rescan completed in %15dms\n", | WalletLogPrintf("Rescan completed in %15dms\n", | ||||
GetTimeMillis() - start_time); | GetTimeMillis() - start_time); | ||||
} | } | ||||
return result; | return result; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 2,533 Lines • ▼ Show 20 Lines | if (tip_height && *tip_height != rescan_height) { | ||||
{ | { | ||||
WalletRescanReserver reserver(*walletInstance); | WalletRescanReserver reserver(*walletInstance); | ||||
if (!reserver.reserve() || | if (!reserver.reserve() || | ||||
(ScanResult::SUCCESS != | (ScanResult::SUCCESS != | ||||
walletInstance | walletInstance | ||||
->ScanForWalletTransactions( | ->ScanForWalletTransactions( | ||||
locked_chain->getBlockHash(rescan_height), | locked_chain->getBlockHash(rescan_height), | ||||
{} /* max_height */, reserver, true /* update */) | rescan_height, {} /* max height */, reserver, | ||||
true /* update */) | |||||
.status)) { | .status)) { | ||||
error = _("Failed to rescan the wallet during initialization"); | error = _("Failed to rescan the wallet during initialization"); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
} | } | ||||
walletInstance->ChainStateFlushed(locked_chain->getTipLocator()); | walletInstance->ChainStateFlushed(locked_chain->getTipLocator()); | ||||
walletInstance->database->IncrementUpdateCounter(); | walletInstance->database->IncrementUpdateCounter(); | ||||
▲ Show 20 Lines • Show All 336 Lines • Show Last 20 Lines |