mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-03-26 17:52:13 +01:00
Merge bitcoin/bitcoin#26039: refactor: Run type check against RPCArgs (1/2)
fa9f6d7bcdba5f18c46fff1dcc0ac6d3dd8db75d rpc: Run type check against RPCArgs (MarcoFalke) faf96721a66dcc215ea9d6affb30f9a00cc37000 test: Fix wrong types passed to RPCs (MarcoFalke) Pull request description: It seems brittle to require `RPCTypeCheck` being called inside the code logic. Without compile-time enforcement this will lead to places where it is forgotten and thus to inconsistencies and bugs. Fix this by removing the calls to `RPCTypeCheck` and doing the check internally. The changes should be reviewed as refactoring changes. However, if they change behavior, it will be a bugfix. For example the changes here happen to also detect/fix bugs like the one fixed in commit 3b5fb6e77a93f58b3d03b1eec3595f5c45e633a9. ACKs for top commit: ajtowns: ACK fa9f6d7bcdba5f18c46fff1dcc0ac6d3dd8db75d Tree-SHA512: fb2c0981fe6e24da3ca7dbc06898730779ea4e02ea485458505a281cf421015e44dad0221a04023fc547ea2c660d94657909843fc85d92b847611ec097532439
This commit is contained in:
commit
7799f53542
@ -445,11 +445,6 @@ static RPCHelpMan getblockfrompeer()
|
||||
},
|
||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||
{
|
||||
RPCTypeCheck(request.params, {
|
||||
UniValue::VSTR, // blockhash
|
||||
UniValue::VNUM, // peer_id
|
||||
});
|
||||
|
||||
const NodeContext& node = EnsureAnyNodeContext(request.context);
|
||||
ChainstateManager& chainman = EnsureChainman(node);
|
||||
PeerManager& peerman = EnsurePeerman(node);
|
||||
@ -655,7 +650,8 @@ static RPCHelpMan getblock()
|
||||
"If verbosity is 3, returns an Object with information about block <hash> and information about each transaction, including prevout information for inputs (only for unpruned blocks in the current best chain).\n",
|
||||
{
|
||||
{"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
|
||||
{"verbosity|verbose", RPCArg::Type::NUM, RPCArg::Default{1}, "0 for hex-encoded data, 1 for a JSON object, 2 for JSON object with transaction data, and 3 for JSON object with transaction data including prevout information for inputs"},
|
||||
{"verbosity|verbose", RPCArg::Type::NUM, RPCArg::Default{1}, "0 for hex-encoded data, 1 for a JSON object, 2 for JSON object with transaction data, and 3 for JSON object with transaction data including prevout information for inputs",
|
||||
RPCArgOptions{.skip_type_check = true}},
|
||||
},
|
||||
{
|
||||
RPCResult{"for verbosity = 0",
|
||||
@ -873,7 +869,11 @@ static RPCHelpMan gettxoutsetinfo()
|
||||
"Note this call may take some time if you are not using coinstatsindex.\n",
|
||||
{
|
||||
{"hash_type", RPCArg::Type::STR, RPCArg::Default{"hash_serialized_2"}, "Which UTXO set hash should be calculated. Options: 'hash_serialized_2' (the legacy algorithm), 'muhash', 'none'."},
|
||||
{"hash_or_height", RPCArg::Type::NUM, RPCArg::DefaultHint{"the current best block"}, "The block hash or height of the target height (only available with coinstatsindex).", RPCArgOptions{.type_str={"", "string or numeric"}}},
|
||||
{"hash_or_height", RPCArg::Type::NUM, RPCArg::DefaultHint{"the current best block"}, "The block hash or height of the target height (only available with coinstatsindex).",
|
||||
RPCArgOptions{
|
||||
.skip_type_check = true,
|
||||
.type_str = {"", "string or numeric"},
|
||||
}},
|
||||
{"use_index", RPCArg::Type::BOOL, RPCArg::Default{true}, "Use coinstatsindex, if available."},
|
||||
},
|
||||
RPCResult{
|
||||
@ -1743,7 +1743,11 @@ static RPCHelpMan getblockstats()
|
||||
"\nCompute per block statistics for a given window. All amounts are in satoshis.\n"
|
||||
"It won't work for some heights with pruning.\n",
|
||||
{
|
||||
{"hash_or_height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The block hash or height of the target block", RPCArgOptions{.type_str={"", "string or numeric"}}},
|
||||
{"hash_or_height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The block hash or height of the target block",
|
||||
RPCArgOptions{
|
||||
.skip_type_check = true,
|
||||
.type_str = {"", "string or numeric"},
|
||||
}},
|
||||
{"stats", RPCArg::Type::ARR, RPCArg::DefaultHint{"all values"}, "Values to plot (see result below)",
|
||||
{
|
||||
{"height", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Selected statistic"},
|
||||
@ -2145,8 +2149,6 @@ static RPCHelpMan scantxoutset()
|
||||
},
|
||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||
{
|
||||
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR});
|
||||
|
||||
UniValue result(UniValue::VOBJ);
|
||||
if (request.params[0].get_str() == "status") {
|
||||
CoinsViewScanReserver reserver;
|
||||
|
@ -63,8 +63,6 @@ static RPCHelpMan estimatesmartfee()
|
||||
},
|
||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||
{
|
||||
RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VSTR});
|
||||
|
||||
CBlockPolicyEstimator& fee_estimator = EnsureAnyFeeEstimator(request.context);
|
||||
const NodeContext& node = EnsureAnyNodeContext(request.context);
|
||||
const CTxMemPool& mempool = EnsureMemPool(node);
|
||||
@ -155,8 +153,6 @@ static RPCHelpMan estimaterawfee()
|
||||
},
|
||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||
{
|
||||
RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VNUM}, true);
|
||||
|
||||
CBlockPolicyEstimator& fee_estimator = EnsureAnyFeeEstimator(request.context);
|
||||
|
||||
unsigned int max_target = fee_estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
|
||||
|
@ -61,11 +61,6 @@ static RPCHelpMan sendrawtransaction()
|
||||
},
|
||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||
{
|
||||
RPCTypeCheck(request.params, {
|
||||
UniValue::VSTR,
|
||||
UniValueType(), // VNUM or VSTR, checked inside AmountFromValue()
|
||||
});
|
||||
|
||||
CMutableTransaction mtx;
|
||||
if (!DecodeHexTx(mtx, request.params[0].get_str())) {
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed. Make sure the tx has at least one input.");
|
||||
@ -147,10 +142,6 @@ static RPCHelpMan testmempoolaccept()
|
||||
},
|
||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||
{
|
||||
RPCTypeCheck(request.params, {
|
||||
UniValue::VARR,
|
||||
UniValueType(), // VNUM or VSTR, checked inside AmountFromValue()
|
||||
});
|
||||
const UniValue raw_transactions = request.params[0].get_array();
|
||||
if (raw_transactions.size() < 1 || raw_transactions.size() > MAX_PACKAGE_COUNT) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER,
|
||||
@ -800,9 +791,6 @@ static RPCHelpMan submitpackage()
|
||||
if (!Params().IsMockableChain()) {
|
||||
throw std::runtime_error("submitpackage is for regression testing (-regtest mode) only");
|
||||
}
|
||||
RPCTypeCheck(request.params, {
|
||||
UniValue::VARR,
|
||||
});
|
||||
const UniValue raw_transactions = request.params[0].get_array();
|
||||
if (raw_transactions.size() < 1 || raw_transactions.size() > MAX_PACKAGE_COUNT) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER,
|
||||
|
@ -362,7 +362,6 @@ static RPCHelpMan addconnection()
|
||||
throw std::runtime_error("addconnection is for regression testing (-regtest mode) only.");
|
||||
}
|
||||
|
||||
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VSTR});
|
||||
const std::string address = request.params[0].get_str();
|
||||
const std::string conn_type_in{TrimString(request.params[1].get_str())};
|
||||
ConnectionType conn_type{};
|
||||
|
@ -53,7 +53,6 @@ static RPCHelpMan setmocktime()
|
||||
// ensure all call sites of GetTime() are accessing this safely.
|
||||
LOCK(cs_main);
|
||||
|
||||
RPCTypeCheck(request.params, {UniValue::VNUM});
|
||||
const int64_t time{request.params[0].getInt<int64_t>()};
|
||||
if (time < 0) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Mocktime cannot be negative: %s.", time));
|
||||
@ -107,8 +106,6 @@ static RPCHelpMan mockscheduler()
|
||||
throw std::runtime_error("mockscheduler is for regression testing (-regtest mode) only");
|
||||
}
|
||||
|
||||
// check params are valid values
|
||||
RPCTypeCheck(request.params, {UniValue::VNUM});
|
||||
int64_t delta_seconds = request.params[0].getInt<int64_t>();
|
||||
if (delta_seconds <= 0 || delta_seconds > 3600) {
|
||||
throw std::runtime_error("delta_time must be between 1 and 3600 seconds (1 hr)");
|
||||
@ -296,18 +293,18 @@ static RPCHelpMan echo(const std::string& name)
|
||||
"\nIt will return an internal bug report when arg9='trigger_internal_bug' is passed.\n"
|
||||
"\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, ""},
|
||||
{"arg1", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
|
||||
{"arg2", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
|
||||
{"arg3", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
|
||||
{"arg4", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
|
||||
{"arg5", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
|
||||
{"arg6", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
|
||||
{"arg7", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
|
||||
{"arg8", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
|
||||
{"arg9", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
|
||||
},
|
||||
{
|
||||
{"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}},
|
||||
},
|
||||
RPCResult{RPCResult::Type::ANY, "", "Returns whatever was passed in"},
|
||||
RPCExamples{""},
|
||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||
|
@ -195,8 +195,6 @@ static RPCHelpMan getdescriptorinfo()
|
||||
},
|
||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||
{
|
||||
RPCTypeCheck(request.params, {UniValue::VSTR});
|
||||
|
||||
FlatSigningProvider provider;
|
||||
std::string error;
|
||||
auto desc = Parse(request.params[0].get_str(), provider, error);
|
||||
@ -247,7 +245,6 @@ static RPCHelpMan deriveaddresses()
|
||||
},
|
||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||
{
|
||||
RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType()}); // Range argument is checked later
|
||||
const std::string desc_str = request.params[0].get_str();
|
||||
|
||||
int64_t range_begin = 0;
|
||||
|
@ -162,7 +162,7 @@ static std::vector<RPCArg> CreateTxDoc()
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
RPCArgOptions{.skip_type_check = true}},
|
||||
{"locktime", RPCArg::Type::NUM, RPCArg::Default{0}, "Raw locktime. Non-0 value also locktime-activates inputs"},
|
||||
{"replaceable", RPCArg::Type::BOOL, RPCArg::Default{true}, "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."},
|
||||
@ -185,7 +185,8 @@ static RPCHelpMan getrawtransaction()
|
||||
"If verbosity is 2, returns a JSON Object with information about the transaction, including fee and prevout information.",
|
||||
{
|
||||
{"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"},
|
||||
{"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"},
|
||||
},
|
||||
{
|
||||
@ -354,14 +355,6 @@ static RPCHelpMan createrawtransaction()
|
||||
},
|
||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||
{
|
||||
RPCTypeCheck(request.params, {
|
||||
UniValue::VARR,
|
||||
UniValueType(), // ARR or OBJ, checked later
|
||||
UniValue::VNUM,
|
||||
UniValue::VBOOL
|
||||
}, true
|
||||
);
|
||||
|
||||
std::optional<bool> rbf;
|
||||
if (!request.params[3].isNull()) {
|
||||
rbf = request.params[3].get_bool();
|
||||
@ -397,8 +390,6 @@ static RPCHelpMan decoderawtransaction()
|
||||
},
|
||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||
{
|
||||
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL});
|
||||
|
||||
CMutableTransaction mtx;
|
||||
|
||||
bool try_witness = request.params[1].isNull() ? true : request.params[1].get_bool();
|
||||
@ -451,8 +442,6 @@ static RPCHelpMan decodescript()
|
||||
},
|
||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||
{
|
||||
RPCTypeCheck(request.params, {UniValue::VSTR});
|
||||
|
||||
UniValue r(UniValue::VOBJ);
|
||||
CScript script;
|
||||
if (request.params[0].get_str().size() > 0){
|
||||
@ -702,8 +691,6 @@ static RPCHelpMan signrawtransactionwithkey()
|
||||
},
|
||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||
{
|
||||
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VARR, UniValue::VSTR}, true);
|
||||
|
||||
CMutableTransaction mtx;
|
||||
if (!DecodeHexTx(mtx, request.params[0].get_str())) {
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed. Make sure the tx has at least one input.");
|
||||
@ -981,8 +968,6 @@ static RPCHelpMan decodepsbt()
|
||||
},
|
||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||
{
|
||||
RPCTypeCheck(request.params, {UniValue::VSTR});
|
||||
|
||||
// Unserialize the transactions
|
||||
PartiallySignedTransaction psbtx;
|
||||
std::string error;
|
||||
@ -1395,8 +1380,6 @@ static RPCHelpMan combinepsbt()
|
||||
},
|
||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||
{
|
||||
RPCTypeCheck(request.params, {UniValue::VARR}, true);
|
||||
|
||||
// Unserialize the transactions
|
||||
std::vector<PartiallySignedTransaction> psbtxs;
|
||||
UniValue txs = request.params[0].get_array();
|
||||
@ -1450,8 +1433,6 @@ static RPCHelpMan finalizepsbt()
|
||||
},
|
||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||
{
|
||||
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL}, true);
|
||||
|
||||
// Unserialize the transactions
|
||||
PartiallySignedTransaction psbtx;
|
||||
std::string error;
|
||||
@ -1499,14 +1480,6 @@ static RPCHelpMan createpsbt()
|
||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||
{
|
||||
|
||||
RPCTypeCheck(request.params, {
|
||||
UniValue::VARR,
|
||||
UniValueType(), // ARR or OBJ, checked later
|
||||
UniValue::VNUM,
|
||||
UniValue::VBOOL,
|
||||
}, true
|
||||
);
|
||||
|
||||
std::optional<bool> rbf;
|
||||
if (!request.params[3].isNull()) {
|
||||
rbf = request.params[3].get_bool();
|
||||
@ -1560,8 +1533,6 @@ static RPCHelpMan converttopsbt()
|
||||
},
|
||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||
{
|
||||
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL, UniValue::VBOOL}, true);
|
||||
|
||||
// parse hex string from parameter
|
||||
CMutableTransaction tx;
|
||||
bool permitsigdata = request.params[1].isNull() ? false : request.params[1].get_bool();
|
||||
@ -1623,8 +1594,6 @@ static RPCHelpMan utxoupdatepsbt()
|
||||
},
|
||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||
{
|
||||
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR}, true);
|
||||
|
||||
// Unserialize the transactions
|
||||
PartiallySignedTransaction psbtx;
|
||||
std::string error;
|
||||
@ -1714,8 +1683,6 @@ static RPCHelpMan joinpsbts()
|
||||
},
|
||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||
{
|
||||
RPCTypeCheck(request.params, {UniValue::VARR}, true);
|
||||
|
||||
// Unserialize the transactions
|
||||
std::vector<PartiallySignedTransaction> psbtxs;
|
||||
UniValue txs = request.params[0].get_array();
|
||||
@ -1842,8 +1809,6 @@ static RPCHelpMan analyzepsbt()
|
||||
},
|
||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||
{
|
||||
RPCTypeCheck(request.params, {UniValue::VSTR});
|
||||
|
||||
// Unserialize the transaction
|
||||
PartiallySignedTransaction psbtx;
|
||||
std::string error;
|
||||
|
@ -30,23 +30,6 @@ std::string GetAllOutputTypes()
|
||||
return Join(ret, ", ");
|
||||
}
|
||||
|
||||
void RPCTypeCheck(const UniValue& params,
|
||||
const std::list<UniValueType>& typesExpected,
|
||||
bool fAllowNull)
|
||||
{
|
||||
unsigned int i = 0;
|
||||
for (const UniValueType& t : typesExpected) {
|
||||
if (params.size() <= i)
|
||||
break;
|
||||
|
||||
const UniValue& v = params[i];
|
||||
if (!(fAllowNull && v.isNull())) {
|
||||
RPCTypeCheckArgument(v, t);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void RPCTypeCheckArgument(const UniValue& value, const UniValueType& typeExpected)
|
||||
{
|
||||
if (!typeExpected.typeAny && value.type() != typeExpected.type) {
|
||||
@ -579,6 +562,9 @@ UniValue RPCHelpMan::HandleRequest(const JSONRPCRequest& request) const
|
||||
if (request.mode == JSONRPCRequest::GET_HELP || !IsValidNumArgs(request.params.size())) {
|
||||
throw std::runtime_error(ToString());
|
||||
}
|
||||
for (size_t i{0}; i < m_args.size(); ++i) {
|
||||
m_args.at(i).MatchesType(request.params[i]);
|
||||
}
|
||||
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); }));
|
||||
@ -677,6 +663,44 @@ UniValue RPCHelpMan::GetArgMap() const
|
||||
return arr;
|
||||
}
|
||||
|
||||
void RPCArg::MatchesType(const UniValue& request) const
|
||||
{
|
||||
if (m_opts.skip_type_check) return;
|
||||
if (IsOptional() && request.isNull()) return;
|
||||
switch (m_type) {
|
||||
case Type::STR_HEX:
|
||||
case Type::STR: {
|
||||
RPCTypeCheckArgument(request, UniValue::VSTR);
|
||||
return;
|
||||
}
|
||||
case Type::NUM: {
|
||||
RPCTypeCheckArgument(request, UniValue::VNUM);
|
||||
return;
|
||||
}
|
||||
case Type::AMOUNT: {
|
||||
// VNUM or VSTR, checked inside AmountFromValue()
|
||||
return;
|
||||
}
|
||||
case Type::RANGE: {
|
||||
// VNUM or VARR, checked inside ParseRange()
|
||||
return;
|
||||
}
|
||||
case Type::BOOL: {
|
||||
RPCTypeCheckArgument(request, UniValue::VBOOL);
|
||||
return;
|
||||
}
|
||||
case Type::OBJ:
|
||||
case Type::OBJ_USER_KEYS: {
|
||||
RPCTypeCheckArgument(request, UniValue::VOBJ);
|
||||
return;
|
||||
}
|
||||
case Type::ARR: {
|
||||
RPCTypeCheckArgument(request, UniValue::VARR);
|
||||
return;
|
||||
}
|
||||
} // no default case, so the compiler can warn about missing cases
|
||||
}
|
||||
|
||||
std::string RPCArg::GetFirstName() const
|
||||
{
|
||||
return m_names.substr(0, m_names.find("|"));
|
||||
|
@ -62,13 +62,6 @@ struct UniValueType {
|
||||
UniValue::VType type;
|
||||
};
|
||||
|
||||
/**
|
||||
* Type-check arguments; throws JSONRPCError if wrong type given. Does not check that
|
||||
* the right number of arguments are passed, just that any passed are the correct type.
|
||||
*/
|
||||
void RPCTypeCheck(const UniValue& params,
|
||||
const std::list<UniValueType>& typesExpected, bool fAllowNull=false);
|
||||
|
||||
/**
|
||||
* Type-check one argument; throws JSONRPCError if wrong type given.
|
||||
*/
|
||||
@ -138,6 +131,7 @@ enum class OuterType {
|
||||
};
|
||||
|
||||
struct RPCArgOptions {
|
||||
bool skip_type_check{false};
|
||||
std::string oneline_description{}; //!< Should be empty unless it is supposed to override the auto-generated summary line
|
||||
std::vector<std::string> type_str{}; //!< Should be empty unless it is supposed to override the auto-generated type strings. Vector length is either 0 or 2, m_opts.type_str.at(0) will override the type of the value in a key-value pair, m_opts.type_str.at(1) will override the type in the argument description.
|
||||
bool hidden{false}; //!< For testing only
|
||||
@ -217,6 +211,9 @@ struct RPCArg {
|
||||
|
||||
bool IsOptional() const;
|
||||
|
||||
/** Check whether the request JSON type matches. */
|
||||
void MatchesType(const UniValue& request) const;
|
||||
|
||||
/** Return the first of all aliases */
|
||||
std::string GetFirstName() const;
|
||||
|
||||
|
@ -1330,8 +1330,6 @@ RPCHelpMan importmulti()
|
||||
// the user could have gotten from another RPC command prior to now
|
||||
wallet.BlockUntilSyncedToCurrentChain();
|
||||
|
||||
RPCTypeCheck(mainRequest.params, {UniValue::VARR, UniValue::VOBJ});
|
||||
|
||||
EnsureLegacyScriptPubKeyMan(*pwallet, true);
|
||||
|
||||
const UniValue& requests = mainRequest.params[0];
|
||||
@ -1652,8 +1650,6 @@ RPCHelpMan importdescriptors()
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "importdescriptors is not available for non-descriptor wallets");
|
||||
}
|
||||
|
||||
RPCTypeCheck(main_request.params, {UniValue::VARR, UniValue::VOBJ});
|
||||
|
||||
WalletRescanReserver reserver(*pwallet);
|
||||
if (!reserver.reserve()) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
|
||||
|
@ -317,7 +317,11 @@ RPCHelpMan sendmany()
|
||||
"\nSend multiple times. Amounts are double-precision floating point numbers." +
|
||||
HELP_REQUIRING_PASSPHRASE,
|
||||
{
|
||||
{"dummy", RPCArg::Type::STR, RPCArg::Optional::NO, "Must be set to \"\" for backwards compatibility.", RPCArgOptions{.oneline_description="\"\""}},
|
||||
{"dummy", RPCArg::Type::STR, RPCArg::Optional::NO, "Must be set to \"\" for backwards compatibility.",
|
||||
RPCArgOptions{
|
||||
.skip_type_check = true,
|
||||
.oneline_description = "\"\"",
|
||||
}},
|
||||
{"amounts", RPCArg::Type::OBJ_USER_KEYS, RPCArg::Optional::NO, "The addresses and amounts",
|
||||
{
|
||||
{"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The bitcoin address is the key, the numeric amount (can be string) in " + CURRENCY_UNIT + " is the value"},
|
||||
@ -798,7 +802,10 @@ RPCHelpMan fundrawtransaction()
|
||||
},
|
||||
},
|
||||
FundTxDoc()),
|
||||
RPCArgOptions{.oneline_description="options"}},
|
||||
RPCArgOptions{
|
||||
.skip_type_check = true,
|
||||
.oneline_description = "options",
|
||||
}},
|
||||
{"iswitness", RPCArg::Type::BOOL, RPCArg::DefaultHint{"depends on heuristic tests"}, "Whether the transaction hex is a serialized witness transaction.\n"
|
||||
"If iswitness is not present, heuristic tests will be used in decoding.\n"
|
||||
"If true, only witness deserialization will be tried.\n"
|
||||
@ -830,8 +837,6 @@ RPCHelpMan fundrawtransaction()
|
||||
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
|
||||
if (!pwallet) return UniValue::VNULL;
|
||||
|
||||
RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType(), UniValue::VBOOL});
|
||||
|
||||
// parse hex string from parameter
|
||||
CMutableTransaction tx;
|
||||
bool try_witness = request.params[2].isNull() ? true : request.params[2].get_bool();
|
||||
@ -920,8 +925,6 @@ RPCHelpMan signrawtransactionwithwallet()
|
||||
const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
|
||||
if (!pwallet) return UniValue::VNULL;
|
||||
|
||||
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VSTR}, true);
|
||||
|
||||
CMutableTransaction mtx;
|
||||
if (!DecodeHexTx(mtx, request.params[0].get_str())) {
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed. Make sure the tx has at least one input.");
|
||||
@ -1021,7 +1024,6 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "bumpfee is not available with wallets that have private keys disabled. Use psbtbumpfee instead.");
|
||||
}
|
||||
|
||||
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VOBJ});
|
||||
uint256 hash(ParseHashV(request.params[0], "txid"));
|
||||
|
||||
CCoinControl coin_control;
|
||||
@ -1155,7 +1157,7 @@ RPCHelpMan send()
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
RPCArgOptions{.skip_type_check = true}},
|
||||
{"conf_target", RPCArg::Type::NUM, RPCArg::DefaultHint{"wallet -txconfirmtarget"}, "Confirmation target in blocks"},
|
||||
{"estimate_mode", RPCArg::Type::STR, RPCArg::Default{"unset"}, "The fee estimate mode, must be one of (case insensitive):\n"
|
||||
"\"" + FeeModes("\"\n\"") + "\""},
|
||||
@ -1227,15 +1229,6 @@ RPCHelpMan send()
|
||||
},
|
||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||
{
|
||||
RPCTypeCheck(request.params, {
|
||||
UniValueType(), // outputs (ARR or OBJ, checked later)
|
||||
UniValue::VNUM, // conf_target
|
||||
UniValue::VSTR, // estimate_mode
|
||||
UniValueType(), // fee_rate, will be checked by AmountFromValue() in SetFeeEstimateMode()
|
||||
UniValue::VOBJ, // options
|
||||
}, true
|
||||
);
|
||||
|
||||
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
|
||||
if (!pwallet) return UniValue::VNULL;
|
||||
|
||||
@ -1338,15 +1331,6 @@ RPCHelpMan sendall()
|
||||
},
|
||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||
{
|
||||
RPCTypeCheck(request.params, {
|
||||
UniValue::VARR, // recipients
|
||||
UniValue::VNUM, // conf_target
|
||||
UniValue::VSTR, // estimate_mode
|
||||
UniValueType(), // fee_rate, will be checked by AmountFromValue() in SetFeeEstimateMode()
|
||||
UniValue::VOBJ, // options
|
||||
}, true
|
||||
);
|
||||
|
||||
std::shared_ptr<CWallet> const pwallet{GetWalletForJSONRPCRequest(request)};
|
||||
if (!pwallet) return UniValue::VNULL;
|
||||
// Make sure the results are valid at least up to the most recent block
|
||||
@ -1561,8 +1545,6 @@ RPCHelpMan walletprocesspsbt()
|
||||
// the user could have gotten from another RPC command prior to now
|
||||
wallet.BlockUntilSyncedToCurrentChain();
|
||||
|
||||
RPCTypeCheck(request.params, {UniValue::VSTR});
|
||||
|
||||
// Unserialize the transaction
|
||||
PartiallySignedTransaction psbtx;
|
||||
std::string error;
|
||||
@ -1637,7 +1619,7 @@ RPCHelpMan walletcreatefundedpsbt()
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
RPCArgOptions{.skip_type_check = true}},
|
||||
{"locktime", RPCArg::Type::NUM, RPCArg::Default{0}, "Raw locktime. Non-0 value also locktime-activates inputs"},
|
||||
{"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
|
||||
Cat<std::vector<RPCArg>>(
|
||||
@ -1690,15 +1672,6 @@ RPCHelpMan walletcreatefundedpsbt()
|
||||
// the user could have gotten from another RPC command prior to now
|
||||
wallet.BlockUntilSyncedToCurrentChain();
|
||||
|
||||
RPCTypeCheck(request.params, {
|
||||
UniValue::VARR,
|
||||
UniValueType(), // ARR or OBJ, checked later
|
||||
UniValue::VNUM,
|
||||
UniValue::VOBJ,
|
||||
UniValue::VBOOL
|
||||
}, true
|
||||
);
|
||||
|
||||
UniValue options{request.params[3].isNull() ? UniValue::VOBJ : request.params[3]};
|
||||
|
||||
CAmount fee;
|
||||
|
@ -568,8 +568,6 @@ static RPCHelpMan upgradewallet()
|
||||
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
|
||||
if (!pwallet) return UniValue::VNULL;
|
||||
|
||||
RPCTypeCheck(request.params, {UniValue::VNUM}, true);
|
||||
|
||||
EnsureWalletIsUnlocked(*pwallet);
|
||||
|
||||
int version = 0;
|
||||
@ -637,8 +635,6 @@ RPCHelpMan simulaterawtransaction()
|
||||
if (!rpc_wallet) return UniValue::VNULL;
|
||||
const CWallet& wallet = *rpc_wallet;
|
||||
|
||||
RPCTypeCheck(request.params, {UniValue::VARR, UniValue::VOBJ}, true);
|
||||
|
||||
LOCK(wallet.cs_wallet);
|
||||
|
||||
UniValue include_watchonly(UniValue::VNULL);
|
||||
|
@ -107,7 +107,7 @@ class DisconnectBanTest(BitcoinTestFramework):
|
||||
|
||||
self.log.info("disconnectnode: fail to disconnect when calling with address and nodeid")
|
||||
address1 = self.nodes[0].getpeerinfo()[0]['addr']
|
||||
node1 = self.nodes[0].getpeerinfo()[0]['addr']
|
||||
node1 = self.nodes[0].getpeerinfo()[0]["id"]
|
||||
assert_raises_rpc_error(-32602, "Only one of address and nodeid should be provided.", self.nodes[0].disconnectnode, address=address1, nodeid=node1)
|
||||
|
||||
self.log.info("disconnectnode: fail to disconnect when calling with junk address")
|
||||
|
@ -114,7 +114,7 @@ class WalletDescriptorTest(BitcoinTestFramework):
|
||||
# Make sure things are disabled
|
||||
self.log.info("Test disabled RPCs")
|
||||
assert_raises_rpc_error(-4, "Only legacy wallets are supported by this command", recv_wrpc.rpc.importprivkey, "cVpF924EspNh8KjYsfhgY96mmxvT6DgdWiTYMtMjuM74hJaU5psW")
|
||||
assert_raises_rpc_error(-4, "Only legacy wallets are supported by this command", recv_wrpc.rpc.importpubkey, send_wrpc.getaddressinfo(send_wrpc.getnewaddress()))
|
||||
assert_raises_rpc_error(-4, "Only legacy wallets are supported by this command", recv_wrpc.rpc.importpubkey, send_wrpc.getaddressinfo(send_wrpc.getnewaddress())["pubkey"])
|
||||
assert_raises_rpc_error(-4, "Only legacy wallets are supported by this command", recv_wrpc.rpc.importaddress, recv_wrpc.getnewaddress())
|
||||
assert_raises_rpc_error(-4, "Only legacy wallets are supported by this command", recv_wrpc.rpc.importmulti, [])
|
||||
assert_raises_rpc_error(-4, "Only legacy wallets are supported by this command", recv_wrpc.rpc.addmultisigaddress, 1, [recv_wrpc.getnewaddress()])
|
||||
|
Loading…
x
Reference in New Issue
Block a user