diff options
Diffstat (limited to 'src/rpc/blockchain.cpp')
-rw-r--r-- | src/rpc/blockchain.cpp | 99 |
1 files changed, 74 insertions, 25 deletions
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 784fb64d36..1a9b265fbe 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> @@ -431,7 +432,8 @@ static RPCHelpMan getblockfrompeer() "We must have the header for this block, e.g. using submitheader.\n" "Subsequent calls for the same block and a new peer will cause the response from the previous peer to be ignored.\n" "Peers generally ignore requests for a stale block that they never fully verified, or one that is more than a month old.\n" - "When a peer does not respond with a block, we will disconnect.\n\n" + "When a peer does not respond with a block, we will disconnect.\n" + "Note: The block could be re-pruned as soon as it is received.\n\n" "Returns an empty JSON object if the request was successfully scheduled.", { {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash to try to fetch"}, @@ -444,11 +446,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 +461,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 +566,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 +651,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 +776,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 +871,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{ @@ -1108,7 +1111,7 @@ static RPCHelpMan verifychain() {"nblocks", RPCArg::Type::NUM, RPCArg::DefaultHint{strprintf("%d, 0=all", DEFAULT_CHECKBLOCKS)}, "The number of blocks to check."}, }, RPCResult{ - RPCResult::Type::BOOL, "", "Verified or not"}, + RPCResult::Type::BOOL, "", "Verification finished successfully. If false, check debug.log for reason."}, RPCExamples{ HelpExampleCli("verifychain", "") + HelpExampleRpc("verifychain", "") @@ -1123,7 +1126,7 @@ static RPCHelpMan verifychain() Chainstate& active_chainstate = chainman.ActiveChainstate(); return CVerifyDB().VerifyDB( - active_chainstate, chainman.GetParams().GetConsensus(), active_chainstate.CoinsTip(), check_level, check_depth); + active_chainstate, chainman.GetParams().GetConsensus(), active_chainstate.CoinsTip(), check_level, check_depth) == VerifyDBResult::SUCCESS; }, }; } @@ -1246,7 +1249,6 @@ RPCHelpMan getblockchaininfo() }, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { - const ArgsManager& args{EnsureAnyArgsman(request.context)}; ChainstateManager& chainman = EnsureAnyChainman(request.context); LOCK(cs_main); Chainstate& active_chainstate = chainman.ActiveChainstate(); @@ -1265,15 +1267,14 @@ 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}; + const bool automatic_pruning{chainman.m_blockman.GetPruneTarget() != BlockManager::PRUNE_TARGET_MANUAL}; 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 +1743,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"}, @@ -2100,6 +2105,10 @@ static RPCHelpMan scantxoutset() " combo(<pubkey>) P2PK, P2PKH, P2WPKH, and P2SH-P2WPKH outputs for the given pubkey\n" " pkh(<pubkey>) P2PKH outputs for the given pubkey\n" " sh(multi(<n>,<pubkey>,<pubkey>,...)) P2SH-multisig outputs for the given threshold and pubkeys\n" + " tr(<pubkey>) P2TR\n" + " tr(<pubkey>,{pk(<pubkey>)}) P2TR with single fallback pubkey in tapscript\n" + " rawtr(<pubkey>) P2TR with the specified key as output key rather than inner\n" + " wsh(and_v(v:pk(<pubkey>),after(2))) P2WSH miniscript with mandatory pubkey and a timelock\n" "\nIn the above, <pubkey> either refers to a fixed public key in hexadecimal notation, or to an xpub/xprv optionally followed by one\n" "or more path elements separated by \"/\", and optionally ending in \"/*\" (unhardened), or \"/*'\" or \"/*h\" (hardened) to specify all\n" "unhardened or hardened child keys.\n" @@ -2144,8 +2153,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 +2275,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 +2375,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 +2448,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()); } |