Page MenuHomePhabricator

D7872.diff
No OneTemporary

D7872.diff

diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -138,12 +138,6 @@
findFirstBlockWithTimeAndHeight(int64_t time, int height,
BlockHash *hash) = 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
@@ -212,6 +206,12 @@
//! the specified block hash are verified.
virtual double guessVerificationProgress(const BlockHash &block_hash) = 0;
+ //! Return true if data is available for all blocks in the specified range
+ //! of blocks. This checks all blocks that are ancestors of block_hash in
+ //! the height range from min_height to max_height, inclusive.
+ virtual bool hasBlocks(const BlockHash &block_hash, int min_height = 0,
+ Optional<int> max_height = {}) = 0;
+
//! Check if transaction has descendants in mempool.
virtual bool hasDescendantsInMempool(const TxId &txid) = 0;
diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp
--- a/src/interfaces/chain.cpp
+++ b/src/interfaces/chain.cpp
@@ -113,21 +113,6 @@
}
return nullopt;
}
- Optional<int> findPruned(int start_height,
- Optional<int> stop_height) override {
- LockAssertion lock(::cs_main);
- if (::fPruneMode) {
- CBlockIndex *block = stop_height ? ::ChainActive()[*stop_height]
- : ::ChainActive().Tip();
- while (block && block->nHeight >= start_height) {
- if (block->nStatus.hasData() == 0) {
- return block->nHeight;
- }
- block = block->pprev;
- }
- }
- return nullopt;
- }
Optional<int> findFork(const BlockHash &hash,
Optional<int> *height) override {
LockAssertion lock(::cs_main);
@@ -336,6 +321,29 @@
return GuessVerificationProgress(Params().TxData(),
LookupBlockIndex(block_hash));
}
+ bool hasBlocks(const BlockHash &block_hash, int min_height,
+ Optional<int> max_height) override {
+ // hasBlocks returns true if all ancestors of block_hash in
+ // specified range have block data (are not pruned), false if any
+ // ancestors in specified range are missing data.
+ //
+ // For simplicity and robustness, min_height and max_height are only
+ // used to limit the range, and passing min_height that's too low or
+ // max_height that's too high will not crash or change the result.
+ LOCK(::cs_main);
+ if (CBlockIndex *block = LookupBlockIndex(block_hash)) {
+ if (max_height && block->nHeight >= *max_height) {
+ block = block->GetAncestor(*max_height);
+ }
+ for (; block->nStatus.hasData(); block = block->pprev) {
+ // Check pprev to not segfault if min_height is too low
+ if (block->nHeight <= min_height || !block->pprev) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
bool hasDescendantsInMempool(const TxId &txid) override {
LOCK(::g_mempool.cs);
auto it = ::g_mempool.GetIter(txid);
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -135,8 +135,8 @@
WalletRescanReserver reserver(*wallet);
reserver.reserve();
CWallet::ScanResult result = wallet->ScanForWalletTransactions(
- Params().GetConsensus().hashGenesisBlock, BlockHash(), reserver,
- true /* fUpdate */);
+ Params().GetConsensus().hashGenesisBlock, {} /* max_height */,
+ reserver, true /* fUpdate */);
QCOMPARE(result.status, CWallet::ScanResult::SUCCESS);
QCOMPARE(result.last_scanned_block,
::ChainActive().Tip()->GetBlockHash());
diff --git a/src/test/interfaces_tests.cpp b/src/test/interfaces_tests.cpp
--- a/src/test/interfaces_tests.cpp
+++ b/src/test/interfaces_tests.cpp
@@ -117,4 +117,39 @@
BOOST_CHECK_EQUAL(fork_hash, active[fork_height]->GetBlockHash());
}
+BOOST_AUTO_TEST_CASE(hasBlocks) {
+ auto chain = interfaces::MakeChain(m_node, Params());
+ auto &active = ChainActive();
+
+ // Test ranges
+ BOOST_CHECK(chain->hasBlocks(active.Tip()->GetBlockHash(), 10, 90));
+ BOOST_CHECK(chain->hasBlocks(active.Tip()->GetBlockHash(), 10, {}));
+ BOOST_CHECK(chain->hasBlocks(active.Tip()->GetBlockHash(), 0, 90));
+ BOOST_CHECK(chain->hasBlocks(active.Tip()->GetBlockHash(), 0, {}));
+ BOOST_CHECK(chain->hasBlocks(active.Tip()->GetBlockHash(), -1000, 1000));
+ active[5]->nStatus = active[5]->nStatus.withData(false);
+ BOOST_CHECK(chain->hasBlocks(active.Tip()->GetBlockHash(), 10, 90));
+ BOOST_CHECK(chain->hasBlocks(active.Tip()->GetBlockHash(), 10, {}));
+ BOOST_CHECK(!chain->hasBlocks(active.Tip()->GetBlockHash(), 0, 90));
+ BOOST_CHECK(!chain->hasBlocks(active.Tip()->GetBlockHash(), 0, {}));
+ BOOST_CHECK(!chain->hasBlocks(active.Tip()->GetBlockHash(), -1000, 1000));
+ active[95]->nStatus = active[95]->nStatus.withData(false);
+ BOOST_CHECK(chain->hasBlocks(active.Tip()->GetBlockHash(), 10, 90));
+ BOOST_CHECK(!chain->hasBlocks(active.Tip()->GetBlockHash(), 10, {}));
+ BOOST_CHECK(!chain->hasBlocks(active.Tip()->GetBlockHash(), 0, 90));
+ BOOST_CHECK(!chain->hasBlocks(active.Tip()->GetBlockHash(), 0, {}));
+ BOOST_CHECK(!chain->hasBlocks(active.Tip()->GetBlockHash(), -1000, 1000));
+ active[50]->nStatus = active[50]->nStatus.withData(false);
+ BOOST_CHECK(!chain->hasBlocks(active.Tip()->GetBlockHash(), 10, 90));
+ BOOST_CHECK(!chain->hasBlocks(active.Tip()->GetBlockHash(), 10, {}));
+ BOOST_CHECK(!chain->hasBlocks(active.Tip()->GetBlockHash(), 0, 90));
+ BOOST_CHECK(!chain->hasBlocks(active.Tip()->GetBlockHash(), 0, {}));
+ BOOST_CHECK(!chain->hasBlocks(active.Tip()->GetBlockHash(), -1000, 1000));
+
+ // Test edge cases
+ BOOST_CHECK(chain->hasBlocks(active.Tip()->GetBlockHash(), 6, 49));
+ BOOST_CHECK(!chain->hasBlocks(active.Tip()->GetBlockHash(), 5, 49));
+ BOOST_CHECK(!chain->hasBlocks(active.Tip()->GetBlockHash(), 6, 50));
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -3994,23 +3994,24 @@
}
int start_height = 0;
- BlockHash start_block, stop_block;
+ Optional<int> stop_height;
+ BlockHash start_block;
{
auto locked_chain = pwallet->chain().lock();
- Optional<int> tip_height = locked_chain->getHeight();
+ LOCK(pwallet->cs_wallet);
+ int tip_height = pwallet->GetLastBlockHeight();
if (!request.params[0].isNull()) {
start_height = request.params[0].get_int();
- if (start_height < 0 || !tip_height || start_height > *tip_height) {
+ if (start_height < 0 || start_height > tip_height) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid start_height");
}
}
- Optional<int> stop_height;
if (!request.params[1].isNull()) {
stop_height = request.params[1].get_int();
- if (*stop_height < 0 || !tip_height || *stop_height > *tip_height) {
+ if (*stop_height < 0 || *stop_height > tip_height) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid stop_height");
} else if (*stop_height < start_height) {
@@ -4021,29 +4022,21 @@
}
// We can't rescan beyond non-pruned blocks, stop and throw an error
- if (locked_chain->findPruned(start_height, stop_height)) {
+ if (!pwallet->chain().hasBlocks(pwallet->GetLastBlockHash(),
+ 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 called with a stop_height, set the stop_height here to
- // trigger a rescan to that height.
- // If called without a stop height, leave stop_height as null here
- // so rescan continues to the tip (even if the tip advances during
- // rescan).
- if (stop_height) {
- stop_block = locked_chain->getBlockHash(*stop_height);
- }
- }
+ CHECK_NONFATAL(pwallet->chain().findAncestorByHeight(
+ pwallet->GetLastBlockHash(), start_height,
+ FoundBlock().hash(start_block)));
}
CWallet::ScanResult result = pwallet->ScanForWalletTransactions(
- start_block, stop_block, reserver, true /* fUpdate */);
+ start_block, stop_height, reserver, true /* fUpdate */);
switch (result.status) {
case CWallet::ScanResult::SUCCESS:
break;
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -59,7 +59,7 @@
WalletRescanReserver reserver(wallet);
reserver.reserve();
CWallet::ScanResult result = wallet.ScanForWalletTransactions(
- BlockHash(), BlockHash(), reserver, false /* update */);
+ BlockHash(), {} /* max_height */, reserver, false /* update */);
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS);
BOOST_CHECK(result.last_failed_block.IsNull());
BOOST_CHECK(result.last_scanned_block.IsNull());
@@ -81,7 +81,8 @@
WalletRescanReserver reserver(wallet);
reserver.reserve();
CWallet::ScanResult result = wallet.ScanForWalletTransactions(
- oldTip->GetBlockHash(), BlockHash(), reserver, false /* update */);
+ oldTip->GetBlockHash(), {} /* max_height */, reserver,
+ false /* update */);
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS);
BOOST_CHECK(result.last_failed_block.IsNull());
BOOST_CHECK_EQUAL(result.last_scanned_block, newTip->GetBlockHash());
@@ -107,7 +108,8 @@
WalletRescanReserver reserver(wallet);
reserver.reserve();
CWallet::ScanResult result = wallet.ScanForWalletTransactions(
- oldTip->GetBlockHash(), BlockHash(), reserver, false /* update */);
+ oldTip->GetBlockHash(), {} /* max_height */, reserver,
+ false /* update */);
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::FAILURE);
BOOST_CHECK_EQUAL(result.last_failed_block, oldTip->GetBlockHash());
BOOST_CHECK_EQUAL(result.last_scanned_block, newTip->GetBlockHash());
@@ -132,7 +134,8 @@
WalletRescanReserver reserver(wallet);
reserver.reserve();
CWallet::ScanResult result = wallet.ScanForWalletTransactions(
- oldTip->GetBlockHash(), BlockHash(), reserver, false /* update */);
+ oldTip->GetBlockHash(), {} /* max_height */, reserver,
+ false /* update */);
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::FAILURE);
BOOST_CHECK_EQUAL(result.last_failed_block, newTip->GetBlockHash());
BOOST_CHECK(result.last_scanned_block.IsNull());
@@ -512,8 +515,8 @@
WalletRescanReserver reserver(*wallet);
reserver.reserve();
CWallet::ScanResult result = wallet->ScanForWalletTransactions(
- ::ChainActive().Genesis()->GetBlockHash(), BlockHash(), reserver,
- false /* update */);
+ ::ChainActive().Genesis()->GetBlockHash(), {} /* max_height */,
+ reserver, false /* update */);
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS);
BOOST_CHECK_EQUAL(result.last_scanned_block,
::ChainActive().Tip()->GetBlockHash());
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -1069,7 +1069,7 @@
BlockHash last_failed_block;
};
ScanResult ScanForWalletTransactions(const BlockHash &first_block,
- const BlockHash &last_block,
+ Optional<int> max_height,
const WalletRescanReserver &reserver,
bool fUpdate);
void TransactionRemovedFromMempool(const CTransactionRef &ptx) override;
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -1720,8 +1720,8 @@
if (start) {
// TODO: this should take into account failure by ScanResult::USER_ABORT
- ScanResult result = ScanForWalletTransactions(start_block, BlockHash(),
- reserver, update);
+ ScanResult result = ScanForWalletTransactions(
+ start_block, {} /* max_height */, reserver, update);
if (result.status == ScanResult::FAILURE) {
int64_t time_max;
CHECK_NONFATAL(chain().findBlock(result.last_failed_block,
@@ -1739,9 +1739,8 @@
*
* @param[in] start_block Scan starting block. If block is not on the active
* chain, the scan will return SUCCESS immediately.
- * @param[in] stop_block Scan ending block. If block is not on the active
- * chain, the scan will continue until it reaches the
- * chain tip.
+ * @param[in] max_height Optional max scanning height. If unset there is
+ * no maximum and scanning can continue to the tip
*
* @return ScanResult returning scan information and indicating success or
* failure. Return status will be set to SUCCESS if scan was
@@ -1754,7 +1753,7 @@
* transactions for.
*/
CWallet::ScanResult CWallet::ScanForWalletTransactions(
- const BlockHash &start_block, const BlockHash &stop_block,
+ const BlockHash &start_block, Optional<int> max_height,
const WalletRescanReserver &reserver, bool fUpdate) {
int64_t nNow = GetTime();
int64_t start_time = GetTimeMillis();
@@ -1785,9 +1784,13 @@
tip_hash = locked_chain->getBlockHash(*tip_height);
}
block_height = locked_chain->getBlockHeight(block_hash);
+ BlockHash end_hash = tip_hash;
+ if (max_height) {
+ chain().findAncestorByHeight(tip_hash, *max_height,
+ FoundBlock().hash(end_hash));
+ }
progress_begin = chain().guessVerificationProgress(block_hash);
- progress_end = chain().guessVerificationProgress(
- stop_block.IsNull() ? tip_hash : stop_block);
+ progress_end = chain().guessVerificationProgress(end_hash);
}
double progress_current = progress_begin;
while (block_height && !fAbortRescan && !chain().shutdownRequested()) {
@@ -1837,7 +1840,7 @@
result.last_failed_block = block_hash;
result.status = ScanResult::FAILURE;
}
- if (block_hash == stop_block) {
+ if (max_height && *block_height >= *max_height) {
break;
}
{
@@ -1857,7 +1860,7 @@
// handle updated tip hash
const BlockHash prev_tip_hash = tip_hash;
tip_hash = locked_chain->getBlockHash(*tip_height);
- if (stop_block.IsNull() && prev_tip_hash != tip_hash) {
+ if (!max_height && prev_tip_hash != tip_hash) {
// in case the tip has changed, update progress max
progress_end = chain().guessVerificationProgress(tip_hash);
}
@@ -4424,8 +4427,8 @@
(ScanResult::SUCCESS !=
walletInstance
->ScanForWalletTransactions(
- locked_chain->getBlockHash(rescan_height), BlockHash(),
- reserver, true /* update */)
+ locked_chain->getBlockHash(rescan_height),
+ {} /* max_height */, reserver, true /* update */)
.status)) {
error = _("Failed to rescan the wallet during initialization");
return nullptr;

File Metadata

Mime Type
text/plain
Expires
Sat, Mar 1, 11:05 (19 h, 33 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5187490
Default Alt Text
D7872.diff (16 KB)

Event Timeline