0
0
Fork 0
mirror of https://github.com/bitcoin/bitcoin.git synced 2025-02-02 09:46:52 -05:00

Merge bitcoin/bitcoin#26780: rpc: simplify scan blocks

b922f6b526 rpc: scanblocks, add "completed" flag to the result obj (furszy)
ce50acc54f rpc: scanblocks, do not traverse the whole chain block by block (furszy)

Pull request description:

  Coming from https://github.com/bitcoin/bitcoin/pull/23549#pullrequestreview-1105712566

  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 b922f6b526
  TheCharlatan:
    ACK b922f6b526

Tree-SHA512: 0587e6d9cf87a59184adb2dbc26a4e2bce3a16233594c6c330f69feb49bf7dc63fdacf44fc20308e93441159ebc604c63eb7de19204d3e745a2ff16004892b45
This commit is contained in:
Andrew Chow 2023-05-01 09:03:37 -04:00
commit 0eae93e65f
No known key found for this signature in database
GPG key ID: 17565732E08E5E41
2 changed files with 53 additions and 51 deletions

View file

@ -2325,6 +2325,7 @@ static RPCHelpMan scanblocks()
{RPCResult::Type::ARR, "relevant_blocks", "Blocks that may have matched a scanobject.", {
{RPCResult::Type::STR_HEX, "blockhash", "A relevant blockhash"},
}},
{RPCResult::Type::BOOL, "completed", "true if the scan process was not aborted"}
}},
RPCResult{"when action=='status' and a scan is currently in progress", RPCResult::Type::OBJ, "", "", {
{RPCResult::Type::NUM, "progress", "Approximate percent complete"},
@ -2362,8 +2363,7 @@ static RPCHelpMan scanblocks()
// set the abort flag
g_scanfilter_should_abort_scan = true;
return true;
}
else if (request.params[0].get_str() == "start") {
} else if (request.params[0].get_str() == "start") {
BlockFiltersScanReserver reserver;
if (!reserver.reserve()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan already in progress, use action \"abort\" or \"status\"");
@ -2387,27 +2387,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;
@ -2420,31 +2421,30 @@ 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;
bool completed = true;
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);
completed = false;
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)) {
// 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)) {
@ -2458,26 +2458,27 @@ static RPCHelpMan scanblocks()
}
blocks.push_back(filter.GetBlockHash().GetHex());
LogPrint(BCLog::RPC, "scanblocks: found match in %s\n", filter.GetBlockHash().GetHex());
}
}
}
start_index = block;
start_index = end_range;
// update progress
int blocks_processed = block->nHeight - start_block->nHeight;
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 = block->nHeight;
}
block = next;
}
ret.pushKV("from_height", start_block->nHeight);
ret.pushKV("to_height", g_scanfilter_progress_height.load());
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", start_index->nHeight); // start_index is always the last scanned block here
ret.pushKV("relevant_blocks", blocks);
ret.pushKV("completed", completed);
}
else {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid action '%s'", request.params[0].get_str()));

View file

@ -49,6 +49,7 @@ class ScanblocksTest(BitcoinTestFramework):
assert blockhash in out['relevant_blocks']
assert_equal(height, out['to_height'])
assert_equal(0, out['from_height'])
assert_equal(True, out['completed'])
# mine another block
blockhash_new = self.generate(node, 1)[0]