Merge #10403: Fix importmulti failure to return rescan errors

4d2d604 Fix importmulti failure to return rescan errors (Russell Yanofsky)

Tree-SHA512: e5e6d6c5a2bb7230e1bcac1903a4b766cd57bf781fade50c6c9cd5713cd3e768db0987cfda9699b57a53d3a0a60951b96dce5283b3d3ec1f954162c439bc932b
This commit is contained in:
Wladimir J. van der Laan 2017-06-05 16:38:48 +02:00
commit 08d0390a5f
No known key found for this signature in database
GPG key ID: 1E4AED62986CD25D
3 changed files with 34 additions and 29 deletions

View file

@ -1118,13 +1118,13 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
if (fRescan && fRunScan && requests.size()) {
CBlockIndex* pindex = nLowestTimestamp > minimumTimestamp ? chainActive.FindEarliestAtLeast(std::max<int64_t>(nLowestTimestamp - TIMESTAMP_WINDOW, 0)) : chainActive.Genesis();
CBlockIndex* scannedRange = nullptr;
CBlockIndex* scanFailed = nullptr;
if (pindex) {
scannedRange = pwallet->ScanForWalletTransactions(pindex, true);
scanFailed = pwallet->ScanForWalletTransactions(pindex, true);
pwallet->ReacceptWalletTransactions();
}
if (!scannedRange || scannedRange->nHeight > pindex->nHeight) {
if (scanFailed) {
std::vector<UniValue> results = response.getValues();
response.clear();
response.setArray();
@ -1134,12 +1134,23 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
// range, or if the import result already has an error set, let
// the result stand unmodified. Otherwise replace the result
// with an error message.
if (GetImportTimestamp(request, now) - TIMESTAMP_WINDOW >= scannedRange->GetBlockTimeMax() || results.at(i).exists("error")) {
if (GetImportTimestamp(request, now) - TIMESTAMP_WINDOW > scanFailed->GetBlockTimeMax() || results.at(i).exists("error")) {
response.push_back(results.at(i));
} else {
UniValue result = UniValue(UniValue::VOBJ);
result.pushKV("success", UniValue(false));
result.pushKV("error", JSONRPCError(RPC_MISC_ERROR, strprintf("Failed to rescan before time %d, transactions may be missing.", scannedRange->GetBlockTimeMax())));
result.pushKV(
"error",
JSONRPCError(
RPC_MISC_ERROR,
strprintf("Rescan failed for key with creation timestamp %d. There was an error reading a "
"block from time %d, which is after or within %d seconds of key creation, and "
"could contain transactions pertaining to the key. As a result, transactions "
"and coins using this key may not appear in the wallet. This error could be "
"caused by pruning or data corruption (see bitcoind log for details) and could "
"be dealt with by downloading and rescanning the relevant blocks (see -reindex "
"and -rescan options).",
GetImportTimestamp(request, now), scanFailed->GetBlockTimeMax(), TIMESTAMP_WINDOW)));
response.push_back(std::move(result));
}
++i;

View file

@ -367,6 +367,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
LOCK(cs_main);
// Cap last block file size, and mine new block in a new block file.
CBlockIndex* const nullBlock = nullptr;
CBlockIndex* oldTip = chainActive.Tip();
GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE;
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
@ -378,7 +379,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
CWallet wallet;
LOCK(wallet.cs_wallet);
wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
BOOST_CHECK_EQUAL(oldTip, wallet.ScanForWalletTransactions(oldTip));
BOOST_CHECK_EQUAL(nullBlock, wallet.ScanForWalletTransactions(oldTip));
BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 100 * COIN);
}
@ -392,7 +393,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
CWallet wallet;
LOCK(wallet.cs_wallet);
wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
BOOST_CHECK_EQUAL(newTip, wallet.ScanForWalletTransactions(oldTip));
BOOST_CHECK_EQUAL(oldTip, wallet.ScanForWalletTransactions(oldTip));
BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN);
}
@ -416,7 +417,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
CKey futureKey;
futureKey.MakeNewKey(true);
key.pushKV("scriptPubKey", HexStr(GetScriptForRawPubKey(futureKey.GetPubKey())));
key.pushKV("timestamp", newTip->GetBlockTimeMax() + TIMESTAMP_WINDOW);
key.pushKV("timestamp", newTip->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1);
key.pushKV("internal", UniValue(true));
keys.push_back(key);
JSONRPCRequest request;
@ -424,20 +425,17 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
request.params.push_back(keys);
UniValue response = importmulti(request);
BOOST_CHECK_EQUAL(response.write(), strprintf("[{\"success\":false,\"error\":{\"code\":-1,\"message\":\"Failed to rescan before time %d, transactions may be missing.\"}},{\"success\":true}]", newTip->GetBlockTimeMax()));
BOOST_CHECK_EQUAL(response.write(),
strprintf("[{\"success\":false,\"error\":{\"code\":-1,\"message\":\"Rescan failed for key with creation "
"timestamp %d. There was an error reading a block from time %d, which is after or within %d "
"seconds of key creation, and could contain transactions pertaining to the key. As a result, "
"transactions and coins using this key may not appear in the wallet. This error could be caused "
"by pruning or data corruption (see bitcoind log for details) and could be dealt with by "
"downloading and rescanning the relevant blocks (see -reindex and -rescan "
"options).\"}},{\"success\":true}]",
0, oldTip->GetBlockTimeMax(), TIMESTAMP_WINDOW));
::pwalletMain = backup;
}
// Verify ScanForWalletTransactions does not return null when the scan is
// elided due to the nTimeFirstKey optimization.
{
CWallet wallet;
{
LOCK(wallet.cs_wallet);
wallet.UpdateTimeFirstKey(newTip->GetBlockTime() + 7200 + 1);
}
BOOST_CHECK_EQUAL(newTip, wallet.ScanForWalletTransactions(newTip));
}
}
// Verify importwallet RPC starts rescan at earliest block with timestamp

View file

@ -1463,10 +1463,9 @@ void CWalletTx::GetAmounts(std::list<COutputEntry>& listReceived,
* from or to us. If fUpdate is true, found transactions that already
* exist in the wallet will be updated.
*
* Returns pointer to the first block in the last contiguous range that was
* successfully scanned or elided (elided if pIndexStart points at a block
* before CWallet::nTimeFirstKey). Returns null if there is no such range, or
* the range doesn't include chainActive.Tip().
* 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.
*/
CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
{
@ -1474,7 +1473,7 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool f
const CChainParams& chainParams = Params();
CBlockIndex* pindex = pindexStart;
CBlockIndex* ret = pindexStart;
CBlockIndex* ret = nullptr;
{
LOCK2(cs_main, cs_wallet);
fAbortRescan = false;
@ -1502,11 +1501,8 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool f
for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {
AddToWalletIfInvolvingMe(block.vtx[posInBlock], pindex, posInBlock, fUpdate);
}
if (!ret) {
ret = pindex;
}
} else {
ret = nullptr;
ret = pindex;
}
pindex = chainActive.Next(pindex);
}