diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index b3b8ee2b78d..76170c32018 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -110,7 +110,7 @@ static RPCHelpMan getnetworkhashps() { ChainstateManager& chainman = EnsureAnyChainman(request.context); LOCK(cs_main); - return GetNetworkHashPS(!request.params[0].isNull() ? request.params[0].getInt() : 120, !request.params[1].isNull() ? request.params[1].getInt() : -1, chainman.ActiveChain()); + return GetNetworkHashPS(self.Arg(0), self.Arg(1), chainman.ActiveChain()); }, }; } @@ -217,12 +217,12 @@ static RPCHelpMan generatetodescriptor() "\nGenerate 11 blocks to mydesc\n" + HelpExampleCli("generatetodescriptor", "11 \"mydesc\"")}, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { - const int num_blocks{request.params[0].getInt()}; - const uint64_t max_tries{request.params[2].isNull() ? DEFAULT_MAX_TRIES : request.params[2].getInt()}; + const auto num_blocks{self.Arg(0)}; + const auto max_tries{self.Arg(2)}; CScript coinbase_script; std::string error; - if (!getScriptFromDescriptor(request.params[1].get_str(), coinbase_script, error)) { + if (!getScriptFromDescriptor(self.Arg(1), coinbase_script, error)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error); } @@ -468,9 +468,10 @@ static RPCHelpMan prioritisetransaction() LOCK(cs_main); uint256 hash(ParseHashV(request.params[0], "txid")); + const auto dummy{self.MaybeArg(1)}; CAmount nAmount = request.params[2].getInt(); - if (!(request.params[1].isNull() || request.params[1].get_real() == 0)) { + if (dummy && *dummy != 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Priority is no longer supported, dummy argument to prioritisetransaction must be 0."); } diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index 08a778a60e8..45b7d89a7bc 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -609,7 +609,10 @@ UniValue RPCHelpMan::HandleRequest(const JSONRPCRequest& request) const if (!arg_mismatch.empty()) { throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Wrong type passed:\n%s", arg_mismatch.write(4))); } + CHECK_NONFATAL(m_req == nullptr); + m_req = &request; UniValue ret = m_fun(*this, request); + m_req = nullptr; if (gArgs.GetBoolArg("-rpcdoccheck", DEFAULT_RPC_DOC_CHECK)) { UniValue mismatch{UniValue::VARR}; for (const auto& res : m_results.m_results) { @@ -635,6 +638,49 @@ UniValue RPCHelpMan::HandleRequest(const JSONRPCRequest& request) const return ret; } +using CheckFn = void(const RPCArg&); +static const UniValue* DetailMaybeArg(CheckFn* check, const std::vector& params, const JSONRPCRequest* req, size_t i) +{ + CHECK_NONFATAL(i < params.size()); + const UniValue& arg{CHECK_NONFATAL(req)->params[i]}; + const RPCArg& param{params.at(i)}; + if (check) check(param); + + if (!arg.isNull()) return &arg; + if (!std::holds_alternative(param.m_fallback)) return nullptr; + return &std::get(param.m_fallback); +} + +static void CheckRequiredOrDefault(const RPCArg& param) +{ + // Must use `Arg(i)` to get the argument or its default value. + const bool required{ + std::holds_alternative(param.m_fallback) && RPCArg::Optional::NO == std::get(param.m_fallback), + }; + CHECK_NONFATAL(required || std::holds_alternative(param.m_fallback)); +} + +#define TMPL_INST(check_param, ret_type, return_code) \ + template <> \ + ret_type RPCHelpMan::ArgValue(size_t i) const \ + { \ + const UniValue* maybe_arg{ \ + DetailMaybeArg(check_param, m_args, m_req, i), \ + }; \ + return return_code \ + } \ + void force_semicolon(ret_type) + +// Optional arg (without default). Can also be called on required args, if needed. +TMPL_INST(nullptr, std::optional, maybe_arg ? std::optional{maybe_arg->get_real()} : std::nullopt;); +TMPL_INST(nullptr, std::optional, maybe_arg ? std::optional{maybe_arg->get_bool()} : std::nullopt;); +TMPL_INST(nullptr, const std::string*, maybe_arg ? &maybe_arg->get_str() : nullptr;); + +// Required arg or optional arg with default value. +TMPL_INST(CheckRequiredOrDefault, int, CHECK_NONFATAL(maybe_arg)->getInt();); +TMPL_INST(CheckRequiredOrDefault, uint64_t, CHECK_NONFATAL(maybe_arg)->getInt();); +TMPL_INST(CheckRequiredOrDefault, const std::string&, CHECK_NONFATAL(maybe_arg)->get_str();); + bool RPCHelpMan::IsValidNumArgs(size_t num_args) const { size_t num_required_args = 0; diff --git a/src/rpc/util.h b/src/rpc/util.h index ac0b2c088ee..392540ffad5 100644 --- a/src/rpc/util.h +++ b/src/rpc/util.h @@ -6,6 +6,7 @@ #define BITCOIN_RPC_UTIL_H #include +#include #include #include #include @@ -14,13 +15,29 @@ #include #include