Merge #10410: Fix importwallet edge case rescan bug

2a8e35a Fix importwallet edge case rescan bug (Russell Yanofsky)

Tree-SHA512: 59522c962290f9ef64436349d11183dd1fd829e515d1f5ec802b63dd813d04303e28d4f3ba38df77a6c151ee4c14f3ca5d3d82204c57456ac94054de62ae4bc7
This commit is contained in:
Wladimir J. van der Laan 2017-05-23 16:38:19 +02:00
commit e76a3927c3
No known key found for this signature in database
GPG key ID: 1E4AED62986CD25D
2 changed files with 65 additions and 6 deletions

View file

@ -536,14 +536,11 @@ UniValue importwallet(const JSONRPCRequest& request)
} }
file.close(); file.close();
pwallet->ShowProgress("", 100); // hide progress dialog in GUI pwallet->ShowProgress("", 100); // hide progress dialog in GUI
CBlockIndex *pindex = chainActive.Tip();
while (pindex && pindex->pprev && pindex->GetBlockTime() > nTimeBegin - TIMESTAMP_WINDOW)
pindex = pindex->pprev;
pwallet->UpdateTimeFirstKey(nTimeBegin); pwallet->UpdateTimeFirstKey(nTimeBegin);
LogPrintf("Rescanning last %i blocks\n", chainActive.Height() - pindex->nHeight + 1); CBlockIndex *pindex = chainActive.FindEarliestAtLeast(nTimeBegin - TIMESTAMP_WINDOW);
LogPrintf("Rescanning last %i blocks\n", pindex ? chainActive.Height() - pindex->nHeight + 1 : 0);
pwallet->ScanForWalletTransactions(pindex); pwallet->ScanForWalletTransactions(pindex);
pwallet->MarkDirty(); pwallet->MarkDirty();

View file

@ -19,6 +19,8 @@
#include <univalue.h> #include <univalue.h>
extern UniValue importmulti(const JSONRPCRequest& request); 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 // 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 #define RUN_TESTS 100
@ -437,6 +439,66 @@ 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 + TIMESTAMP_WINDOW;
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;
}
// Check that GetImmatureCredit() returns a newly calculated value instead of // Check that GetImmatureCredit() returns a newly calculated value instead of
// the cached value after a MarkDirty() call. // the cached value after a MarkDirty() call.
// //