Skip to content

Commit

Permalink
Convert CWallet::ScanForWalletTransactions and SyncTransaction to the…
Browse files Browse the repository at this point in the history
… new Chain apis

Only change in behavior is "Rescan started from block <height>" message
replaced by "Rescan started from block <hash>" message in
ScanForWalletTransactions.

Co-authored-by: Ben Woosley <ben.woosley@gmail.com>
  • Loading branch information
ryanofsky and Empact committed Jan 15, 2019
1 parent 2ffb079 commit db21f02
Show file tree
Hide file tree
Showing 7 changed files with 209 additions and 125 deletions.
41 changes: 41 additions & 0 deletions src/interfaces/chain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,42 @@ class LockImpl : public Chain::Lock
assert(block != nullptr);
return block->GetMedianTimePast();
}
Optional<int> findFirstBlockWithTime(int64_t time, uint256* hash) override
{
CBlockIndex* block = ::chainActive.FindEarliestAtLeast(time);
if (block) {
if (hash) *hash = block->GetBlockHash();
return block->nHeight;
}
return nullopt;
}
Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height) override
{
// TODO: Could update CChain::FindEarliestAtLeast() to take a height
// parameter and use it with std::lower_bound() to make this
// implementation more efficient and allow combining
// findFirstBlockWithTime and findFirstBlockWithTimeAndHeight into one
// method.
for (CBlockIndex* block = ::chainActive[height]; block; block = ::chainActive.Next(block)) {
if (block->GetBlockTime() >= time) {
return block->nHeight;
}
}
return nullopt;
}
Optional<int> findPruned(int start_height, Optional<int> stop_height) override
{
if (::fPruneMode) {
CBlockIndex* block = stop_height ? ::chainActive[*stop_height] : ::chainActive.Tip();
while (block && block->nHeight >= start_height) {
if ((block->nStatus & BLOCK_HAVE_DATA) == 0) {
return block->nHeight;
}
block = block->pprev;
}
}
return nullopt;
}
Optional<int> findFork(const uint256& hash, Optional<int>* height) override
{
const CBlockIndex* block = LookupBlockIndex(hash);
Expand Down Expand Up @@ -116,6 +152,11 @@ class ChainImpl : public Chain
}
return true;
}
double guessVerificationProgress(const uint256& block_hash) override
{
LOCK(cs_main);
return GuessVerificationProgress(Params().TxData(), LookupBlockIndex(block_hash));
}
};

} // namespace
Expand Down
24 changes: 24 additions & 0 deletions src/interfaces/chain.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,26 @@ class Chain
//! will abort.
virtual int64_t getBlockMedianTimePast(int height) = 0;

//! Return height of the first block in the chain with timestamp equal
//! or greater than the given time, or nullopt if there is no block with
//! a high enough timestamp. Also return the block hash as an optional
//! output parameter (to avoid the cost of a second lookup in case this
//! information is needed.)
virtual Optional<int> findFirstBlockWithTime(int64_t time, uint256* hash) = 0;

//! Return height of the first block in the chain with timestamp equal
//! or greater than the given time and height equal or greater than the
//! given height, or nullopt if there is no such block.
//!
//! Calling this with height 0 is equivalent to calling
//! findFirstBlockWithTime, but less efficient because it requires a
//! linear instead of a binary search.
virtual Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height) = 0;

//! Return height of last block in the specified range which is pruned, or
//! nullopt if no block in the range is pruned. Range is inclusive.
virtual Optional<int> findPruned(int start_height = 0, Optional<int> stop_height = nullopt) = 0;

//! Return height of the highest block on the chain that is an ancestor
//! of the specified block, or nullopt if no common ancestor is found.
//! Also return the height of the specified block as an optional output
Expand Down Expand Up @@ -85,6 +105,10 @@ class Chain
CBlock* block = nullptr,
int64_t* time = nullptr,
int64_t* max_time = nullptr) = 0;

//! Estimate fraction of total transactions verified if blocks up to
//! the specified block hash are verified.
virtual double guessVerificationProgress(const uint256& block_hash) = 0;
};

//! Interface to let node manage chain clients (wallets, or maybe tools for
Expand Down
12 changes: 5 additions & 7 deletions src/qt/test/wallettests.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <qt/test/wallettests.h>
#include <qt/test/util.h>

#include <init.h>
#include <interfaces/chain.h>
#include <interfaces/node.h>
#include <base58.h>
Expand Down Expand Up @@ -146,13 +147,10 @@ void TestGUI()
auto locked_chain = wallet->chain().lock();
WalletRescanReserver reserver(wallet.get());
reserver.reserve();
const CBlockIndex* const null_block = nullptr;
const CBlockIndex *stop_block, *failed_block;
QCOMPARE(
wallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver, failed_block, stop_block, true /* fUpdate */),
CWallet::ScanResult::SUCCESS);
QCOMPARE(stop_block, chainActive.Tip());
QCOMPARE(failed_block, null_block);
CWallet::ScanResult result = wallet->ScanForWalletTransactions(locked_chain->getBlockHash(0), {} /* stop_block */, reserver, true /* fUpdate */);
QCOMPARE(result.status, CWallet::ScanResult::SUCCESS);
QCOMPARE(result.stop_block, chainActive.Tip()->GetBlockHash());
QVERIFY(result.failed_block.IsNull());
}
wallet->SetBroadcastTransactions(true);

Expand Down
48 changes: 23 additions & 25 deletions src/wallet/rpcwallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3387,59 +3387,57 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
}

CBlockIndex *pindexStart = nullptr;
CBlockIndex *pindexStop = nullptr;
CBlockIndex *pChainTip = nullptr;
int start_height = 0;
uint256 start_block, stop_block;
{
auto locked_chain = pwallet->chain().lock();
pindexStart = chainActive.Genesis();
pChainTip = chainActive.Tip();
Optional<int> tip_height = locked_chain->getHeight();

if (!request.params[0].isNull()) {
pindexStart = chainActive[request.params[0].get_int()];
if (!pindexStart) {
start_height = request.params[0].get_int();
if (start_height < 0 || !tip_height || start_height > *tip_height) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid start_height");
}
}

Optional<int> stop_height;
if (!request.params[1].isNull()) {
pindexStop = chainActive[request.params[1].get_int()];
if (!pindexStop) {
stop_height = request.params[1].get_int();
if (*stop_height < 0 || !tip_height || *stop_height > *tip_height) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid stop_height");
}
else if (pindexStop->nHeight < pindexStart->nHeight) {
else if (*stop_height < start_height) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "stop_height must be greater than start_height");
}
}
}

// We can't rescan beyond non-pruned blocks, stop and throw an error
if (fPruneMode) {
auto locked_chain = pwallet->chain().lock();
CBlockIndex *block = pindexStop ? pindexStop : pChainTip;
while (block && block->nHeight >= pindexStart->nHeight) {
if (!(block->nStatus & BLOCK_HAVE_DATA)) {
throw JSONRPCError(RPC_MISC_ERROR, "Can't rescan beyond pruned data. Use RPC call getblockchaininfo to determine your pruned height.");
// We can't rescan beyond non-pruned blocks, stop and throw an error
if (locked_chain->findPruned(start_height, stop_height)) {
throw JSONRPCError(RPC_MISC_ERROR, "Can't rescan beyond pruned data. Use RPC call getblockchaininfo to determine your pruned height.");
}

if (tip_height) {
start_block = locked_chain->getBlockHash(start_height);
if (stop_height) {
stop_block = locked_chain->getBlockHash(*stop_height);
}
block = block->pprev;
}
}

const CBlockIndex *failed_block, *stopBlock;
CWallet::ScanResult result =
pwallet->ScanForWalletTransactions(pindexStart, pindexStop, reserver, failed_block, stopBlock, true);
switch (result) {
pwallet->ScanForWalletTransactions(start_block, stop_block, reserver, true /* fUpdate */);
switch (result.status) {
case CWallet::ScanResult::SUCCESS:
break; // stopBlock set by ScanForWalletTransactions
break;
case CWallet::ScanResult::FAILURE:
throw JSONRPCError(RPC_MISC_ERROR, "Rescan failed. Potentially corrupted data files.");
case CWallet::ScanResult::USER_ABORT:
throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted.");
// no default case, so the compiler can warn about missing cases
}
UniValue response(UniValue::VOBJ);
response.pushKV("start_height", pindexStart->nHeight);
response.pushKV("stop_height", stopBlock->nHeight);
response.pushKV("start_height", start_height);
response.pushKV("stop_height", result.stop_height ? *result.stop_height : UniValue());
return response;
}

Expand Down
47 changes: 25 additions & 22 deletions src/wallet/test/wallet_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
auto chain = interfaces::MakeChain();

// Cap last block file size, and mine new block in a new block file.
const CBlockIndex* const null_block = nullptr;
CBlockIndex* oldTip = chainActive.Tip();
GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE;
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
Expand All @@ -53,10 +52,11 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet);
reserver.reserve();
const CBlockIndex *stop_block = null_block + 1, *failed_block = null_block + 1;
BOOST_CHECK_EQUAL(wallet.ScanForWalletTransactions(nullptr, nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::SUCCESS);
BOOST_CHECK_EQUAL(failed_block, null_block);
BOOST_CHECK_EQUAL(stop_block, null_block);
CWallet::ScanResult result = wallet.ScanForWalletTransactions({} /* start_block */, {} /* stop_block */, reserver, false /* update */);
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS);
BOOST_CHECK(result.failed_block.IsNull());
BOOST_CHECK(result.stop_block.IsNull());
BOOST_CHECK(!result.stop_height);
BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 0);
}

Expand All @@ -67,10 +67,11 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet);
reserver.reserve();
const CBlockIndex *stop_block = null_block + 1, *failed_block = null_block + 1;
BOOST_CHECK_EQUAL(wallet.ScanForWalletTransactions(oldTip, nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::SUCCESS);
BOOST_CHECK_EQUAL(failed_block, null_block);
BOOST_CHECK_EQUAL(stop_block, newTip);
CWallet::ScanResult result = wallet.ScanForWalletTransactions(oldTip->GetBlockHash(), {} /* stop_block */, reserver, false /* update */);
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS);
BOOST_CHECK(result.failed_block.IsNull());
BOOST_CHECK_EQUAL(result.stop_block, newTip->GetBlockHash());
BOOST_CHECK_EQUAL(*result.stop_height, newTip->nHeight);
BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 100 * COIN);
}

Expand All @@ -85,10 +86,11 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet);
reserver.reserve();
const CBlockIndex *stop_block = null_block + 1, *failed_block = null_block + 1;
BOOST_CHECK_EQUAL(wallet.ScanForWalletTransactions(oldTip, nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::FAILURE);
BOOST_CHECK_EQUAL(failed_block, oldTip);
BOOST_CHECK_EQUAL(stop_block, newTip);
CWallet::ScanResult result = wallet.ScanForWalletTransactions(oldTip->GetBlockHash(), {} /* stop_block */, reserver, false /* update */);
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::FAILURE);
BOOST_CHECK_EQUAL(result.failed_block, oldTip->GetBlockHash());
BOOST_CHECK_EQUAL(result.stop_block, newTip->GetBlockHash());
BOOST_CHECK_EQUAL(*result.stop_height, newTip->nHeight);
BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN);
}

Expand All @@ -102,10 +104,11 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet);
reserver.reserve();
const CBlockIndex *stop_block = null_block + 1, *failed_block = null_block + 1;
BOOST_CHECK_EQUAL(wallet.ScanForWalletTransactions(oldTip, nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::FAILURE);
BOOST_CHECK_EQUAL(failed_block, newTip);
BOOST_CHECK_EQUAL(stop_block, null_block);
CWallet::ScanResult result = wallet.ScanForWalletTransactions(oldTip->GetBlockHash(), {} /* stop_block */, reserver, false /* update */);
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::FAILURE);
BOOST_CHECK_EQUAL(result.failed_block, newTip->GetBlockHash());
BOOST_CHECK(result.stop_block.IsNull());
BOOST_CHECK(!result.stop_height);
BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 0);
}
}
Expand Down Expand Up @@ -340,11 +343,11 @@ class ListCoinsTestingSetup : public TestChain100Setup
AddKey(*wallet, coinbaseKey);
WalletRescanReserver reserver(wallet.get());
reserver.reserve();
const CBlockIndex* const null_block = nullptr;
const CBlockIndex *stop_block = null_block + 1, *failed_block = null_block + 1;
BOOST_CHECK_EQUAL(wallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::SUCCESS);
BOOST_CHECK_EQUAL(stop_block, chainActive.Tip());
BOOST_CHECK_EQUAL(failed_block, null_block);
CWallet::ScanResult result = wallet->ScanForWalletTransactions(chainActive.Genesis()->GetBlockHash(), {} /* stop_block */, reserver, false /* update */);
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS);
BOOST_CHECK_EQUAL(result.stop_block, chainActive.Tip()->GetBlockHash());
BOOST_CHECK_EQUAL(*result.stop_height, chainActive.Height());
BOOST_CHECK(result.failed_block.IsNull());
}

~ListCoinsTestingSetup()
Expand Down
Loading

0 comments on commit db21f02

Please sign in to comment.