diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 3ba89d08d98..c2733094101 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -2365,9 +2365,17 @@ public: return true; } + void release() { + if (!m_could_reserve) { + throw std::runtime_error("Attempt to release unreserved BlockFiltersScanReserver"); + } + g_scanfilter_in_progress = false; + m_could_reserve = false; + } + ~BlockFiltersScanReserver() { if (m_could_reserve) { - g_scanfilter_in_progress = false; + release(); } } }; @@ -2427,6 +2435,9 @@ static RPCHelpMan scanblocks() RPCResult{"when action=='status' and a scan is currently in progress", RPCResult::Type::OBJ, "", "", { {RPCResult::Type::NUM, "progress", "Approximate percent complete"}, {RPCResult::Type::NUM, "current_height", "Height of the block currently being scanned"}, + {RPCResult::Type::ARR, "relevant_blocks", "Blocks that may have matched a scanobject.", { + {RPCResult::Type::STR_HEX, "blockhash", "A relevant blockhash"}, + }}, }, }, scan_result_abort, @@ -2441,15 +2452,20 @@ static RPCHelpMan scanblocks() }, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { + static GlobalMutex cs_relevant_blocks; + static UniValue relevant_blocks GUARDED_BY(cs_relevant_blocks); + UniValue ret(UniValue::VOBJ); if (request.params[0].get_str() == "status") { BlockFiltersScanReserver reserver; + LOCK(cs_relevant_blocks); if (reserver.reserve()) { // no scan in progress return NullUniValue; } ret.pushKV("progress", g_scanfilter_progress.load()); ret.pushKV("current_height", g_scanfilter_progress_height.load()); + ret.pushKV("relevant_blocks", relevant_blocks); return ret; } else if (request.params[0].get_str() == "abort") { BlockFiltersScanReserver reserver; @@ -2461,6 +2477,11 @@ static RPCHelpMan scanblocks() g_scanfilter_should_abort_scan = true; return true; } else if (request.params[0].get_str() == "start") { + { + LOCK(cs_relevant_blocks); + relevant_blocks = UniValue(UniValue::VARR); + } + BlockFiltersScanReserver reserver; if (!reserver.reserve()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan already in progress, use action \"abort\" or \"status\""); @@ -2516,7 +2537,7 @@ static RPCHelpMan scanblocks() needle_set.emplace(script.begin(), script.end()); } } - UniValue blocks(UniValue::VARR); + const int amount_per_chunk = 10000; std::vector filters; int start_block_height = start_index->nHeight; // for progress reporting @@ -2554,7 +2575,8 @@ static RPCHelpMan scanblocks() } } - blocks.push_back(filter.GetBlockHash().GetHex()); + LOCK(cs_relevant_blocks); + relevant_blocks.push_back(filter.GetBlockHash().GetHex()); } } } @@ -2574,8 +2596,10 @@ static RPCHelpMan scanblocks() 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", std::move(blocks)); + LOCK(cs_relevant_blocks); + ret.pushKV("relevant_blocks", std::move(relevant_blocks)); ret.pushKV("completed", completed); + reserver.release(); // ensure this is before cs_relevant_blocks is released, so status doesn't try to use moved relevant_blocks } else { throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid action '%s'", request.params[0].get_str()));