Merge #13076: Fix ScanForWalletTransactions to return an enum indicating scan result: success / failure / user_abort
bd3b0361d
Add stop_block out arg to ScanForWalletTransactions (Ben Woosley)3002d6cf3
Return a status enum from ScanForWalletTransactions (Ben Woosley)bb24d6865
Make CWallet::ScanForWalletTransactions args and return value const (Ben Woosley) Pull request description: Return the failed block as an out arg. Fixes #11450. /cc #12275 Tree-SHA512: 6a523e5425ebfe24e664a942ae21c797ccc1281c25b1bf8d02ad95c19dae343fd8051985ef11853474de7628fd6bed5f15190fbc087c3466ce6fdecab37d72a9
This commit is contained in:
commit
ed2a2cebd3
6 changed files with 78 additions and 34 deletions
|
@ -146,7 +146,13 @@ void TestGUI()
|
|||
auto locked_chain = wallet->chain().lock();
|
||||
WalletRescanReserver reserver(wallet.get());
|
||||
reserver.reserve();
|
||||
wallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver, true);
|
||||
const CBlockIndex* const null_block = nullptr;
|
||||
const CBlockIndex *stop_block, *failed_block;
|
||||
QCOMPARE(
|
||||
wallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver, failed_block, stop_block, true /* fUpdate */),
|
||||
CWallet::ScanResult::SUCCESS);
|
||||
QCOMPARE(stop_block, chainActive.Tip());
|
||||
QCOMPARE(failed_block, null_block);
|
||||
}
|
||||
wallet->SetBroadcastTransactions(true);
|
||||
|
||||
|
|
|
@ -15,9 +15,17 @@
|
|||
#include <txmempool.h>
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
// Enable BOOST_CHECK_EQUAL for enum class types
|
||||
template <typename T>
|
||||
std::ostream& operator<<(typename std::enable_if<std::is_enum<T>::value, std::ostream>::type& stream, const T& e)
|
||||
{
|
||||
return stream << static_cast<typename std::underlying_type<T>::type>(e);
|
||||
}
|
||||
|
||||
extern uint256 insecure_rand_seed;
|
||||
extern FastRandomContext insecure_rand_ctx;
|
||||
|
||||
|
|
|
@ -3422,16 +3422,17 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
|
|||
}
|
||||
}
|
||||
|
||||
CBlockIndex *stopBlock = pwallet->ScanForWalletTransactions(pindexStart, pindexStop, reserver, true);
|
||||
if (!stopBlock) {
|
||||
if (pwallet->IsAbortingRescan()) {
|
||||
throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted.");
|
||||
}
|
||||
// if we got a nullptr returned, ScanForWalletTransactions did rescan up to the requested stopindex
|
||||
stopBlock = pindexStop ? pindexStop : pChainTip;
|
||||
}
|
||||
else {
|
||||
const CBlockIndex *failed_block, *stopBlock;
|
||||
CWallet::ScanResult result =
|
||||
pwallet->ScanForWalletTransactions(pindexStart, pindexStop, reserver, failed_block, stopBlock, true);
|
||||
switch (result) {
|
||||
case CWallet::ScanResult::SUCCESS:
|
||||
break; // stopBlock set by ScanForWalletTransactions
|
||||
case CWallet::ScanResult::FAILURE:
|
||||
throw JSONRPCError(RPC_MISC_ERROR, "Rescan failed. Potentially corrupted data files.");
|
||||
case CWallet::ScanResult::USER_ABORT:
|
||||
throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted.");
|
||||
// no default case, so the compiler can warn about missing cases
|
||||
}
|
||||
UniValue response(UniValue::VOBJ);
|
||||
response.pushKV("start_height", pindexStart->nHeight);
|
||||
|
|
|
@ -39,7 +39,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
|
|||
auto chain = interfaces::MakeChain();
|
||||
|
||||
// Cap last block file size, and mine new block in a new block file.
|
||||
CBlockIndex* const nullBlock = nullptr;
|
||||
const CBlockIndex* const null_block = nullptr;
|
||||
CBlockIndex* oldTip = chainActive.Tip();
|
||||
GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE;
|
||||
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
|
||||
|
@ -54,7 +54,10 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
|
|||
AddKey(wallet, coinbaseKey);
|
||||
WalletRescanReserver reserver(&wallet);
|
||||
reserver.reserve();
|
||||
BOOST_CHECK_EQUAL(nullBlock, wallet.ScanForWalletTransactions(oldTip, nullptr, reserver));
|
||||
const CBlockIndex *stop_block, *failed_block;
|
||||
BOOST_CHECK_EQUAL(wallet.ScanForWalletTransactions(oldTip, nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::SUCCESS);
|
||||
BOOST_CHECK_EQUAL(failed_block, null_block);
|
||||
BOOST_CHECK_EQUAL(stop_block, newTip);
|
||||
BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 100 * COIN);
|
||||
}
|
||||
|
||||
|
@ -69,7 +72,10 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
|
|||
AddKey(wallet, coinbaseKey);
|
||||
WalletRescanReserver reserver(&wallet);
|
||||
reserver.reserve();
|
||||
BOOST_CHECK_EQUAL(oldTip, wallet.ScanForWalletTransactions(oldTip, nullptr, reserver));
|
||||
const CBlockIndex *stop_block, *failed_block;
|
||||
BOOST_CHECK_EQUAL(wallet.ScanForWalletTransactions(oldTip, nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::FAILURE);
|
||||
BOOST_CHECK_EQUAL(failed_block, oldTip);
|
||||
BOOST_CHECK_EQUAL(stop_block, newTip);
|
||||
BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN);
|
||||
}
|
||||
|
||||
|
@ -287,7 +293,11 @@ public:
|
|||
AddKey(*wallet, coinbaseKey);
|
||||
WalletRescanReserver reserver(wallet.get());
|
||||
reserver.reserve();
|
||||
wallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver);
|
||||
const CBlockIndex* const null_block = nullptr;
|
||||
const CBlockIndex *stop_block, *failed_block;
|
||||
BOOST_CHECK_EQUAL(wallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::SUCCESS);
|
||||
BOOST_CHECK_EQUAL(stop_block, chainActive.Tip());
|
||||
BOOST_CHECK_EQUAL(failed_block, null_block);
|
||||
}
|
||||
|
||||
~ListCoinsTestingSetup()
|
||||
|
|
|
@ -1611,8 +1611,9 @@ int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& r
|
|||
}
|
||||
|
||||
if (startBlock) {
|
||||
const CBlockIndex* const failedBlock = ScanForWalletTransactions(startBlock, nullptr, reserver, update);
|
||||
if (failedBlock) {
|
||||
const CBlockIndex *failedBlock, *stop_block;
|
||||
// TODO: this should take into account failure by ScanResult::USER_ABORT
|
||||
if (ScanResult::FAILURE == ScanForWalletTransactions(startBlock, nullptr, reserver, failedBlock, stop_block, update)) {
|
||||
return failedBlock->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1;
|
||||
}
|
||||
}
|
||||
|
@ -1624,18 +1625,22 @@ int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& r
|
|||
* from or to us. If fUpdate is true, found transactions that already
|
||||
* exist in the wallet will be updated.
|
||||
*
|
||||
* Returns null if scan was successful. Otherwise, if a complete rescan was not
|
||||
* possible (due to pruning or corruption), returns pointer to the most recent
|
||||
* block that could not be scanned.
|
||||
* @param[in] pindexStop if not a nullptr, the scan will stop at this block-index
|
||||
* @param[out] failed_block if FAILURE is returned, the most recent block
|
||||
* that could not be scanned, otherwise nullptr
|
||||
* @param[out] stop_block the most recent block that could be scanned,
|
||||
* otherwise nullptr if no block could be scanned
|
||||
*
|
||||
* If pindexStop is not a nullptr, the scan will stop at the block-index
|
||||
* defined by pindexStop
|
||||
* @return ScanResult indicating success or failure of the scan. SUCCESS if
|
||||
* scan was successful. FAILURE if a complete rescan was not possible (due to
|
||||
* pruning or corruption). USER_ABORT if the rescan was aborted before it
|
||||
* could complete.
|
||||
*
|
||||
* Caller needs to make sure pindexStop (and the optional pindexStart) are on
|
||||
* @pre Caller needs to make sure pindexStop (and the optional pindexStart) are on
|
||||
* the main chain after to the addition of any new keys you want to detect
|
||||
* transactions for.
|
||||
*/
|
||||
CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlockIndex* pindexStop, const WalletRescanReserver &reserver, bool fUpdate)
|
||||
CWallet::ScanResult CWallet::ScanForWalletTransactions(const CBlockIndex* const pindexStart, const CBlockIndex* const pindexStop, const WalletRescanReserver& reserver, const CBlockIndex*& failed_block, const CBlockIndex*& stop_block, bool fUpdate)
|
||||
{
|
||||
int64_t nNow = GetTime();
|
||||
const CChainParams& chainParams = Params();
|
||||
|
@ -1645,8 +1650,8 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock
|
|||
assert(pindexStop->nHeight >= pindexStart->nHeight);
|
||||
}
|
||||
|
||||
CBlockIndex* pindex = pindexStart;
|
||||
CBlockIndex* ret = nullptr;
|
||||
const CBlockIndex* pindex = pindexStart;
|
||||
failed_block = nullptr;
|
||||
|
||||
if (pindex) WalletLogPrintf("Rescan started from block %d...\n", pindex->nHeight);
|
||||
|
||||
|
@ -1667,8 +1672,7 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock
|
|||
}
|
||||
}
|
||||
double progress_current = progress_begin;
|
||||
while (pindex && !fAbortRescan && !ShutdownRequested())
|
||||
{
|
||||
while (pindex && !fAbortRescan && !ShutdownRequested()) {
|
||||
if (pindex->nHeight % 100 == 0 && progress_end - progress_begin > 0.0) {
|
||||
ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), std::max(1, std::min(99, (int)((progress_current - progress_begin) / (progress_end - progress_begin) * 100))));
|
||||
}
|
||||
|
@ -1684,14 +1688,17 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock
|
|||
if (pindex && !chainActive.Contains(pindex)) {
|
||||
// Abort scan if current block is no longer active, to prevent
|
||||
// marking transactions as coming from the wrong block.
|
||||
ret = pindex;
|
||||
failed_block = pindex;
|
||||
break;
|
||||
}
|
||||
for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {
|
||||
SyncTransaction(block.vtx[posInBlock], pindex, posInBlock, fUpdate);
|
||||
}
|
||||
// scan succeeded, record block as most recent successfully scanned
|
||||
stop_block = pindex;
|
||||
} else {
|
||||
ret = pindex;
|
||||
// could not scan block, keep scanning but record this block as the most recent failure
|
||||
failed_block = pindex;
|
||||
}
|
||||
if (pindex == pindexStop) {
|
||||
break;
|
||||
|
@ -1707,14 +1714,20 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock
|
|||
}
|
||||
}
|
||||
}
|
||||
ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), 100); // hide progress dialog in GUI
|
||||
if (pindex && fAbortRescan) {
|
||||
WalletLogPrintf("Rescan aborted at block %d. Progress=%f\n", pindex->nHeight, progress_current);
|
||||
return ScanResult::USER_ABORT;
|
||||
} else if (pindex && ShutdownRequested()) {
|
||||
WalletLogPrintf("Rescan interrupted by shutdown request at block %d. Progress=%f\n", pindex->nHeight, progress_current);
|
||||
return ScanResult::USER_ABORT;
|
||||
}
|
||||
ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), 100); // hide progress dialog in GUI
|
||||
}
|
||||
return ret;
|
||||
if (failed_block) {
|
||||
return ScanResult::FAILURE;
|
||||
} else {
|
||||
return ScanResult::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
void CWallet::ReacceptWalletTransactions()
|
||||
|
@ -4169,11 +4182,11 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
|
|||
nStart = GetTimeMillis();
|
||||
{
|
||||
WalletRescanReserver reserver(walletInstance.get());
|
||||
if (!reserver.reserve()) {
|
||||
const CBlockIndex *stop_block, *failed_block;
|
||||
if (!reserver.reserve() || (ScanResult::SUCCESS != walletInstance->ScanForWalletTransactions(pindexRescan, nullptr, reserver, failed_block, stop_block, true))) {
|
||||
InitError(_("Failed to rescan the wallet during initialization"));
|
||||
return nullptr;
|
||||
}
|
||||
walletInstance->ScanForWalletTransactions(pindexRescan, nullptr, reserver, true);
|
||||
}
|
||||
walletInstance->WalletLogPrintf("Rescan completed in %15dms\n", GetTimeMillis() - nStart);
|
||||
walletInstance->ChainStateFlushed(chainActive.GetLocator());
|
||||
|
|
|
@ -899,7 +899,13 @@ public:
|
|||
void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex *pindex, const std::vector<CTransactionRef>& vtxConflicted) override;
|
||||
void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) override;
|
||||
int64_t RescanFromTime(int64_t startTime, const WalletRescanReserver& reserver, bool update);
|
||||
CBlockIndex* ScanForWalletTransactions(CBlockIndex* pindexStart, CBlockIndex* pindexStop, const WalletRescanReserver& reserver, bool fUpdate = false);
|
||||
|
||||
enum class ScanResult {
|
||||
SUCCESS,
|
||||
FAILURE,
|
||||
USER_ABORT
|
||||
};
|
||||
ScanResult ScanForWalletTransactions(const CBlockIndex* const pindexStart, const CBlockIndex* const pindexStop, const WalletRescanReserver& reserver, const CBlockIndex*& failed_block, const CBlockIndex*& stop_block, bool fUpdate = false);
|
||||
void TransactionRemovedFromMempool(const CTransactionRef &ptx) override;
|
||||
void ReacceptWalletTransactions();
|
||||
void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
|
|
Loading…
Add table
Reference in a new issue