Browse Source

Merge #10403: Fix importmulti failure to return rescan errors

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

Tree-SHA512: e5e6d6c5a2bb7230e1bcac1903a4b766cd57bf781fade50c6c9cd5713cd3e768db0987cfda9699b57a53d3a0a60951b96dce5283b3d3ec1f954162c439bc932b
0.15
Wladimir J. van der Laan 8 years ago
parent
commit
08d0390a5f
No known key found for this signature in database
GPG Key ID: 1E4AED62986CD25D
  1. 21
      src/wallet/rpcdump.cpp
  2. 28
      src/wallet/test/wallet_tests.cpp
  3. 14
      src/wallet/wallet.cpp

21
src/wallet/rpcdump.cpp

@ -1118,13 +1118,13 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
if (fRescan && fRunScan && requests.size()) { if (fRescan && fRunScan && requests.size()) {
CBlockIndex* pindex = nLowestTimestamp > minimumTimestamp ? chainActive.FindEarliestAtLeast(std::max<int64_t>(nLowestTimestamp - TIMESTAMP_WINDOW, 0)) : chainActive.Genesis(); CBlockIndex* pindex = nLowestTimestamp > minimumTimestamp ? chainActive.FindEarliestAtLeast(std::max<int64_t>(nLowestTimestamp - TIMESTAMP_WINDOW, 0)) : chainActive.Genesis();
CBlockIndex* scannedRange = nullptr; CBlockIndex* scanFailed = nullptr;
if (pindex) { if (pindex) {
scannedRange = pwallet->ScanForWalletTransactions(pindex, true); scanFailed = pwallet->ScanForWalletTransactions(pindex, true);
pwallet->ReacceptWalletTransactions(); pwallet->ReacceptWalletTransactions();
} }
if (!scannedRange || scannedRange->nHeight > pindex->nHeight) { if (scanFailed) {
std::vector<UniValue> results = response.getValues(); std::vector<UniValue> results = response.getValues();
response.clear(); response.clear();
response.setArray(); response.setArray();
@ -1134,12 +1134,23 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
// range, or if the import result already has an error set, let // range, or if the import result already has an error set, let
// the result stand unmodified. Otherwise replace the result // the result stand unmodified. Otherwise replace the result
// with an error message. // 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)); response.push_back(results.at(i));
} else { } else {
UniValue result = UniValue(UniValue::VOBJ); UniValue result = UniValue(UniValue::VOBJ);
result.pushKV("success", UniValue(false)); 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)); response.push_back(std::move(result));
} }
++i; ++i;

28
src/wallet/test/wallet_tests.cpp

@ -367,6 +367,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
LOCK(cs_main); LOCK(cs_main);
// Cap last block file size, and mine new block in a new block file. // Cap last block file size, and mine new block in a new block file.
CBlockIndex* const nullBlock = nullptr;
CBlockIndex* oldTip = chainActive.Tip(); CBlockIndex* oldTip = chainActive.Tip();
GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE; GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE;
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
@ -378,7 +379,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
CWallet wallet; CWallet wallet;
LOCK(wallet.cs_wallet); LOCK(wallet.cs_wallet);
wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); 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); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 100 * COIN);
} }
@ -392,7 +393,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
CWallet wallet; CWallet wallet;
LOCK(wallet.cs_wallet); LOCK(wallet.cs_wallet);
wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); 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); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN);
} }
@ -416,7 +417,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
CKey futureKey; CKey futureKey;
futureKey.MakeNewKey(true); futureKey.MakeNewKey(true);
key.pushKV("scriptPubKey", HexStr(GetScriptForRawPubKey(futureKey.GetPubKey()))); 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)); key.pushKV("internal", UniValue(true));
keys.push_back(key); keys.push_back(key);
JSONRPCRequest request; JSONRPCRequest request;
@ -424,20 +425,17 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
request.params.push_back(keys); request.params.push_back(keys);
UniValue response = importmulti(request); 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; ::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 // Verify importwallet RPC starts rescan at earliest block with timestamp

14
src/wallet/wallet.cpp

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

Loading…
Cancel
Save