aboutsummaryrefslogtreecommitdiff
path: root/src/rpc
diff options
context:
space:
mode:
authorAurèle Oulès <aurele@oules.com>2022-10-17 15:49:29 +0200
committerAurèle Oulès <aurele@oules.com>2023-01-06 12:01:22 +0100
commit5ca7a7be76f2521dca895daa70949fd11df0844c (patch)
treee744b66906f837e275180ce5d32d28e2f23a7633 /src/rpc
parent911a40ead256b8849166cff1b745b9c9898e2da8 (diff)
rpc: Return accurate results for scanblocks
This makes use of undo data to accurately verify results from blockfilters.
Diffstat (limited to 'src/rpc')
-rw-r--r--src/rpc/blockchain.cpp47
-rw-r--r--src/rpc/client.cpp1
2 files changed, 46 insertions, 2 deletions
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 784fb64d36..fade2ad504 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -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());
}
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index e6ce188a56..b046e1a17b 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -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" },