Repository navigation
backport: bitcoin#26094, #26325, #26499, #26649, #26965, #27033, #27066, #27093, #34185, partial #35070, bitcon-core/gui#704 #7768
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
bf38b20
3e2b99a
cb46cb4
74127d3
6eab9c5
7584580
3e9eb83
66ef8a6
2b04bf3
8ff8083
62bab13
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -28,6 +28,7 @@ | |||||||||||||
| #include <net.h> | ||||||||||||||
| #include <net_processing.h> | ||||||||||||||
| #include <node/context.h> | ||||||||||||||
| #include <node/transaction.h> | ||||||||||||||
| #include <node/utxo_snapshot.h> | ||||||||||||||
| #include <merkleblock.h> | ||||||||||||||
| #include <primitives/transaction.h> | ||||||||||||||
|
|
@@ -57,6 +58,7 @@ | |||||||||||||
| #include <evo/evodb.h> | ||||||||||||||
| #include <evo/mnhftx.h> | ||||||||||||||
| #include <evo/specialtx.h> | ||||||||||||||
| #include <evo/specialtx_filter.h> | ||||||||||||||
| #include <instantsend/instantsend.h> | ||||||||||||||
|
|
||||||||||||||
| #include <stdint.h> | ||||||||||||||
|
|
@@ -2833,17 +2835,52 @@ class BlockFiltersScanReserver | |||||||||||||
| } | ||||||||||||||
| }; | ||||||||||||||
|
|
||||||||||||||
| 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; | ||||||||||||||
|
coderabbitai[bot] marked this conversation as resolved.
|
||||||||||||||
| })) { | ||||||||||||||
|
Comment on lines
+2845
to
+2847
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔴 Blocking: Exclude OP_RETURN outputs from accurate scan matching BasicFilterElements excludes empty output scripts and scripts beginning with OP_RETURN, but CheckBlockFilterMatches compares every transaction output against the requested needles. If an OP_RETURN needle collides with an included basic-filter element, MatchAny selects the block and this exact-match pass then incorrectly accepts the excluded output as a real match. That violates filter_false_positives=true, which should return only blocks containing actual basic-filter elements.
Suggested change
source:
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Identical to upstream and still so on master. A needle starting with OP_RETURN that appears in the block is a real match of the block contents, only not an indexed one. No Dash-specific reason to diverge.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Withdrawn (re-reviewed at |
||||||||||||||
| return true; | ||||||||||||||
| } | ||||||||||||||
| bool special_tx_match{false}; | ||||||||||||||
| ExtractSpecialTxFilterElements(*tx, [&](Span<const unsigned char> element) { | ||||||||||||||
| special_tx_match = special_tx_match || needles.count(std::vector<unsigned char>(element.begin(), element.end())) != 0; | ||||||||||||||
| }); | ||||||||||||||
| if (special_tx_match) 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_FILTER)}, "The type name of the filter"} | ||||||||||||||
| RPCArg{"filtertype", RPCArg::Type::STR, RPCArg::Default{BlockFilterTypeName(BlockFilterType::BASIC_FILTER)}, "The type name of the filter"}, | ||||||||||||||
| RPCArg{"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "", | ||||||||||||||
| { | ||||||||||||||
| {"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, | ||||||||||||||
|
|
@@ -2903,6 +2940,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); | ||||||||||||||
|
|
@@ -2973,6 +3013,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()); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.