Skip to content

Commit ed2a2ce

Browse files
committed
Merge #13076: Fix ScanForWalletTransactions to return an enum indicating scan result: success / failure / user_abort
bd3b036 Add stop_block out arg to ScanForWalletTransactions (Ben Woosley) 3002d6c Return a status enum from ScanForWalletTransactions (Ben Woosley) bb24d68 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
2 parents 3fff1ab + bd3b036 commit ed2a2ce

File tree

6 files changed

+78
-34
lines changed

6 files changed

+78
-34
lines changed

src/qt/test/wallettests.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,13 @@ void TestGUI()
146146
auto locked_chain = wallet->chain().lock();
147147
WalletRescanReserver reserver(wallet.get());
148148
reserver.reserve();
149-
wallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver, true);
149+
const CBlockIndex* const null_block = nullptr;
150+
const CBlockIndex *stop_block, *failed_block;
151+
QCOMPARE(
152+
wallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver, failed_block, stop_block, true /* fUpdate */),
153+
CWallet::ScanResult::SUCCESS);
154+
QCOMPARE(stop_block, chainActive.Tip());
155+
QCOMPARE(failed_block, null_block);
150156
}
151157
wallet->SetBroadcastTransactions(true);
152158

src/test/test_bitcoin.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,17 @@
1515
#include <txmempool.h>
1616

1717
#include <memory>
18+
#include <type_traits>
1819

1920
#include <boost/thread.hpp>
2021

22+
// Enable BOOST_CHECK_EQUAL for enum class types
23+
template <typename T>
24+
std::ostream& operator<<(typename std::enable_if<std::is_enum<T>::value, std::ostream>::type& stream, const T& e)
25+
{
26+
return stream << static_cast<typename std::underlying_type<T>::type>(e);
27+
}
28+
2129
extern uint256 insecure_rand_seed;
2230
extern FastRandomContext insecure_rand_ctx;
2331

src/wallet/rpcwallet.cpp

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3422,16 +3422,17 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
34223422
}
34233423
}
34243424

3425-
CBlockIndex *stopBlock = pwallet->ScanForWalletTransactions(pindexStart, pindexStop, reserver, true);
3426-
if (!stopBlock) {
3427-
if (pwallet->IsAbortingRescan()) {
3428-
throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted.");
3429-
}
3430-
// if we got a nullptr returned, ScanForWalletTransactions did rescan up to the requested stopindex
3431-
stopBlock = pindexStop ? pindexStop : pChainTip;
3432-
}
3433-
else {
3425+
const CBlockIndex *failed_block, *stopBlock;
3426+
CWallet::ScanResult result =
3427+
pwallet->ScanForWalletTransactions(pindexStart, pindexStop, reserver, failed_block, stopBlock, true);
3428+
switch (result) {
3429+
case CWallet::ScanResult::SUCCESS:
3430+
break; // stopBlock set by ScanForWalletTransactions
3431+
case CWallet::ScanResult::FAILURE:
34343432
throw JSONRPCError(RPC_MISC_ERROR, "Rescan failed. Potentially corrupted data files.");
3433+
case CWallet::ScanResult::USER_ABORT:
3434+
throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted.");
3435+
// no default case, so the compiler can warn about missing cases
34353436
}
34363437
UniValue response(UniValue::VOBJ);
34373438
response.pushKV("start_height", pindexStart->nHeight);

src/wallet/test/wallet_tests.cpp

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
3939
auto chain = interfaces::MakeChain();
4040

4141
// Cap last block file size, and mine new block in a new block file.
42-
CBlockIndex* const nullBlock = nullptr;
42+
const CBlockIndex* const null_block = nullptr;
4343
CBlockIndex* oldTip = chainActive.Tip();
4444
GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE;
4545
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
@@ -54,7 +54,10 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
5454
AddKey(wallet, coinbaseKey);
5555
WalletRescanReserver reserver(&wallet);
5656
reserver.reserve();
57-
BOOST_CHECK_EQUAL(nullBlock, wallet.ScanForWalletTransactions(oldTip, nullptr, reserver));
57+
const CBlockIndex *stop_block, *failed_block;
58+
BOOST_CHECK_EQUAL(wallet.ScanForWalletTransactions(oldTip, nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::SUCCESS);
59+
BOOST_CHECK_EQUAL(failed_block, null_block);
60+
BOOST_CHECK_EQUAL(stop_block, newTip);
5861
BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 100 * COIN);
5962
}
6063

@@ -69,7 +72,10 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
6972
AddKey(wallet, coinbaseKey);
7073
WalletRescanReserver reserver(&wallet);
7174
reserver.reserve();
72-
BOOST_CHECK_EQUAL(oldTip, wallet.ScanForWalletTransactions(oldTip, nullptr, reserver));
75+
const CBlockIndex *stop_block, *failed_block;
76+
BOOST_CHECK_EQUAL(wallet.ScanForWalletTransactions(oldTip, nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::FAILURE);
77+
BOOST_CHECK_EQUAL(failed_block, oldTip);
78+
BOOST_CHECK_EQUAL(stop_block, newTip);
7379
BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN);
7480
}
7581

@@ -287,7 +293,11 @@ class ListCoinsTestingSetup : public TestChain100Setup
287293
AddKey(*wallet, coinbaseKey);
288294
WalletRescanReserver reserver(wallet.get());
289295
reserver.reserve();
290-
wallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver);
296+
const CBlockIndex* const null_block = nullptr;
297+
const CBlockIndex *stop_block, *failed_block;
298+
BOOST_CHECK_EQUAL(wallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::SUCCESS);
299+
BOOST_CHECK_EQUAL(stop_block, chainActive.Tip());
300+
BOOST_CHECK_EQUAL(failed_block, null_block);
291301
}
292302

293303
~ListCoinsTestingSetup()

src/wallet/wallet.cpp

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1611,8 +1611,9 @@ int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& r
16111611
}
16121612

16131613
if (startBlock) {
1614-
const CBlockIndex* const failedBlock = ScanForWalletTransactions(startBlock, nullptr, reserver, update);
1615-
if (failedBlock) {
1614+
const CBlockIndex *failedBlock, *stop_block;
1615+
// TODO: this should take into account failure by ScanResult::USER_ABORT
1616+
if (ScanResult::FAILURE == ScanForWalletTransactions(startBlock, nullptr, reserver, failedBlock, stop_block, update)) {
16161617
return failedBlock->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1;
16171618
}
16181619
}
@@ -1624,18 +1625,22 @@ int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& r
16241625
* from or to us. If fUpdate is true, found transactions that already
16251626
* exist in the wallet will be updated.
16261627
*
1627-
* Returns null if scan was successful. Otherwise, if a complete rescan was not
1628-
* possible (due to pruning or corruption), returns pointer to the most recent
1629-
* block that could not be scanned.
1628+
* @param[in] pindexStop if not a nullptr, the scan will stop at this block-index
1629+
* @param[out] failed_block if FAILURE is returned, the most recent block
1630+
* that could not be scanned, otherwise nullptr
1631+
* @param[out] stop_block the most recent block that could be scanned,
1632+
* otherwise nullptr if no block could be scanned
16301633
*
1631-
* If pindexStop is not a nullptr, the scan will stop at the block-index
1632-
* defined by pindexStop
1634+
* @return ScanResult indicating success or failure of the scan. SUCCESS if
1635+
* scan was successful. FAILURE if a complete rescan was not possible (due to
1636+
* pruning or corruption). USER_ABORT if the rescan was aborted before it
1637+
* could complete.
16331638
*
1634-
* Caller needs to make sure pindexStop (and the optional pindexStart) are on
1639+
* @pre Caller needs to make sure pindexStop (and the optional pindexStart) are on
16351640
* the main chain after to the addition of any new keys you want to detect
16361641
* transactions for.
16371642
*/
1638-
CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlockIndex* pindexStop, const WalletRescanReserver &reserver, bool fUpdate)
1643+
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)
16391644
{
16401645
int64_t nNow = GetTime();
16411646
const CChainParams& chainParams = Params();
@@ -1645,8 +1650,8 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock
16451650
assert(pindexStop->nHeight >= pindexStart->nHeight);
16461651
}
16471652

1648-
CBlockIndex* pindex = pindexStart;
1649-
CBlockIndex* ret = nullptr;
1653+
const CBlockIndex* pindex = pindexStart;
1654+
failed_block = nullptr;
16501655

16511656
if (pindex) WalletLogPrintf("Rescan started from block %d...\n", pindex->nHeight);
16521657

@@ -1667,8 +1672,7 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock
16671672
}
16681673
}
16691674
double progress_current = progress_begin;
1670-
while (pindex && !fAbortRescan && !ShutdownRequested())
1671-
{
1675+
while (pindex && !fAbortRescan && !ShutdownRequested()) {
16721676
if (pindex->nHeight % 100 == 0 && progress_end - progress_begin > 0.0) {
16731677
ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), std::max(1, std::min(99, (int)((progress_current - progress_begin) / (progress_end - progress_begin) * 100))));
16741678
}
@@ -1684,14 +1688,17 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock
16841688
if (pindex && !chainActive.Contains(pindex)) {
16851689
// Abort scan if current block is no longer active, to prevent
16861690
// marking transactions as coming from the wrong block.
1687-
ret = pindex;
1691+
failed_block = pindex;
16881692
break;
16891693
}
16901694
for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {
16911695
SyncTransaction(block.vtx[posInBlock], pindex, posInBlock, fUpdate);
16921696
}
1697+
// scan succeeded, record block as most recent successfully scanned
1698+
stop_block = pindex;
16931699
} else {
1694-
ret = pindex;
1700+
// could not scan block, keep scanning but record this block as the most recent failure
1701+
failed_block = pindex;
16951702
}
16961703
if (pindex == pindexStop) {
16971704
break;
@@ -1707,14 +1714,20 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock
17071714
}
17081715
}
17091716
}
1717+
ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), 100); // hide progress dialog in GUI
17101718
if (pindex && fAbortRescan) {
17111719
WalletLogPrintf("Rescan aborted at block %d. Progress=%f\n", pindex->nHeight, progress_current);
1720+
return ScanResult::USER_ABORT;
17121721
} else if (pindex && ShutdownRequested()) {
17131722
WalletLogPrintf("Rescan interrupted by shutdown request at block %d. Progress=%f\n", pindex->nHeight, progress_current);
1723+
return ScanResult::USER_ABORT;
17141724
}
1715-
ShowProgress(strprintf("%s " + _("Rescanning..."), GetDisplayName()), 100); // hide progress dialog in GUI
17161725
}
1717-
return ret;
1726+
if (failed_block) {
1727+
return ScanResult::FAILURE;
1728+
} else {
1729+
return ScanResult::SUCCESS;
1730+
}
17181731
}
17191732

17201733
void CWallet::ReacceptWalletTransactions()
@@ -4169,11 +4182,11 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
41694182
nStart = GetTimeMillis();
41704183
{
41714184
WalletRescanReserver reserver(walletInstance.get());
4172-
if (!reserver.reserve()) {
4185+
const CBlockIndex *stop_block, *failed_block;
4186+
if (!reserver.reserve() || (ScanResult::SUCCESS != walletInstance->ScanForWalletTransactions(pindexRescan, nullptr, reserver, failed_block, stop_block, true))) {
41734187
InitError(_("Failed to rescan the wallet during initialization"));
41744188
return nullptr;
41754189
}
4176-
walletInstance->ScanForWalletTransactions(pindexRescan, nullptr, reserver, true);
41774190
}
41784191
walletInstance->WalletLogPrintf("Rescan completed in %15dms\n", GetTimeMillis() - nStart);
41794192
walletInstance->ChainStateFlushed(chainActive.GetLocator());

src/wallet/wallet.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -899,7 +899,13 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface
899899
void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex *pindex, const std::vector<CTransactionRef>& vtxConflicted) override;
900900
void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) override;
901901
int64_t RescanFromTime(int64_t startTime, const WalletRescanReserver& reserver, bool update);
902-
CBlockIndex* ScanForWalletTransactions(CBlockIndex* pindexStart, CBlockIndex* pindexStop, const WalletRescanReserver& reserver, bool fUpdate = false);
902+
903+
enum class ScanResult {
904+
SUCCESS,
905+
FAILURE,
906+
USER_ABORT
907+
};
908+
ScanResult ScanForWalletTransactions(const CBlockIndex* const pindexStart, const CBlockIndex* const pindexStop, const WalletRescanReserver& reserver, const CBlockIndex*& failed_block, const CBlockIndex*& stop_block, bool fUpdate = false);
903909
void TransactionRemovedFromMempool(const CTransactionRef &ptx) override;
904910
void ReacceptWalletTransactions();
905911
void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override EXCLUSIVE_LOCKS_REQUIRED(cs_main);

0 commit comments

Comments
 (0)