aboutsummaryrefslogtreecommitdiff
path: root/src/rpc
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpc')
-rw-r--r--src/rpc/blockchain.cpp21
-rw-r--r--src/rpc/mempool.cpp7
-rw-r--r--src/rpc/mining.cpp6
-rw-r--r--src/rpc/net.cpp36
-rw-r--r--src/rpc/node.cpp26
-rw-r--r--src/rpc/output_script.cpp2
-rw-r--r--src/rpc/rawtransaction.cpp15
-rw-r--r--src/rpc/rawtransaction_util.cpp43
-rw-r--r--src/rpc/rawtransaction_util.h7
-rw-r--r--src/rpc/request.cpp2
-rw-r--r--src/rpc/server.cpp2
-rw-r--r--src/rpc/server_util.cpp14
-rw-r--r--src/rpc/server_util.h3
-rw-r--r--src/rpc/txoutproof.cpp6
-rw-r--r--src/rpc/util.cpp161
-rw-r--r--src/rpc/util.h32
16 files changed, 231 insertions, 152 deletions
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 2b39580043..28a619fe54 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -460,7 +460,7 @@ static RPCHelpMan getblockfrompeer()
// Fetching blocks before the node has syncing past their height can prevent block files from
// being pruned, so we avoid it if the node is in prune mode.
- if (node::fPruneMode && index->nHeight > WITH_LOCK(chainman.GetMutex(), return chainman.ActiveTip()->nHeight)) {
+ if (chainman.m_blockman.IsPruneMode() && index->nHeight > WITH_LOCK(chainman.GetMutex(), return chainman.ActiveTip()->nHeight)) {
throw JSONRPCError(RPC_MISC_ERROR, "In prune mode, only blocks that the node has already synced previously can be fetched from a peer");
}
@@ -565,7 +565,7 @@ static RPCHelpMan getblockheader()
if (!fVerbose)
{
- CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
+ DataStream ssBlock{};
ssBlock << pblockindex->GetBlockHeader();
std::string strHex = HexStr(ssBlock);
return strHex;
@@ -775,10 +775,11 @@ static RPCHelpMan pruneblockchain()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- if (!node::fPruneMode)
+ ChainstateManager& chainman = EnsureAnyChainman(request.context);
+ if (!chainman.m_blockman.IsPruneMode()) {
throw JSONRPCError(RPC_MISC_ERROR, "Cannot prune blocks because node is not in prune mode.");
+ }
- ChainstateManager& chainman = EnsureAnyChainman(request.context);
LOCK(cs_main);
Chainstate& active_chainstate = chainman.ActiveChainstate();
CChain& active_chain = active_chainstate.m_chain;
@@ -1109,7 +1110,7 @@ static RPCHelpMan verifychain()
{"nblocks", RPCArg::Type::NUM, RPCArg::DefaultHint{strprintf("%d, 0=all", DEFAULT_CHECKBLOCKS)}, "The number of blocks to check."},
},
RPCResult{
- RPCResult::Type::BOOL, "", "Verified or not"},
+ RPCResult::Type::BOOL, "", "Verification finished successfully. If false, check debug.log for reason."},
RPCExamples{
HelpExampleCli("verifychain", "")
+ HelpExampleRpc("verifychain", "")
@@ -1124,7 +1125,7 @@ static RPCHelpMan verifychain()
Chainstate& active_chainstate = chainman.ActiveChainstate();
return CVerifyDB().VerifyDB(
- active_chainstate, chainman.GetParams().GetConsensus(), active_chainstate.CoinsTip(), check_level, check_depth);
+ active_chainstate, chainman.GetParams().GetConsensus(), active_chainstate.CoinsTip(), check_level, check_depth) == VerifyDBResult::SUCCESS;
},
};
}
@@ -1266,15 +1267,15 @@ RPCHelpMan getblockchaininfo()
obj.pushKV("initialblockdownload", active_chainstate.IsInitialBlockDownload());
obj.pushKV("chainwork", tip.nChainWork.GetHex());
obj.pushKV("size_on_disk", chainman.m_blockman.CalculateCurrentUsage());
- obj.pushKV("pruned", node::fPruneMode);
- if (node::fPruneMode) {
+ obj.pushKV("pruned", chainman.m_blockman.IsPruneMode());
+ if (chainman.m_blockman.IsPruneMode()) {
obj.pushKV("pruneheight", chainman.m_blockman.GetFirstStoredBlock(tip)->nHeight);
// if 0, execution bypasses the whole if block.
bool automatic_pruning{args.GetIntArg("-prune", 0) != 1};
obj.pushKV("automatic_pruning", automatic_pruning);
if (automatic_pruning) {
- obj.pushKV("prune_target_size", node::nPruneTarget);
+ obj.pushKV("prune_target_size", chainman.m_blockman.GetPruneTarget());
}
}
@@ -2307,7 +2308,7 @@ static RPCHelpMan scanblocks()
RPCArg{"start_height", RPCArg::Type::NUM, RPCArg::Default{0}, "Height to start to scan from"},
RPCArg{"stop_height", RPCArg::Type::NUM, RPCArg::DefaultHint{"chain tip"}, "Height to stop to scan"},
RPCArg{"filtertype", RPCArg::Type::STR, RPCArg::Default{BlockFilterTypeName(BlockFilterType::BASIC)}, "The type name of the filter"},
- RPCArg{"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
+ RPCArg{"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
{
{"filter_false_positives", RPCArg::Type::BOOL, RPCArg::Default{false}, "Filter false positives (slower and may fail on pruned nodes). Otherwise they may occur at a rate of 1/M"},
},
diff --git a/src/rpc/mempool.cpp b/src/rpc/mempool.cpp
index 0e202a963d..3a69e2d8a2 100644
--- a/src/rpc/mempool.cpp
+++ b/src/rpc/mempool.cpp
@@ -853,15 +853,16 @@ static RPCHelpMan submitpackage()
NONFATAL_UNREACHABLE();
}
}
+ size_t num_broadcast{0};
for (const auto& tx : txns) {
- size_t num_submitted{0};
std::string err_string;
- const auto err = BroadcastTransaction(node, tx, err_string, 0, true, true);
+ const auto err = BroadcastTransaction(node, tx, err_string, /*max_tx_fee=*/0, /*relay=*/true, /*wait_callback=*/true);
if (err != TransactionError::OK) {
throw JSONRPCTransactionError(err,
strprintf("transaction broadcast failed: %s (all transactions were submitted, %d transactions were broadcast successfully)",
- err_string, num_submitted));
+ err_string, num_broadcast));
}
+ num_broadcast++;
}
UniValue rpc_result{UniValue::VOBJ};
UniValue tx_result_map{UniValue::VOBJ};
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 764c4c675b..8753f845a5 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -441,7 +441,7 @@ static RPCHelpMan prioritisetransaction()
"Accepts the transaction into mined blocks at a higher (or lower) priority\n",
{
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id."},
- {"dummy", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "API-Compatibility for previous API. Must be zero or null.\n"
+ {"dummy", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "API-Compatibility for previous API. Must be zero or null.\n"
" DEPRECATED. For forward compatibility use named arguments and omit this parameter."},
{"fee_delta", RPCArg::Type::NUM, RPCArg::Optional::NO, "The fee value (in satoshis) to add (or subtract, if negative).\n"
" Note, that this value is not a fee rate. It is a value to modify absolute fee of the TX.\n"
@@ -513,8 +513,8 @@ static RPCHelpMan getblocktemplate()
{
{"template_request", RPCArg::Type::OBJ, RPCArg::Default{UniValue::VOBJ}, "Format of the template",
{
- {"mode", RPCArg::Type::STR, /* treat as named arg */ RPCArg::Optional::OMITTED_NAMED_ARG, "This must be set to \"template\", \"proposal\" (see BIP 23), or omitted"},
- {"capabilities", RPCArg::Type::ARR, /* treat as named arg */ RPCArg::Optional::OMITTED_NAMED_ARG, "A list of strings",
+ {"mode", RPCArg::Type::STR, /* treat as named arg */ RPCArg::Optional::OMITTED, "This must be set to \"template\", \"proposal\" (see BIP 23), or omitted"},
+ {"capabilities", RPCArg::Type::ARR, /* treat as named arg */ RPCArg::Optional::OMITTED, "A list of strings",
{
{"str", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "client side supported feature, 'longpoll', 'coinbasevalue', 'proposal', 'serverlist', 'workid'"},
}},
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index f0e5b90509..7ffa777ef4 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -196,7 +196,7 @@ static RPCHelpMan getpeerinfo()
obj.pushKV("id", stats.nodeid);
obj.pushKV("addr", stats.m_addr_name);
if (stats.addrBind.IsValid()) {
- obj.pushKV("addrbind", stats.addrBind.ToString());
+ obj.pushKV("addrbind", stats.addrBind.ToStringAddrPort());
}
if (!(stats.addrLocal.empty())) {
obj.pushKV("addrlocal", stats.addrLocal);
@@ -496,7 +496,7 @@ static RPCHelpMan getaddednodeinfo()
UniValue addresses(UniValue::VARR);
if (info.fConnected) {
UniValue address(UniValue::VOBJ);
- address.pushKV("address", info.resolvedAddress.ToString());
+ address.pushKV("address", info.resolvedAddress.ToStringAddrPort());
address.pushKV("connected", info.fInbound ? "inbound" : "outbound");
addresses.push_back(address);
}
@@ -571,7 +571,7 @@ static UniValue GetNetworksInfo()
obj.pushKV("name", GetNetworkName(network));
obj.pushKV("limited", !IsReachable(network));
obj.pushKV("reachable", IsReachable(network));
- obj.pushKV("proxy", proxy.IsValid() ? proxy.proxy.ToStringIPPort() : std::string());
+ obj.pushKV("proxy", proxy.IsValid() ? proxy.proxy.ToStringAddrPort() : std::string());
obj.pushKV("proxy_randomize_credentials", proxy.randomize_credentials);
networks.push_back(obj);
}
@@ -664,7 +664,7 @@ static RPCHelpMan getnetworkinfo()
for (const std::pair<const CNetAddr, LocalServiceInfo> &item : mapLocalHost)
{
UniValue rec(UniValue::VOBJ);
- rec.pushKV("address", item.first.ToString());
+ rec.pushKV("address", item.first.ToStringAddr());
rec.pushKV("port", item.second.nPort);
rec.pushKV("score", item.second.nScore);
localAddresses.push_back(rec);
@@ -702,9 +702,7 @@ static RPCHelpMan setban()
throw std::runtime_error(help.ToString());
}
NodeContext& node = EnsureAnyNodeContext(request.context);
- if (!node.banman) {
- throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded");
- }
+ BanMan& banman = EnsureBanman(node);
CSubNet subNet;
CNetAddr netAddr;
@@ -726,7 +724,7 @@ static RPCHelpMan setban()
if (strCommand == "add")
{
- if (isSubnet ? node.banman->IsBanned(subNet) : node.banman->IsBanned(netAddr)) {
+ if (isSubnet ? banman.IsBanned(subNet) : banman.IsBanned(netAddr)) {
throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: IP/Subnet already banned");
}
@@ -741,12 +739,12 @@ static RPCHelpMan setban()
}
if (isSubnet) {
- node.banman->Ban(subNet, banTime, absolute);
+ banman.Ban(subNet, banTime, absolute);
if (node.connman) {
node.connman->DisconnectNode(subNet);
}
} else {
- node.banman->Ban(netAddr, banTime, absolute);
+ banman.Ban(netAddr, banTime, absolute);
if (node.connman) {
node.connman->DisconnectNode(netAddr);
}
@@ -754,7 +752,7 @@ static RPCHelpMan setban()
}
else if(strCommand == "remove")
{
- if (!( isSubnet ? node.banman->Unban(subNet) : node.banman->Unban(netAddr) )) {
+ if (!( isSubnet ? banman.Unban(subNet) : banman.Unban(netAddr) )) {
throw JSONRPCError(RPC_CLIENT_INVALID_IP_OR_SUBNET, "Error: Unban failed. Requested address/subnet was not previously manually banned.");
}
}
@@ -785,13 +783,10 @@ static RPCHelpMan listbanned()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- NodeContext& node = EnsureAnyNodeContext(request.context);
- if(!node.banman) {
- throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded");
- }
+ BanMan& banman = EnsureAnyBanman(request.context);
banmap_t banMap;
- node.banman->GetBanned(banMap);
+ banman.GetBanned(banMap);
const int64_t current_time{GetTime()};
UniValue bannedAddresses(UniValue::VARR);
@@ -825,12 +820,9 @@ static RPCHelpMan clearbanned()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- NodeContext& node = EnsureAnyNodeContext(request.context);
- if (!node.banman) {
- throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded");
- }
+ BanMan& banman = EnsureAnyBanman(request.context);
- node.banman->ClearBanned();
+ banman.ClearBanned();
return UniValue::VNULL;
},
@@ -909,7 +901,7 @@ static RPCHelpMan getnodeaddresses()
UniValue obj(UniValue::VOBJ);
obj.pushKV("time", int64_t{TicksSinceEpoch<std::chrono::seconds>(addr.nTime)});
obj.pushKV("services", (uint64_t)addr.nServices);
- obj.pushKV("address", addr.ToStringIP());
+ obj.pushKV("address", addr.ToStringAddr());
obj.pushKV("port", addr.GetPort());
obj.pushKV("network", GetNetworkName(addr.GetNetClass()));
ret.push_back(obj);
diff --git a/src/rpc/node.cpp b/src/rpc/node.cpp
index 79b8277968..5918bc6e38 100644
--- a/src/rpc/node.cpp
+++ b/src/rpc/node.cpp
@@ -240,11 +240,11 @@ static RPCHelpMan logging()
" - \"none\", \"0\" : even if other logging categories are specified, ignore all of them.\n"
,
{
- {"include", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "The categories to add to debug logging",
+ {"include", RPCArg::Type::ARR, RPCArg::Optional::OMITTED, "The categories to add to debug logging",
{
{"include_category", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "the valid logging category"},
}},
- {"exclude", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "The categories to remove from debug logging",
+ {"exclude", RPCArg::Type::ARR, RPCArg::Optional::OMITTED, "The categories to remove from debug logging",
{
{"exclude_category", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "the valid logging category"},
}},
@@ -294,16 +294,16 @@ static RPCHelpMan echo(const std::string& name)
"\nThe difference between echo and echojson is that echojson has argument conversion enabled in the client-side table in "
"bitcoin-cli and the GUI. There is no server-side difference.",
{
- {"arg0", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "", RPCArgOptions{.skip_type_check = true}},
- {"arg1", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "", RPCArgOptions{.skip_type_check = true}},
- {"arg2", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "", RPCArgOptions{.skip_type_check = true}},
- {"arg3", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "", RPCArgOptions{.skip_type_check = true}},
- {"arg4", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "", RPCArgOptions{.skip_type_check = true}},
- {"arg5", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "", RPCArgOptions{.skip_type_check = true}},
- {"arg6", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "", RPCArgOptions{.skip_type_check = true}},
- {"arg7", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "", RPCArgOptions{.skip_type_check = true}},
- {"arg8", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "", RPCArgOptions{.skip_type_check = true}},
- {"arg9", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "", RPCArgOptions{.skip_type_check = true}},
+ {"arg0", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}},
+ {"arg1", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}},
+ {"arg2", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}},
+ {"arg3", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}},
+ {"arg4", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}},
+ {"arg5", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}},
+ {"arg6", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}},
+ {"arg7", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}},
+ {"arg8", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}},
+ {"arg9", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}},
},
RPCResult{RPCResult::Type::ANY, "", "Returns whatever was passed in"},
RPCExamples{""},
@@ -376,7 +376,7 @@ static RPCHelpMan getindexinfo()
return RPCHelpMan{"getindexinfo",
"\nReturns the status of one or all available indices currently running in the node.\n",
{
- {"index_name", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Filter results for an index with a specific name."},
+ {"index_name", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Filter results for an index with a specific name."},
},
RPCResult{
RPCResult::Type::OBJ_DYN, "", "", {
diff --git a/src/rpc/output_script.cpp b/src/rpc/output_script.cpp
index 911c769e61..bb04f58424 100644
--- a/src/rpc/output_script.cpp
+++ b/src/rpc/output_script.cpp
@@ -230,7 +230,7 @@ static RPCHelpMan deriveaddresses()
"For more information on output descriptors, see the documentation in the doc/descriptors.md file.\n"},
{
{"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The descriptor."},
- {"range", RPCArg::Type::RANGE, RPCArg::Optional::OMITTED_NAMED_ARG, "If a ranged descriptor is used, this specifies the end or the range (in [begin,end] notation) to derive."},
+ {"range", RPCArg::Type::RANGE, RPCArg::Optional::OMITTED, "If a ranged descriptor is used, this specifies the end or the range (in [begin,end] notation) to derive."},
},
RPCResult{
RPCResult::Type::ARR, "", "",
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 981dead3b8..5ed8aee9ea 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -54,8 +54,11 @@ using node::PSBTAnalysis;
using node::ReadBlockFromDisk;
using node::UndoReadFromDisk;
-static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry, Chainstate& active_chainstate, const CTxUndo* txundo = nullptr, TxVerbosity verbosity = TxVerbosity::SHOW_TXID)
+static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry,
+ Chainstate& active_chainstate, const CTxUndo* txundo = nullptr,
+ TxVerbosity verbosity = TxVerbosity::SHOW_DETAILS)
{
+ CHECK_NONFATAL(verbosity >= TxVerbosity::SHOW_DETAILS);
// Call into TxToUniv() in bitcoin-common to decode the transaction hex.
//
// Blockchain contextual information (confirmations and blocktime) is not
@@ -187,7 +190,7 @@ static RPCHelpMan getrawtransaction()
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
{"verbosity|verbose", RPCArg::Type::NUM, RPCArg::Default{0}, "0 for hex-encoded data, 1 for a JSON object, and 2 for JSON object with fee and prevout",
RPCArgOptions{.skip_type_check = true}},
- {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED_NAMED_ARG, "The block in which to look for the transaction"},
+ {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "The block in which to look for the transaction"},
},
{
RPCResult{"if verbosity is not set or set to 0",
@@ -519,15 +522,17 @@ static RPCHelpMan decodescript()
if (can_wrap_P2WSH) {
UniValue sr(UniValue::VOBJ);
CScript segwitScr;
+ FlatSigningProvider provider;
if (which_type == TxoutType::PUBKEY) {
segwitScr = GetScriptForDestination(WitnessV0KeyHash(Hash160(solutions_data[0])));
} else if (which_type == TxoutType::PUBKEYHASH) {
segwitScr = GetScriptForDestination(WitnessV0KeyHash(uint160{solutions_data[0]}));
} else {
// Scripts that are not fit for P2WPKH are encoded as P2WSH.
+ provider.scripts[CScriptID(script)] = script;
segwitScr = GetScriptForDestination(WitnessV0ScriptHash(script));
}
- ScriptToUniv(segwitScr, /*out=*/sr, /*include_hex=*/true, /*include_address=*/true);
+ ScriptToUniv(segwitScr, /*out=*/sr, /*include_hex=*/true, /*include_address=*/true, /*provider=*/&provider);
sr.pushKV("p2sh-segwit", EncodeDestination(ScriptHash(segwitScr)));
r.pushKV("segwit", sr);
}
@@ -639,7 +644,7 @@ static RPCHelpMan signrawtransactionwithkey()
{"privatekey", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "private key in base58-encoding"},
},
},
- {"prevtxs", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "The previous dependent transaction outputs",
+ {"prevtxs", RPCArg::Type::ARR, RPCArg::Optional::OMITTED, "The previous dependent transaction outputs",
{
{"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
{
@@ -1578,7 +1583,7 @@ static RPCHelpMan utxoupdatepsbt()
"\nUpdates all segwit inputs and outputs in a PSBT with data from output descriptors, the UTXO set or the mempool.\n",
{
{"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"},
- {"descriptors", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "An array of either strings or objects", {
+ {"descriptors", RPCArg::Type::ARR, RPCArg::Optional::OMITTED, "An array of either strings or objects", {
{"", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "An output descriptor"},
{"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "An object with an output descriptor and extra information", {
{"desc", RPCArg::Type::STR, RPCArg::Optional::NO, "An output descriptor"},
diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp
index 15b8e1dcd0..3ba930f84f 100644
--- a/src/rpc/rawtransaction_util.cpp
+++ b/src/rpc/rawtransaction_util.cpp
@@ -21,12 +21,8 @@
#include <util/strencodings.h>
#include <util/translation.h>
-CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, std::optional<bool> rbf)
+void AddInputs(CMutableTransaction& rawTx, const UniValue& inputs_in, std::optional<bool> rbf)
{
- if (outputs_in.isNull()) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, output argument must be non-null");
- }
-
UniValue inputs;
if (inputs_in.isNull()) {
inputs = UniValue::VARR;
@@ -34,18 +30,6 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
inputs = inputs_in.get_array();
}
- const bool outputs_is_obj = outputs_in.isObject();
- UniValue outputs = outputs_is_obj ? outputs_in.get_obj() : outputs_in.get_array();
-
- CMutableTransaction rawTx;
-
- if (!locktime.isNull()) {
- int64_t nLockTime = locktime.getInt<int64_t>();
- if (nLockTime < 0 || nLockTime > LOCKTIME_MAX)
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range");
- rawTx.nLockTime = nLockTime;
- }
-
for (unsigned int idx = 0; idx < inputs.size(); idx++) {
const UniValue& input = inputs[idx];
const UniValue& o = input.get_obj();
@@ -84,6 +68,16 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
rawTx.vin.push_back(in);
}
+}
+
+void AddOutputs(CMutableTransaction& rawTx, const UniValue& outputs_in)
+{
+ if (outputs_in.isNull()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, output argument must be non-null");
+ }
+
+ const bool outputs_is_obj = outputs_in.isObject();
+ UniValue outputs = outputs_is_obj ? outputs_in.get_obj() : outputs_in.get_array();
if (!outputs_is_obj) {
// Translate array of key-value pairs into dict
@@ -132,6 +126,21 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
rawTx.vout.push_back(out);
}
}
+}
+
+CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, std::optional<bool> rbf)
+{
+ CMutableTransaction rawTx;
+
+ if (!locktime.isNull()) {
+ int64_t nLockTime = locktime.getInt<int64_t>();
+ if (nLockTime < 0 || nLockTime > LOCKTIME_MAX)
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range");
+ rawTx.nLockTime = nLockTime;
+ }
+
+ AddInputs(rawTx, inputs_in, rbf);
+ AddOutputs(rawTx, outputs_in);
if (rbf.has_value() && rbf.value() && rawTx.vin.size() > 0 && !SignalsOptInRBF(CTransaction(rawTx))) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option");
diff --git a/src/rpc/rawtransaction_util.h b/src/rpc/rawtransaction_util.h
index 0c3823bc1e..a863432b7a 100644
--- a/src/rpc/rawtransaction_util.h
+++ b/src/rpc/rawtransaction_util.h
@@ -38,6 +38,13 @@ void SignTransactionResultToJSON(CMutableTransaction& mtx, bool complete, const
*/
void ParsePrevouts(const UniValue& prevTxsUnival, FillableSigningProvider* keystore, std::map<COutPoint, Coin>& coins);
+
+/** Normalize univalue-represented inputs and add them to the transaction */
+void AddInputs(CMutableTransaction& rawTx, const UniValue& inputs_in, bool rbf);
+
+/** Normalize univalue-represented outputs and add them to the transaction */
+void AddOutputs(CMutableTransaction& rawTx, const UniValue& outputs_in);
+
/** Create a transaction from univalue parameters */
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, std::optional<bool> rbf);
diff --git a/src/rpc/request.cpp b/src/rpc/request.cpp
index 0bb5533d71..6f37fe2a99 100644
--- a/src/rpc/request.cpp
+++ b/src/rpc/request.cpp
@@ -86,7 +86,7 @@ bool GenerateAuthCookie(std::string *cookie_out)
std::string cookie = COOKIEAUTH_USER + ":" + HexStr(rand_pwd);
/** the umask determines what permissions are used to create this file -
- * these are set to 077 in init.cpp unless overridden with -sysperms.
+ * these are set to 0077 in util/system.cpp.
*/
std::ofstream file;
fs::path filepath_tmp = GetAuthCookieFile(true);
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index 9f57a56297..44d7e2676b 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -168,7 +168,7 @@ static RPCHelpMan stop()
// to the client (intended for testing)
"\nRequest a graceful shutdown of " PACKAGE_NAME ".",
{
- {"wait", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "how long to wait in ms", RPCArgOptions{.hidden=true}},
+ {"wait", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "how long to wait in ms", RPCArgOptions{.hidden=true}},
},
RPCResult{RPCResult::Type::STR, "", "A string with the content '" + RESULT + "'"},
RPCExamples{""},
diff --git a/src/rpc/server_util.cpp b/src/rpc/server_util.cpp
index 50f9ce7b3c..7a708ec813 100644
--- a/src/rpc/server_util.cpp
+++ b/src/rpc/server_util.cpp
@@ -39,6 +39,20 @@ CTxMemPool& EnsureAnyMemPool(const std::any& context)
return EnsureMemPool(EnsureAnyNodeContext(context));
}
+
+BanMan& EnsureBanman(const NodeContext& node)
+{
+ if (!node.banman) {
+ throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded");
+ }
+ return *node.banman;
+}
+
+BanMan& EnsureAnyBanman(const std::any& context)
+{
+ return EnsureBanman(EnsureAnyNodeContext(context));
+}
+
ArgsManager& EnsureArgsman(const NodeContext& node)
{
if (!node.args) {
diff --git a/src/rpc/server_util.h b/src/rpc/server_util.h
index fa008a8155..9af9572431 100644
--- a/src/rpc/server_util.h
+++ b/src/rpc/server_util.h
@@ -13,6 +13,7 @@ class CConnman;
class CTxMemPool;
class ChainstateManager;
class PeerManager;
+class BanMan;
namespace node {
struct NodeContext;
} // namespace node
@@ -20,6 +21,8 @@ struct NodeContext;
node::NodeContext& EnsureAnyNodeContext(const std::any& context);
CTxMemPool& EnsureMemPool(const node::NodeContext& node);
CTxMemPool& EnsureAnyMemPool(const std::any& context);
+BanMan& EnsureBanman(const node::NodeContext& node);
+BanMan& EnsureAnyBanman(const std::any& context);
ArgsManager& EnsureArgsman(const node::NodeContext& node);
ArgsManager& EnsureAnyArgsman(const std::any& context);
ChainstateManager& EnsureChainman(const node::NodeContext& node);
diff --git a/src/rpc/txoutproof.cpp b/src/rpc/txoutproof.cpp
index 8c5468634d..24b5d04115 100644
--- a/src/rpc/txoutproof.cpp
+++ b/src/rpc/txoutproof.cpp
@@ -34,7 +34,7 @@ static RPCHelpMan gettxoutproof()
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "A transaction hash"},
},
},
- {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED_NAMED_ARG, "If specified, looks for txid in the block with this hash"},
+ {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "If specified, looks for txid in the block with this hash"},
},
RPCResult{
RPCResult::Type::STR, "data", "A string that is a serialized, hex-encoded data for the proof."
@@ -112,7 +112,7 @@ static RPCHelpMan gettxoutproof()
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Not all transactions found in specified or retrieved block");
}
- CDataStream ssMB(SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
+ DataStream ssMB{};
CMerkleBlock mb(block, setTxids);
ssMB << mb;
std::string strHex = HexStr(ssMB);
@@ -138,7 +138,7 @@ static RPCHelpMan verifytxoutproof()
RPCExamples{""},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- CDataStream ssMB(ParseHexV(request.params[0], "proof"), SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
+ DataStream ssMB{ParseHexV(request.params[0], "proof")};
CMerkleBlock merkleBlock;
ssMB >> merkleBlock;
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index 9619c3df99..a1020c3b2b 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <clientversion.h>
#include <consensus/amount.h>
#include <key_io.h>
#include <outputtype.h>
@@ -30,14 +31,6 @@ std::string GetAllOutputTypes()
return Join(ret, ", ");
}
-void RPCTypeCheckArgument(const UniValue& value, const UniValueType& typeExpected)
-{
- if (!typeExpected.typeAny && value.type() != typeExpected.type) {
- throw JSONRPCError(RPC_TYPE_ERROR,
- strprintf("JSON value of type %s is not of expected type %s", uvTypeName(value.type()), uvTypeName(typeExpected.type)));
- }
-}
-
void RPCTypeCheckObj(const UniValue& o,
const std::map<std::string, UniValueType>& typesExpected,
bool fAllowNull,
@@ -563,12 +556,39 @@ UniValue RPCHelpMan::HandleRequest(const JSONRPCRequest& request) const
if (request.mode == JSONRPCRequest::GET_HELP || !IsValidNumArgs(request.params.size())) {
throw std::runtime_error(ToString());
}
+ UniValue arg_mismatch{UniValue::VOBJ};
for (size_t i{0}; i < m_args.size(); ++i) {
- m_args.at(i).MatchesType(request.params[i]);
+ const auto& arg{m_args.at(i)};
+ UniValue match{arg.MatchesType(request.params[i])};
+ if (!match.isTrue()) {
+ arg_mismatch.pushKV(strprintf("Position %s (%s)", i + 1, arg.m_names), std::move(match));
+ }
+ }
+ if (!arg_mismatch.empty()) {
+ throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Wrong type passed:\n%s", arg_mismatch.write(4)));
}
UniValue ret = m_fun(*this, request);
if (gArgs.GetBoolArg("-rpcdoccheck", DEFAULT_RPC_DOC_CHECK)) {
- CHECK_NONFATAL(std::any_of(m_results.m_results.begin(), m_results.m_results.end(), [&ret](const RPCResult& res) { return res.MatchesType(ret); }));
+ UniValue mismatch{UniValue::VARR};
+ for (const auto& res : m_results.m_results) {
+ UniValue match{res.MatchesType(ret)};
+ if (match.isTrue()) {
+ mismatch.setNull();
+ break;
+ }
+ mismatch.push_back(match);
+ }
+ if (!mismatch.isNull()) {
+ std::string explain{
+ mismatch.empty() ? "no possible results defined" :
+ mismatch.size() == 1 ? mismatch[0].write(4) :
+ mismatch.write(4)};
+ throw std::runtime_error{
+ strprintf("Internal bug detected: RPC call \"%s\" returned incorrect type:\n%s\n%s %s\nPlease report this issue here: %s\n",
+ m_name, explain,
+ PACKAGE_NAME, FormatFullVersion(),
+ PACKAGE_BUGREPORT)};
+ }
}
return ret;
}
@@ -664,42 +684,50 @@ UniValue RPCHelpMan::GetArgMap() const
return arr;
}
-void RPCArg::MatchesType(const UniValue& request) const
+static std::optional<UniValue::VType> ExpectedType(RPCArg::Type type)
{
- if (m_opts.skip_type_check) return;
- if (IsOptional() && request.isNull()) return;
- switch (m_type) {
+ using Type = RPCArg::Type;
+ switch (type) {
case Type::STR_HEX:
case Type::STR: {
- RPCTypeCheckArgument(request, UniValue::VSTR);
- return;
+ return UniValue::VSTR;
}
case Type::NUM: {
- RPCTypeCheckArgument(request, UniValue::VNUM);
- return;
+ return UniValue::VNUM;
}
case Type::AMOUNT: {
// VNUM or VSTR, checked inside AmountFromValue()
- return;
+ return std::nullopt;
}
case Type::RANGE: {
// VNUM or VARR, checked inside ParseRange()
- return;
+ return std::nullopt;
}
case Type::BOOL: {
- RPCTypeCheckArgument(request, UniValue::VBOOL);
- return;
+ return UniValue::VBOOL;
}
case Type::OBJ:
case Type::OBJ_USER_KEYS: {
- RPCTypeCheckArgument(request, UniValue::VOBJ);
- return;
+ return UniValue::VOBJ;
}
case Type::ARR: {
- RPCTypeCheckArgument(request, UniValue::VARR);
- return;
+ return UniValue::VARR;
}
} // no default case, so the compiler can warn about missing cases
+ NONFATAL_UNREACHABLE();
+}
+
+UniValue RPCArg::MatchesType(const UniValue& request) const
+{
+ if (m_opts.skip_type_check) return true;
+ if (IsOptional() && request.isNull()) return true;
+ const auto exp_type{ExpectedType(m_type)};
+ if (!exp_type) return true; // nothing to check
+
+ if (*exp_type != request.getType()) {
+ return strprintf("JSON value of type %s is not of expected type %s", uvTypeName(request.getType()), uvTypeName(*exp_type));
+ }
+ return true;
}
std::string RPCArg::GetFirstName() const
@@ -768,7 +796,6 @@ std::string RPCArg::ToDescriptionString(bool is_named_arg) const
ret += ", optional, default=" + std::get<RPCArg::Default>(m_fallback).write();
} else {
switch (std::get<RPCArg::Optional>(m_fallback)) {
- case RPCArg::Optional::OMITTED_NAMED_ARG: // Deprecated alias for OMITTED, can be removed
case RPCArg::Optional::OMITTED: {
if (is_named_arg) ret += ", optional"; // Default value is "null" in dicts. Otherwise,
// nothing to do. Element is treated as if not present and has no default value
@@ -883,53 +910,77 @@ void RPCResult::ToSections(Sections& sections, const OuterType outer_type, const
NONFATAL_UNREACHABLE();
}
-bool RPCResult::MatchesType(const UniValue& result) const
+static std::optional<UniValue::VType> ExpectedType(RPCResult::Type type)
{
- if (m_skip_type_check) {
- return true;
- }
- switch (m_type) {
+ using Type = RPCResult::Type;
+ switch (type) {
case Type::ELISION:
case Type::ANY: {
- return true;
+ return std::nullopt;
}
case Type::NONE: {
- return UniValue::VNULL == result.getType();
+ return UniValue::VNULL;
}
case Type::STR:
case Type::STR_HEX: {
- return UniValue::VSTR == result.getType();
+ return UniValue::VSTR;
}
case Type::NUM:
case Type::STR_AMOUNT:
case Type::NUM_TIME: {
- return UniValue::VNUM == result.getType();
+ return UniValue::VNUM;
}
case Type::BOOL: {
- return UniValue::VBOOL == result.getType();
+ return UniValue::VBOOL;
}
case Type::ARR_FIXED:
case Type::ARR: {
- if (UniValue::VARR != result.getType()) return false;
+ return UniValue::VARR;
+ }
+ case Type::OBJ_DYN:
+ case Type::OBJ: {
+ return UniValue::VOBJ;
+ }
+ } // no default case, so the compiler can warn about missing cases
+ NONFATAL_UNREACHABLE();
+}
+
+UniValue RPCResult::MatchesType(const UniValue& result) const
+{
+ if (m_skip_type_check) {
+ return true;
+ }
+
+ const auto exp_type = ExpectedType(m_type);
+ if (!exp_type) return true; // can be any type, so nothing to check
+
+ if (*exp_type != result.getType()) {
+ return strprintf("returned type is %s, but declared as %s in doc", uvTypeName(result.getType()), uvTypeName(*exp_type));
+ }
+
+ if (UniValue::VARR == result.getType()) {
+ UniValue errors(UniValue::VOBJ);
for (size_t i{0}; i < result.get_array().size(); ++i) {
// If there are more results than documented, re-use the last doc_inner.
const RPCResult& doc_inner{m_inner.at(std::min(m_inner.size() - 1, i))};
- if (!doc_inner.MatchesType(result.get_array()[i])) return false;
+ UniValue match{doc_inner.MatchesType(result.get_array()[i])};
+ if (!match.isTrue()) errors.pushKV(strprintf("%d", i), match);
}
- return true; // empty result array is valid
+ if (errors.empty()) return true; // empty result array is valid
+ return errors;
}
- case Type::OBJ_DYN:
- case Type::OBJ: {
- if (UniValue::VOBJ != result.getType()) return false;
+
+ if (UniValue::VOBJ == result.getType()) {
if (!m_inner.empty() && m_inner.at(0).m_type == Type::ELISION) return true;
+ UniValue errors(UniValue::VOBJ);
if (m_type == Type::OBJ_DYN) {
const RPCResult& doc_inner{m_inner.at(0)}; // Assume all types are the same, randomly pick the first
for (size_t i{0}; i < result.get_obj().size(); ++i) {
- if (!doc_inner.MatchesType(result.get_obj()[i])) {
- return false;
- }
+ UniValue match{doc_inner.MatchesType(result.get_obj()[i])};
+ if (!match.isTrue()) errors.pushKV(result.getKeys()[i], match);
}
- return true; // empty result obj is valid
+ if (errors.empty()) return true; // empty result obj is valid
+ return errors;
}
std::set<std::string> doc_keys;
for (const auto& doc_entry : m_inner) {
@@ -939,7 +990,7 @@ bool RPCResult::MatchesType(const UniValue& result) const
result.getObjMap(result_obj);
for (const auto& result_entry : result_obj) {
if (doc_keys.find(result_entry.first) == doc_keys.end()) {
- return false; // missing documentation
+ errors.pushKV(result_entry.first, "key returned that was not in doc");
}
}
@@ -947,18 +998,18 @@ bool RPCResult::MatchesType(const UniValue& result) const
const auto result_it{result_obj.find(doc_entry.m_key_name)};
if (result_it == result_obj.end()) {
if (!doc_entry.m_optional) {
- return false; // result is missing a required key
+ errors.pushKV(doc_entry.m_key_name, "key missing, despite not being optional in doc");
}
continue;
}
- if (!doc_entry.MatchesType(result_it->second)) {
- return false; // wrong type
- }
+ UniValue match{doc_entry.MatchesType(result_it->second)};
+ if (!match.isTrue()) errors.pushKV(doc_entry.m_key_name, match);
}
- return true;
+ if (errors.empty()) return true;
+ return errors;
}
- } // no default case, so the compiler can warn about missing cases
- NONFATAL_UNREACHABLE();
+
+ return true;
}
void RPCResult::CheckInnerDoc() const
diff --git a/src/rpc/util.h b/src/rpc/util.h
index 30c46bfdcd..e3783c8f76 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -62,11 +62,6 @@ struct UniValueType {
UniValue::VType type;
};
-/**
- * Type-check one argument; throws JSONRPCError if wrong type given.
- */
-void RPCTypeCheckArgument(const UniValue& value, const UniValueType& typeExpected);
-
/*
Check for expected keys/value types in an Object.
*/
@@ -154,18 +149,14 @@ struct RPCArg {
/** Required arg */
NO,
/**
- * The arg is optional for one of two reasons:
- *
- * Optional arg that is a named argument and has a default value of
- * `null`.
- *
- * Optional argument with default value omitted because they are
- * implicitly clear. That is, elements in an array may not
- * exist by default.
+ * Optional argument for which the default value is omitted from
+ * help text for one of two reasons:
+ * - It's a named argument and has a default value of `null`.
+ * - Its default value is implicitly clear. That is, elements in an
+ * array may not exist by default.
* When possible, the default value should be specified.
*/
OMITTED,
- OMITTED_NAMED_ARG, // Deprecated alias for OMITTED, can be removed
};
/** Hint for default value */
using DefaultHint = std::string;
@@ -214,8 +205,11 @@ struct RPCArg {
bool IsOptional() const;
- /** Check whether the request JSON type matches. */
- void MatchesType(const UniValue& request) const;
+ /**
+ * Check whether the request JSON type matches.
+ * Returns true if type matches, or object describing error(s) if not.
+ */
+ UniValue MatchesType(const UniValue& request) const;
/** Return the first of all aliases */
std::string GetFirstName() const;
@@ -324,8 +318,10 @@ struct RPCResult {
std::string ToStringObj() const;
/** Return the description string, including the result type. */
std::string ToDescriptionString() const;
- /** Check whether the result JSON type matches. */
- bool MatchesType(const UniValue& result) const;
+ /** Check whether the result JSON type matches.
+ * Returns true if type matches, or object describing error(s) if not.
+ */
+ UniValue MatchesType(const UniValue& result) const;
private:
void CheckInnerDoc() const;