mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-03-31 10:56:03 +02:00
rpc: Return accurate results for scanblocks
This makes use of undo data to accurately verify results from blockfilters.
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
#include <net_processing.h>
|
||||
#include <node/blockstorage.h>
|
||||
#include <node/context.h>
|
||||
#include <node/transaction.h>
|
||||
#include <node/utxo_snapshot.h>
|
||||
#include <primitives/transaction.h>
|
||||
#include <rpc/server.h>
|
||||
@@ -2268,17 +2269,47 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
static bool CheckBlockFilterMatches(BlockManager& blockman, const CBlockIndex& blockindex, const GCSFilter::ElementSet& needles)
|
||||
{
|
||||
const CBlock block{GetBlockChecked(blockman, &blockindex)};
|
||||
const CBlockUndo block_undo{GetUndoChecked(blockman, &blockindex)};
|
||||
|
||||
// Check if any of the outputs match the scriptPubKey
|
||||
for (const auto& tx : block.vtx) {
|
||||
if (std::any_of(tx->vout.cbegin(), tx->vout.cend(), [&](const auto& txout) {
|
||||
return needles.count(std::vector<unsigned char>(txout.scriptPubKey.begin(), txout.scriptPubKey.end())) != 0;
|
||||
})) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Check if any of the inputs match the scriptPubKey
|
||||
for (const auto& txundo : block_undo.vtxundo) {
|
||||
if (std::any_of(txundo.vprevout.cbegin(), txundo.vprevout.cend(), [&](const auto& coin) {
|
||||
return needles.count(std::vector<unsigned char>(coin.out.scriptPubKey.begin(), coin.out.scriptPubKey.end())) != 0;
|
||||
})) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static RPCHelpMan scanblocks()
|
||||
{
|
||||
return RPCHelpMan{"scanblocks",
|
||||
"\nReturn relevant blockhashes for given descriptors.\n"
|
||||
"\nReturn relevant blockhashes for given descriptors (requires blockfilterindex).\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::Default{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::Default{BlockFilterTypeName(BlockFilterType::BASIC)}, "The type name of the filter"}
|
||||
RPCArg{"filtertype", RPCArg::Type::STR, RPCArg::Default{BlockFilterTypeName(BlockFilterType::BASIC)}, "The type name of the filter"},
|
||||
RPCArg{"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
|
||||
{
|
||||
{"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"},
|
||||
},
|
||||
RPCArgOptions{.oneline_description="\"options\""}},
|
||||
},
|
||||
{
|
||||
scan_result_status_none,
|
||||
@@ -2338,6 +2369,9 @@ static RPCHelpMan scanblocks()
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unknown filtertype");
|
||||
}
|
||||
|
||||
UniValue options{request.params[5].isNull() ? UniValue::VOBJ : request.params[5]};
|
||||
bool filter_false_positives{options.exists("filter_false_positives") ? options["filter_false_positives"].get_bool() : false};
|
||||
|
||||
BlockFilterIndex* index = GetBlockFilterIndex(filtertype);
|
||||
if (!index) {
|
||||
throw JSONRPCError(RPC_MISC_ERROR, "Index is not enabled for filtertype " + filtertype_name);
|
||||
@@ -2408,6 +2442,15 @@ static RPCHelpMan scanblocks()
|
||||
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());
|
||||
}
|
||||
|
||||
@@ -86,6 +86,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
||||
{ "scanblocks", 1, "scanobjects" },
|
||||
{ "scanblocks", 2, "start_height" },
|
||||
{ "scanblocks", 3, "stop_height" },
|
||||
{ "scanblocks", 5, "options" },
|
||||
{ "scantxoutset", 1, "scanobjects" },
|
||||
{ "addmultisigaddress", 0, "nrequired" },
|
||||
{ "addmultisigaddress", 1, "keys" },
|
||||
|
||||
Reference in New Issue
Block a user