Return errors from importmulti if complete rescans are not successful
This commit is contained in:
parent
9828f9a996
commit
e2e2f4c856
6 changed files with 114 additions and 13 deletions
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -1074,11 +1074,32 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
|
||||||
|
|
||||||
if (fRescan && fRunScan && requests.size() && nLowestTimestamp <= chainActive.Tip()->GetBlockTimeMax()) {
|
if (fRescan && fRunScan && requests.size() && nLowestTimestamp <= chainActive.Tip()->GetBlockTimeMax()) {
|
||||||
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;
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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…
Add table
Reference in a new issue