Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F13115432
D7872.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
16 KB
Subscribers
None
D7872.diff
View Options
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
Details
Attached
Mime Type
text/plain
Expires
Sat, Mar 1, 11:05 (16 h, 4 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5187490
Default Alt Text
D7872.diff (16 KB)
Attached To
D7872: wallet: Avoid use of Chain::Lock in rescanblockchain
Event Timeline
Log In to Comment