aboutsummaryrefslogtreecommitdiff
path: root/src/rpc/blockchain.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpc/blockchain.cpp')
-rw-r--r--src/rpc/blockchain.cpp84
1 files changed, 65 insertions, 19 deletions
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 784fb64d36..8bee066ab8 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>
@@ -444,11 +445,6 @@ static RPCHelpMan getblockfrompeer()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- RPCTypeCheck(request.params, {
- UniValue::VSTR, // blockhash
- UniValue::VNUM, // peer_id
- });
-
const NodeContext& node = EnsureAnyNodeContext(request.context);
ChainstateManager& chainman = EnsureChainman(node);
PeerManager& peerman = EnsurePeerman(node);
@@ -464,7 +460,7 @@ static RPCHelpMan getblockfrompeer()
// Fetching blocks before the node has syncing past their height can prevent block files from
// being pruned, so we avoid it if the node is in prune mode.
- if (node::fPruneMode && index->nHeight > WITH_LOCK(chainman.GetMutex(), return chainman.ActiveTip()->nHeight)) {
+ if (chainman.m_blockman.IsPruneMode() && index->nHeight > WITH_LOCK(chainman.GetMutex(), return chainman.ActiveTip()->nHeight)) {
throw JSONRPCError(RPC_MISC_ERROR, "In prune mode, only blocks that the node has already synced previously can be fetched from a peer");
}
@@ -569,7 +565,7 @@ static RPCHelpMan getblockheader()
if (!fVerbose)
{
- CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
+ DataStream ssBlock{};
ssBlock << pblockindex->GetBlockHeader();
std::string strHex = HexStr(ssBlock);
return strHex;
@@ -654,7 +650,8 @@ static RPCHelpMan getblock()
"If verbosity is 3, returns an Object with information about block <hash> and information about each transaction, including prevout information for inputs (only for unpruned blocks in the current best chain).\n",
{
{"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
- {"verbosity|verbose", RPCArg::Type::NUM, RPCArg::Default{1}, "0 for hex-encoded data, 1 for a JSON object, 2 for JSON object with transaction data, and 3 for JSON object with transaction data including prevout information for inputs"},
+ {"verbosity|verbose", RPCArg::Type::NUM, RPCArg::Default{1}, "0 for hex-encoded data, 1 for a JSON object, 2 for JSON object with transaction data, and 3 for JSON object with transaction data including prevout information for inputs",
+ RPCArgOptions{.skip_type_check = true}},
},
{
RPCResult{"for verbosity = 0",
@@ -778,10 +775,11 @@ static RPCHelpMan pruneblockchain()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- if (!node::fPruneMode)
+ ChainstateManager& chainman = EnsureAnyChainman(request.context);
+ if (!chainman.m_blockman.IsPruneMode()) {
throw JSONRPCError(RPC_MISC_ERROR, "Cannot prune blocks because node is not in prune mode.");
+ }
- ChainstateManager& chainman = EnsureAnyChainman(request.context);
LOCK(cs_main);
Chainstate& active_chainstate = chainman.ActiveChainstate();
CChain& active_chain = active_chainstate.m_chain;
@@ -872,7 +870,11 @@ static RPCHelpMan gettxoutsetinfo()
"Note this call may take some time if you are not using coinstatsindex.\n",
{
{"hash_type", RPCArg::Type::STR, RPCArg::Default{"hash_serialized_2"}, "Which UTXO set hash should be calculated. Options: 'hash_serialized_2' (the legacy algorithm), 'muhash', 'none'."},
- {"hash_or_height", RPCArg::Type::NUM, RPCArg::DefaultHint{"the current best block"}, "The block hash or height of the target height (only available with coinstatsindex).", RPCArgOptions{.type_str={"", "string or numeric"}}},
+ {"hash_or_height", RPCArg::Type::NUM, RPCArg::DefaultHint{"the current best block"}, "The block hash or height of the target height (only available with coinstatsindex).",
+ RPCArgOptions{
+ .skip_type_check = true,
+ .type_str = {"", "string or numeric"},
+ }},
{"use_index", RPCArg::Type::BOOL, RPCArg::Default{true}, "Use coinstatsindex, if available."},
},
RPCResult{
@@ -1265,15 +1267,15 @@ RPCHelpMan getblockchaininfo()
obj.pushKV("initialblockdownload", active_chainstate.IsInitialBlockDownload());
obj.pushKV("chainwork", tip.nChainWork.GetHex());
obj.pushKV("size_on_disk", chainman.m_blockman.CalculateCurrentUsage());
- obj.pushKV("pruned", node::fPruneMode);
- if (node::fPruneMode) {
+ obj.pushKV("pruned", chainman.m_blockman.IsPruneMode());
+ if (chainman.m_blockman.IsPruneMode()) {
obj.pushKV("pruneheight", chainman.m_blockman.GetFirstStoredBlock(tip)->nHeight);
// if 0, execution bypasses the whole if block.
bool automatic_pruning{args.GetIntArg("-prune", 0) != 1};
obj.pushKV("automatic_pruning", automatic_pruning);
if (automatic_pruning) {
- obj.pushKV("prune_target_size", node::nPruneTarget);
+ obj.pushKV("prune_target_size", chainman.m_blockman.GetPruneTarget());
}
}
@@ -1742,7 +1744,11 @@ static RPCHelpMan getblockstats()
"\nCompute per block statistics for a given window. All amounts are in satoshis.\n"
"It won't work for some heights with pruning.\n",
{
- {"hash_or_height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The block hash or height of the target block", RPCArgOptions{.type_str={"", "string or numeric"}}},
+ {"hash_or_height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The block hash or height of the target block",
+ RPCArgOptions{
+ .skip_type_check = true,
+ .type_str = {"", "string or numeric"},
+ }},
{"stats", RPCArg::Type::ARR, RPCArg::DefaultHint{"all values"}, "Values to plot (see result below)",
{
{"height", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Selected statistic"},
@@ -2144,8 +2150,6 @@ static RPCHelpMan scantxoutset()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR});
-
UniValue result(UniValue::VOBJ);
if (request.params[0].get_str() == "status") {
CoinsViewScanReserver reserver;
@@ -2268,17 +2272,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, "",
+ {
+ {"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 +2372,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 +2445,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());
}