Skip to content

Commit db21f02

Browse files
ryanofskyEmpact
andcommitted
Convert CWallet::ScanForWalletTransactions and SyncTransaction to the 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 <[email protected]>
1 parent 2ffb079 commit db21f02

File tree

7 files changed

+209
-125
lines changed

7 files changed

+209
-125
lines changed

src/interfaces/chain.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,42 @@ class LockImpl : public Chain::Lock
6060
assert(block != nullptr);
6161
return block->GetMedianTimePast();
6262
}
63+
Optional<int> findFirstBlockWithTime(int64_t time, uint256* hash) override
64+
{
65+
CBlockIndex* block = ::chainActive.FindEarliestAtLeast(time);
66+
if (block) {
67+
if (hash) *hash = block->GetBlockHash();
68+
return block->nHeight;
69+
}
70+
return nullopt;
71+
}
72+
Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height) override
73+
{
74+
// TODO: Could update CChain::FindEarliestAtLeast() to take a height
75+
// parameter and use it with std::lower_bound() to make this
76+
// implementation more efficient and allow combining
77+
// findFirstBlockWithTime and findFirstBlockWithTimeAndHeight into one
78+
// method.
79+
for (CBlockIndex* block = ::chainActive[height]; block; block = ::chainActive.Next(block)) {
80+
if (block->GetBlockTime() >= time) {
81+
return block->nHeight;
82+
}
83+
}
84+
return nullopt;
85+
}
86+
Optional<int> findPruned(int start_height, Optional<int> stop_height) override
87+
{
88+
if (::fPruneMode) {
89+
CBlockIndex* block = stop_height ? ::chainActive[*stop_height] : ::chainActive.Tip();
90+
while (block && block->nHeight >= start_height) {
91+
if ((block->nStatus & BLOCK_HAVE_DATA) == 0) {
92+
return block->nHeight;
93+
}
94+
block = block->pprev;
95+
}
96+
}
97+
return nullopt;
98+
}
6399
Optional<int> findFork(const uint256& hash, Optional<int>* height) override
64100
{
65101
const CBlockIndex* block = LookupBlockIndex(hash);
@@ -116,6 +152,11 @@ class ChainImpl : public Chain
116152
}
117153
return true;
118154
}
155+
double guessVerificationProgress(const uint256& block_hash) override
156+
{
157+
LOCK(cs_main);
158+
return GuessVerificationProgress(Params().TxData(), LookupBlockIndex(block_hash));
159+
}
119160
};
120161

121162
} // namespace

src/interfaces/chain.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,26 @@ class Chain
5858
//! will abort.
5959
virtual int64_t getBlockMedianTimePast(int height) = 0;
6060

61+
//! Return height of the first block in the chain with timestamp equal
62+
//! or greater than the given time, or nullopt if there is no block with
63+
//! a high enough timestamp. Also return the block hash as an optional
64+
//! output parameter (to avoid the cost of a second lookup in case this
65+
//! information is needed.)
66+
virtual Optional<int> findFirstBlockWithTime(int64_t time, uint256* hash) = 0;
67+
68+
//! Return height of the first block in the chain with timestamp equal
69+
//! or greater than the given time and height equal or greater than the
70+
//! given height, or nullopt if there is no such block.
71+
//!
72+
//! Calling this with height 0 is equivalent to calling
73+
//! findFirstBlockWithTime, but less efficient because it requires a
74+
//! linear instead of a binary search.
75+
virtual Optional<int> findFirstBlockWithTimeAndHeight(int64_t time, int height) = 0;
76+
77+
//! Return height of last block in the specified range which is pruned, or
78+
//! nullopt if no block in the range is pruned. Range is inclusive.
79+
virtual Optional<int> findPruned(int start_height = 0, Optional<int> stop_height = nullopt) = 0;
80+
6181
//! Return height of the highest block on the chain that is an ancestor
6282
//! of the specified block, or nullopt if no common ancestor is found.
6383
//! Also return the height of the specified block as an optional output
@@ -85,6 +105,10 @@ class Chain
85105
CBlock* block = nullptr,
86106
int64_t* time = nullptr,
87107
int64_t* max_time = nullptr) = 0;
108+
109+
//! Estimate fraction of total transactions verified if blocks up to
110+
//! the specified block hash are verified.
111+
virtual double guessVerificationProgress(const uint256& block_hash) = 0;
88112
};
89113

90114
//! Interface to let node manage chain clients (wallets, or maybe tools for

src/qt/test/wallettests.cpp

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include <qt/test/wallettests.h>
22
#include <qt/test/util.h>
33

4+
#include <init.h>
45
#include <interfaces/chain.h>
56
#include <interfaces/node.h>
67
#include <base58.h>
@@ -146,13 +147,10 @@ void TestGUI()
146147
auto locked_chain = wallet->chain().lock();
147148
WalletRescanReserver reserver(wallet.get());
148149
reserver.reserve();
149-
const CBlockIndex* const null_block = nullptr;
150-
const CBlockIndex *stop_block, *failed_block;
151-
QCOMPARE(
152-
wallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver, failed_block, stop_block, true /* fUpdate */),
153-
CWallet::ScanResult::SUCCESS);
154-
QCOMPARE(stop_block, chainActive.Tip());
155-
QCOMPARE(failed_block, null_block);
150+
CWallet::ScanResult result = wallet->ScanForWalletTransactions(locked_chain->getBlockHash(0), {} /* stop_block */, reserver, true /* fUpdate */);
151+
QCOMPARE(result.status, CWallet::ScanResult::SUCCESS);
152+
QCOMPARE(result.stop_block, chainActive.Tip()->GetBlockHash());
153+
QVERIFY(result.failed_block.IsNull());
156154
}
157155
wallet->SetBroadcastTransactions(true);
158156

src/wallet/rpcwallet.cpp

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3387,59 +3387,57 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
33873387
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
33883388
}
33893389

3390-
CBlockIndex *pindexStart = nullptr;
3391-
CBlockIndex *pindexStop = nullptr;
3392-
CBlockIndex *pChainTip = nullptr;
3390+
int start_height = 0;
3391+
uint256 start_block, stop_block;
33933392
{
33943393
auto locked_chain = pwallet->chain().lock();
3395-
pindexStart = chainActive.Genesis();
3396-
pChainTip = chainActive.Tip();
3394+
Optional<int> tip_height = locked_chain->getHeight();
33973395

33983396
if (!request.params[0].isNull()) {
3399-
pindexStart = chainActive[request.params[0].get_int()];
3400-
if (!pindexStart) {
3397+
start_height = request.params[0].get_int();
3398+
if (start_height < 0 || !tip_height || start_height > *tip_height) {
34013399
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid start_height");
34023400
}
34033401
}
34043402

3403+
Optional<int> stop_height;
34053404
if (!request.params[1].isNull()) {
3406-
pindexStop = chainActive[request.params[1].get_int()];
3407-
if (!pindexStop) {
3405+
stop_height = request.params[1].get_int();
3406+
if (*stop_height < 0 || !tip_height || *stop_height > *tip_height) {
34083407
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid stop_height");
34093408
}
3410-
else if (pindexStop->nHeight < pindexStart->nHeight) {
3409+
else if (*stop_height < start_height) {
34113410
throw JSONRPCError(RPC_INVALID_PARAMETER, "stop_height must be greater than start_height");
34123411
}
34133412
}
3414-
}
34153413

3416-
// We can't rescan beyond non-pruned blocks, stop and throw an error
3417-
if (fPruneMode) {
3418-
auto locked_chain = pwallet->chain().lock();
3419-
CBlockIndex *block = pindexStop ? pindexStop : pChainTip;
3420-
while (block && block->nHeight >= pindexStart->nHeight) {
3421-
if (!(block->nStatus & BLOCK_HAVE_DATA)) {
3422-
throw JSONRPCError(RPC_MISC_ERROR, "Can't rescan beyond pruned data. Use RPC call getblockchaininfo to determine your pruned height.");
3414+
// We can't rescan beyond non-pruned blocks, stop and throw an error
3415+
if (locked_chain->findPruned(start_height, stop_height)) {
3416+
throw JSONRPCError(RPC_MISC_ERROR, "Can't rescan beyond pruned data. Use RPC call getblockchaininfo to determine your pruned height.");
3417+
}
3418+
3419+
if (tip_height) {
3420+
start_block = locked_chain->getBlockHash(start_height);
3421+
if (stop_height) {
3422+
stop_block = locked_chain->getBlockHash(*stop_height);
34233423
}
3424-
block = block->pprev;
34253424
}
34263425
}
34273426

3428-
const CBlockIndex *failed_block, *stopBlock;
34293427
CWallet::ScanResult result =
3430-
pwallet->ScanForWalletTransactions(pindexStart, pindexStop, reserver, failed_block, stopBlock, true);
3431-
switch (result) {
3428+
pwallet->ScanForWalletTransactions(start_block, stop_block, reserver, true /* fUpdate */);
3429+
switch (result.status) {
34323430
case CWallet::ScanResult::SUCCESS:
3433-
break; // stopBlock set by ScanForWalletTransactions
3431+
break;
34343432
case CWallet::ScanResult::FAILURE:
34353433
throw JSONRPCError(RPC_MISC_ERROR, "Rescan failed. Potentially corrupted data files.");
34363434
case CWallet::ScanResult::USER_ABORT:
34373435
throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted.");
34383436
// no default case, so the compiler can warn about missing cases
34393437
}
34403438
UniValue response(UniValue::VOBJ);
3441-
response.pushKV("start_height", pindexStart->nHeight);
3442-
response.pushKV("stop_height", stopBlock->nHeight);
3439+
response.pushKV("start_height", start_height);
3440+
response.pushKV("stop_height", result.stop_height ? *result.stop_height : UniValue());
34433441
return response;
34443442
}
34453443

src/wallet/test/wallet_tests.cpp

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
3939
auto chain = interfaces::MakeChain();
4040

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

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

@@ -85,10 +86,11 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
8586
AddKey(wallet, coinbaseKey);
8687
WalletRescanReserver reserver(&wallet);
8788
reserver.reserve();
88-
const CBlockIndex *stop_block = null_block + 1, *failed_block = null_block + 1;
89-
BOOST_CHECK_EQUAL(wallet.ScanForWalletTransactions(oldTip, nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::FAILURE);
90-
BOOST_CHECK_EQUAL(failed_block, oldTip);
91-
BOOST_CHECK_EQUAL(stop_block, newTip);
89+
CWallet::ScanResult result = wallet.ScanForWalletTransactions(oldTip->GetBlockHash(), {} /* stop_block */, reserver, false /* update */);
90+
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::FAILURE);
91+
BOOST_CHECK_EQUAL(result.failed_block, oldTip->GetBlockHash());
92+
BOOST_CHECK_EQUAL(result.stop_block, newTip->GetBlockHash());
93+
BOOST_CHECK_EQUAL(*result.stop_height, newTip->nHeight);
9294
BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN);
9395
}
9496

@@ -102,10 +104,11 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
102104
AddKey(wallet, coinbaseKey);
103105
WalletRescanReserver reserver(&wallet);
104106
reserver.reserve();
105-
const CBlockIndex *stop_block = null_block + 1, *failed_block = null_block + 1;
106-
BOOST_CHECK_EQUAL(wallet.ScanForWalletTransactions(oldTip, nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::FAILURE);
107-
BOOST_CHECK_EQUAL(failed_block, newTip);
108-
BOOST_CHECK_EQUAL(stop_block, null_block);
107+
CWallet::ScanResult result = wallet.ScanForWalletTransactions(oldTip->GetBlockHash(), {} /* stop_block */, reserver, false /* update */);
108+
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::FAILURE);
109+
BOOST_CHECK_EQUAL(result.failed_block, newTip->GetBlockHash());
110+
BOOST_CHECK(result.stop_block.IsNull());
111+
BOOST_CHECK(!result.stop_height);
109112
BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 0);
110113
}
111114
}
@@ -340,11 +343,11 @@ class ListCoinsTestingSetup : public TestChain100Setup
340343
AddKey(*wallet, coinbaseKey);
341344
WalletRescanReserver reserver(wallet.get());
342345
reserver.reserve();
343-
const CBlockIndex* const null_block = nullptr;
344-
const CBlockIndex *stop_block = null_block + 1, *failed_block = null_block + 1;
345-
BOOST_CHECK_EQUAL(wallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver, failed_block, stop_block), CWallet::ScanResult::SUCCESS);
346-
BOOST_CHECK_EQUAL(stop_block, chainActive.Tip());
347-
BOOST_CHECK_EQUAL(failed_block, null_block);
346+
CWallet::ScanResult result = wallet->ScanForWalletTransactions(chainActive.Genesis()->GetBlockHash(), {} /* stop_block */, reserver, false /* update */);
347+
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS);
348+
BOOST_CHECK_EQUAL(result.stop_block, chainActive.Tip()->GetBlockHash());
349+
BOOST_CHECK_EQUAL(*result.stop_height, chainActive.Height());
350+
BOOST_CHECK(result.failed_block.IsNull());
348351
}
349352

350353
~ListCoinsTestingSetup()

0 commit comments

Comments
 (0)