aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell Yanofsky <russ@yanofsky.org>2017-05-16 11:34:28 -0400
committerWladimir J. van der Laan <laanwj@gmail.com>2017-05-23 17:09:42 +0200
commit321419bc06fdc19e3713b2bcfc10c3c9bbbb8b62 (patch)
tree18144663278653e9d1038b0f37d97ef1b76eed30
parent37a8fc54d480788a06ff17415128d815ac55eaed (diff)
downloadbitcoin-321419bc06fdc19e3713b2bcfc10c3c9bbbb8b62.tar.xz
Fix importwallet edge case rescan bug
Start importwallet rescans at the first block with timestamp greater or equal to the wallet birthday instead of the last block with timestamp less or equal. This fixes an edge case bug where importwallet could fail to start the rescan early enough if there are blocks with decreasing timestamps or multiple blocks with the same timestamp. Github-Pull: #10410 Rebased-From: 2a8e35a11d4bd4828631654fc7b8b8fe8f0a2460
-rw-r--r--src/wallet/rpcdump.cpp9
-rw-r--r--src/wallet/test/wallet_tests.cpp62
2 files changed, 65 insertions, 6 deletions
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index cfb53af497..cd6ec207f9 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -497,14 +497,11 @@ UniValue importwallet(const JSONRPCRequest& request)
}
file.close();
pwalletMain->ShowProgress("", 100); // hide progress dialog in GUI
-
- CBlockIndex *pindex = chainActive.Tip();
- while (pindex && pindex->pprev && pindex->GetBlockTime() > nTimeBegin - 7200)
- pindex = pindex->pprev;
-
pwalletMain->UpdateTimeFirstKey(nTimeBegin);
- LogPrintf("Rescanning last %i blocks\n", chainActive.Height() - pindex->nHeight + 1);
+ CBlockIndex *pindex = chainActive.FindEarliestAtLeast(nTimeBegin - 7200);
+
+ LogPrintf("Rescanning last %i blocks\n", pindex ? chainActive.Height() - pindex->nHeight + 1 : 0);
pwalletMain->ScanForWalletTransactions(pindex);
pwalletMain->MarkDirty();
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 7ac2112dd2..083ff0afc3 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -19,6 +19,8 @@
#include <univalue.h>
extern UniValue importmulti(const JSONRPCRequest& request);
+extern UniValue dumpwallet(const JSONRPCRequest& request);
+extern UniValue importwallet(const JSONRPCRequest& request);
// how many times to run all the tests to have a chance to catch errors that only show up with particular random shuffles
#define RUN_TESTS 100
@@ -428,4 +430,64 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
}
}
+// Verify importwallet RPC starts rescan at earliest block with timestamp
+// greater or equal than key birthday. Previously there was a bug where
+// importwallet RPC would start the scan at the latest block with timestamp less
+// than or equal to key birthday.
+BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
+{
+ CWallet *pwalletMainBackup = ::pwalletMain;
+ LOCK(cs_main);
+
+ // Create two blocks with same timestamp to verify that importwallet rescan
+ // will pick up both blocks, not just the first.
+ const int64_t BLOCK_TIME = chainActive.Tip()->GetBlockTimeMax() + 5;
+ SetMockTime(BLOCK_TIME);
+ coinbaseTxns.emplace_back(*CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
+ coinbaseTxns.emplace_back(*CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
+
+ // Set key birthday to block time increased by the timestamp window, so
+ // rescan will start at the block time.
+ const int64_t KEY_TIME = BLOCK_TIME + 7200;
+ SetMockTime(KEY_TIME);
+ coinbaseTxns.emplace_back(*CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
+
+ // Import key into wallet and call dumpwallet to create backup file.
+ {
+ CWallet wallet;
+ LOCK(wallet.cs_wallet);
+ wallet.mapKeyMetadata[coinbaseKey.GetPubKey().GetID()].nCreateTime = KEY_TIME;
+ wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
+
+ JSONRPCRequest request;
+ request.params.setArray();
+ request.params.push_back("wallet.backup");
+ ::pwalletMain = &wallet;
+ ::dumpwallet(request);
+ }
+
+ // Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME
+ // were scanned, and no prior blocks were scanned.
+ {
+ CWallet wallet;
+
+ JSONRPCRequest request;
+ request.params.setArray();
+ request.params.push_back("wallet.backup");
+ ::pwalletMain = &wallet;
+ ::importwallet(request);
+
+ BOOST_CHECK_EQUAL(wallet.mapWallet.size(), 3);
+ BOOST_CHECK_EQUAL(coinbaseTxns.size(), 103);
+ for (size_t i = 0; i < coinbaseTxns.size(); ++i) {
+ bool found = wallet.GetWalletTx(coinbaseTxns[i].GetHash());
+ bool expected = i >= 100;
+ BOOST_CHECK_EQUAL(found, expected);
+ }
+ }
+
+ SetMockTime(0);
+ ::pwalletMain = pwalletMainBackup;
+}
+
BOOST_AUTO_TEST_SUITE_END()