diff options
Diffstat (limited to 'src/rpc')
-rw-r--r-- | src/rpc/blockchain.cpp | 392 | ||||
-rw-r--r-- | src/rpc/blockchain.h | 22 | ||||
-rw-r--r-- | src/rpc/client.cpp | 8 | ||||
-rw-r--r-- | src/rpc/mining.cpp | 49 | ||||
-rw-r--r-- | src/rpc/misc.cpp | 77 | ||||
-rw-r--r-- | src/rpc/net.cpp | 187 | ||||
-rw-r--r-- | src/rpc/net.h | 15 | ||||
-rw-r--r-- | src/rpc/protocol.h | 3 | ||||
-rw-r--r-- | src/rpc/rawtransaction.cpp | 508 | ||||
-rw-r--r-- | src/rpc/rawtransaction_util.cpp | 2 | ||||
-rw-r--r-- | src/rpc/rawtransaction_util.h | 2 | ||||
-rw-r--r-- | src/rpc/register.h | 2 | ||||
-rw-r--r-- | src/rpc/request.cpp | 2 | ||||
-rw-r--r-- | src/rpc/request.h | 2 | ||||
-rw-r--r-- | src/rpc/server.cpp | 2 | ||||
-rw-r--r-- | src/rpc/server.h | 2 | ||||
-rw-r--r-- | src/rpc/server_util.cpp | 95 | ||||
-rw-r--r-- | src/rpc/server_util.h | 32 | ||||
-rw-r--r-- | src/rpc/util.cpp | 7 |
19 files changed, 914 insertions, 495 deletions
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 55048f6811..ccc859619d 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2020 The Bitcoin Core developers +// Copyright (c) 2009-2022 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -15,9 +15,13 @@ #include <core_io.h> #include <deploymentinfo.h> #include <deploymentstatus.h> +#include <fs.h> #include <hash.h> #include <index/blockfilterindex.h> #include <index/coinstatsindex.h> +#include <logging/timer.h> +#include <net.h> +#include <net_processing.h> #include <node/blockstorage.h> #include <node/coinstats.h> #include <node/context.h> @@ -28,6 +32,7 @@ #include <policy/rbf.h> #include <primitives/transaction.h> #include <rpc/server.h> +#include <rpc/server_util.h> #include <rpc/util.h> #include <script/descriptor.h> #include <streams.h> @@ -37,7 +42,6 @@ #include <undo.h> #include <util/strencodings.h> #include <util/string.h> -#include <util/system.h> #include <util/translation.h> #include <validation.h> #include <validationinterface.h> @@ -52,6 +56,16 @@ #include <memory> #include <mutex> +using node::BlockManager; +using node::CCoinsStats; +using node::CoinStatsHashType; +using node::GetUTXOStats; +using node::IsBlockPruned; +using node::NodeContext; +using node::ReadBlockFromDisk; +using node::SnapshotMetadata; +using node::UndoReadFromDisk; + struct CUpdatedBlock { uint256 hash; @@ -62,54 +76,6 @@ static Mutex cs_blockchange; static std::condition_variable cond_blockchange; static CUpdatedBlock latestblock GUARDED_BY(cs_blockchange); -NodeContext& EnsureAnyNodeContext(const std::any& context) -{ - auto node_context = util::AnyPtr<NodeContext>(context); - if (!node_context) { - throw JSONRPCError(RPC_INTERNAL_ERROR, "Node context not found"); - } - return *node_context; -} - -CTxMemPool& EnsureMemPool(const NodeContext& node) -{ - if (!node.mempool) { - throw JSONRPCError(RPC_CLIENT_MEMPOOL_DISABLED, "Mempool disabled or instance not found"); - } - return *node.mempool; -} - -CTxMemPool& EnsureAnyMemPool(const std::any& context) -{ - return EnsureMemPool(EnsureAnyNodeContext(context)); -} - -ChainstateManager& EnsureChainman(const NodeContext& node) -{ - if (!node.chainman) { - throw JSONRPCError(RPC_INTERNAL_ERROR, "Node chainman not found"); - } - return *node.chainman; -} - -ChainstateManager& EnsureAnyChainman(const std::any& context) -{ - return EnsureChainman(EnsureAnyNodeContext(context)); -} - -CBlockPolicyEstimator& EnsureFeeEstimator(const NodeContext& node) -{ - if (!node.fee_estimator) { - throw JSONRPCError(RPC_INTERNAL_ERROR, "Fee estimation disabled"); - } - return *node.fee_estimator; -} - -CBlockPolicyEstimator& EnsureAnyFeeEstimator(const std::any& context) -{ - return EnsureFeeEstimator(EnsureAnyNodeContext(context)); -} - /* Calculate the difficulty for a given block index. */ double GetDifficulty(const CBlockIndex* blockindex) @@ -229,6 +195,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIn TxToUniv(*tx, uint256(), objTx, true, RPCSerializationFlags(), txundo, verbosity); txs.push_back(objTx); } + break; } result.pushKV("tx", txs); @@ -462,23 +429,30 @@ static RPCHelpMan getdifficulty() static std::vector<RPCResult> MempoolEntryDescription() { return { RPCResult{RPCResult::Type::NUM, "vsize", "virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted."}, RPCResult{RPCResult::Type::NUM, "weight", "transaction weight as defined in BIP 141."}, - RPCResult{RPCResult::Type::STR_AMOUNT, "fee", "transaction fee in " + CURRENCY_UNIT + " (DEPRECATED)"}, - RPCResult{RPCResult::Type::STR_AMOUNT, "modifiedfee", "transaction fee with fee deltas used for mining priority (DEPRECATED)"}, + RPCResult{RPCResult::Type::STR_AMOUNT, "fee", /*optional=*/true, + "transaction fee, denominated in " + CURRENCY_UNIT + " (DEPRECATED, returned only if config option -deprecatedrpc=fees is passed)"}, + RPCResult{RPCResult::Type::STR_AMOUNT, "modifiedfee", /*optional=*/true, + "transaction fee with fee deltas used for mining priority, denominated in " + CURRENCY_UNIT + + " (DEPRECATED, returned only if config option -deprecatedrpc=fees is passed)"}, RPCResult{RPCResult::Type::NUM_TIME, "time", "local time transaction entered pool in seconds since 1 Jan 1970 GMT"}, RPCResult{RPCResult::Type::NUM, "height", "block height when transaction entered pool"}, RPCResult{RPCResult::Type::NUM, "descendantcount", "number of in-mempool descendant transactions (including this one)"}, RPCResult{RPCResult::Type::NUM, "descendantsize", "virtual transaction size of in-mempool descendants (including this one)"}, - RPCResult{RPCResult::Type::STR_AMOUNT, "descendantfees", "modified fees (see above) of in-mempool descendants (including this one) (DEPRECATED)"}, + RPCResult{RPCResult::Type::STR_AMOUNT, "descendantfees", /*optional=*/true, + "transaction fees of in-mempool descendants (including this one) with fee deltas used for mining priority, denominated in " + + CURRENCY_ATOM + "s (DEPRECATED, returned only if config option -deprecatedrpc=fees is passed)"}, RPCResult{RPCResult::Type::NUM, "ancestorcount", "number of in-mempool ancestor transactions (including this one)"}, RPCResult{RPCResult::Type::NUM, "ancestorsize", "virtual transaction size of in-mempool ancestors (including this one)"}, - RPCResult{RPCResult::Type::STR_AMOUNT, "ancestorfees", "modified fees (see above) of in-mempool ancestors (including this one) (DEPRECATED)"}, + RPCResult{RPCResult::Type::STR_AMOUNT, "ancestorfees", /*optional=*/true, + "transaction fees of in-mempool ancestors (including this one) with fee deltas used for mining priority, denominated in " + + CURRENCY_ATOM + "s (DEPRECATED, returned only if config option -deprecatedrpc=fees is passed)"}, RPCResult{RPCResult::Type::STR_HEX, "wtxid", "hash of serialized transaction, including witness data"}, RPCResult{RPCResult::Type::OBJ, "fees", "", { - RPCResult{RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT}, - RPCResult{RPCResult::Type::STR_AMOUNT, "modified", "transaction fee with fee deltas used for mining priority in " + CURRENCY_UNIT}, - RPCResult{RPCResult::Type::STR_AMOUNT, "ancestor", "modified fees (see above) of in-mempool ancestors (including this one) in " + CURRENCY_UNIT}, - RPCResult{RPCResult::Type::STR_AMOUNT, "descendant", "modified fees (see above) of in-mempool descendants (including this one) in " + CURRENCY_UNIT}, + RPCResult{RPCResult::Type::STR_AMOUNT, "base", "transaction fee, denominated in " + CURRENCY_UNIT}, + RPCResult{RPCResult::Type::STR_AMOUNT, "modified", "transaction fee with fee deltas used for mining priority, denominated in " + CURRENCY_UNIT}, + RPCResult{RPCResult::Type::STR_AMOUNT, "ancestor", "transaction fees of in-mempool ancestors (including this one) with fee deltas used for mining priority, denominated in " + CURRENCY_UNIT}, + RPCResult{RPCResult::Type::STR_AMOUNT, "descendant", "transaction fees of in-mempool descendants (including this one) with fee deltas used for mining priority, denominated in " + CURRENCY_UNIT}, }}, RPCResult{RPCResult::Type::ARR, "depends", "unconfirmed transactions used as inputs for this transaction", {RPCResult{RPCResult::Type::STR_HEX, "transactionid", "parent transaction id"}}}, @@ -492,26 +466,35 @@ static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPool { AssertLockHeld(pool.cs); - UniValue fees(UniValue::VOBJ); - fees.pushKV("base", ValueFromAmount(e.GetFee())); - fees.pushKV("modified", ValueFromAmount(e.GetModifiedFee())); - fees.pushKV("ancestor", ValueFromAmount(e.GetModFeesWithAncestors())); - fees.pushKV("descendant", ValueFromAmount(e.GetModFeesWithDescendants())); - info.pushKV("fees", fees); - info.pushKV("vsize", (int)e.GetTxSize()); info.pushKV("weight", (int)e.GetTxWeight()); - info.pushKV("fee", ValueFromAmount(e.GetFee())); - info.pushKV("modifiedfee", ValueFromAmount(e.GetModifiedFee())); + // TODO: top-level fee fields are deprecated. deprecated_fee_fields_enabled blocks should be removed in v24 + const bool deprecated_fee_fields_enabled{IsDeprecatedRPCEnabled("fees")}; + if (deprecated_fee_fields_enabled) { + info.pushKV("fee", ValueFromAmount(e.GetFee())); + info.pushKV("modifiedfee", ValueFromAmount(e.GetModifiedFee())); + } info.pushKV("time", count_seconds(e.GetTime())); info.pushKV("height", (int)e.GetHeight()); info.pushKV("descendantcount", e.GetCountWithDescendants()); info.pushKV("descendantsize", e.GetSizeWithDescendants()); - info.pushKV("descendantfees", e.GetModFeesWithDescendants()); + if (deprecated_fee_fields_enabled) { + info.pushKV("descendantfees", e.GetModFeesWithDescendants()); + } info.pushKV("ancestorcount", e.GetCountWithAncestors()); info.pushKV("ancestorsize", e.GetSizeWithAncestors()); - info.pushKV("ancestorfees", e.GetModFeesWithAncestors()); + if (deprecated_fee_fields_enabled) { + info.pushKV("ancestorfees", e.GetModFeesWithAncestors()); + } info.pushKV("wtxid", pool.vTxHashes[e.vTxHashesIdx].first.ToString()); + + UniValue fees(UniValue::VOBJ); + fees.pushKV("base", ValueFromAmount(e.GetFee())); + fees.pushKV("modified", ValueFromAmount(e.GetModifiedFee())); + fees.pushKV("ancestor", ValueFromAmount(e.GetModFeesWithAncestors())); + fees.pushKV("descendant", ValueFromAmount(e.GetModFeesWithDescendants())); + info.pushKV("fees", fees); + const CTransaction& tx = e.GetTx(); std::set<std::string> setDepends; for (const CTxIn& txin : tx.vin) @@ -803,6 +786,59 @@ static RPCHelpMan getmempoolentry() }; } +static RPCHelpMan getblockfrompeer() +{ + return RPCHelpMan{ + "getblockfrompeer", + "\nAttempt to fetch block from a given peer.\n" + "\nWe must have the header for this block, e.g. using submitheader.\n" + "\nReturns {} if a block-request was successfully scheduled\n", + { + {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"}, + {"nodeid", RPCArg::Type::NUM, RPCArg::Optional::NO, "The node ID (see getpeerinfo for node IDs)"}, + }, + RPCResult{RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "warnings", /*optional=*/true, "any warnings"}, + }}, + RPCExamples{ + HelpExampleCli("getblockfrompeer", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" 0") + + HelpExampleRpc("getblockfrompeer", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" 0") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + const NodeContext& node = EnsureAnyNodeContext(request.context); + ChainstateManager& chainman = EnsureChainman(node); + PeerManager& peerman = EnsurePeerman(node); + CConnman& connman = EnsureConnman(node); + + uint256 hash(ParseHashV(request.params[0], "hash")); + + const NodeId nodeid = static_cast<NodeId>(request.params[1].get_int64()); + + // Check that the peer with nodeid exists + if (!connman.ForNode(nodeid, [](CNode* node) {return true;})) { + throw JSONRPCError(RPC_MISC_ERROR, strprintf("Peer nodeid %d does not exist", nodeid)); + } + + const CBlockIndex* const index = WITH_LOCK(cs_main, return chainman.m_blockman.LookupBlockIndex(hash);); + + if (!index) { + throw JSONRPCError(RPC_MISC_ERROR, "Block header missing"); + } + + UniValue result = UniValue::VOBJ; + + if (index->nStatus & BLOCK_HAVE_DATA) { + result.pushKV("warnings", "Block already downloaded"); + } else if (!peerman.FetchBlock(nodeid, hash, *index)) { + throw JSONRPCError(RPC_MISC_ERROR, "Failed to fetch block from peer"); + } + return result; +}, + }; +} + static RPCHelpMan getblockhash() { return RPCHelpMan{"getblockhash", @@ -858,8 +894,8 @@ static RPCHelpMan getblockheader() {RPCResult::Type::NUM, "difficulty", "The difficulty"}, {RPCResult::Type::STR_HEX, "chainwork", "Expected number of hashes required to produce the current chain"}, {RPCResult::Type::NUM, "nTx", "The number of transactions in the block"}, - {RPCResult::Type::STR_HEX, "previousblockhash", /* optional */ true, "The hash of the previous block (if available)"}, - {RPCResult::Type::STR_HEX, "nextblockhash", /* optional */ true, "The hash of the next block (if available)"}, + {RPCResult::Type::STR_HEX, "previousblockhash", /*optional=*/true, "The hash of the previous block (if available)"}, + {RPCResult::Type::STR_HEX, "nextblockhash", /*optional=*/true, "The hash of the next block (if available)"}, }}, RPCResult{"for verbose=false", RPCResult::Type::STR_HEX, "", "A string that is serialized, hex-encoded data for block 'hash'"}, @@ -942,7 +978,7 @@ 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, and 2 for json object with transaction data"}, + {"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"}, }, { RPCResult{"for verbosity = 0", @@ -968,8 +1004,8 @@ static RPCHelpMan getblock() {RPCResult::Type::NUM, "difficulty", "The difficulty"}, {RPCResult::Type::STR_HEX, "chainwork", "Expected number of hashes required to produce the chain up to this block (in hex)"}, {RPCResult::Type::NUM, "nTx", "The number of transactions in the block"}, - {RPCResult::Type::STR_HEX, "previousblockhash", /* optional */ true, "The hash of the previous block (if available)"}, - {RPCResult::Type::STR_HEX, "nextblockhash", /* optional */ true, "The hash of the next block (if available)"}, + {RPCResult::Type::STR_HEX, "previousblockhash", /*optional=*/true, "The hash of the previous block (if available)"}, + {RPCResult::Type::STR_HEX, "nextblockhash", /*optional=*/true, "The hash of the next block (if available)"}, }}, RPCResult{"for verbosity = 2", RPCResult::Type::OBJ, "", "", @@ -984,6 +1020,37 @@ static RPCHelpMan getblock() }}, }}, }}, + RPCResult{"for verbosity = 3", + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::ELISION, "", "Same output as verbosity = 2"}, + {RPCResult::Type::ARR, "tx", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::ARR, "vin", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::ELISION, "", "The same output as verbosity = 2"}, + {RPCResult::Type::OBJ, "prevout", "(Only if undo information is available)", + { + {RPCResult::Type::BOOL, "generated", "Coinbase or not"}, + {RPCResult::Type::NUM, "height", "The height of the prevout"}, + {RPCResult::Type::NUM, "value", "The value in " + CURRENCY_UNIT}, + {RPCResult::Type::OBJ, "scriptPubKey", "", + { + {RPCResult::Type::STR, "asm", "The asm"}, + {RPCResult::Type::STR, "hex", "The hex"}, + {RPCResult::Type::STR, "address", /* optional */ true, "The Bitcoin address (only if a well-defined address exists)"}, + {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"}, + }}, + }}, + }}, + }}, + }}, + }}, + }}, }, RPCExamples{ HelpExampleCli("getblock", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\"") @@ -1055,7 +1122,7 @@ static RPCHelpMan pruneblockchain() }, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { - if (!fPruneMode) + if (!node::fPruneMode) throw JSONRPCError(RPC_MISC_ERROR, "Cannot prune blocks because node is not in prune mode."); ChainstateManager& chainman = EnsureAnyChainman(request.context); @@ -1109,7 +1176,7 @@ CoinStatsHashType ParseHashType(const std::string& hash_type_input) } else if (hash_type_input == "none") { return CoinStatsHashType::NONE; } else { - throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s is not a valid hash_type", hash_type_input)); + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("'%s' is not a valid hash_type", hash_type_input)); } } @@ -1130,13 +1197,13 @@ static RPCHelpMan gettxoutsetinfo() {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at which these statistics are calculated"}, {RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs"}, {RPCResult::Type::NUM, "bogosize", "Database-independent, meaningless metric indicating the UTXO set size"}, - {RPCResult::Type::STR_HEX, "hash_serialized_2", /* optional */ true, "The serialized hash (only present if 'hash_serialized_2' hash_type is chosen)"}, - {RPCResult::Type::STR_HEX, "muhash", /* optional */ true, "The serialized hash (only present if 'muhash' hash_type is chosen)"}, - {RPCResult::Type::NUM, "transactions", /* optional */ true, "The number of transactions with unspent outputs (not available when coinstatsindex is used)"}, - {RPCResult::Type::NUM, "disk_size", /* optional */ true, "The estimated size of the chainstate on disk (not available when coinstatsindex is used)"}, + {RPCResult::Type::STR_HEX, "hash_serialized_2", /*optional=*/true, "The serialized hash (only present if 'hash_serialized_2' hash_type is chosen)"}, + {RPCResult::Type::STR_HEX, "muhash", /*optional=*/true, "The serialized hash (only present if 'muhash' hash_type is chosen)"}, + {RPCResult::Type::NUM, "transactions", /*optional=*/true, "The number of transactions with unspent outputs (not available when coinstatsindex is used)"}, + {RPCResult::Type::NUM, "disk_size", /*optional=*/true, "The estimated size of the chainstate on disk (not available when coinstatsindex is used)"}, {RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount of coins in the UTXO set"}, - {RPCResult::Type::STR_AMOUNT, "total_unspendable_amount", /* optional */ true, "The total amount of coins permanently excluded from the UTXO set (only available if coinstatsindex is used)"}, - {RPCResult::Type::OBJ, "block_info", /* optional */ true, "Info on amounts in the block at this block height (only available if coinstatsindex is used)", + {RPCResult::Type::STR_AMOUNT, "total_unspendable_amount", /*optional=*/true, "The total amount of coins permanently excluded from the UTXO set (only available if coinstatsindex is used)"}, + {RPCResult::Type::OBJ, "block_info", /*optional=*/true, "Info on amounts in the block at this block height (only available if coinstatsindex is used)", { {RPCResult::Type::STR_AMOUNT, "prevout_spent", "Total amount of all prevouts spent in this block"}, {RPCResult::Type::STR_AMOUNT, "coinbase", "Coinbase subsidy amount of this block"}, @@ -1217,9 +1284,10 @@ static RPCHelpMan gettxoutsetinfo() ret.pushKV("hash_serialized_2", stats.hashSerialized.GetHex()); } if (hash_type == CoinStatsHashType::MUHASH) { - ret.pushKV("muhash", stats.hashSerialized.GetHex()); + ret.pushKV("muhash", stats.hashSerialized.GetHex()); } - ret.pushKV("total_amount", ValueFromAmount(stats.nTotalAmount)); + CHECK_NONFATAL(stats.total_amount.has_value()); + ret.pushKV("total_amount", ValueFromAmount(stats.total_amount.value())); if (!stats.index_used) { ret.pushKV("transactions", static_cast<int64_t>(stats.nTransactions)); ret.pushKV("disk_size", stats.nDiskSize); @@ -1274,7 +1342,7 @@ static RPCHelpMan gettxout() {RPCResult::Type::STR, "asm", ""}, {RPCResult::Type::STR_HEX, "hex", ""}, {RPCResult::Type::STR, "type", "The type, eg pubkeyhash"}, - {RPCResult::Type::STR, "address", /* optional */ true, "The Bitcoin address (only if a well-defined address exists)"}, + {RPCResult::Type::STR, "address", /*optional=*/true, "The Bitcoin address (only if a well-defined address exists)"}, }}, {RPCResult::Type::BOOL, "coinbase", "Coinbase or not"}, }}, @@ -1362,7 +1430,7 @@ static RPCHelpMan verifychain() CChainState& active_chainstate = chainman.ActiveChainstate(); return CVerifyDB().VerifyDB( - active_chainstate, Params(), active_chainstate.CoinsTip(), check_level, check_depth); + active_chainstate, Params().GetConsensus(), active_chainstate.CoinsTip(), check_level, check_depth); }, }; } @@ -1450,32 +1518,32 @@ RPCHelpMan getblockchaininfo() {RPCResult::Type::STR_HEX, "chainwork", "total amount of work in active chain, in hexadecimal"}, {RPCResult::Type::NUM, "size_on_disk", "the estimated size of the block and undo files on disk"}, {RPCResult::Type::BOOL, "pruned", "if the blocks are subject to pruning"}, - {RPCResult::Type::NUM, "pruneheight", /* optional */ true, "lowest-height complete block stored (only present if pruning is enabled)"}, - {RPCResult::Type::BOOL, "automatic_pruning", /* optional */ true, "whether automatic pruning is enabled (only present if pruning is enabled)"}, - {RPCResult::Type::NUM, "prune_target_size", /* optional */ true, "the target size used by pruning (only present if automatic pruning is enabled)"}, + {RPCResult::Type::NUM, "pruneheight", /*optional=*/true, "lowest-height complete block stored (only present if pruning is enabled)"}, + {RPCResult::Type::BOOL, "automatic_pruning", /*optional=*/true, "whether automatic pruning is enabled (only present if pruning is enabled)"}, + {RPCResult::Type::NUM, "prune_target_size", /*optional=*/true, "the target size used by pruning (only present if automatic pruning is enabled)"}, {RPCResult::Type::OBJ_DYN, "softforks", "status of softforks", { {RPCResult::Type::OBJ, "xxxx", "name of the softfork", { {RPCResult::Type::STR, "type", "one of \"buried\", \"bip9\""}, - {RPCResult::Type::OBJ, "bip9", /* optional */ true, "status of bip9 softforks (only for \"bip9\" type)", + {RPCResult::Type::OBJ, "bip9", /*optional=*/true, "status of bip9 softforks (only for \"bip9\" type)", { {RPCResult::Type::STR, "status", "one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\""}, - {RPCResult::Type::NUM, "bit", /* optional */ true, "the bit (0-28) in the block version field used to signal this softfork (only for \"started\" and \"locked_in\" status)"}, + {RPCResult::Type::NUM, "bit", /*optional=*/true, "the bit (0-28) in the block version field used to signal this softfork (only for \"started\" and \"locked_in\" status)"}, {RPCResult::Type::NUM_TIME, "start_time", "the minimum median time past of a block at which the bit gains its meaning"}, {RPCResult::Type::NUM_TIME, "timeout", "the median time past of a block at which the deployment is considered failed if not yet locked in"}, {RPCResult::Type::NUM, "since", "height of the first block to which the status applies"}, {RPCResult::Type::NUM, "min_activation_height", "minimum height of blocks for which the rules may be enforced"}, - {RPCResult::Type::OBJ, "statistics", /* optional */ true, "numeric statistics about signalling for a softfork (only for \"started\" and \"locked_in\" status)", + {RPCResult::Type::OBJ, "statistics", /*optional=*/true, "numeric statistics about signalling for a softfork (only for \"started\" and \"locked_in\" status)", { {RPCResult::Type::NUM, "period", "the length in blocks of the signalling period"}, - {RPCResult::Type::NUM, "threshold", /* optional */ true, "the number of blocks with the version bit set required to activate the feature (only for \"started\" status)"}, + {RPCResult::Type::NUM, "threshold", /*optional=*/true, "the number of blocks with the version bit set required to activate the feature (only for \"started\" status)"}, {RPCResult::Type::NUM, "elapsed", "the number of blocks elapsed since the beginning of the current period"}, {RPCResult::Type::NUM, "count", "the number of blocks with the version bit set in the current period"}, - {RPCResult::Type::BOOL, "possible", /* optional */ true, "returns false if there are not enough blocks left in this period to pass activation threshold (only for \"started\" status)"}, + {RPCResult::Type::BOOL, "possible", /*optional=*/true, "returns false if there are not enough blocks left in this period to pass activation threshold (only for \"started\" status)"}, }}, }}, - {RPCResult::Type::NUM, "height", /* optional */ true, "height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)"}, + {RPCResult::Type::NUM, "height", /*optional=*/true, "height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)"}, {RPCResult::Type::BOOL, "active", "true if the rules are enforced for the mempool and the next block"}, }}, }}, @@ -1487,6 +1555,7 @@ RPCHelpMan getblockchaininfo() }, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { + const ArgsManager& args{EnsureAnyArgsman(request.context)}; ChainstateManager& chainman = EnsureAnyChainman(request.context); LOCK(cs_main); CChainState& active_chainstate = chainman.ActiveChainstate(); @@ -1505,9 +1574,9 @@ RPCHelpMan getblockchaininfo() obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), tip)); obj.pushKV("initialblockdownload", active_chainstate.IsInitialBlockDownload()); obj.pushKV("chainwork", tip->nChainWork.GetHex()); - obj.pushKV("size_on_disk", CalculateCurrentUsage()); - obj.pushKV("pruned", fPruneMode); - if (fPruneMode) { + obj.pushKV("size_on_disk", chainman.m_blockman.CalculateCurrentUsage()); + obj.pushKV("pruned", node::fPruneMode); + if (node::fPruneMode) { const CBlockIndex* block = tip; CHECK_NONFATAL(block); while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) { @@ -1517,10 +1586,10 @@ RPCHelpMan getblockchaininfo() obj.pushKV("pruneheight", block->nHeight); // if 0, execution bypasses the whole if block. - bool automatic_pruning = (gArgs.GetIntArg("-prune", 0) != 1); + bool automatic_pruning{args.GetIntArg("-prune", 0) != 1}; obj.pushKV("automatic_pruning", automatic_pruning); if (automatic_pruning) { - obj.pushKV("prune_target_size", nPruneTarget); + obj.pushKV("prune_target_size", node::nPruneTarget); } } @@ -1684,7 +1753,7 @@ static RPCHelpMan getmempoolinfo() {RPCResult::Type::NUM, "size", "Current tx count"}, {RPCResult::Type::NUM, "bytes", "Sum of all virtual transaction sizes as defined in BIP 141. Differs from actual serialized size because witness data is discounted"}, {RPCResult::Type::NUM, "usage", "Total memory usage for the mempool"}, - {RPCResult::Type::STR_AMOUNT, "total_fee", "Total fees for the mempool in " + CURRENCY_UNIT + ", ignoring modified fees through prioritizetransaction"}, + {RPCResult::Type::STR_AMOUNT, "total_fee", "Total fees for the mempool in " + CURRENCY_UNIT + ", ignoring modified fees through prioritisetransaction"}, {RPCResult::Type::NUM, "maxmempool", "Maximum memory usage for the mempool"}, {RPCResult::Type::STR_AMOUNT, "mempoolminfee", "Minimum fee rate in " + CURRENCY_UNIT + "/kvB for tx to be accepted. Is the maximum of minrelaytxfee and minimum mempool fee"}, {RPCResult::Type::STR_AMOUNT, "minrelaytxfee", "Current minimum relay fee for transactions"}, @@ -1838,9 +1907,9 @@ static RPCHelpMan getchaintxstats() {RPCResult::Type::STR_HEX, "window_final_block_hash", "The hash of the final block in the window"}, {RPCResult::Type::NUM, "window_final_block_height", "The height of the final block in the window."}, {RPCResult::Type::NUM, "window_block_count", "Size of the window in number of blocks"}, - {RPCResult::Type::NUM, "window_tx_count", /* optional */ true, "The number of transactions in the window. Only returned if \"window_block_count\" is > 0"}, - {RPCResult::Type::NUM, "window_interval", /* optional */ true, "The elapsed time in the window in seconds. Only returned if \"window_block_count\" is > 0"}, - {RPCResult::Type::NUM, "txrate", /* optional */ true, "The average rate of transactions per second in the window. Only returned if \"window_interval\" is > 0"}, + {RPCResult::Type::NUM, "window_tx_count", /*optional=*/true, "The number of transactions in the window. Only returned if \"window_block_count\" is > 0"}, + {RPCResult::Type::NUM, "window_interval", /*optional=*/true, "The elapsed time in the window in seconds. Only returned if \"window_block_count\" is > 0"}, + {RPCResult::Type::NUM, "txrate", /*optional=*/true, "The average rate of transactions per second in the window. Only returned if \"window_interval\" is > 0"}, }}, RPCExamples{ HelpExampleCli("getchaintxstats", "") @@ -1975,11 +2044,11 @@ static RPCHelpMan getblockstats() RPCResult{ RPCResult::Type::OBJ, "", "", { - {RPCResult::Type::NUM, "avgfee", /* optional */ true, "Average fee in the block"}, - {RPCResult::Type::NUM, "avgfeerate", /* optional */ true, "Average feerate (in satoshis per virtual byte)"}, - {RPCResult::Type::NUM, "avgtxsize", /* optional */ true, "Average transaction size"}, - {RPCResult::Type::STR_HEX, "blockhash", /* optional */ true, "The block hash (to check for potential reorgs)"}, - {RPCResult::Type::ARR_FIXED, "feerate_percentiles", /* optional */ true, "Feerates at the 10th, 25th, 50th, 75th, and 90th percentile weight unit (in satoshis per virtual byte)", + {RPCResult::Type::NUM, "avgfee", /*optional=*/true, "Average fee in the block"}, + {RPCResult::Type::NUM, "avgfeerate", /*optional=*/true, "Average feerate (in satoshis per virtual byte)"}, + {RPCResult::Type::NUM, "avgtxsize", /*optional=*/true, "Average transaction size"}, + {RPCResult::Type::STR_HEX, "blockhash", /*optional=*/true, "The block hash (to check for potential reorgs)"}, + {RPCResult::Type::ARR_FIXED, "feerate_percentiles", /*optional=*/true, "Feerates at the 10th, 25th, 50th, 75th, and 90th percentile weight unit (in satoshis per virtual byte)", { {RPCResult::Type::NUM, "10th_percentile_feerate", "The 10th percentile feerate"}, {RPCResult::Type::NUM, "25th_percentile_feerate", "The 25th percentile feerate"}, @@ -1987,30 +2056,30 @@ static RPCHelpMan getblockstats() {RPCResult::Type::NUM, "75th_percentile_feerate", "The 75th percentile feerate"}, {RPCResult::Type::NUM, "90th_percentile_feerate", "The 90th percentile feerate"}, }}, - {RPCResult::Type::NUM, "height", /* optional */ true, "The height of the block"}, - {RPCResult::Type::NUM, "ins", /* optional */ true, "The number of inputs (excluding coinbase)"}, - {RPCResult::Type::NUM, "maxfee", /* optional */ true, "Maximum fee in the block"}, - {RPCResult::Type::NUM, "maxfeerate", /* optional */ true, "Maximum feerate (in satoshis per virtual byte)"}, - {RPCResult::Type::NUM, "maxtxsize", /* optional */ true, "Maximum transaction size"}, - {RPCResult::Type::NUM, "medianfee", /* optional */ true, "Truncated median fee in the block"}, - {RPCResult::Type::NUM, "mediantime", /* optional */ true, "The block median time past"}, - {RPCResult::Type::NUM, "mediantxsize", /* optional */ true, "Truncated median transaction size"}, - {RPCResult::Type::NUM, "minfee", /* optional */ true, "Minimum fee in the block"}, - {RPCResult::Type::NUM, "minfeerate", /* optional */ true, "Minimum feerate (in satoshis per virtual byte)"}, - {RPCResult::Type::NUM, "mintxsize", /* optional */ true, "Minimum transaction size"}, - {RPCResult::Type::NUM, "outs", /* optional */ true, "The number of outputs"}, - {RPCResult::Type::NUM, "subsidy", /* optional */ true, "The block subsidy"}, - {RPCResult::Type::NUM, "swtotal_size", /* optional */ true, "Total size of all segwit transactions"}, - {RPCResult::Type::NUM, "swtotal_weight", /* optional */ true, "Total weight of all segwit transactions"}, - {RPCResult::Type::NUM, "swtxs", /* optional */ true, "The number of segwit transactions"}, - {RPCResult::Type::NUM, "time", /* optional */ true, "The block time"}, - {RPCResult::Type::NUM, "total_out", /* optional */ true, "Total amount in all outputs (excluding coinbase and thus reward [ie subsidy + totalfee])"}, - {RPCResult::Type::NUM, "total_size", /* optional */ true, "Total size of all non-coinbase transactions"}, - {RPCResult::Type::NUM, "total_weight", /* optional */ true, "Total weight of all non-coinbase transactions"}, - {RPCResult::Type::NUM, "totalfee", /* optional */ true, "The fee total"}, - {RPCResult::Type::NUM, "txs", /* optional */ true, "The number of transactions (including coinbase)"}, - {RPCResult::Type::NUM, "utxo_increase", /* optional */ true, "The increase/decrease in the number of unspent outputs"}, - {RPCResult::Type::NUM, "utxo_size_inc", /* optional */ true, "The increase/decrease in size for the utxo index (not discounting op_return and similar)"}, + {RPCResult::Type::NUM, "height", /*optional=*/true, "The height of the block"}, + {RPCResult::Type::NUM, "ins", /*optional=*/true, "The number of inputs (excluding coinbase)"}, + {RPCResult::Type::NUM, "maxfee", /*optional=*/true, "Maximum fee in the block"}, + {RPCResult::Type::NUM, "maxfeerate", /*optional=*/true, "Maximum feerate (in satoshis per virtual byte)"}, + {RPCResult::Type::NUM, "maxtxsize", /*optional=*/true, "Maximum transaction size"}, + {RPCResult::Type::NUM, "medianfee", /*optional=*/true, "Truncated median fee in the block"}, + {RPCResult::Type::NUM, "mediantime", /*optional=*/true, "The block median time past"}, + {RPCResult::Type::NUM, "mediantxsize", /*optional=*/true, "Truncated median transaction size"}, + {RPCResult::Type::NUM, "minfee", /*optional=*/true, "Minimum fee in the block"}, + {RPCResult::Type::NUM, "minfeerate", /*optional=*/true, "Minimum feerate (in satoshis per virtual byte)"}, + {RPCResult::Type::NUM, "mintxsize", /*optional=*/true, "Minimum transaction size"}, + {RPCResult::Type::NUM, "outs", /*optional=*/true, "The number of outputs"}, + {RPCResult::Type::NUM, "subsidy", /*optional=*/true, "The block subsidy"}, + {RPCResult::Type::NUM, "swtotal_size", /*optional=*/true, "Total size of all segwit transactions"}, + {RPCResult::Type::NUM, "swtotal_weight", /*optional=*/true, "Total weight of all segwit transactions"}, + {RPCResult::Type::NUM, "swtxs", /*optional=*/true, "The number of segwit transactions"}, + {RPCResult::Type::NUM, "time", /*optional=*/true, "The block time"}, + {RPCResult::Type::NUM, "total_out", /*optional=*/true, "Total amount in all outputs (excluding coinbase and thus reward [ie subsidy + totalfee])"}, + {RPCResult::Type::NUM, "total_size", /*optional=*/true, "Total size of all non-coinbase transactions"}, + {RPCResult::Type::NUM, "total_weight", /*optional=*/true, "Total weight of all non-coinbase transactions"}, + {RPCResult::Type::NUM, "totalfee", /*optional=*/true, "The fee total"}, + {RPCResult::Type::NUM, "txs", /*optional=*/true, "The number of transactions (including coinbase)"}, + {RPCResult::Type::NUM, "utxo_increase", /*optional=*/true, "The increase/decrease in the number of unspent outputs"}, + {RPCResult::Type::NUM, "utxo_size_inc", /*optional=*/true, "The increase/decrease in size for the utxo index (not discounting op_return and similar)"}, }}, RPCExamples{ HelpExampleCli("getblockstats", R"('"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09"' '["minfeerate","avgfeerate"]')") + @@ -2188,7 +2257,7 @@ static RPCHelpMan getblockstats() for (const std::string& stat : stats) { const UniValue& value = ret_all[stat]; if (value.isNull()) { - throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid selected statistic %s", stat)); + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid selected statistic '%s'", stat)); } ret.pushKV(stat, value); } @@ -2213,10 +2282,9 @@ static RPCHelpMan savemempool() }, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { + const ArgsManager& args{EnsureAnyArgsman(request.context)}; const CTxMemPool& mempool = EnsureAnyMemPool(request.context); - const NodeContext& node = EnsureAnyNodeContext(request.context); - if (!mempool.IsLoaded()) { throw JSONRPCError(RPC_MISC_ERROR, "The mempool was not loaded yet"); } @@ -2226,7 +2294,7 @@ static RPCHelpMan savemempool() } UniValue ret(UniValue::VOBJ); - ret.pushKV("filename", fs::path((node.args->GetDataDirNet() / "mempool.dat")).u8string()); + ret.pushKV("filename", fs::path((args.GetDataDirNet() / "mempool.dat")).u8string()); return ret; }, @@ -2296,6 +2364,9 @@ public: static RPCHelpMan scantxoutset() { + // scriptPubKey corresponding to mainnet address 12cbQLTFMXRnSzktFkuoG3eHoMeFtpTu3S + const std::string EXAMPLE_DESCRIPTOR_RAW = "raw(76a91411b366edfc0a8b66feebae5c2e25a7b6a5d1cf3188ac)#fm24fxxy"; + return RPCHelpMan{"scantxoutset", "\nScans the unspent transaction output set for entries that match certain output descriptors.\n" "Examples of output descriptors are:\n" @@ -2353,7 +2424,14 @@ static RPCHelpMan scantxoutset() {RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount of all found unspent outputs in " + CURRENCY_UNIT}, }}, }, - RPCExamples{""}, + RPCExamples{ + HelpExampleCli("scantxoutset", "start \'[\"" + EXAMPLE_DESCRIPTOR_RAW + "\"]\'") + + HelpExampleCli("scantxoutset", "status") + + HelpExampleCli("scantxoutset", "abort") + + HelpExampleRpc("scantxoutset", "\"start\", [\"" + EXAMPLE_DESCRIPTOR_RAW + "\"]") + + HelpExampleRpc("scantxoutset", "\"status\"") + + HelpExampleRpc("scantxoutset", "\"abort\"") + }, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR}); @@ -2541,13 +2619,9 @@ static RPCHelpMan dumptxoutset() { return RPCHelpMan{ "dumptxoutset", - "\nWrite the serialized UTXO set to disk.\n", + "Write the serialized UTXO set to disk.", { - {"path", - RPCArg::Type::STR, - RPCArg::Optional::NO, - /* default_val */ "", - "path to the output file. If relative, will be prefixed by datadir."}, + {"path", RPCArg::Type::STR, RPCArg::Optional::NO, "Path to the output file. If relative, will be prefixed by datadir."}, }, RPCResult{ RPCResult::Type::OBJ, "", "", @@ -2556,6 +2630,8 @@ static RPCHelpMan dumptxoutset() {RPCResult::Type::STR_HEX, "base_hash", "the hash of the base of the snapshot"}, {RPCResult::Type::NUM, "base_height", "the height of the base of the snapshot"}, {RPCResult::Type::STR, "path", "the absolute path that the snapshot was written to"}, + {RPCResult::Type::STR_HEX, "txoutset_hash", "the hash of the UTXO set contents"}, + {RPCResult::Type::NUM, "nchaintx", "the number of transactions in the chain up to and including the base block"}, } }, RPCExamples{ @@ -2563,10 +2639,11 @@ static RPCHelpMan dumptxoutset() }, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { - const fs::path path = fsbridge::AbsPathJoin(gArgs.GetDataDirNet(), fs::u8path(request.params[0].get_str())); + const ArgsManager& args{EnsureAnyArgsman(request.context)}; + const fs::path path = fsbridge::AbsPathJoin(args.GetDataDirNet(), fs::u8path(request.params[0].get_str())); // Write to a temporary path and then move into `path` on completion // to avoid confusion due to an interruption. - const fs::path temppath = fsbridge::AbsPathJoin(gArgs.GetDataDirNet(), fs::u8path(request.params[0].get_str() + ".incomplete")); + const fs::path temppath = fsbridge::AbsPathJoin(args.GetDataDirNet(), fs::u8path(request.params[0].get_str() + ".incomplete")); if (fs::exists(path)) { throw JSONRPCError( @@ -2578,7 +2655,8 @@ static RPCHelpMan dumptxoutset() FILE* file{fsbridge::fopen(temppath, "wb")}; CAutoFile afile{file, SER_DISK, CLIENT_VERSION}; NodeContext& node = EnsureAnyNodeContext(request.context); - UniValue result = CreateUTXOSnapshot(node, node.chainman->ActiveChainstate(), afile); + UniValue result = CreateUTXOSnapshot( + node, node.chainman->ActiveChainstate(), afile, path, temppath); fs::rename(temppath, path); result.pushKV("path", path.u8string()); @@ -2587,10 +2665,15 @@ static RPCHelpMan dumptxoutset() }; } -UniValue CreateUTXOSnapshot(NodeContext& node, CChainState& chainstate, CAutoFile& afile) +UniValue CreateUTXOSnapshot( + NodeContext& node, + CChainState& chainstate, + CAutoFile& afile, + const fs::path& path, + const fs::path& temppath) { std::unique_ptr<CCoinsViewCursor> pcursor; - CCoinsStats stats{CoinStatsHashType::NONE}; + CCoinsStats stats{CoinStatsHashType::HASH_SERIALIZED}; CBlockIndex* tip; { @@ -2619,6 +2702,10 @@ UniValue CreateUTXOSnapshot(NodeContext& node, CChainState& chainstate, CAutoFil CHECK_NONFATAL(tip); } + LOG_TIME_SECONDS(strprintf("writing UTXO snapshot at height %s (%s) to file %s (via %s)", + tip->nHeight, tip->GetBlockHash().ToString(), + fs::PathToString(path), fs::PathToString(temppath))); + SnapshotMetadata metadata{tip->GetBlockHash(), stats.coins_count, tip->nChainTx}; afile << metadata; @@ -2644,7 +2731,11 @@ UniValue CreateUTXOSnapshot(NodeContext& node, CChainState& chainstate, CAutoFil result.pushKV("coins_written", stats.coins_count); result.pushKV("base_hash", tip->GetBlockHash().ToString()); result.pushKV("base_height", tip->nHeight); - + result.pushKV("path", path.u8string()); + result.pushKV("txoutset_hash", stats.hashSerialized.ToString()); + // Cast required because univalue doesn't have serialization specified for + // `unsigned int`, nChainTx's type. + result.pushKV("nchaintx", uint64_t{tip->nChainTx}); return result; } @@ -2660,6 +2751,7 @@ static const CRPCCommand commands[] = { "blockchain", &getbestblockhash, }, { "blockchain", &getblockcount, }, { "blockchain", &getblock, }, + { "blockchain", &getblockfrompeer, }, { "blockchain", &getblockhash, }, { "blockchain", &getblockheader, }, { "blockchain", &getchaintips, }, diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h index 65f6b1429e..1f51d7c1ad 100644 --- a/src/rpc/blockchain.h +++ b/src/rpc/blockchain.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2020 The Bitcoin Core developers +// Copyright (c) 2017-2021 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -7,6 +7,7 @@ #include <consensus/amount.h> #include <core_io.h> +#include <fs.h> #include <streams.h> #include <sync.h> @@ -18,12 +19,12 @@ extern RecursiveMutex cs_main; class CBlock; class CBlockIndex; -class CBlockPolicyEstimator; class CChainState; class CTxMemPool; -class ChainstateManager; class UniValue; +namespace node { struct NodeContext; +} // namespace node static constexpr int NUM_GETBLOCKSTATS_PERCENTILES = 5; @@ -53,18 +54,15 @@ UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex /** Used by getblockstats to get feerates at different percentiles by weight */ void CalculatePercentilesByWeight(CAmount result[NUM_GETBLOCKSTATS_PERCENTILES], std::vector<std::pair<CAmount, int64_t>>& scores, int64_t total_weight); -NodeContext& EnsureAnyNodeContext(const std::any& context); -CTxMemPool& EnsureMemPool(const NodeContext& node); -CTxMemPool& EnsureAnyMemPool(const std::any& context); -ChainstateManager& EnsureChainman(const NodeContext& node); -ChainstateManager& EnsureAnyChainman(const std::any& context); -CBlockPolicyEstimator& EnsureFeeEstimator(const NodeContext& node); -CBlockPolicyEstimator& EnsureAnyFeeEstimator(const std::any& context); - /** * Helper to create UTXO snapshots given a chainstate and a file handle. * @return a UniValue map containing metadata about the snapshot. */ -UniValue CreateUTXOSnapshot(NodeContext& node, CChainState& chainstate, CAutoFile& afile); +UniValue CreateUTXOSnapshot( + node::NodeContext& node, + CChainState& chainstate, + CAutoFile& afile, + const fs::path& path, + const fs::path& tmppath); #endif // BITCOIN_RPC_BLOCKCHAIN_H diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 93e49cb9a8..003ba8bb20 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2020 The Bitcoin Core developers +// Copyright (c) 2009-2021 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -46,16 +46,21 @@ static const CRPCConvertParam vRPCConvertParams[] = { "settxfee", 0, "amount" }, { "sethdseed", 0, "newkeypool" }, { "getreceivedbyaddress", 1, "minconf" }, + { "getreceivedbyaddress", 2, "include_immature_coinbase" }, { "getreceivedbylabel", 1, "minconf" }, + { "getreceivedbylabel", 2, "include_immature_coinbase" }, { "listreceivedbyaddress", 0, "minconf" }, { "listreceivedbyaddress", 1, "include_empty" }, { "listreceivedbyaddress", 2, "include_watchonly" }, + { "listreceivedbyaddress", 4, "include_immature_coinbase" }, { "listreceivedbylabel", 0, "minconf" }, { "listreceivedbylabel", 1, "include_empty" }, { "listreceivedbylabel", 2, "include_watchonly" }, + { "listreceivedbylabel", 3, "include_immature_coinbase" }, { "getbalance", 1, "minconf" }, { "getbalance", 2, "include_watchonly" }, { "getbalance", 3, "avoid_reuse" }, + { "getblockfrompeer", 1, "nodeid" }, { "getblockhash", 0, "height" }, { "waitforblockheight", 0, "height" }, { "waitforblockheight", 1, "timeout" }, @@ -115,6 +120,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "walletcreatefundedpsbt", 4, "bip32derivs" }, { "walletprocesspsbt", 1, "sign" }, { "walletprocesspsbt", 3, "bip32derivs" }, + { "walletprocesspsbt", 4, "finalize" }, { "createpsbt", 0, "inputs" }, { "createpsbt", 1, "outputs" }, { "createpsbt", 2, "locktime" }, diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 9e2b1ab07e..0554367672 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2020 The Bitcoin Core developers +// Copyright (c) 2009-2021 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -13,15 +13,15 @@ #include <deploymentinfo.h> #include <deploymentstatus.h> #include <key_io.h> -#include <miner.h> #include <net.h> #include <node/context.h> +#include <node/miner.h> #include <policy/fees.h> #include <pow.h> #include <rpc/blockchain.h> #include <rpc/mining.h> -#include <rpc/net.h> #include <rpc/server.h> +#include <rpc/server_util.h> #include <rpc/util.h> #include <script/descriptor.h> #include <script/script.h> @@ -41,6 +41,13 @@ #include <memory> #include <stdint.h> +using node::BlockAssembler; +using node::CBlockTemplate; +using node::IncrementExtraNonce; +using node::NodeContext; +using node::RegenerateCommitments; +using node::UpdateTime; + /** * Return average network hashes per second based on the last 'lookup' blocks, * or from the last difficulty change if 'lookup' is nonpositive. @@ -184,7 +191,7 @@ static bool getScriptFromDescriptor(const std::string& descriptor, CScript& scri FlatSigningProvider provider; std::vector<CScript> scripts; if (!desc->Expand(0, key_provider, scripts, provider)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Cannot derive script without private keys")); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot derive script without private keys"); } // Combo descriptors can have 2 or 4 scripts, so we can't just check scripts.size() == 1 @@ -412,8 +419,8 @@ static RPCHelpMan getmininginfo() RPCResult::Type::OBJ, "", "", { {RPCResult::Type::NUM, "blocks", "The current block"}, - {RPCResult::Type::NUM, "currentblockweight", /* optional */ true, "The block weight of the last assembled block (only present if a block was ever assembled)"}, - {RPCResult::Type::NUM, "currentblocktx", /* optional */ true, "The number of block transactions of the last assembled block (only present if a block was ever assembled)"}, + {RPCResult::Type::NUM, "currentblockweight", /*optional=*/true, "The block weight of the last assembled block (only present if a block was ever assembled)"}, + {RPCResult::Type::NUM, "currentblocktx", /*optional=*/true, "The number of block transactions of the last assembled block (only present if a block was ever assembled)"}, {RPCResult::Type::NUM, "difficulty", "The current difficulty"}, {RPCResult::Type::NUM, "networkhashps", "The network hashes per second"}, {RPCResult::Type::NUM, "pooledtx", "The size of the mempool"}, @@ -590,12 +597,12 @@ static RPCHelpMan getblocktemplate() {RPCResult::Type::STR_HEX, "noncerange", "A range of valid nonces"}, {RPCResult::Type::NUM, "sigoplimit", "limit of sigops in blocks"}, {RPCResult::Type::NUM, "sizelimit", "limit of block size"}, - {RPCResult::Type::NUM, "weightlimit", /* optional */ true, "limit of block weight"}, + {RPCResult::Type::NUM, "weightlimit", /*optional=*/true, "limit of block weight"}, {RPCResult::Type::NUM_TIME, "curtime", "current timestamp in " + UNIX_EPOCH_TIME}, {RPCResult::Type::STR, "bits", "compressed target of next block"}, {RPCResult::Type::NUM, "height", "The height of the next block"}, - {RPCResult::Type::STR_HEX, "signet_challenge", /* optional */ true, "Only on signet"}, - {RPCResult::Type::STR_HEX, "default_witness_commitment", /* optional */ true, "a valid witness commitment for the unmodified block template"}, + {RPCResult::Type::STR_HEX, "signet_challenge", /*optional=*/true, "Only on signet"}, + {RPCResult::Type::STR_HEX, "default_witness_commitment", /*optional=*/true, "a valid witness commitment for the unmodified block template"}, }}, }, RPCExamples{ @@ -1082,8 +1089,8 @@ static RPCHelpMan estimatesmartfee() RPCResult{ RPCResult::Type::OBJ, "", "", { - {RPCResult::Type::NUM, "feerate", /* optional */ true, "estimate fee rate in " + CURRENCY_UNIT + "/kvB (only present if no errors were encountered)"}, - {RPCResult::Type::ARR, "errors", /* optional */ true, "Errors encountered during processing (if there are any)", + {RPCResult::Type::NUM, "feerate", /*optional=*/true, "estimate fee rate in " + CURRENCY_UNIT + "/kvB (only present if no errors were encountered)"}, + {RPCResult::Type::ARR, "errors", /*optional=*/true, "Errors encountered during processing (if there are any)", { {RPCResult::Type::STR, "", "error"}, }}, @@ -1121,10 +1128,10 @@ static RPCHelpMan estimatesmartfee() UniValue errors(UniValue::VARR); FeeCalculation feeCalc; CFeeRate feeRate{fee_estimator.estimateSmartFee(conf_target, &feeCalc, conservative)}; - CFeeRate min_mempool_feerate{mempool.GetMinFee(gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000)}; - CFeeRate min_relay_feerate{::minRelayTxFee}; - feeRate = std::max({feeRate, min_mempool_feerate, min_relay_feerate}); if (feeRate != CFeeRate(0)) { + CFeeRate min_mempool_feerate{mempool.GetMinFee(gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000)}; + CFeeRate min_relay_feerate{::minRelayTxFee}; + feeRate = std::max({feeRate, min_mempool_feerate, min_relay_feerate}); result.pushKV("feerate", ValueFromAmount(feeRate.GetFeePerK())); } else { errors.push_back("Insufficient data or no feerate found"); @@ -1155,12 +1162,12 @@ static RPCHelpMan estimaterawfee() RPCResult{ RPCResult::Type::OBJ, "", "Results are returned for any horizon which tracks blocks up to the confirmation target", { - {RPCResult::Type::OBJ, "short", /* optional */ true, "estimate for short time horizon", + {RPCResult::Type::OBJ, "short", /*optional=*/true, "estimate for short time horizon", { - {RPCResult::Type::NUM, "feerate", /* optional */ true, "estimate fee rate in " + CURRENCY_UNIT + "/kvB"}, + {RPCResult::Type::NUM, "feerate", /*optional=*/true, "estimate fee rate in " + CURRENCY_UNIT + "/kvB"}, {RPCResult::Type::NUM, "decay", "exponential decay (per block) for historical moving average of confirmation data"}, {RPCResult::Type::NUM, "scale", "The resolution of confirmation targets at this time horizon"}, - {RPCResult::Type::OBJ, "pass", /* optional */ true, "information about the lowest range of feerates to succeed in meeting the threshold", + {RPCResult::Type::OBJ, "pass", /*optional=*/true, "information about the lowest range of feerates to succeed in meeting the threshold", { {RPCResult::Type::NUM, "startrange", "start of feerate range"}, {RPCResult::Type::NUM, "endrange", "end of feerate range"}, @@ -1169,20 +1176,20 @@ static RPCHelpMan estimaterawfee() {RPCResult::Type::NUM, "inmempool", "current number of txs in mempool in the feerate range unconfirmed for at least target blocks"}, {RPCResult::Type::NUM, "leftmempool", "number of txs over history horizon in the feerate range that left mempool unconfirmed after target"}, }}, - {RPCResult::Type::OBJ, "fail", /* optional */ true, "information about the highest range of feerates to fail to meet the threshold", + {RPCResult::Type::OBJ, "fail", /*optional=*/true, "information about the highest range of feerates to fail to meet the threshold", { {RPCResult::Type::ELISION, "", ""}, }}, - {RPCResult::Type::ARR, "errors", /* optional */ true, "Errors encountered during processing (if there are any)", + {RPCResult::Type::ARR, "errors", /*optional=*/true, "Errors encountered during processing (if there are any)", { {RPCResult::Type::STR, "error", ""}, }}, }}, - {RPCResult::Type::OBJ, "medium", /* optional */ true, "estimate for medium time horizon", + {RPCResult::Type::OBJ, "medium", /*optional=*/true, "estimate for medium time horizon", { {RPCResult::Type::ELISION, "", ""}, }}, - {RPCResult::Type::OBJ, "long", /* optional */ true, "estimate for long time horizon", + {RPCResult::Type::OBJ, "long", /*optional=*/true, "estimate for long time horizon", { {RPCResult::Type::ELISION, "", ""}, }}, diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 39bd9c6091..8d574e0359 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2020 The Bitcoin Core developers +// Copyright (c) 2009-2021 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -16,6 +16,7 @@ #include <outputtype.h> #include <rpc/blockchain.h> #include <rpc/server.h> +#include <rpc/server_util.h> #include <rpc/util.h> #include <scheduler.h> #include <script/descriptor.h> @@ -34,34 +35,42 @@ #include <univalue.h> +using node::NodeContext; + static RPCHelpMan validateaddress() { - return RPCHelpMan{"validateaddress", - "\nReturn information about the given bitcoin address.\n", - { - {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to validate"}, - }, - RPCResult{ - RPCResult::Type::OBJ, "", "", + return RPCHelpMan{ + "validateaddress", + "\nReturn information about the given bitcoin address.\n", + { + {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to validate"}, + }, + RPCResult{ + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::BOOL, "isvalid", "If the address is valid or not"}, + {RPCResult::Type::STR, "address", /*optional=*/true, "The bitcoin address validated"}, + {RPCResult::Type::STR_HEX, "scriptPubKey", /*optional=*/true, "The hex-encoded scriptPubKey generated by the address"}, + {RPCResult::Type::BOOL, "isscript", /*optional=*/true, "If the key is a script"}, + {RPCResult::Type::BOOL, "iswitness", /*optional=*/true, "If the address is a witness address"}, + {RPCResult::Type::NUM, "witness_version", /*optional=*/true, "The version number of the witness program"}, + {RPCResult::Type::STR_HEX, "witness_program", /*optional=*/true, "The hex value of the witness program"}, + {RPCResult::Type::STR, "error", /*optional=*/true, "Error message, if any"}, + {RPCResult::Type::ARR, "error_locations", /*optional=*/true, "Indices of likely error locations in address, if known (e.g. Bech32 errors)", { - {RPCResult::Type::BOOL, "isvalid", "If the address is valid or not"}, - {RPCResult::Type::STR, "address", /* optional */ true, "The bitcoin address validated"}, - {RPCResult::Type::STR_HEX, "scriptPubKey", /* optional */ true, "The hex-encoded scriptPubKey generated by the address"}, - {RPCResult::Type::BOOL, "isscript", /* optional */ true, "If the key is a script"}, - {RPCResult::Type::BOOL, "iswitness", /* optional */ true, "If the address is a witness address"}, - {RPCResult::Type::NUM, "witness_version", /* optional */ true, "The version number of the witness program"}, - {RPCResult::Type::STR_HEX, "witness_program", /* optional */ true, "The hex value of the witness program"}, - {RPCResult::Type::STR, "error", /* optional */ true, "Error message, if any"}, - } - }, - RPCExamples{ - HelpExampleCli("validateaddress", "\"" + EXAMPLE_ADDRESS[0] + "\"") + - HelpExampleRpc("validateaddress", "\"" + EXAMPLE_ADDRESS[0] + "\"") - }, + {RPCResult::Type::NUM, "index", "index of a potential error"}, + }}, + } + }, + RPCExamples{ + HelpExampleCli("validateaddress", "\"" + EXAMPLE_ADDRESS[0] + "\"") + + HelpExampleRpc("validateaddress", "\"" + EXAMPLE_ADDRESS[0] + "\"") + }, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { std::string error_msg; - CTxDestination dest = DecodeDestination(request.params[0].get_str(), error_msg); + std::vector<int> error_locations; + CTxDestination dest = DecodeDestination(request.params[0].get_str(), error_msg, &error_locations); const bool isValid = IsValidDestination(dest); CHECK_NONFATAL(isValid == error_msg.empty()); @@ -77,6 +86,9 @@ static RPCHelpMan validateaddress() UniValue detail = DescribeAddress(dest); ret.pushKVs(detail); } else { + UniValue error_indices(UniValue::VARR); + for (int i : error_locations) error_indices.push_back(i); + ret.pushKV("error_locations", error_indices); ret.pushKV("error", error_msg); } @@ -104,6 +116,10 @@ static RPCHelpMan createmultisig() {RPCResult::Type::STR, "address", "The value of the new multisig address."}, {RPCResult::Type::STR_HEX, "redeemScript", "The string value of the hex-encoded redemption script."}, {RPCResult::Type::STR, "descriptor", "The descriptor for this multisig"}, + {RPCResult::Type::ARR, "warnings", /* optional */ true, "Any warnings resulting from the creation of this multisig", + { + {RPCResult::Type::STR, "", ""}, + }}, } }, RPCExamples{ @@ -152,6 +168,13 @@ static RPCHelpMan createmultisig() result.pushKV("redeemScript", HexStr(inner)); result.pushKV("descriptor", descriptor->ToString()); + UniValue warnings(UniValue::VARR); + if (!request.params[2].isNull() && OutputTypeFromDestination(dest) != output_type) { + // Only warns if the user has explicitly chosen an address type we cannot generate + warnings.push_back("Unable to make chosen address type, please ensure no uncompressed public keys are present."); + } + if (warnings.size()) result.pushKV("warnings", warnings); + return result; }, }; @@ -265,13 +288,13 @@ static RPCHelpMan deriveaddresses() FlatSigningProvider provider; std::vector<CScript> scripts; if (!desc->Expand(i, key_provider, scripts, provider)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Cannot derive script without private keys")); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot derive script without private keys"); } for (const CScript &script : scripts) { CTxDestination dest; if (!ExtractDestination(script, dest)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Descriptor does not have a corresponding address")); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Descriptor does not have a corresponding address"); } addresses.push_back(EncodeDestination(dest)); @@ -291,7 +314,7 @@ static RPCHelpMan deriveaddresses() static RPCHelpMan verifymessage() { return RPCHelpMan{"verifymessage", - "\nVerify a signed message\n", + "Verify a signed message.", { {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to use for the signature."}, {"signature", RPCArg::Type::STR, RPCArg::Optional::NO, "The signature provided by the signer in base 64 encoding (see signmessage)."}, @@ -550,7 +573,7 @@ static RPCHelpMan getmemoryinfo() #ifdef HAVE_MALLOC_INFO return RPCMallocInfo(); #else - throw JSONRPCError(RPC_INVALID_PARAMETER, "mallocinfo is only available when compiled with glibc 2.10+"); + throw JSONRPCError(RPC_INVALID_PARAMETER, "mallocinfo mode not available"); #endif } else { throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown mode " + mode); diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index e33f1ce4a3..3d7c00edfc 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -1,14 +1,14 @@ -// Copyright (c) 2009-2020 The Bitcoin Core developers +// Copyright (c) 2009-2021 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <rpc/server.h> +#include <addrman.h> #include <banman.h> #include <chainparams.h> #include <clientversion.h> #include <core_io.h> -#include <net.h> #include <net_permissions.h> #include <net_processing.h> #include <net_types.h> // For banmap_t @@ -17,12 +17,12 @@ #include <policy/settings.h> #include <rpc/blockchain.h> #include <rpc/protocol.h> +#include <rpc/server_util.h> #include <rpc/util.h> #include <sync.h> #include <timedata.h> #include <util/strencodings.h> #include <util/string.h> -#include <util/system.h> #include <util/translation.h> #include <validation.h> #include <version.h> @@ -32,6 +32,8 @@ #include <univalue.h> +using node::NodeContext; + const std::vector<std::string> CONNECTION_TYPE_DOC{ "outbound-full-relay (default automatic connections)", "block-relay-only (does not relay transactions or addresses)", @@ -41,22 +43,6 @@ const std::vector<std::string> CONNECTION_TYPE_DOC{ "feeler (short-lived automatic connection for testing addresses)" }; -CConnman& EnsureConnman(const NodeContext& node) -{ - if (!node.connman) { - throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); - } - return *node.connman; -} - -PeerManager& EnsurePeerman(const NodeContext& node) -{ - if (!node.peerman) { - throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); - } - return *node.peerman; -} - static RPCHelpMan getconnectioncount() { return RPCHelpMan{"getconnectioncount", @@ -105,82 +91,83 @@ static RPCHelpMan ping() static RPCHelpMan getpeerinfo() { - return RPCHelpMan{"getpeerinfo", - "\nReturns data about each connected network node as a json array of objects.\n", - {}, - RPCResult{ - RPCResult::Type::ARR, "", "", + return RPCHelpMan{ + "getpeerinfo", + "\nReturns data about each connected network node as a json array of objects.\n", + {}, + RPCResult{ + RPCResult::Type::ARR, "", "", + { + {RPCResult::Type::OBJ, "", "", + { { - {RPCResult::Type::OBJ, "", "", - { - { - {RPCResult::Type::NUM, "id", "Peer index"}, - {RPCResult::Type::STR, "addr", "(host:port) The IP address and port of the peer"}, - {RPCResult::Type::STR, "addrbind", /* optional */ true, "(ip:port) Bind address of the connection to the peer"}, - {RPCResult::Type::STR, "addrlocal", /* optional */ true, "(ip:port) Local address as reported by the peer"}, - {RPCResult::Type::STR, "network", "Network (" + Join(GetNetworkNames(/* append_unroutable */ true), ", ") + ")"}, - {RPCResult::Type::NUM, "mapped_as", /* optional */ true, "The AS in the BGP route to the peer used for diversifying\n" - "peer selection (only available if the asmap config flag is set)"}, - {RPCResult::Type::STR_HEX, "services", "The services offered"}, - {RPCResult::Type::ARR, "servicesnames", "the services offered, in human-readable form", - { - {RPCResult::Type::STR, "SERVICE_NAME", "the service name if it is recognised"} - }}, - {RPCResult::Type::BOOL, "relaytxes", "Whether peer has asked us to relay transactions to it"}, - {RPCResult::Type::NUM_TIME, "lastsend", "The " + UNIX_EPOCH_TIME + " of the last send"}, - {RPCResult::Type::NUM_TIME, "lastrecv", "The " + UNIX_EPOCH_TIME + " of the last receive"}, - {RPCResult::Type::NUM_TIME, "last_transaction", "The " + UNIX_EPOCH_TIME + " of the last valid transaction received from this peer"}, - {RPCResult::Type::NUM_TIME, "last_block", "The " + UNIX_EPOCH_TIME + " of the last block received from this peer"}, - {RPCResult::Type::NUM, "bytessent", "The total bytes sent"}, - {RPCResult::Type::NUM, "bytesrecv", "The total bytes received"}, - {RPCResult::Type::NUM_TIME, "conntime", "The " + UNIX_EPOCH_TIME + " of the connection"}, - {RPCResult::Type::NUM, "timeoffset", "The time offset in seconds"}, - {RPCResult::Type::NUM, "pingtime", /* optional */ true, "ping time (if available)"}, - {RPCResult::Type::NUM, "minping", /* optional */ true, "minimum observed ping time (if any at all)"}, - {RPCResult::Type::NUM, "pingwait", /* optional */ true, "ping wait (if non-zero)"}, - {RPCResult::Type::NUM, "version", "The peer version, such as 70001"}, - {RPCResult::Type::STR, "subver", "The string version"}, - {RPCResult::Type::BOOL, "inbound", "Inbound (true) or Outbound (false)"}, - {RPCResult::Type::BOOL, "bip152_hb_to", "Whether we selected peer as (compact blocks) high-bandwidth peer"}, - {RPCResult::Type::BOOL, "bip152_hb_from", "Whether peer selected us as (compact blocks) high-bandwidth peer"}, - {RPCResult::Type::NUM, "startingheight", "The starting height (block) of the peer"}, - {RPCResult::Type::NUM, "synced_headers", "The last header we have in common with this peer"}, - {RPCResult::Type::NUM, "synced_blocks", "The last block we have in common with this peer"}, - {RPCResult::Type::ARR, "inflight", "", - { - {RPCResult::Type::NUM, "n", "The heights of blocks we're currently asking from this peer"}, - }}, - {RPCResult::Type::BOOL, "addr_relay_enabled", "Whether we participate in address relay with this peer"}, - {RPCResult::Type::NUM, "addr_processed", "The total number of addresses processed, excluding those dropped due to rate limiting"}, - {RPCResult::Type::NUM, "addr_rate_limited", "The total number of addresses dropped due to rate limiting"}, - {RPCResult::Type::ARR, "permissions", "Any special permissions that have been granted to this peer", - { - {RPCResult::Type::STR, "permission_type", Join(NET_PERMISSIONS_DOC, ",\n") + ".\n"}, - }}, - {RPCResult::Type::NUM, "minfeefilter", "The minimum fee rate for transactions this peer accepts"}, - {RPCResult::Type::OBJ_DYN, "bytessent_per_msg", "", - { - {RPCResult::Type::NUM, "msg", "The total bytes sent aggregated by message type\n" - "When a message type is not listed in this json object, the bytes sent are 0.\n" - "Only known message types can appear as keys in the object."} - }}, - {RPCResult::Type::OBJ_DYN, "bytesrecv_per_msg", "", - { - {RPCResult::Type::NUM, "msg", "The total bytes received aggregated by message type\n" - "When a message type is not listed in this json object, the bytes received are 0.\n" - "Only known message types can appear as keys in the object and all bytes received\n" - "of unknown message types are listed under '"+NET_MESSAGE_COMMAND_OTHER+"'."} - }}, - {RPCResult::Type::STR, "connection_type", "Type of connection: \n" + Join(CONNECTION_TYPE_DOC, ",\n") + ".\n" - "Please note this output is unlikely to be stable in upcoming releases as we iterate to\n" - "best capture connection behaviors."}, - }}, + {RPCResult::Type::NUM, "id", "Peer index"}, + {RPCResult::Type::STR, "addr", "(host:port) The IP address and port of the peer"}, + {RPCResult::Type::STR, "addrbind", /*optional=*/true, "(ip:port) Bind address of the connection to the peer"}, + {RPCResult::Type::STR, "addrlocal", /*optional=*/true, "(ip:port) Local address as reported by the peer"}, + {RPCResult::Type::STR, "network", "Network (" + Join(GetNetworkNames(/* append_unroutable */ true), ", ") + ")"}, + {RPCResult::Type::NUM, "mapped_as", /*optional=*/true, "The AS in the BGP route to the peer used for diversifying\n" + "peer selection (only available if the asmap config flag is set)"}, + {RPCResult::Type::STR_HEX, "services", "The services offered"}, + {RPCResult::Type::ARR, "servicesnames", "the services offered, in human-readable form", + { + {RPCResult::Type::STR, "SERVICE_NAME", "the service name if it is recognised"} }}, - }, - RPCExamples{ - HelpExampleCli("getpeerinfo", "") + {RPCResult::Type::BOOL, "relaytxes", "Whether peer has asked us to relay transactions to it"}, + {RPCResult::Type::NUM_TIME, "lastsend", "The " + UNIX_EPOCH_TIME + " of the last send"}, + {RPCResult::Type::NUM_TIME, "lastrecv", "The " + UNIX_EPOCH_TIME + " of the last receive"}, + {RPCResult::Type::NUM_TIME, "last_transaction", "The " + UNIX_EPOCH_TIME + " of the last valid transaction received from this peer"}, + {RPCResult::Type::NUM_TIME, "last_block", "The " + UNIX_EPOCH_TIME + " of the last block received from this peer"}, + {RPCResult::Type::NUM, "bytessent", "The total bytes sent"}, + {RPCResult::Type::NUM, "bytesrecv", "The total bytes received"}, + {RPCResult::Type::NUM_TIME, "conntime", "The " + UNIX_EPOCH_TIME + " of the connection"}, + {RPCResult::Type::NUM, "timeoffset", "The time offset in seconds"}, + {RPCResult::Type::NUM, "pingtime", /*optional=*/true, "ping time (if available)"}, + {RPCResult::Type::NUM, "minping", /*optional=*/true, "minimum observed ping time (if any at all)"}, + {RPCResult::Type::NUM, "pingwait", /*optional=*/true, "ping wait (if non-zero)"}, + {RPCResult::Type::NUM, "version", "The peer version, such as 70001"}, + {RPCResult::Type::STR, "subver", "The string version"}, + {RPCResult::Type::BOOL, "inbound", "Inbound (true) or Outbound (false)"}, + {RPCResult::Type::BOOL, "bip152_hb_to", "Whether we selected peer as (compact blocks) high-bandwidth peer"}, + {RPCResult::Type::BOOL, "bip152_hb_from", "Whether peer selected us as (compact blocks) high-bandwidth peer"}, + {RPCResult::Type::NUM, "startingheight", /*optional=*/true, "The starting height (block) of the peer"}, + {RPCResult::Type::NUM, "synced_headers", /*optional=*/true, "The last header we have in common with this peer"}, + {RPCResult::Type::NUM, "synced_blocks", /*optional=*/true, "The last block we have in common with this peer"}, + {RPCResult::Type::ARR, "inflight", /*optional=*/true, "", + { + {RPCResult::Type::NUM, "n", "The heights of blocks we're currently asking from this peer"}, + }}, + {RPCResult::Type::BOOL, "addr_relay_enabled", /*optional=*/true, "Whether we participate in address relay with this peer"}, + {RPCResult::Type::NUM, "addr_processed", /*optional=*/true, "The total number of addresses processed, excluding those dropped due to rate limiting"}, + {RPCResult::Type::NUM, "addr_rate_limited", /*optional=*/true, "The total number of addresses dropped due to rate limiting"}, + {RPCResult::Type::ARR, "permissions", "Any special permissions that have been granted to this peer", + { + {RPCResult::Type::STR, "permission_type", Join(NET_PERMISSIONS_DOC, ",\n") + ".\n"}, + }}, + {RPCResult::Type::NUM, "minfeefilter", "The minimum fee rate for transactions this peer accepts"}, + {RPCResult::Type::OBJ_DYN, "bytessent_per_msg", "", + { + {RPCResult::Type::NUM, "msg", "The total bytes sent aggregated by message type\n" + "When a message type is not listed in this json object, the bytes sent are 0.\n" + "Only known message types can appear as keys in the object."} + }}, + {RPCResult::Type::OBJ_DYN, "bytesrecv_per_msg", "", + { + {RPCResult::Type::NUM, "msg", "The total bytes received aggregated by message type\n" + "When a message type is not listed in this json object, the bytes received are 0.\n" + "Only known message types can appear as keys in the object and all bytes received\n" + "of unknown message types are listed under '"+NET_MESSAGE_COMMAND_OTHER+"'."} + }}, + {RPCResult::Type::STR, "connection_type", "Type of connection: \n" + Join(CONNECTION_TYPE_DOC, ",\n") + ".\n" + "Please note this output is unlikely to be stable in upcoming releases as we iterate to\n" + "best capture connection behaviors."}, + }}, + }}, + }, + RPCExamples{ + HelpExampleCli("getpeerinfo", "") + HelpExampleRpc("getpeerinfo", "") - }, + }, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { NodeContext& node = EnsureAnyNodeContext(request.context); @@ -211,13 +198,13 @@ static RPCHelpMan getpeerinfo() obj.pushKV("services", strprintf("%016x", stats.nServices)); obj.pushKV("servicesnames", GetServicesNames(stats.nServices)); obj.pushKV("relaytxes", stats.fRelayTxes); - obj.pushKV("lastsend", stats.nLastSend); - obj.pushKV("lastrecv", stats.nLastRecv); - obj.pushKV("last_transaction", stats.nLastTXTime); - obj.pushKV("last_block", stats.nLastBlockTime); + obj.pushKV("lastsend", count_seconds(stats.m_last_send)); + obj.pushKV("lastrecv", count_seconds(stats.m_last_recv)); + obj.pushKV("last_transaction", count_seconds(stats.m_last_tx_time)); + obj.pushKV("last_block", count_seconds(stats.m_last_block_time)); obj.pushKV("bytessent", stats.nSendBytes); obj.pushKV("bytesrecv", stats.nRecvBytes); - obj.pushKV("conntime", stats.nTimeConnected); + obj.pushKV("conntime", count_seconds(stats.m_connected)); obj.pushKV("timeoffset", stats.nTimeOffset); if (stats.m_last_ping_time > 0us) { obj.pushKV("pingtime", CountSecondsDouble(stats.m_last_ping_time)); @@ -343,7 +330,7 @@ static RPCHelpMan addconnection() "\nOpen an outbound connection to a specified node. This RPC is for testing only.\n", { {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The IP address and port to attempt connecting to."}, - {"connection_type", RPCArg::Type::STR, RPCArg::Optional::NO, "Type of connection to open (\"outbound-full-relay\", \"block-relay-only\" or \"addr-fetch\")."}, + {"connection_type", RPCArg::Type::STR, RPCArg::Optional::NO, "Type of connection to open (\"outbound-full-relay\", \"block-relay-only\", \"addr-fetch\" or \"feeler\")."}, }, RPCResult{ RPCResult::Type::OBJ, "", "", @@ -371,6 +358,8 @@ static RPCHelpMan addconnection() conn_type = ConnectionType::BLOCK_RELAY; } else if (conn_type_in == "addr-fetch") { conn_type = ConnectionType::ADDR_FETCH; + } else if (conn_type_in == "feeler") { + conn_type = ConnectionType::FEELER; } else { throw JSONRPCError(RPC_INVALID_PARAMETER, self.ToString()); } @@ -659,7 +648,7 @@ static RPCHelpMan getnetworkinfo() obj.pushKV("incrementalfee", ValueFromAmount(::incrementalRelayFee.GetFeePerK())); UniValue localAddresses(UniValue::VARR); { - LOCK(cs_mapLocalHost); + LOCK(g_maplocalhost_mutex); for (const std::pair<const CNetAddr, LocalServiceInfo> &item : mapLocalHost) { UniValue rec(UniValue::VOBJ); diff --git a/src/rpc/net.h b/src/rpc/net.h deleted file mode 100644 index 7a4d8440ba..0000000000 --- a/src/rpc/net.h +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2021 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef BITCOIN_RPC_NET_H -#define BITCOIN_RPC_NET_H - -class CConnman; -class PeerManager; -struct NodeContext; - -CConnman& EnsureConnman(const NodeContext& node); -PeerManager& EnsurePeerman(const NodeContext& node); - -#endif // BITCOIN_RPC_NET_H diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h index fc00a1efad..75e42e4c88 100644 --- a/src/rpc/protocol.h +++ b/src/rpc/protocol.h @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2019 The Bitcoin Core developers +// Copyright (c) 2009-2021 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -80,6 +80,7 @@ enum RPCErrorCode RPC_WALLET_NOT_FOUND = -18, //!< Invalid wallet specified RPC_WALLET_NOT_SPECIFIED = -19, //!< No wallet specified (error when there are multiple wallets loaded) RPC_WALLET_ALREADY_LOADED = -35, //!< This same wallet is already loaded + RPC_WALLET_ALREADY_EXISTS = -36, //!< There is already a wallet with the same name //! Backwards compatible aliases RPC_WALLET_INVALID_ACCOUNT_NAME = RPC_WALLET_INVALID_LABEL_NAME, diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 2dd121c6f6..f227fde0f7 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -1,8 +1,9 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2020 The Bitcoin Core developers +// Copyright (c) 2009-2021 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include <base58.h> #include <chain.h> #include <coins.h> #include <consensus/amount.h> @@ -25,6 +26,7 @@ #include <rpc/blockchain.h> #include <rpc/rawtransaction_util.h> #include <rpc/server.h> +#include <rpc/server_util.h> #include <rpc/util.h> #include <script/script.h> #include <script/sign.h> @@ -43,6 +45,15 @@ #include <univalue.h> +using node::AnalyzePSBT; +using node::BroadcastTransaction; +using node::DEFAULT_MAX_RAW_TX_FEE_RATE; +using node::FindCoins; +using node::GetTransaction; +using node::NodeContext; +using node::PSBTAnalysis; +using node::ReadBlockFromDisk; + static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry, CChainState& active_chainstate) { // Call into TxToUniv() in bitcoin-common to decode the transaction hex. @@ -69,6 +80,43 @@ static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& } } +static std::vector<RPCArg> CreateTxDoc() +{ + return { + {"inputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The inputs", + { + {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "", + { + {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"}, + {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"}, + {"sequence", RPCArg::Type::NUM, RPCArg::DefaultHint{"depends on the value of the 'replaceable' and 'locktime' arguments"}, "The sequence number"}, + }, + }, + }, + }, + {"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The outputs (key-value pairs), where none of the keys are duplicated.\n" + "That is, each address can only appear once and there can only be one 'data' object.\n" + "For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n" + " accepted as second parameter.", + { + {"", RPCArg::Type::OBJ_USER_KEYS, RPCArg::Optional::OMITTED, "", + { + {"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT}, + }, + }, + {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "", + { + {"data", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A key-value pair. The key must be \"data\", the value is hex-encoded data"}, + }, + }, + }, + }, + {"locktime", RPCArg::Type::NUM, RPCArg::Default{0}, "Raw locktime. Non-0 value also locktime-activates inputs"}, + {"replaceable", RPCArg::Type::BOOL, RPCArg::Default{false}, "Marks this transaction as BIP125-replaceable.\n" + "Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible."}, + }; +} + static RPCHelpMan getrawtransaction() { return RPCHelpMan{ @@ -95,7 +143,7 @@ static RPCHelpMan getrawtransaction() RPCResult{"if verbose is set to true", RPCResult::Type::OBJ, "", "", { - {RPCResult::Type::BOOL, "in_active_chain", /* optional */ true, "Whether specified block is in the active chain or not (only present with explicit \"blockhash\" argument)"}, + {RPCResult::Type::BOOL, "in_active_chain", /*optional=*/true, "Whether specified block is in the active chain or not (only present with explicit \"blockhash\" argument)"}, {RPCResult::Type::STR_HEX, "hex", "The serialized, hex-encoded data for 'txid'"}, {RPCResult::Type::STR_HEX, "txid", "The transaction id (same as provided)"}, {RPCResult::Type::STR_HEX, "hash", "The transaction hash (differs from txid for witness transactions)"}, @@ -116,7 +164,7 @@ static RPCHelpMan getrawtransaction() {RPCResult::Type::STR_HEX, "hex", "hex"}, }}, {RPCResult::Type::NUM, "sequence", "The script sequence number"}, - {RPCResult::Type::ARR, "txinwitness", /* optional */ true, "", + {RPCResult::Type::ARR, "txinwitness", /*optional=*/true, "", { {RPCResult::Type::STR_HEX, "hex", "hex-encoded witness data (if any)"}, }}, @@ -133,14 +181,14 @@ static RPCHelpMan getrawtransaction() {RPCResult::Type::STR, "asm", "the asm"}, {RPCResult::Type::STR, "hex", "the hex"}, {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"}, - {RPCResult::Type::STR, "address", /* optional */ true, "The Bitcoin address (only if a well-defined address exists)"}, + {RPCResult::Type::STR, "address", /*optional=*/true, "The Bitcoin address (only if a well-defined address exists)"}, }}, }}, }}, - {RPCResult::Type::STR_HEX, "blockhash", /* optional */ true, "the block hash"}, - {RPCResult::Type::NUM, "confirmations", /* optional */ true, "The confirmations"}, - {RPCResult::Type::NUM_TIME, "blocktime", /* optional */ true, "The block time expressed in " + UNIX_EPOCH_TIME}, - {RPCResult::Type::NUM, "time", /* optional */ true, "Same as \"blocktime\""}, + {RPCResult::Type::STR_HEX, "blockhash", /*optional=*/true, "the block hash"}, + {RPCResult::Type::NUM, "confirmations", /*optional=*/true, "The confirmations"}, + {RPCResult::Type::NUM_TIME, "blocktime", /*optional=*/true, "The block time expressed in " + UNIX_EPOCH_TIME}, + {RPCResult::Type::NUM, "time", /*optional=*/true, "Same as \"blocktime\""}, } }, }, @@ -375,39 +423,7 @@ static RPCHelpMan createrawtransaction() "Returns hex-encoded raw transaction.\n" "Note that the transaction's inputs are not signed, and\n" "it is not stored in the wallet or transmitted to the network.\n", - { - {"inputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The inputs", - { - {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "", - { - {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"}, - {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"}, - {"sequence", RPCArg::Type::NUM, RPCArg::DefaultHint{"depends on the value of the 'replaceable' and 'locktime' arguments"}, "The sequence number"}, - }, - }, - }, - }, - {"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The outputs (key-value pairs), where none of the keys are duplicated.\n" - "That is, each address can only appear once and there can only be one 'data' object.\n" - "For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n" - " accepted as second parameter.", - { - {"", RPCArg::Type::OBJ_USER_KEYS, RPCArg::Optional::OMITTED, "", - { - {"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT}, - }, - }, - {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "", - { - {"data", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A key-value pair. The key must be \"data\", the value is hex-encoded data"}, - }, - }, - }, - }, - {"locktime", RPCArg::Type::NUM, RPCArg::Default{0}, "Raw locktime. Non-0 value also locktime-activates inputs"}, - {"replaceable", RPCArg::Type::BOOL, RPCArg::Default{false}, "Marks this transaction as BIP125-replaceable.\n" - " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible."}, - }, + CreateTxDoc(), RPCResult{ RPCResult::Type::STR_HEX, "transaction", "hex string of the transaction" }, @@ -466,15 +482,15 @@ static RPCHelpMan decoderawtransaction() { {RPCResult::Type::OBJ, "", "", { - {RPCResult::Type::STR_HEX, "coinbase", /* optional */ true, ""}, - {RPCResult::Type::STR_HEX, "txid", /* optional */ true, "The transaction id"}, - {RPCResult::Type::NUM, "vout", /* optional */ true, "The output number"}, - {RPCResult::Type::OBJ, "scriptSig", /* optional */ true, "The script", + {RPCResult::Type::STR_HEX, "coinbase", /*optional=*/true, ""}, + {RPCResult::Type::STR_HEX, "txid", /*optional=*/true, "The transaction id"}, + {RPCResult::Type::NUM, "vout", /*optional=*/true, "The output number"}, + {RPCResult::Type::OBJ, "scriptSig", /*optional=*/true, "The script", { {RPCResult::Type::STR, "asm", "asm"}, {RPCResult::Type::STR_HEX, "hex", "hex"}, }}, - {RPCResult::Type::ARR, "txinwitness", /* optional */ true, "", + {RPCResult::Type::ARR, "txinwitness", /*optional=*/true, "", { {RPCResult::Type::STR_HEX, "hex", "hex-encoded witness data (if any)"}, }}, @@ -492,7 +508,7 @@ static RPCHelpMan decoderawtransaction() {RPCResult::Type::STR, "asm", "the asm"}, {RPCResult::Type::STR_HEX, "hex", "the hex"}, {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"}, - {RPCResult::Type::STR, "address", /* optional */ true, "The Bitcoin address (only if a well-defined address exists)"}, + {RPCResult::Type::STR, "address", /*optional=*/true, "The Bitcoin address (only if a well-defined address exists)"}, }}, }}, }}, @@ -535,32 +551,35 @@ static std::string GetAllOutputTypes() static RPCHelpMan decodescript() { - return RPCHelpMan{"decodescript", - "\nDecode a hex-encoded script.\n", - { - {"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hex-encoded script"}, - }, - RPCResult{ - RPCResult::Type::OBJ, "", "", - { - {RPCResult::Type::STR, "asm", "Script public key"}, - {RPCResult::Type::STR, "type", "The output type (e.g. "+GetAllOutputTypes()+")"}, - {RPCResult::Type::STR, "address", /* optional */ true, "The Bitcoin address (only if a well-defined address exists)"}, - {RPCResult::Type::STR, "p2sh", /* optional */ true, "address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH)"}, - {RPCResult::Type::OBJ, "segwit", /* optional */ true, "Result of a witness script public key wrapping this redeem script (not returned if the script is a P2SH or witness)", - { - {RPCResult::Type::STR, "asm", "String representation of the script public key"}, - {RPCResult::Type::STR_HEX, "hex", "Hex string of the script public key"}, - {RPCResult::Type::STR, "type", "The type of the script public key (e.g. witness_v0_keyhash or witness_v0_scripthash)"}, - {RPCResult::Type::STR, "address", /* optional */ true, "The Bitcoin address (only if a well-defined address exists)"}, - {RPCResult::Type::STR, "p2sh-segwit", "address of the P2SH script wrapping this witness redeem script"}, - }}, - } - }, - RPCExamples{ - HelpExampleCli("decodescript", "\"hexstring\"") - + HelpExampleRpc("decodescript", "\"hexstring\"") - }, + return RPCHelpMan{ + "decodescript", + "\nDecode a hex-encoded script.\n", + { + {"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hex-encoded script"}, + }, + RPCResult{ + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "asm", "Script public key"}, + {RPCResult::Type::STR, "type", "The output type (e.g. " + GetAllOutputTypes() + ")"}, + {RPCResult::Type::STR, "address", /*optional=*/true, "The Bitcoin address (only if a well-defined address exists)"}, + {RPCResult::Type::STR, "p2sh", /*optional=*/true, + "address of P2SH script wrapping this redeem script (not returned for types that should not be wrapped)"}, + {RPCResult::Type::OBJ, "segwit", /*optional=*/true, + "Result of a witness script public key wrapping this redeem script (not returned for types that should not be wrapped)", + { + {RPCResult::Type::STR, "asm", "String representation of the script public key"}, + {RPCResult::Type::STR_HEX, "hex", "Hex string of the script public key"}, + {RPCResult::Type::STR, "type", "The type of the script public key (e.g. witness_v0_keyhash or witness_v0_scripthash)"}, + {RPCResult::Type::STR, "address", /*optional=*/true, "The Bitcoin address (only if a well-defined address exists)"}, + {RPCResult::Type::STR, "p2sh-segwit", "address of the P2SH script wrapping this witness redeem script"}, + }}, + }, + }, + RPCExamples{ + HelpExampleCli("decodescript", "\"hexstring\"") + + HelpExampleRpc("decodescript", "\"hexstring\"") + }, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { RPCTypeCheck(request.params, {UniValue::VSTR}); @@ -575,27 +594,71 @@ static RPCHelpMan decodescript() } ScriptPubKeyToUniv(script, r, /* include_hex */ false); - UniValue type; - type = find_value(r, "type"); + std::vector<std::vector<unsigned char>> solutions_data; + const TxoutType which_type{Solver(script, solutions_data)}; + + const bool can_wrap{[&] { + switch (which_type) { + case TxoutType::MULTISIG: + case TxoutType::NONSTANDARD: + case TxoutType::PUBKEY: + case TxoutType::PUBKEYHASH: + case TxoutType::WITNESS_V0_KEYHASH: + case TxoutType::WITNESS_V0_SCRIPTHASH: + // Can be wrapped if the checks below pass + break; + case TxoutType::NULL_DATA: + case TxoutType::SCRIPTHASH: + case TxoutType::WITNESS_UNKNOWN: + case TxoutType::WITNESS_V1_TAPROOT: + // Should not be wrapped + return false; + } // no default case, so the compiler can warn about missing cases + if (!script.HasValidOps() || script.IsUnspendable()) { + return false; + } + for (CScript::const_iterator it{script.begin()}; it != script.end();) { + opcodetype op; + CHECK_NONFATAL(script.GetOp(it, op)); + if (op == OP_CHECKSIGADD || IsOpSuccess(op)) { + return false; + } + } + return true; + }()}; - if (type.isStr() && type.get_str() != "scripthash") { - // P2SH cannot be wrapped in a P2SH. If this script is already a P2SH, - // don't return the address for a P2SH of the P2SH. + if (can_wrap) { r.pushKV("p2sh", EncodeDestination(ScriptHash(script))); // P2SH and witness programs cannot be wrapped in P2WSH, if this script // is a witness program, don't return addresses for a segwit programs. - if (type.get_str() == "pubkey" || type.get_str() == "pubkeyhash" || type.get_str() == "multisig" || type.get_str() == "nonstandard") { - std::vector<std::vector<unsigned char>> solutions_data; - TxoutType which_type = Solver(script, solutions_data); + const bool can_wrap_P2WSH{[&] { + switch (which_type) { + case TxoutType::MULTISIG: + case TxoutType::PUBKEY: // Uncompressed pubkeys cannot be used with segwit checksigs. // If the script contains an uncompressed pubkey, skip encoding of a segwit program. - if ((which_type == TxoutType::PUBKEY) || (which_type == TxoutType::MULTISIG)) { for (const auto& solution : solutions_data) { if ((solution.size() != 1) && !CPubKey(solution).IsCompressed()) { - return r; + return false; } } - } + return true; + case TxoutType::NONSTANDARD: + case TxoutType::PUBKEYHASH: + // Can be P2WSH wrapped + return true; + case TxoutType::NULL_DATA: + case TxoutType::SCRIPTHASH: + case TxoutType::WITNESS_UNKNOWN: + case TxoutType::WITNESS_V0_KEYHASH: + case TxoutType::WITNESS_V0_SCRIPTHASH: + case TxoutType::WITNESS_V1_TAPROOT: + // Should not be wrapped + return false; + } // no default case, so the compiler can warn about missing cases + CHECK_NONFATAL(false); + }()}; + if (can_wrap_P2WSH) { UniValue sr(UniValue::VOBJ); CScript segwitScr; if (which_type == TxoutType::PUBKEY) { @@ -604,7 +667,6 @@ static RPCHelpMan decodescript() segwitScr = GetScriptForDestination(WitnessV0KeyHash(uint160{solutions_data[0]})); } else { // Scripts that are not fit for P2WPKH are encoded as P2WSH. - // Newer segwit program versions should be considered when then become available. segwitScr = GetScriptForDestination(WitnessV0ScriptHash(script)); } ScriptPubKeyToUniv(segwitScr, sr, /* include_hex */ true); @@ -733,7 +795,7 @@ static RPCHelpMan signrawtransactionwithkey() }, }, }, - {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"DEFAULT"}, "The signature hash type. Must be one of:\n" + {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"DEFAULT for Taproot, ALL otherwise"}, "The signature hash type. Must be one of:\n" " \"DEFAULT\"\n" " \"ALL\"\n" " \"NONE\"\n" @@ -748,7 +810,7 @@ static RPCHelpMan signrawtransactionwithkey() { {RPCResult::Type::STR_HEX, "hex", "The hex-encoded raw transaction with signature(s)"}, {RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"}, - {RPCResult::Type::ARR, "errors", /* optional */ true, "Script verification errors (if there are any)", + {RPCResult::Type::ARR, "errors", /*optional=*/true, "Script verification errors (if there are any)", { {RPCResult::Type::OBJ, "", "", { @@ -895,15 +957,15 @@ static RPCHelpMan testmempoolaccept() { {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"}, {RPCResult::Type::STR_HEX, "wtxid", "The transaction witness hash in hex"}, - {RPCResult::Type::STR, "package-error", /* optional */ true, "Package validation error, if any (only possible if rawtxs had more than 1 transaction)."}, - {RPCResult::Type::BOOL, "allowed", /* optional */ true, "Whether this tx would be accepted to the mempool and pass client-specified maxfeerate. " + {RPCResult::Type::STR, "package-error", /*optional=*/true, "Package validation error, if any (only possible if rawtxs had more than 1 transaction)."}, + {RPCResult::Type::BOOL, "allowed", /*optional=*/true, "Whether this tx would be accepted to the mempool and pass client-specified maxfeerate. " "If not present, the tx was not fully validated due to a failure in another tx in the list."}, - {RPCResult::Type::NUM, "vsize", /* optional */ true, "Virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted (only present when 'allowed' is true)"}, - {RPCResult::Type::OBJ, "fees", /* optional */ true, "Transaction fees (only present if 'allowed' is true)", + {RPCResult::Type::NUM, "vsize", /*optional=*/true, "Virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted (only present when 'allowed' is true)"}, + {RPCResult::Type::OBJ, "fees", /*optional=*/true, "Transaction fees (only present if 'allowed' is true)", { {RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT}, }}, - {RPCResult::Type::STR, "reject-reason", /* optional */ true, "Rejection string (only present when 'allowed' is false)"}, + {RPCResult::Type::STR, "reject-reason", /*optional=*/true, "Rejection string (only present when 'allowed' is false)"}, }}, } }, @@ -975,6 +1037,8 @@ static RPCHelpMan testmempoolaccept() continue; } const auto& tx_result = it->second; + // Package testmempoolaccept doesn't allow transactions to already be in the mempool. + CHECK_NONFATAL(tx_result.m_result_type != MempoolAcceptResult::ResultType::MEMPOOL_ENTRY); if (tx_result.m_result_type == MempoolAcceptResult::ResultType::VALID) { const CAmount fee = tx_result.m_base_fees.value(); // Check that fee does not exceed maximum fee @@ -1011,8 +1075,9 @@ static RPCHelpMan testmempoolaccept() static RPCHelpMan decodepsbt() { - return RPCHelpMan{"decodepsbt", - "\nReturn a JSON object representing the serialized, base64-encoded partially signed Bitcoin transaction.\n", + return RPCHelpMan{ + "decodepsbt", + "Return a JSON object representing the serialized, base64-encoded partially signed Bitcoin transaction.", { {"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "The PSBT base64 string"}, }, @@ -1023,6 +1088,26 @@ static RPCHelpMan decodepsbt() { {RPCResult::Type::ELISION, "", "The layout is the same as the output of decoderawtransaction."}, }}, + {RPCResult::Type::ARR, "global_xpubs", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "xpub", "The extended public key this path corresponds to"}, + {RPCResult::Type::STR_HEX, "master_fingerprint", "The fingerprint of the master key"}, + {RPCResult::Type::STR, "path", "The path"}, + }}, + }}, + {RPCResult::Type::NUM, "psbt_version", "The PSBT version number. Not to be confused with the unsigned transaction version"}, + {RPCResult::Type::ARR, "proprietary", "The global proprietary map", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "identifier", "The hex string for the proprietary identifier"}, + {RPCResult::Type::NUM, "subtype", "The number for the subtype"}, + {RPCResult::Type::STR_HEX, "key", "The hex for the key"}, + {RPCResult::Type::STR_HEX, "value", "The hex for the value"}, + }}, + }}, {RPCResult::Type::OBJ_DYN, "unknown", "The unknown global fields", { {RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"}, @@ -1031,11 +1116,11 @@ static RPCHelpMan decodepsbt() { {RPCResult::Type::OBJ, "", "", { - {RPCResult::Type::OBJ, "non_witness_utxo", /* optional */ true, "Decoded network transaction for non-witness UTXOs", + {RPCResult::Type::OBJ, "non_witness_utxo", /*optional=*/true, "Decoded network transaction for non-witness UTXOs", { {RPCResult::Type::ELISION, "",""}, }}, - {RPCResult::Type::OBJ, "witness_utxo", /* optional */ true, "Transaction output for witness UTXOs", + {RPCResult::Type::OBJ, "witness_utxo", /*optional=*/true, "Transaction output for witness UTXOs", { {RPCResult::Type::NUM, "amount", "The value in " + CURRENCY_UNIT}, {RPCResult::Type::OBJ, "scriptPubKey", "", @@ -1043,27 +1128,27 @@ static RPCHelpMan decodepsbt() {RPCResult::Type::STR, "asm", "The asm"}, {RPCResult::Type::STR_HEX, "hex", "The hex"}, {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"}, - {RPCResult::Type::STR, "address", /* optional */ true, "The Bitcoin address (only if a well-defined address exists)"}, + {RPCResult::Type::STR, "address", /*optional=*/true, "The Bitcoin address (only if a well-defined address exists)"}, }}, }}, - {RPCResult::Type::OBJ_DYN, "partial_signatures", /* optional */ true, "", + {RPCResult::Type::OBJ_DYN, "partial_signatures", /*optional=*/true, "", { {RPCResult::Type::STR, "pubkey", "The public key and signature that corresponds to it."}, }}, - {RPCResult::Type::STR, "sighash", /* optional */ true, "The sighash type to be used"}, - {RPCResult::Type::OBJ, "redeem_script", /* optional */ true, "", + {RPCResult::Type::STR, "sighash", /*optional=*/true, "The sighash type to be used"}, + {RPCResult::Type::OBJ, "redeem_script", /*optional=*/true, "", { {RPCResult::Type::STR, "asm", "The asm"}, {RPCResult::Type::STR_HEX, "hex", "The hex"}, {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"}, }}, - {RPCResult::Type::OBJ, "witness_script", /* optional */ true, "", + {RPCResult::Type::OBJ, "witness_script", /*optional=*/true, "", { {RPCResult::Type::STR, "asm", "The asm"}, {RPCResult::Type::STR_HEX, "hex", "The hex"}, {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"}, }}, - {RPCResult::Type::ARR, "bip32_derivs", /* optional */ true, "", + {RPCResult::Type::ARR, "bip32_derivs", /*optional=*/true, "", { {RPCResult::Type::OBJ, "", "", { @@ -1072,38 +1157,64 @@ static RPCHelpMan decodepsbt() {RPCResult::Type::STR, "path", "The path"}, }}, }}, - {RPCResult::Type::OBJ, "final_scriptSig", /* optional */ true, "", + {RPCResult::Type::OBJ, "final_scriptSig", /*optional=*/true, "", { {RPCResult::Type::STR, "asm", "The asm"}, {RPCResult::Type::STR, "hex", "The hex"}, }}, - {RPCResult::Type::ARR, "final_scriptwitness", /* optional */ true, "", + {RPCResult::Type::ARR, "final_scriptwitness", /*optional=*/true, "", { {RPCResult::Type::STR_HEX, "", "hex-encoded witness data (if any)"}, }}, - {RPCResult::Type::OBJ_DYN, "unknown", /* optional */ true, "The unknown global fields", + {RPCResult::Type::OBJ_DYN, "ripemd160_preimages", /*optional=*/ true, "", + { + {RPCResult::Type::STR, "hash", "The hash and preimage that corresponds to it."}, + }}, + {RPCResult::Type::OBJ_DYN, "sha256_preimages", /*optional=*/ true, "", + { + {RPCResult::Type::STR, "hash", "The hash and preimage that corresponds to it."}, + }}, + {RPCResult::Type::OBJ_DYN, "hash160_preimages", /*optional=*/ true, "", + { + {RPCResult::Type::STR, "hash", "The hash and preimage that corresponds to it."}, + }}, + {RPCResult::Type::OBJ_DYN, "hash256_preimages", /*optional=*/ true, "", + { + {RPCResult::Type::STR, "hash", "The hash and preimage that corresponds to it."}, + }}, + {RPCResult::Type::OBJ_DYN, "unknown", /*optional=*/ true, "The unknown input fields", { {RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"}, }}, + {RPCResult::Type::ARR, "proprietary", /*optional=*/true, "The input proprietary map", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "identifier", "The hex string for the proprietary identifier"}, + {RPCResult::Type::NUM, "subtype", "The number for the subtype"}, + {RPCResult::Type::STR_HEX, "key", "The hex for the key"}, + {RPCResult::Type::STR_HEX, "value", "The hex for the value"}, + }}, + }}, }}, }}, {RPCResult::Type::ARR, "outputs", "", { {RPCResult::Type::OBJ, "", "", { - {RPCResult::Type::OBJ, "redeem_script", /* optional */ true, "", + {RPCResult::Type::OBJ, "redeem_script", /*optional=*/true, "", { {RPCResult::Type::STR, "asm", "The asm"}, {RPCResult::Type::STR_HEX, "hex", "The hex"}, {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"}, }}, - {RPCResult::Type::OBJ, "witness_script", /* optional */ true, "", + {RPCResult::Type::OBJ, "witness_script", /*optional=*/true, "", { {RPCResult::Type::STR, "asm", "The asm"}, {RPCResult::Type::STR_HEX, "hex", "The hex"}, {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"}, }}, - {RPCResult::Type::ARR, "bip32_derivs", /* optional */ true, "", + {RPCResult::Type::ARR, "bip32_derivs", /*optional=*/true, "", { {RPCResult::Type::OBJ, "", "", { @@ -1112,13 +1223,23 @@ static RPCHelpMan decodepsbt() {RPCResult::Type::STR, "path", "The path"}, }}, }}, - {RPCResult::Type::OBJ_DYN, "unknown", /* optional */ true, "The unknown global fields", + {RPCResult::Type::OBJ_DYN, "unknown", /*optional=*/true, "The unknown global fields", { {RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"}, }}, + {RPCResult::Type::ARR, "proprietary", /*optional=*/true, "The output proprietary map", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "identifier", "The hex string for the proprietary identifier"}, + {RPCResult::Type::NUM, "subtype", "The number for the subtype"}, + {RPCResult::Type::STR_HEX, "key", "The hex for the key"}, + {RPCResult::Type::STR_HEX, "value", "The hex for the value"}, + }}, + }}, }}, }}, - {RPCResult::Type::STR_AMOUNT, "fee", /* optional */ true, "The transaction fee paid if all UTXOs slots in the PSBT have been filled."}, + {RPCResult::Type::STR_AMOUNT, "fee", /*optional=*/true, "The transaction fee paid if all UTXOs slots in the PSBT have been filled."}, } }, RPCExamples{ @@ -1142,6 +1263,38 @@ static RPCHelpMan decodepsbt() TxToUniv(CTransaction(*psbtx.tx), uint256(), tx_univ, false); result.pushKV("tx", tx_univ); + // Add the global xpubs + UniValue global_xpubs(UniValue::VARR); + for (std::pair<KeyOriginInfo, std::set<CExtPubKey>> xpub_pair : psbtx.m_xpubs) { + for (auto& xpub : xpub_pair.second) { + std::vector<unsigned char> ser_xpub; + ser_xpub.assign(BIP32_EXTKEY_WITH_VERSION_SIZE, 0); + xpub.EncodeWithVersion(ser_xpub.data()); + + UniValue keypath(UniValue::VOBJ); + keypath.pushKV("xpub", EncodeBase58Check(ser_xpub)); + keypath.pushKV("master_fingerprint", HexStr(Span<unsigned char>(xpub_pair.first.fingerprint, xpub_pair.first.fingerprint + 4))); + keypath.pushKV("path", WriteHDKeypath(xpub_pair.first.path)); + global_xpubs.push_back(keypath); + } + } + result.pushKV("global_xpubs", global_xpubs); + + // PSBT version + result.pushKV("psbt_version", static_cast<uint64_t>(psbtx.GetVersion())); + + // Proprietary + UniValue proprietary(UniValue::VARR); + for (const auto& entry : psbtx.m_proprietary) { + UniValue this_prop(UniValue::VOBJ); + this_prop.pushKV("identifier", HexStr(entry.identifier)); + this_prop.pushKV("subtype", entry.subtype); + this_prop.pushKV("key", HexStr(entry.key)); + this_prop.pushKV("value", HexStr(entry.value)); + proprietary.push_back(this_prop); + } + result.pushKV("proprietary", proprietary); + // Unknown data UniValue unknowns(UniValue::VOBJ); for (auto entry : psbtx.unknown) { @@ -1203,8 +1356,8 @@ static RPCHelpMan decodepsbt() } // Sighash - if (input.sighash_type > 0) { - in.pushKV("sighash", SighashToStr((unsigned char)input.sighash_type)); + if (input.sighash_type != std::nullopt) { + in.pushKV("sighash", SighashToStr((unsigned char)*input.sighash_type)); } // Redeem script and witness script @@ -1248,6 +1401,56 @@ static RPCHelpMan decodepsbt() in.pushKV("final_scriptwitness", txinwitness); } + // Ripemd160 hash preimages + if (!input.ripemd160_preimages.empty()) { + UniValue ripemd160_preimages(UniValue::VOBJ); + for (const auto& [hash, preimage] : input.ripemd160_preimages) { + ripemd160_preimages.pushKV(HexStr(hash), HexStr(preimage)); + } + in.pushKV("ripemd160_preimages", ripemd160_preimages); + } + + // Sha256 hash preimages + if (!input.sha256_preimages.empty()) { + UniValue sha256_preimages(UniValue::VOBJ); + for (const auto& [hash, preimage] : input.sha256_preimages) { + sha256_preimages.pushKV(HexStr(hash), HexStr(preimage)); + } + in.pushKV("sha256_preimages", sha256_preimages); + } + + // Hash160 hash preimages + if (!input.hash160_preimages.empty()) { + UniValue hash160_preimages(UniValue::VOBJ); + for (const auto& [hash, preimage] : input.hash160_preimages) { + hash160_preimages.pushKV(HexStr(hash), HexStr(preimage)); + } + in.pushKV("hash160_preimages", hash160_preimages); + } + + // Hash256 hash preimages + if (!input.hash256_preimages.empty()) { + UniValue hash256_preimages(UniValue::VOBJ); + for (const auto& [hash, preimage] : input.hash256_preimages) { + hash256_preimages.pushKV(HexStr(hash), HexStr(preimage)); + } + in.pushKV("hash256_preimages", hash256_preimages); + } + + // Proprietary + if (!input.m_proprietary.empty()) { + UniValue proprietary(UniValue::VARR); + for (const auto& entry : input.m_proprietary) { + UniValue this_prop(UniValue::VOBJ); + this_prop.pushKV("identifier", HexStr(entry.identifier)); + this_prop.pushKV("subtype", entry.subtype); + this_prop.pushKV("key", HexStr(entry.key)); + this_prop.pushKV("value", HexStr(entry.value)); + proprietary.push_back(this_prop); + } + in.pushKV("proprietary", proprietary); + } + // Unknown data if (input.unknown.size() > 0) { UniValue unknowns(UniValue::VOBJ); @@ -1292,6 +1495,20 @@ static RPCHelpMan decodepsbt() out.pushKV("bip32_derivs", keypaths); } + // Proprietary + if (!output.m_proprietary.empty()) { + UniValue proprietary(UniValue::VARR); + for (const auto& entry : output.m_proprietary) { + UniValue this_prop(UniValue::VOBJ); + this_prop.pushKV("identifier", HexStr(entry.identifier)); + this_prop.pushKV("subtype", entry.subtype); + this_prop.pushKV("key", HexStr(entry.key)); + this_prop.pushKV("value", HexStr(entry.value)); + proprietary.push_back(this_prop); + } + out.pushKV("proprietary", proprietary); + } + // Unknown data if (output.unknown.size() > 0) { UniValue unknowns(UniValue::VOBJ); @@ -1386,8 +1603,8 @@ static RPCHelpMan finalizepsbt() RPCResult{ RPCResult::Type::OBJ, "", "", { - {RPCResult::Type::STR, "psbt", /* optional */ true, "The base64-encoded partially signed transaction if not extracted"}, - {RPCResult::Type::STR_HEX, "hex", /* optional */ true, "The hex-encoded network transaction if extracted"}, + {RPCResult::Type::STR, "psbt", /*optional=*/true, "The base64-encoded partially signed transaction if not extracted"}, + {RPCResult::Type::STR_HEX, "hex", /*optional=*/true, "The hex-encoded network transaction if extracted"}, {RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"}, } }, @@ -1435,39 +1652,7 @@ static RPCHelpMan createpsbt() return RPCHelpMan{"createpsbt", "\nCreates a transaction in the Partially Signed Transaction format.\n" "Implements the Creator role.\n", - { - {"inputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The json objects", - { - {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "", - { - {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"}, - {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"}, - {"sequence", RPCArg::Type::NUM, RPCArg::DefaultHint{"depends on the value of the 'replaceable' and 'locktime' arguments"}, "The sequence number"}, - }, - }, - }, - }, - {"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The outputs (key-value pairs), where none of the keys are duplicated.\n" - "That is, each address can only appear once and there can only be one 'data' object.\n" - "For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n" - " accepted as second parameter.", - { - {"", RPCArg::Type::OBJ_USER_KEYS, RPCArg::Optional::OMITTED, "", - { - {"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT}, - }, - }, - {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "", - { - {"data", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A key-value pair. The key must be \"data\", the value is hex-encoded data"}, - }, - }, - }, - }, - {"locktime", RPCArg::Type::NUM, RPCArg::Default{0}, "Raw locktime. Non-0 value also locktime-activates inputs"}, - {"replaceable", RPCArg::Type::BOOL, RPCArg::Default{false}, "Marks this transaction as BIP125 replaceable.\n" - " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible."}, - }, + CreateTxDoc(), RPCResult{ RPCResult::Type::STR, "", "The resulting raw transaction (base64-encoded string)" }, @@ -1737,6 +1922,13 @@ static RPCHelpMan joinpsbts() for (unsigned int i = 0; i < psbt.tx->vout.size(); ++i) { merged_psbt.AddOutput(psbt.tx->vout[i], psbt.outputs[i]); } + for (auto& xpub_pair : psbt.m_xpubs) { + if (merged_psbt.m_xpubs.count(xpub_pair.first) == 0) { + merged_psbt.m_xpubs[xpub_pair.first] = xpub_pair.second; + } else { + merged_psbt.m_xpubs[xpub_pair.first].insert(xpub_pair.second.begin(), xpub_pair.second.end()); + } + } merged_psbt.unknown.insert(psbt.unknown.begin(), psbt.unknown.end()); } @@ -1779,33 +1971,33 @@ static RPCHelpMan analyzepsbt() RPCResult { RPCResult::Type::OBJ, "", "", { - {RPCResult::Type::ARR, "inputs", /* optional */ true, "", + {RPCResult::Type::ARR, "inputs", /*optional=*/true, "", { {RPCResult::Type::OBJ, "", "", { {RPCResult::Type::BOOL, "has_utxo", "Whether a UTXO is provided"}, {RPCResult::Type::BOOL, "is_final", "Whether the input is finalized"}, - {RPCResult::Type::OBJ, "missing", /* optional */ true, "Things that are missing that are required to complete this input", + {RPCResult::Type::OBJ, "missing", /*optional=*/true, "Things that are missing that are required to complete this input", { - {RPCResult::Type::ARR, "pubkeys", /* optional */ true, "", + {RPCResult::Type::ARR, "pubkeys", /*optional=*/true, "", { {RPCResult::Type::STR_HEX, "keyid", "Public key ID, hash160 of the public key, of a public key whose BIP 32 derivation path is missing"}, }}, - {RPCResult::Type::ARR, "signatures", /* optional */ true, "", + {RPCResult::Type::ARR, "signatures", /*optional=*/true, "", { {RPCResult::Type::STR_HEX, "keyid", "Public key ID, hash160 of the public key, of a public key whose signature is missing"}, }}, - {RPCResult::Type::STR_HEX, "redeemscript", /* optional */ true, "Hash160 of the redeemScript that is missing"}, - {RPCResult::Type::STR_HEX, "witnessscript", /* optional */ true, "SHA256 of the witnessScript that is missing"}, + {RPCResult::Type::STR_HEX, "redeemscript", /*optional=*/true, "Hash160 of the redeemScript that is missing"}, + {RPCResult::Type::STR_HEX, "witnessscript", /*optional=*/true, "SHA256 of the witnessScript that is missing"}, }}, - {RPCResult::Type::STR, "next", /* optional */ true, "Role of the next person that this input needs to go to"}, + {RPCResult::Type::STR, "next", /*optional=*/true, "Role of the next person that this input needs to go to"}, }}, }}, - {RPCResult::Type::NUM, "estimated_vsize", /* optional */ true, "Estimated vsize of the final signed transaction"}, - {RPCResult::Type::STR_AMOUNT, "estimated_feerate", /* optional */ true, "Estimated feerate of the final signed transaction in " + CURRENCY_UNIT + "/kvB. Shown only if all UTXO slots in the PSBT have been filled"}, - {RPCResult::Type::STR_AMOUNT, "fee", /* optional */ true, "The transaction fee paid. Shown only if all UTXO slots in the PSBT have been filled"}, + {RPCResult::Type::NUM, "estimated_vsize", /*optional=*/true, "Estimated vsize of the final signed transaction"}, + {RPCResult::Type::STR_AMOUNT, "estimated_feerate", /*optional=*/true, "Estimated feerate of the final signed transaction in " + CURRENCY_UNIT + "/kvB. Shown only if all UTXO slots in the PSBT have been filled"}, + {RPCResult::Type::STR_AMOUNT, "fee", /*optional=*/true, "The transaction fee paid. Shown only if all UTXO slots in the PSBT have been filled"}, {RPCResult::Type::STR, "next", "Role of the next person that this psbt needs to go to"}, - {RPCResult::Type::STR, "error", /* optional */ true, "Error message (if there is one)"}, + {RPCResult::Type::STR, "error", /*optional=*/true, "Error message (if there is one)"}, } }, RPCExamples { diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp index d550160260..3459897fe5 100644 --- a/src/rpc/rawtransaction_util.cpp +++ b/src/rpc/rawtransaction_util.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2020 The Bitcoin Core developers +// Copyright (c) 2009-2021 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/rpc/rawtransaction_util.h b/src/rpc/rawtransaction_util.h index d2e116f7ee..c3eb1417f8 100644 --- a/src/rpc/rawtransaction_util.h +++ b/src/rpc/rawtransaction_util.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2020 The Bitcoin Core developers +// Copyright (c) 2017-2021 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/rpc/register.h b/src/rpc/register.h index 6724203ffe..c5055cc9d7 100644 --- a/src/rpc/register.h +++ b/src/rpc/register.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2018 The Bitcoin Core developers +// Copyright (c) 2009-2021 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/rpc/request.cpp b/src/rpc/request.cpp index 3245e04cdf..fbb4e5ddd0 100644 --- a/src/rpc/request.cpp +++ b/src/rpc/request.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2020 The Bitcoin Core developers +// Copyright (c) 2009-2021 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/rpc/request.h b/src/rpc/request.h index bb091efea8..a682c58d96 100644 --- a/src/rpc/request.h +++ b/src/rpc/request.h @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2020 The Bitcoin Core developers +// Copyright (c) 2009-2021 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 9bcfba3507..c70236cc1c 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2020 The Bitcoin Core developers +// Copyright (c) 2009-2021 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/rpc/server.h b/src/rpc/server.h index e6bb35fc33..01e8556050 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2020 The Bitcoin Core developers +// Copyright (c) 2009-2021 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/rpc/server_util.cpp b/src/rpc/server_util.cpp new file mode 100644 index 0000000000..6a1b41f066 --- /dev/null +++ b/src/rpc/server_util.cpp @@ -0,0 +1,95 @@ +// Copyright (c) 2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <rpc/server_util.h> + +#include <net_processing.h> +#include <node/context.h> +#include <policy/fees.h> +#include <rpc/protocol.h> +#include <rpc/request.h> +#include <txmempool.h> +#include <util/system.h> +#include <validation.h> + +#include <any> + +using node::NodeContext; + +NodeContext& EnsureAnyNodeContext(const std::any& context) +{ + auto node_context = util::AnyPtr<NodeContext>(context); + if (!node_context) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Node context not found"); + } + return *node_context; +} + +CTxMemPool& EnsureMemPool(const NodeContext& node) +{ + if (!node.mempool) { + throw JSONRPCError(RPC_CLIENT_MEMPOOL_DISABLED, "Mempool disabled or instance not found"); + } + return *node.mempool; +} + +CTxMemPool& EnsureAnyMemPool(const std::any& context) +{ + return EnsureMemPool(EnsureAnyNodeContext(context)); +} + +ArgsManager& EnsureArgsman(const NodeContext& node) +{ + if (!node.args) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Node args not found"); + } + return *node.args; +} + +ArgsManager& EnsureAnyArgsman(const std::any& context) +{ + return EnsureArgsman(EnsureAnyNodeContext(context)); +} + +ChainstateManager& EnsureChainman(const NodeContext& node) +{ + if (!node.chainman) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Node chainman not found"); + } + return *node.chainman; +} + +ChainstateManager& EnsureAnyChainman(const std::any& context) +{ + return EnsureChainman(EnsureAnyNodeContext(context)); +} + +CBlockPolicyEstimator& EnsureFeeEstimator(const NodeContext& node) +{ + if (!node.fee_estimator) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Fee estimation disabled"); + } + return *node.fee_estimator; +} + +CBlockPolicyEstimator& EnsureAnyFeeEstimator(const std::any& context) +{ + return EnsureFeeEstimator(EnsureAnyNodeContext(context)); +} + +CConnman& EnsureConnman(const NodeContext& node) +{ + if (!node.connman) { + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + } + return *node.connman; +} + +PeerManager& EnsurePeerman(const NodeContext& node) +{ + if (!node.peerman) { + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + } + return *node.peerman; +} diff --git a/src/rpc/server_util.h b/src/rpc/server_util.h new file mode 100644 index 0000000000..2cc710a803 --- /dev/null +++ b/src/rpc/server_util.h @@ -0,0 +1,32 @@ +// Copyright (c) 2021 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_RPC_SERVER_UTIL_H +#define BITCOIN_RPC_SERVER_UTIL_H + +#include <any> + +class ArgsManager; +class CBlockPolicyEstimator; +class CConnman; +class CTxMemPool; +class ChainstateManager; +class PeerManager; +namespace node { +struct NodeContext; +} // namespace node + +node::NodeContext& EnsureAnyNodeContext(const std::any& context); +CTxMemPool& EnsureMemPool(const node::NodeContext& node); +CTxMemPool& EnsureAnyMemPool(const std::any& context); +ArgsManager& EnsureArgsman(const node::NodeContext& node); +ArgsManager& EnsureAnyArgsman(const std::any& context); +ChainstateManager& EnsureChainman(const node::NodeContext& node); +ChainstateManager& EnsureAnyChainman(const std::any& context); +CBlockPolicyEstimator& EnsureFeeEstimator(const node::NodeContext& node); +CBlockPolicyEstimator& EnsureAnyFeeEstimator(const std::any& context); +CConnman& EnsureConnman(const node::NodeContext& node); +PeerManager& EnsurePeerman(const node::NodeContext& node); + +#endif // BITCOIN_RPC_SERVER_UTIL_H diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index 2d7f5f2894..57e3da0351 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -210,7 +210,7 @@ CPubKey AddrToPubKey(const FillableSigningProvider& keystore, const std::string& } CKeyID key = GetKeyForDestination(keystore, dest); if (key.IsNull()) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("%s does not refer to a key", addr_in)); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("'%s' does not refer to a key", addr_in)); } CPubKey vchPubKey; if (!keystore.GetPubKey(key, vchPubKey)) { @@ -317,7 +317,7 @@ public: UniValue obj(UniValue::VOBJ); obj.pushKV("iswitness", true); obj.pushKV("witness_version", (int)id.version); - obj.pushKV("witness_program", HexStr(Span<const unsigned char>(id.program, id.length))); + obj.pushKV("witness_program", HexStr({id.program, id.length})); return obj; } }; @@ -620,10 +620,9 @@ std::string RPCHelpMan::ToString() const ret += arg.ToString(/* oneline */ true); } if (was_optional) ret += " )"; - ret += "\n"; // Description - ret += m_description; + ret += "\n\n" + TrimString(m_description) + "\n"; // Arguments Sections sections; |