|
25 | 25 | #include <net_processing.h>
|
26 | 26 | #include <node/blockstorage.h>
|
27 | 27 | #include <node/context.h>
|
| 28 | +#include <node/transaction.h> |
28 | 29 | #include <node/utxo_snapshot.h>
|
29 | 30 | #include <primitives/transaction.h>
|
30 | 31 | #include <rpc/server.h>
|
@@ -2268,17 +2269,47 @@ class BlockFiltersScanReserver
|
2268 | 2269 | }
|
2269 | 2270 | };
|
2270 | 2271 |
|
| 2272 | +static bool CheckBlockFilterMatches(BlockManager& blockman, const CBlockIndex& blockindex, const GCSFilter::ElementSet& needles) |
| 2273 | +{ |
| 2274 | + const CBlock block{GetBlockChecked(blockman, &blockindex)}; |
| 2275 | + const CBlockUndo block_undo{GetUndoChecked(blockman, &blockindex)}; |
| 2276 | + |
| 2277 | + // Check if any of the outputs match the scriptPubKey |
| 2278 | + for (const auto& tx : block.vtx) { |
| 2279 | + if (std::any_of(tx->vout.cbegin(), tx->vout.cend(), [&](const auto& txout) { |
| 2280 | + return needles.count(std::vector<unsigned char>(txout.scriptPubKey.begin(), txout.scriptPubKey.end())) != 0; |
| 2281 | + })) { |
| 2282 | + return true; |
| 2283 | + } |
| 2284 | + } |
| 2285 | + // Check if any of the inputs match the scriptPubKey |
| 2286 | + for (const auto& txundo : block_undo.vtxundo) { |
| 2287 | + if (std::any_of(txundo.vprevout.cbegin(), txundo.vprevout.cend(), [&](const auto& coin) { |
| 2288 | + return needles.count(std::vector<unsigned char>(coin.out.scriptPubKey.begin(), coin.out.scriptPubKey.end())) != 0; |
| 2289 | + })) { |
| 2290 | + return true; |
| 2291 | + } |
| 2292 | + } |
| 2293 | + |
| 2294 | + return false; |
| 2295 | +} |
| 2296 | + |
2271 | 2297 | static RPCHelpMan scanblocks()
|
2272 | 2298 | {
|
2273 | 2299 | return RPCHelpMan{"scanblocks",
|
2274 |
| - "\nReturn relevant blockhashes for given descriptors.\n" |
| 2300 | + "\nReturn relevant blockhashes for given descriptors (requires blockfilterindex).\n" |
2275 | 2301 | "This call may take several minutes. Make sure to use no RPC timeout (bitcoin-cli -rpcclienttimeout=0)",
|
2276 | 2302 | {
|
2277 | 2303 | scan_action_arg_desc,
|
2278 | 2304 | scan_objects_arg_desc,
|
2279 | 2305 | RPCArg{"start_height", RPCArg::Type::NUM, RPCArg::Default{0}, "Height to start to scan from"},
|
2280 | 2306 | RPCArg{"stop_height", RPCArg::Type::NUM, RPCArg::DefaultHint{"chain tip"}, "Height to stop to scan"},
|
2281 |
| - RPCArg{"filtertype", RPCArg::Type::STR, RPCArg::Default{BlockFilterTypeName(BlockFilterType::BASIC)}, "The type name of the filter"} |
| 2307 | + RPCArg{"filtertype", RPCArg::Type::STR, RPCArg::Default{BlockFilterTypeName(BlockFilterType::BASIC)}, "The type name of the filter"}, |
| 2308 | + RPCArg{"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "", |
| 2309 | + { |
| 2310 | + {"filter_false_positives", RPCArg::Type::BOOL, RPCArg::Default{false}, "Filter false positives (slower and may fail on pruned nodes). Otherwise they may occur at a rate of 1/M"}, |
| 2311 | + }, |
| 2312 | + RPCArgOptions{.oneline_description="\"options\""}}, |
2282 | 2313 | },
|
2283 | 2314 | {
|
2284 | 2315 | scan_result_status_none,
|
@@ -2338,6 +2369,9 @@ static RPCHelpMan scanblocks()
|
2338 | 2369 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unknown filtertype");
|
2339 | 2370 | }
|
2340 | 2371 |
|
| 2372 | + UniValue options{request.params[5].isNull() ? UniValue::VOBJ : request.params[5]}; |
| 2373 | + bool filter_false_positives{options.exists("filter_false_positives") ? options["filter_false_positives"].get_bool() : false}; |
| 2374 | + |
2341 | 2375 | BlockFilterIndex* index = GetBlockFilterIndex(filtertype);
|
2342 | 2376 | if (!index) {
|
2343 | 2377 | throw JSONRPCError(RPC_MISC_ERROR, "Index is not enabled for filtertype " + filtertype_name);
|
@@ -2408,6 +2442,15 @@ static RPCHelpMan scanblocks()
|
2408 | 2442 | for (const BlockFilter& filter : filters) {
|
2409 | 2443 | // compare the elements-set with each filter
|
2410 | 2444 | if (filter.GetFilter().MatchAny(needle_set)) {
|
| 2445 | + if (filter_false_positives) { |
| 2446 | + // Double check the filter matches by scanning the block |
| 2447 | + const CBlockIndex& blockindex = *CHECK_NONFATAL(WITH_LOCK(cs_main, return chainman.m_blockman.LookupBlockIndex(filter.GetBlockHash()))); |
| 2448 | + |
| 2449 | + if (!CheckBlockFilterMatches(chainman.m_blockman, blockindex, needle_set)) { |
| 2450 | + continue; |
| 2451 | + } |
| 2452 | + } |
| 2453 | + |
2411 | 2454 | blocks.push_back(filter.GetBlockHash().GetHex());
|
2412 | 2455 | LogPrint(BCLog::RPC, "scanblocks: found match in %s\n", filter.GetBlockHash().GetHex());
|
2413 | 2456 | }
|
|
0 commit comments