mirror of
https://github.com/bitcoin/bitcoin.git
synced 2024-11-20 02:25:40 +01:00
rpc: scanblocks, do not traverse the whole chain block by block
The current flow walks-through every block in the active chain until hits the chain tip or processes 10k blocks, then calls `lookupFilterRange()` to obtain all the 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.
This commit is contained in:
parent
3ce7b27124
commit
ce50acc54f
@ -2383,27 +2383,28 @@ static RPCHelpMan scanblocks()
|
||||
ChainstateManager& chainman = EnsureChainman(node);
|
||||
|
||||
// set the start-height
|
||||
const CBlockIndex* block = nullptr;
|
||||
const CBlockIndex* start_index = nullptr;
|
||||
const CBlockIndex* stop_block = nullptr;
|
||||
{
|
||||
LOCK(cs_main);
|
||||
CChain& active_chain = chainman.ActiveChain();
|
||||
block = active_chain.Genesis();
|
||||
stop_block = active_chain.Tip();
|
||||
start_index = active_chain.Genesis();
|
||||
stop_block = active_chain.Tip(); // If no stop block is provided, stop at the chain tip.
|
||||
if (!request.params[2].isNull()) {
|
||||
block = active_chain[request.params[2].getInt<int>()];
|
||||
if (!block) {
|
||||
start_index = active_chain[request.params[2].getInt<int>()];
|
||||
if (!start_index) {
|
||||
throw JSONRPCError(RPC_MISC_ERROR, "Invalid start_height");
|
||||
}
|
||||
}
|
||||
if (!request.params[3].isNull()) {
|
||||
stop_block = active_chain[request.params[3].getInt<int>()];
|
||||
if (!stop_block || stop_block->nHeight < block->nHeight) {
|
||||
if (!stop_block || stop_block->nHeight < start_index->nHeight) {
|
||||
throw JSONRPCError(RPC_MISC_ERROR, "Invalid stop_height");
|
||||
}
|
||||
}
|
||||
}
|
||||
CHECK_NONFATAL(block);
|
||||
CHECK_NONFATAL(start_index);
|
||||
CHECK_NONFATAL(stop_block);
|
||||
|
||||
// loop through the scan objects, add scripts to the needle_set
|
||||
GCSFilter::ElementSet needle_set;
|
||||
@ -2416,63 +2417,62 @@ static RPCHelpMan scanblocks()
|
||||
}
|
||||
UniValue blocks(UniValue::VARR);
|
||||
const int amount_per_chunk = 10000;
|
||||
const CBlockIndex* start_index = block; // for remembering the start of a blockfilter range
|
||||
std::vector<BlockFilter> filters;
|
||||
const CBlockIndex* start_block = block; // for progress reporting
|
||||
const int total_blocks_to_process = stop_block->nHeight - start_block->nHeight;
|
||||
int start_block_height = start_index->nHeight; // for progress reporting
|
||||
const int total_blocks_to_process = stop_block->nHeight - start_block_height;
|
||||
|
||||
g_scanfilter_should_abort_scan = false;
|
||||
g_scanfilter_progress = 0;
|
||||
g_scanfilter_progress_height = start_block->nHeight;
|
||||
g_scanfilter_progress_height = start_block_height;
|
||||
|
||||
while (block) {
|
||||
const CBlockIndex* end_range = nullptr;
|
||||
do {
|
||||
node.rpc_interruption_point(); // allow a clean shutdown
|
||||
if (g_scanfilter_should_abort_scan) {
|
||||
LogPrintf("scanblocks RPC aborted at height %d.\n", block->nHeight);
|
||||
LogPrintf("scanblocks RPC aborted at height %d.\n", end_range->nHeight);
|
||||
break;
|
||||
}
|
||||
const CBlockIndex* next = nullptr;
|
||||
{
|
||||
LOCK(cs_main);
|
||||
CChain& active_chain = chainman.ActiveChain();
|
||||
next = active_chain.Next(block);
|
||||
if (block == stop_block) next = nullptr;
|
||||
}
|
||||
if (start_index->nHeight + amount_per_chunk == block->nHeight || next == nullptr) {
|
||||
LogPrint(BCLog::RPC, "Fetching blockfilters from height %d to height %d.\n", start_index->nHeight, block->nHeight);
|
||||
if (index->LookupFilterRange(start_index->nHeight, block, filters)) {
|
||||
for (const BlockFilter& filter : filters) {
|
||||
// compare the elements-set with each filter
|
||||
if (filter.GetFilter().MatchAny(needle_set)) {
|
||||
if (filter_false_positives) {
|
||||
// Double check the filter matches by scanning the block
|
||||
const CBlockIndex& blockindex = *CHECK_NONFATAL(WITH_LOCK(cs_main, return chainman.m_blockman.LookupBlockIndex(filter.GetBlockHash())));
|
||||
|
||||
if (!CheckBlockFilterMatches(chainman.m_blockman, blockindex, needle_set)) {
|
||||
continue;
|
||||
}
|
||||
// split the lookup range in chunks if we are deeper than 'amount_per_chunk' blocks from the stopping block
|
||||
int start_block = !end_range ? start_index->nHeight : start_index->nHeight + 1; // to not include the previous round 'end_range' block
|
||||
end_range = (start_block + amount_per_chunk < stop_block->nHeight) ?
|
||||
WITH_LOCK(::cs_main, return chainman.ActiveChain()[start_block + amount_per_chunk]) :
|
||||
stop_block;
|
||||
|
||||
if (index->LookupFilterRange(start_block, end_range, filters)) {
|
||||
for (const BlockFilter& filter : filters) {
|
||||
// compare the elements-set with each filter
|
||||
if (filter.GetFilter().MatchAny(needle_set)) {
|
||||
if (filter_false_positives) {
|
||||
// Double check the filter matches by scanning the block
|
||||
const CBlockIndex& blockindex = *CHECK_NONFATAL(WITH_LOCK(cs_main, return chainman.m_blockman.LookupBlockIndex(filter.GetBlockHash())));
|
||||
|
||||
if (!CheckBlockFilterMatches(chainman.m_blockman, blockindex, needle_set)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
blocks.push_back(filter.GetBlockHash().GetHex());
|
||||
LogPrint(BCLog::RPC, "scanblocks: found match in %s\n", filter.GetBlockHash().GetHex());
|
||||
}
|
||||
|
||||
blocks.push_back(filter.GetBlockHash().GetHex());
|
||||
LogPrint(BCLog::RPC, "scanblocks: found match in %s\n", filter.GetBlockHash().GetHex());
|
||||
}
|
||||
}
|
||||
start_index = block;
|
||||
|
||||
// update progress
|
||||
int blocks_processed = block->nHeight - start_block->nHeight;
|
||||
if (total_blocks_to_process > 0) { // avoid division by zero
|
||||
g_scanfilter_progress = (int)(100.0 / total_blocks_to_process * blocks_processed);
|
||||
} else {
|
||||
g_scanfilter_progress = 100;
|
||||
}
|
||||
g_scanfilter_progress_height = block->nHeight;
|
||||
}
|
||||
block = next;
|
||||
}
|
||||
ret.pushKV("from_height", start_block->nHeight);
|
||||
ret.pushKV("to_height", g_scanfilter_progress_height.load());
|
||||
start_index = end_range;
|
||||
|
||||
// update progress
|
||||
int blocks_processed = end_range->nHeight - start_block_height;
|
||||
if (total_blocks_to_process > 0) { // avoid division by zero
|
||||
g_scanfilter_progress = (int)(100.0 / total_blocks_to_process * blocks_processed);
|
||||
} else {
|
||||
g_scanfilter_progress = 100;
|
||||
}
|
||||
g_scanfilter_progress_height = end_range->nHeight;
|
||||
|
||||
// Finish if we reached the stop block
|
||||
} while (start_index != stop_block);
|
||||
|
||||
ret.pushKV("from_height", start_block_height);
|
||||
ret.pushKV("to_height", stop_block->nHeight);
|
||||
ret.pushKV("relevant_blocks", blocks);
|
||||
}
|
||||
else {
|
||||
|
Loading…
Reference in New Issue
Block a user