Skip to content

Commit 0eae93e

Browse files
committed
Merge bitcoin/bitcoin#26780: rpc: simplify scan blocks
b922f6b rpc: scanblocks, add "completed" flag to the result obj (furszy) ce50acc rpc: scanblocks, do not traverse the whole chain block by block (furszy) Pull request description: Coming from bitcoin/bitcoin#23549 (review) The current `scanblocks` flow walks-through every block in the active chain until hits the chain tip or processes 10k blocks, then calls `lookupFilterRange` function to obtain all filters from that particular range. This is only done to obtain the heights range to look up the block filters. Which is unneeded. As `scanblocks` only lookup block filters in the active chain, we can directly calculate the lookup range heights, by using the chain tip, without requiring to traverse the chain block by block. ACKs for top commit: achow101: ACK b922f6b TheCharlatan: ACK b922f6b Tree-SHA512: 0587e6d9cf87a59184adb2dbc26a4e2bce3a16233594c6c330f69feb49bf7dc63fdacf44fc20308e93441159ebc604c63eb7de19204d3e745a2ff16004892b45
2 parents 3497df4 + b922f6b commit 0eae93e

File tree

2 files changed

+54
-52
lines changed

2 files changed

+54
-52
lines changed

src/rpc/blockchain.cpp

Lines changed: 53 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2325,6 +2325,7 @@ static RPCHelpMan scanblocks()
23252325
{RPCResult::Type::ARR, "relevant_blocks", "Blocks that may have matched a scanobject.", {
23262326
{RPCResult::Type::STR_HEX, "blockhash", "A relevant blockhash"},
23272327
}},
2328+
{RPCResult::Type::BOOL, "completed", "true if the scan process was not aborted"}
23282329
}},
23292330
RPCResult{"when action=='status' and a scan is currently in progress", RPCResult::Type::OBJ, "", "", {
23302331
{RPCResult::Type::NUM, "progress", "Approximate percent complete"},
@@ -2362,8 +2363,7 @@ static RPCHelpMan scanblocks()
23622363
// set the abort flag
23632364
g_scanfilter_should_abort_scan = true;
23642365
return true;
2365-
}
2366-
else if (request.params[0].get_str() == "start") {
2366+
} else if (request.params[0].get_str() == "start") {
23672367
BlockFiltersScanReserver reserver;
23682368
if (!reserver.reserve()) {
23692369
throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan already in progress, use action \"abort\" or \"status\"");
@@ -2387,27 +2387,28 @@ static RPCHelpMan scanblocks()
23872387
ChainstateManager& chainman = EnsureChainman(node);
23882388

23892389
// set the start-height
2390-
const CBlockIndex* block = nullptr;
2390+
const CBlockIndex* start_index = nullptr;
23912391
const CBlockIndex* stop_block = nullptr;
23922392
{
23932393
LOCK(cs_main);
23942394
CChain& active_chain = chainman.ActiveChain();
2395-
block = active_chain.Genesis();
2396-
stop_block = active_chain.Tip();
2395+
start_index = active_chain.Genesis();
2396+
stop_block = active_chain.Tip(); // If no stop block is provided, stop at the chain tip.
23972397
if (!request.params[2].isNull()) {
2398-
block = active_chain[request.params[2].getInt<int>()];
2399-
if (!block) {
2398+
start_index = active_chain[request.params[2].getInt<int>()];
2399+
if (!start_index) {
24002400
throw JSONRPCError(RPC_MISC_ERROR, "Invalid start_height");
24012401
}
24022402
}
24032403
if (!request.params[3].isNull()) {
24042404
stop_block = active_chain[request.params[3].getInt<int>()];
2405-
if (!stop_block || stop_block->nHeight < block->nHeight) {
2405+
if (!stop_block || stop_block->nHeight < start_index->nHeight) {
24062406
throw JSONRPCError(RPC_MISC_ERROR, "Invalid stop_height");
24072407
}
24082408
}
24092409
}
2410-
CHECK_NONFATAL(block);
2410+
CHECK_NONFATAL(start_index);
2411+
CHECK_NONFATAL(stop_block);
24112412

24122413
// loop through the scan objects, add scripts to the needle_set
24132414
GCSFilter::ElementSet needle_set;
@@ -2420,64 +2421,64 @@ static RPCHelpMan scanblocks()
24202421
}
24212422
UniValue blocks(UniValue::VARR);
24222423
const int amount_per_chunk = 10000;
2423-
const CBlockIndex* start_index = block; // for remembering the start of a blockfilter range
24242424
std::vector<BlockFilter> filters;
2425-
const CBlockIndex* start_block = block; // for progress reporting
2426-
const int total_blocks_to_process = stop_block->nHeight - start_block->nHeight;
2425+
int start_block_height = start_index->nHeight; // for progress reporting
2426+
const int total_blocks_to_process = stop_block->nHeight - start_block_height;
24272427

24282428
g_scanfilter_should_abort_scan = false;
24292429
g_scanfilter_progress = 0;
2430-
g_scanfilter_progress_height = start_block->nHeight;
2430+
g_scanfilter_progress_height = start_block_height;
2431+
bool completed = true;
24312432

2432-
while (block) {
2433+
const CBlockIndex* end_range = nullptr;
2434+
do {
24332435
node.rpc_interruption_point(); // allow a clean shutdown
24342436
if (g_scanfilter_should_abort_scan) {
2435-
LogPrintf("scanblocks RPC aborted at height %d.\n", block->nHeight);
2437+
completed = false;
24362438
break;
24372439
}
2438-
const CBlockIndex* next = nullptr;
2439-
{
2440-
LOCK(cs_main);
2441-
CChain& active_chain = chainman.ActiveChain();
2442-
next = active_chain.Next(block);
2443-
if (block == stop_block) next = nullptr;
2444-
}
2445-
if (start_index->nHeight + amount_per_chunk == block->nHeight || next == nullptr) {
2446-
LogPrint(BCLog::RPC, "Fetching blockfilters from height %d to height %d.\n", start_index->nHeight, block->nHeight);
2447-
if (index->LookupFilterRange(start_index->nHeight, block, filters)) {
2448-
for (const BlockFilter& filter : filters) {
2449-
// compare the elements-set with each filter
2450-
if (filter.GetFilter().MatchAny(needle_set)) {
2451-
if (filter_false_positives) {
2452-
// Double check the filter matches by scanning the block
2453-
const CBlockIndex& blockindex = *CHECK_NONFATAL(WITH_LOCK(cs_main, return chainman.m_blockman.LookupBlockIndex(filter.GetBlockHash())));
2454-
2455-
if (!CheckBlockFilterMatches(chainman.m_blockman, blockindex, needle_set)) {
2456-
continue;
2457-
}
2458-
}
24592440

2460-
blocks.push_back(filter.GetBlockHash().GetHex());
2461-
LogPrint(BCLog::RPC, "scanblocks: found match in %s\n", filter.GetBlockHash().GetHex());
2441+
// split the lookup range in chunks if we are deeper than 'amount_per_chunk' blocks from the stopping block
2442+
int start_block = !end_range ? start_index->nHeight : start_index->nHeight + 1; // to not include the previous round 'end_range' block
2443+
end_range = (start_block + amount_per_chunk < stop_block->nHeight) ?
2444+
WITH_LOCK(::cs_main, return chainman.ActiveChain()[start_block + amount_per_chunk]) :
2445+
stop_block;
2446+
2447+
if (index->LookupFilterRange(start_block, end_range, filters)) {
2448+
for (const BlockFilter& filter : filters) {
2449+
// compare the elements-set with each filter
2450+
if (filter.GetFilter().MatchAny(needle_set)) {
2451+
if (filter_false_positives) {
2452+
// Double check the filter matches by scanning the block
2453+
const CBlockIndex& blockindex = *CHECK_NONFATAL(WITH_LOCK(cs_main, return chainman.m_blockman.LookupBlockIndex(filter.GetBlockHash())));
2454+
2455+
if (!CheckBlockFilterMatches(chainman.m_blockman, blockindex, needle_set)) {
2456+
continue;
2457+
}
24622458
}
2459+
2460+
blocks.push_back(filter.GetBlockHash().GetHex());
24632461
}
24642462
}
2465-
start_index = block;
2466-
2467-
// update progress
2468-
int blocks_processed = block->nHeight - start_block->nHeight;
2469-
if (total_blocks_to_process > 0) { // avoid division by zero
2470-
g_scanfilter_progress = (int)(100.0 / total_blocks_to_process * blocks_processed);
2471-
} else {
2472-
g_scanfilter_progress = 100;
2473-
}
2474-
g_scanfilter_progress_height = block->nHeight;
24752463
}
2476-
block = next;
2477-
}
2478-
ret.pushKV("from_height", start_block->nHeight);
2479-
ret.pushKV("to_height", g_scanfilter_progress_height.load());
2464+
start_index = end_range;
2465+
2466+
// update progress
2467+
int blocks_processed = end_range->nHeight - start_block_height;
2468+
if (total_blocks_to_process > 0) { // avoid division by zero
2469+
g_scanfilter_progress = (int)(100.0 / total_blocks_to_process * blocks_processed);
2470+
} else {
2471+
g_scanfilter_progress = 100;
2472+
}
2473+
g_scanfilter_progress_height = end_range->nHeight;
2474+
2475+
// Finish if we reached the stop block
2476+
} while (start_index != stop_block);
2477+
2478+
ret.pushKV("from_height", start_block_height);
2479+
ret.pushKV("to_height", start_index->nHeight); // start_index is always the last scanned block here
24802480
ret.pushKV("relevant_blocks", blocks);
2481+
ret.pushKV("completed", completed);
24812482
}
24822483
else {
24832484
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid action '%s'", request.params[0].get_str()));

test/functional/rpc_scanblocks.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ def run_test(self):
4949
assert blockhash in out['relevant_blocks']
5050
assert_equal(height, out['to_height'])
5151
assert_equal(0, out['from_height'])
52+
assert_equal(True, out['completed'])
5253

5354
# mine another block
5455
blockhash_new = self.generate(node, 1)[0]

0 commit comments

Comments
 (0)