Skip to content

Instantly share code, notes, and snippets.

@jamesob
Created November 18, 2021 21:55
Show Gist options
  • Save jamesob/aa4a975344209f0316444b8de2ec1d18 to your computer and use it in GitHub Desktop.
Save jamesob/aa4a975344209f0316444b8de2ec1d18 to your computer and use it in GitHub Desktop.
git range-diff --color=never upstream/master jonasschnelli/2020/12/filterblocks_rpc HEAD
-: ---------- > 1: 608d8b4a62 rpc: move-only: consolidate blockchain scan args
1: 6a69dd267e ! 2: d836ce3f2f Add scanblocks RPC call - scan for relevant blocks with descriptors
@@ Metadata
Author: Jonas Schnelli <dev@jonasschnelli.ch>
## Commit message ##
- Add scanblocks RPC call - scan for relevant blocks with descriptors
+ rpc: add scanblocks - scan for relevant blocks with descriptors
+
+ Co-authored-by: James O'Beirne <james.obeirne@gmail.com>
## src/rpc/blockchain.cpp ##
@@ src/rpc/blockchain.cpp: static RPCHelpMan scantxoutset()
@@ src/rpc/blockchain.cpp: static RPCHelpMan scantxoutset()
+static RPCHelpMan scanblocks()
+{
+ return RPCHelpMan{"scanblocks",
-+ "\nReturn relevant blockhashes for given descriptors.\n"
-+ "This call may take serval minutes. Make sure to use no RPC timeout (bitcoin-cli -rpcclienttimeout=0)",
-+ {
-+ {"action", RPCArg::Type::STR, RPCArg::Optional::NO, "The action to execute\n"
-+ " \"start\" for starting a scan\n"
-+ " \"abort\" for aborting the current scan (returns true when abort was successful)\n"
-+ " \"status\" for progress report (in %) of the current scan (returns Null if there is no ongoing scan)"},
-+ {"scanobjects", RPCArg::Type::ARR, RPCArg::Optional::OMITTED, "Array of scan objects.\n"
-+ " Every scan object is either a string descriptor or an object:",
-+ {
-+ {"descriptor", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "An output descriptor"},
-+ {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "An object with output descriptor and metadata",
-+ {
-+ {"desc", RPCArg::Type::STR, RPCArg::Optional::NO, "An output descriptor"},
-+ {"range", RPCArg::Type::RANGE, /* default */ "1000", "The range of HD chain indexes to explore (either end or [begin,end])"},
-+ },
-+ },
-+ },
-+ "[scanobjects,...]"},
-+ {"start_height", RPCArg::Type::NUM, /*default*/ "0", "height to start to filter from"},
-+ {"stop_height", RPCArg::Type::NUM, /*default*/ "<tip>", "height to stop to scan"},
-+ {"filtertype", RPCArg::Type::STR, /*default*/ "basic", "The type name of the filter"}
-+ },
-+ RPCResult{
-+ RPCResult::Type::ARR, "", "",
-+ {
-+ {RPCResult::Type::STR_HEX, "", "The blockhash"},
-+ }},
-+ RPCExamples{
-+ HelpExampleCli("scanblocks", "\"[\"addr(bcrt1q4u4nsgk6ug0sqz7r3rj9tykjxrsl0yy4d0wwte)\"]\" 300000") +
-+ HelpExampleRpc("scanblocks", "\"[\"addr(bcrt1q4u4nsgk6ug0sqz7r3rj9tykjxrsl0yy4d0wwte)\"]\" 300000")
++ "\nReturn relevant blockhashes for given descriptors.\n"
++ "This call may take several minutes. Make sure to use no RPC timeout (bitcoin-cli -rpcclienttimeout=0)",
++ {
++ scan_action_arg_desc,
++ scan_objects_arg_desc,
++ RPCArg{"start_height", RPCArg::Type::NUM, RPCArg::DefaultHint{"0"}, "Height to start to scan from"},
++ RPCArg{"stop_height", RPCArg::Type::NUM, RPCArg::DefaultHint{"chain tip"}, "Height to stop to scan"},
++ RPCArg{"filtertype", RPCArg::Type::STR, RPCArg::DefaultHint{"basic"}, "The type name of the filter"}
++ },
++ {
++ scan_result_abort,
++ scan_result_status_none,
++ scan_result_status_some,
++ RPCResult{"When action=='start'", RPCResult::Type::OBJ, "", "", {
++ {RPCResult::Type::NUM, "from_height", "The height we started the scan from"},
++ {RPCResult::Type::NUM, "to_height", "The height we ended the scan at"},
++ {RPCResult::Type::ARR, "relevant_blocks", "", {{RPCResult::Type::STR_HEX, "blockhash", "A relevant blockhash"},}},
+ },
++ },
++ },
++ RPCExamples{
++ HelpExampleCli("scanblocks", "\"[\"addr(bcrt1q4u4nsgk6ug0sqz7r3rj9tykjxrsl0yy4d0wwte)\"]\" 300000") +
++ HelpExampleRpc("scanblocks", "\"[\"addr(bcrt1q4u4nsgk6ug0sqz7r3rj9tykjxrsl0yy4d0wwte)\"]\" 300000")
++ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
+ UniValue ret(UniValue::VOBJ);
@@ src/rpc/blockchain.cpp: static RPCHelpMan scantxoutset()
+ throw JSONRPCError(RPC_MISC_ERROR, "Index is not enabled for filtertype " + filtertype_name);
+ }
+
++ NodeContext& node = EnsureAnyNodeContext(request.context);
++ ChainstateManager& chainman = EnsureChainman(node);
++
+ // set the start-height
+ const CBlockIndex* block = nullptr;
+ const CBlockIndex* stop_block = nullptr;
+ {
+ LOCK(cs_main);
-+ block = ::ChainActive().Genesis();
-+ stop_block = ::ChainActive().Tip();
++ CChain& active_chain = chainman.ActiveChain();
++ block = active_chain.Genesis();
++ stop_block = active_chain.Tip();
+ if (!request.params[2].isNull()) {
-+ block = ::ChainActive()[request.params[2].get_int()];
++ block = active_chain[request.params[2].get_int()];
+ if (!block) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Invalid start_height");
+ }
+ }
+ if (!request.params[3].isNull()) {
-+ stop_block = ::ChainActive()[request.params[3].get_int()];
++ stop_block = active_chain[request.params[3].get_int()];
+ if (!stop_block || stop_block->nHeight < block->nHeight) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Invalid stop_height");
+ }
@@ src/rpc/blockchain.cpp: static RPCHelpMan scantxoutset()
+ needle_set.emplace(script.begin(), script.end());
+ }
+ }
-+ NodeContext& node = EnsureNodeContext(request.context);
+ 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 CBlockIndex* last_scanned_block = block;
++ const int total_blocks_to_process = stop_block->nHeight - start_block->nHeight;
++
+ g_scanfilter_should_abort_scan = false;
+ g_scanfilter_progress = 0;
+ g_scanfilter_progress_height = start_block->nHeight;
++
+ while (block) {
+ node.rpc_interruption_point(); // allow a clean shutdown
+ if (g_scanfilter_should_abort_scan) {
++ LogPrintf("scanblocks RPC aborted at height %d.\n", block->nHeight);
+ break;
+ }
+ const CBlockIndex* next = nullptr;
+ {
+ LOCK(cs_main);
-+ next = ChainActive().Next(block);
++ 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) {
@@ src/rpc/blockchain.cpp: static RPCHelpMan scantxoutset()
+
+ // update progress
+ int blocks_processed = block->nHeight - start_block->nHeight;
-+ int total_blocks_to_process = stop_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 {
@@ src/rpc/blockchain.cpp: static RPCHelpMan scantxoutset()
+ }
+ g_scanfilter_progress_height = block->nHeight;
+ }
-+ last_scanned_block = block;
+ block = next;
+ }
+ ret.pushKV("from_height", start_block->nHeight);
-+ ret.pushKV("to_height", last_scanned_block->nHeight);
++ ret.pushKV("to_height", g_scanfilter_progress_height);
+ ret.pushKV("relevant_blocks", blocks);
+ }
+ else {
2: 4c8ac1c6c5 = 3: a4da6ee6a1 Unify "invalid action argument" error (fix scantxoutset)
3: 71b7cdb460 < -: ---------- Add scanblock functional test
-: ---------- > 4: c0c2115a14 test: rpc: add scanblocks functional test
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment