Browse Source

Merge #9773: Return errors from importmulti if complete rescans are not successful

e2e2f4c Return errors from importmulti if complete rescans are not successful (Russell Yanofsky)
0.15
Wladimir J. van der Laan 8 years ago
parent
commit
ba7220b5e8
No known key found for this signature in database
GPG Key ID: 74810B012346C9A6
  1. 7
      src/validation.cpp
  2. 10
      src/validation.h
  3. 25
      src/wallet/rpcdump.cpp
  4. 60
      src/wallet/test/wallet_tests.cpp
  5. 23
      src/wallet/wallet.cpp
  6. 2
      src/wallet/wallet.h

7
src/validation.cpp

@ -3331,7 +3331,7 @@ void PruneOneBlockFile(const int fileNumber)
} }
void UnlinkPrunedFiles(std::set<int>& setFilesToPrune) void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune)
{ {
for (std::set<int>::iterator it = setFilesToPrune.begin(); it != setFilesToPrune.end(); ++it) { for (std::set<int>::iterator it = setFilesToPrune.begin(); it != setFilesToPrune.end(); ++it) {
CDiskBlockPos pos(*it, 0); CDiskBlockPos pos(*it, 0);
@ -4163,6 +4163,11 @@ std::string CBlockFileInfo::ToString() const
return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst), DateTimeStrFormat("%Y-%m-%d", nTimeLast)); return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst), DateTimeStrFormat("%Y-%m-%d", nTimeLast));
} }
CBlockFileInfo* GetBlockFileInfo(size_t n)
{
return &vinfoBlockFile.at(n);
}
ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos) ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos)
{ {
LOCK(cs_main); LOCK(cs_main);

10
src/validation.h

@ -299,10 +299,15 @@ double GuessVerificationProgress(const ChainTxData& data, CBlockIndex* pindex);
*/ */
void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight); void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight);
/**
* Mark one block file as pruned.
*/
void PruneOneBlockFile(const int fileNumber);
/** /**
* Actually unlink the specified files * Actually unlink the specified files
*/ */
void UnlinkPrunedFiles(std::set<int>& setFilesToPrune); void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune);
/** Create a new block index entry for a given block hash */ /** Create a new block index entry for a given block hash */
CBlockIndex * InsertBlockIndex(uint256 hash); CBlockIndex * InsertBlockIndex(uint256 hash);
@ -562,6 +567,9 @@ static const unsigned int REJECT_ALREADY_KNOWN = 0x101;
/** Transaction conflicts with a transaction already known */ /** Transaction conflicts with a transaction already known */
static const unsigned int REJECT_CONFLICT = 0x102; static const unsigned int REJECT_CONFLICT = 0x102;
/** Get block file info entry for one block file */
CBlockFileInfo* GetBlockFileInfo(size_t n);
/** Dump the mempool to disk. */ /** Dump the mempool to disk. */
void DumpMempool(); void DumpMempool();

25
src/wallet/rpcdump.cpp

@ -1074,11 +1074,32 @@ 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 - 7200, 0)) : chainActive.Genesis(); CBlockIndex* pindex = nLowestTimestamp > minimumTimestamp ? chainActive.FindEarliestAtLeast(std::max<int64_t>(nLowestTimestamp - 7200, 0)) : chainActive.Genesis();
CBlockIndex* scannedRange = nullptr;
if (pindex) { if (pindex) {
pwalletMain->ScanForWalletTransactions(pindex, true); scannedRange = pwalletMain->ScanForWalletTransactions(pindex, true);
pwalletMain->ReacceptWalletTransactions(); pwalletMain->ReacceptWalletTransactions();
} }
if (!scannedRange || scannedRange->nHeight > pindex->nHeight) {
std::vector<UniValue> results = response.getValues();
response.clear();
response.setArray();
size_t i = 0;
for (const UniValue& request : requests.getValues()) {
// If key creation date is within the successfully scanned
// 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) - 7200 >= scannedRange->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())));
response.push_back(std::move(result));
}
}
}
} }
return response; return response;

60
src/wallet/test/wallet_tests.cpp

@ -9,10 +9,16 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "rpc/server.h"
#include "test/test_bitcoin.h"
#include "validation.h"
#include "wallet/test/wallet_test_fixture.h" #include "wallet/test/wallet_test_fixture.h"
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <univalue.h>
extern UniValue importmulti(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
@ -355,4 +361,58 @@ BOOST_AUTO_TEST_CASE(ApproximateBestSubset)
empty_wallet(); empty_wallet();
} }
BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
{
LOCK(cs_main);
// Cap last block file size, and mine new block in a new block file.
CBlockIndex* oldTip = chainActive.Tip();
GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE;
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
CBlockIndex* newTip = chainActive.Tip();
// Verify ScanForWalletTransactions picks up transactions in both the old
// and new block files.
{
CWallet wallet;
LOCK(wallet.cs_wallet);
wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
BOOST_CHECK_EQUAL(oldTip, wallet.ScanForWalletTransactions(oldTip));
BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 100 * COIN);
}
// Prune the older block file.
PruneOneBlockFile(oldTip->GetBlockPos().nFile);
UnlinkPrunedFiles({oldTip->GetBlockPos().nFile});
// Verify ScanForWalletTransactions only picks transactions in the new block
// file.
{
CWallet wallet;
LOCK(wallet.cs_wallet);
wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
BOOST_CHECK_EQUAL(newTip, wallet.ScanForWalletTransactions(oldTip));
BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN);
}
{
CWallet wallet;
::pwalletMain = &wallet;
UniValue key;
key.setObject();
key.pushKV("scriptPubKey", HexStr(GetScriptForRawPubKey(coinbaseKey.GetPubKey())));
key.pushKV("timestamp", 0);
key.pushKV("internal", UniValue(true));
UniValue keys;
keys.setArray();
keys.push_back(key);
JSONRPCRequest request;
request.params.setArray();
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.\"}}]", newTip->GetBlockTimeMax()));
}
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

23
src/wallet/wallet.cpp

@ -1545,10 +1545,14 @@ void CWalletTx::GetAccountAmounts(const string& strAccount, CAmount& nReceived,
* Scan the block chain (starting in pindexStart) for transactions * Scan the block chain (starting in pindexStart) for transactions
* 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
* successfully scanned.
*
*/ */
int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
{ {
int ret = 0; CBlockIndex* ret = nullptr;
int64_t nNow = GetTime(); int64_t nNow = GetTime();
const CChainParams& chainParams = Params(); const CChainParams& chainParams = Params();
@ -1570,12 +1574,15 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((GuessVerificationProgress(chainParams.TxData(), pindex) - dProgressStart) / (dProgressTip - dProgressStart) * 100)))); ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((GuessVerificationProgress(chainParams.TxData(), pindex) - dProgressStart) / (dProgressTip - dProgressStart) * 100))));
CBlock block; CBlock block;
ReadBlockFromDisk(block, pindex, Params().GetConsensus()); if (ReadBlockFromDisk(block, pindex, Params().GetConsensus())) {
int posInBlock; for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {
for (posInBlock = 0; posInBlock < (int)block.vtx.size(); posInBlock++) AddToWalletIfInvolvingMe(*block.vtx[posInBlock], pindex, posInBlock, fUpdate);
{ }
if (AddToWalletIfInvolvingMe(*block.vtx[posInBlock], pindex, posInBlock, fUpdate)) if (!ret) {
ret++; ret = pindex;
}
} else {
ret = nullptr;
} }
pindex = chainActive.Next(pindex); pindex = chainActive.Next(pindex);
if (GetTime() >= nNow + 60) { if (GetTime() >= nNow + 60) {

2
src/wallet/wallet.h

@ -788,7 +788,7 @@ public:
bool LoadToWallet(const CWalletTx& wtxIn); bool LoadToWallet(const CWalletTx& wtxIn);
void SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock) override; void SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock) override;
bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate); bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate);
int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); CBlockIndex* ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false);
void ReacceptWalletTransactions(); void ReacceptWalletTransactions();
void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override; void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override;
std::vector<uint256> ResendWalletTransactionsBefore(int64_t nTime, CConnman* connman); std::vector<uint256> ResendWalletTransactionsBefore(int64_t nTime, CConnman* connman);

Loading…
Cancel
Save