Skip to content

Commit c77170f

Browse files
committed
[Wallet] add rescanblockchain <start_height> <stop_height> RPC command
1 parent 777519b commit c77170f

File tree

6 files changed

+97
-8
lines changed

6 files changed

+97
-8
lines changed

src/qt/test/wallettests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ void TestSendCoins()
157157
wallet.SetAddressBook(test.coinbaseKey.GetPubKey().GetID(), "", "receive");
158158
wallet.AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey());
159159
}
160-
wallet.ScanForWalletTransactions(chainActive.Genesis(), true);
160+
wallet.ScanForWalletTransactions(chainActive.Genesis(), nullptr, true);
161161
wallet.SetBroadcastTransactions(true);
162162

163163
// Create widgets for sending coins and listing transactions.

src/rpc/client.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ static const CRPCConvertParam vRPCConvertParams[] =
140140
{ "echojson", 7, "arg7" },
141141
{ "echojson", 8, "arg8" },
142142
{ "echojson", 9, "arg9" },
143+
{ "rescanblockchain", 0, "start_height"},
144+
{ "rescanblockchain", 1, "stop_height"},
143145
};
144146

145147
class CRPCConvertTable

src/wallet/rpcwallet.cpp

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3169,6 +3169,81 @@ UniValue generate(const JSONRPCRequest& request)
31693169
return generateBlocks(coinbase_script, num_generate, max_tries, true);
31703170
}
31713171

3172+
UniValue rescanblockchain(const JSONRPCRequest& request)
3173+
{
3174+
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
3175+
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
3176+
return NullUniValue;
3177+
}
3178+
3179+
if (request.fHelp || request.params.size() > 2) {
3180+
throw std::runtime_error(
3181+
"rescanblockchain (\"start_height\") (\"stop_height\")\n"
3182+
"\nRescan the local blockchain for wallet related transactions.\n"
3183+
"\nArguments:\n"
3184+
"1. \"start_height\" (numeric, optional) block height where the rescan should start\n"
3185+
"2. \"stop_height\" (numeric, optional) the last block height that should be scanned\n"
3186+
"\nResult:\n"
3187+
"{\n"
3188+
" \"start_height\" (numeric) The block height where the rescan has started. If omitted, rescan started from the genesis block.\n"
3189+
" \"stop_height\" (numeric) The height of the last rescanned block. If omitted, rescan stopped at the chain tip.\n"
3190+
"}\n"
3191+
"\nExamples:\n"
3192+
+ HelpExampleCli("rescanblockchain", "100000 120000")
3193+
+ HelpExampleRpc("rescanblockchain", "100000 120000")
3194+
);
3195+
}
3196+
3197+
LOCK2(cs_main, pwallet->cs_wallet);
3198+
3199+
CBlockIndex *pindexStart = chainActive.Genesis();
3200+
CBlockIndex *pindexStop = nullptr;
3201+
if (!request.params[0].isNull()) {
3202+
pindexStart = chainActive[request.params[0].get_int()];
3203+
if (!pindexStart) {
3204+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid start_height");
3205+
}
3206+
}
3207+
3208+
if (!request.params[1].isNull()) {
3209+
pindexStop = chainActive[request.params[1].get_int()];
3210+
if (!pindexStop) {
3211+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid stop_height");
3212+
}
3213+
else if (pindexStop->nHeight < pindexStart->nHeight) {
3214+
throw JSONRPCError(RPC_INVALID_PARAMETER, "stop_height must be greater then start_height");
3215+
}
3216+
}
3217+
3218+
// We can't rescan beyond non-pruned blocks, stop and throw an error
3219+
if (fPruneMode) {
3220+
CBlockIndex *block = pindexStop ? pindexStop : chainActive.Tip();
3221+
while (block && block->nHeight >= pindexStart->nHeight) {
3222+
if (!(block->nStatus & BLOCK_HAVE_DATA)) {
3223+
throw JSONRPCError(RPC_MISC_ERROR, "Can't rescan beyond pruned data. Use RPC call getblockchaininfo to determine your pruned height.");
3224+
}
3225+
block = block->pprev;
3226+
}
3227+
}
3228+
3229+
CBlockIndex *stopBlock = pwallet->ScanForWalletTransactions(pindexStart, pindexStop, true);
3230+
if (!stopBlock) {
3231+
if (pwallet->IsAbortingRescan()) {
3232+
throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted.");
3233+
}
3234+
// if we got a nullptr returned, ScanForWalletTransactions did rescan up to the requested stopindex
3235+
stopBlock = pindexStop ? pindexStop : chainActive.Tip();
3236+
}
3237+
else {
3238+
throw JSONRPCError(RPC_MISC_ERROR, "Rescan failed. Potentially corrupted data files.");
3239+
}
3240+
3241+
UniValue response(UniValue::VOBJ);
3242+
response.pushKV("start_height", pindexStart->nHeight);
3243+
response.pushKV("stop_height", stopBlock->nHeight);
3244+
return response;
3245+
}
3246+
31723247
extern UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp
31733248
extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp
31743249
extern UniValue importprivkey(const JSONRPCRequest& request);
@@ -3179,6 +3254,7 @@ extern UniValue importwallet(const JSONRPCRequest& request);
31793254
extern UniValue importprunedfunds(const JSONRPCRequest& request);
31803255
extern UniValue removeprunedfunds(const JSONRPCRequest& request);
31813256
extern UniValue importmulti(const JSONRPCRequest& request);
3257+
extern UniValue rescanblockchain(const JSONRPCRequest& request);
31823258

31833259
static const CRPCCommand commands[] =
31843260
{ // category name actor (function) argNames
@@ -3233,6 +3309,7 @@ static const CRPCCommand commands[] =
32333309
{ "wallet", "walletpassphrasechange", &walletpassphrasechange, {"oldpassphrase","newpassphrase"} },
32343310
{ "wallet", "walletpassphrase", &walletpassphrase, {"passphrase","timeout"} },
32353311
{ "wallet", "removeprunedfunds", &removeprunedfunds, {"txid"} },
3312+
{ "wallet", "rescanblockchain", &rescanblockchain, {"start_height", "stop_height"} },
32363313

32373314
{ "generating", "generate", &generate, {"nblocks","maxtries"} },
32383315
};

src/wallet/test/wallet_tests.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
386386
{
387387
CWallet wallet;
388388
AddKey(wallet, coinbaseKey);
389-
BOOST_CHECK_EQUAL(nullBlock, wallet.ScanForWalletTransactions(oldTip));
389+
BOOST_CHECK_EQUAL(nullBlock, wallet.ScanForWalletTransactions(oldTip, nullptr));
390390
BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 100 * COIN);
391391
}
392392

@@ -399,7 +399,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
399399
{
400400
CWallet wallet;
401401
AddKey(wallet, coinbaseKey);
402-
BOOST_CHECK_EQUAL(oldTip, wallet.ScanForWalletTransactions(oldTip));
402+
BOOST_CHECK_EQUAL(oldTip, wallet.ScanForWalletTransactions(oldTip, nullptr));
403403
BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN);
404404
}
405405

@@ -604,7 +604,7 @@ class ListCoinsTestingSetup : public TestChain100Setup
604604
bool firstRun;
605605
wallet->LoadWallet(firstRun);
606606
AddKey(*wallet, coinbaseKey);
607-
wallet->ScanForWalletTransactions(chainActive.Genesis());
607+
wallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr);
608608
}
609609

610610
~ListCoinsTestingSetup()

src/wallet/wallet.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1539,7 +1539,7 @@ int64_t CWallet::RescanFromTime(int64_t startTime, bool update)
15391539
LogPrintf("%s: Rescanning last %i blocks\n", __func__, startBlock ? chainActive.Height() - startBlock->nHeight + 1 : 0);
15401540

15411541
if (startBlock) {
1542-
const CBlockIndex* const failedBlock = ScanForWalletTransactions(startBlock, update);
1542+
const CBlockIndex* const failedBlock = ScanForWalletTransactions(startBlock, nullptr, update);
15431543
if (failedBlock) {
15441544
return failedBlock->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1;
15451545
}
@@ -1555,12 +1555,19 @@ int64_t CWallet::RescanFromTime(int64_t startTime, bool update)
15551555
* Returns null if scan was successful. Otherwise, if a complete rescan was not
15561556
* possible (due to pruning or corruption), returns pointer to the most recent
15571557
* block that could not be scanned.
1558+
*
1559+
* If pindexStop is not a nullptr, the scan will stop at the block-index
1560+
* defined by pindexStop
15581561
*/
1559-
CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
1562+
CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlockIndex* pindexStop, bool fUpdate)
15601563
{
15611564
int64_t nNow = GetTime();
15621565
const CChainParams& chainParams = Params();
15631566

1567+
if (pindexStop) {
1568+
assert(pindexStop->nHeight >= pindexStart->nHeight);
1569+
}
1570+
15641571
CBlockIndex* pindex = pindexStart;
15651572
CBlockIndex* ret = nullptr;
15661573
{
@@ -1588,6 +1595,9 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool f
15881595
} else {
15891596
ret = pindex;
15901597
}
1598+
if (pindex == pindexStop) {
1599+
break;
1600+
}
15911601
pindex = chainActive.Next(pindex);
15921602
}
15931603
if (pindex && fAbortRescan) {
@@ -3903,7 +3913,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
39033913
}
39043914

39053915
nStart = GetTimeMillis();
3906-
walletInstance->ScanForWalletTransactions(pindexRescan, true);
3916+
walletInstance->ScanForWalletTransactions(pindexRescan, nullptr, true);
39073917
LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart);
39083918
walletInstance->SetBestChain(chainActive.GetLocator());
39093919
walletInstance->dbw->IncrementUpdateCounter();

src/wallet/wallet.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -919,7 +919,7 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface
919919
void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) override;
920920
bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate);
921921
int64_t RescanFromTime(int64_t startTime, bool update);
922-
CBlockIndex* ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false);
922+
CBlockIndex* ScanForWalletTransactions(CBlockIndex* pindexStart, CBlockIndex* pindexStop, bool fUpdate = false);
923923
void ReacceptWalletTransactions();
924924
void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override;
925925
// ResendWalletTransactionsBefore may only be called if fBroadcastTransactions!

0 commit comments

Comments
 (0)