diff options
author | MarcoFalke <*~=`'#}+{/-|&$^_@721217.xyz> | 2023-08-07 14:53:42 +0200 |
---|---|---|
committer | MarcoFalke <*~=`'#}+{/-|&$^_@721217.xyz> | 2023-08-24 10:44:45 +0200 |
commit | c00000df1605788acadceb90c22ae9f00db8a9dc (patch) | |
tree | 547e525ac6821f751bcca2dd9534043aa75fc382 /src | |
parent | cd5d2f5f0938ad4f8737caf4e7c501101818fd36 (diff) |
rpc: Add MaybeArg() and Arg() default helper
Diffstat (limited to 'src')
-rw-r--r-- | src/rpc/mining.cpp | 11 | ||||
-rw-r--r-- | src/rpc/util.cpp | 46 | ||||
-rw-r--r-- | src/rpc/util.h | 58 | ||||
-rw-r--r-- | src/wallet/rpc/coins.cpp | 4 | ||||
-rw-r--r-- | src/wallet/rpc/wallet.cpp | 2 |
5 files changed, 113 insertions, 8 deletions
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index b3b8ee2b78..76170c3201 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<int>() : 120, !request.params[1].isNull() ? request.params[1].getInt<int>() : -1, chainman.ActiveChain()); + return GetNetworkHashPS(self.Arg<int>(0), self.Arg<int>(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<int>()}; - const uint64_t max_tries{request.params[2].isNull() ? DEFAULT_MAX_TRIES : request.params[2].getInt<int>()}; + const auto num_blocks{self.Arg<int>(0)}; + const auto max_tries{self.Arg<uint64_t>(2)}; CScript coinbase_script; std::string error; - if (!getScriptFromDescriptor(request.params[1].get_str(), coinbase_script, error)) { + if (!getScriptFromDescriptor(self.Arg<std::string>(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<double>(1)}; CAmount nAmount = request.params[2].getInt<int64_t>(); - 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 08a778a60e..45b7d89a7b 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<RPCArg>& 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<RPCArg::Default>(param.m_fallback)) return nullptr; + return &std::get<RPCArg::Default>(param.m_fallback); +} + +static void CheckRequiredOrDefault(const RPCArg& param) +{ + // Must use `Arg<Type>(i)` to get the argument or its default value. + const bool required{ + std::holds_alternative<RPCArg::Optional>(param.m_fallback) && RPCArg::Optional::NO == std::get<RPCArg::Optional>(param.m_fallback), + }; + CHECK_NONFATAL(required || std::holds_alternative<RPCArg::Default>(param.m_fallback)); +} + +#define TMPL_INST(check_param, ret_type, return_code) \ + template <> \ + ret_type RPCHelpMan::ArgValue<ret_type>(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<double>, maybe_arg ? std::optional{maybe_arg->get_real()} : std::nullopt;); +TMPL_INST(nullptr, std::optional<bool>, 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<int>();); +TMPL_INST(CheckRequiredOrDefault, uint64_t, CHECK_NONFATAL(maybe_arg)->getInt<uint64_t>();); +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 ac0b2c088e..392540ffad 100644 --- a/src/rpc/util.h +++ b/src/rpc/util.h @@ -6,6 +6,7 @@ #define BITCOIN_RPC_UTIL_H #include <addresstype.h> +#include <consensus/amount.h> #include <node/transaction.h> #include <outputtype.h> #include <protocol.h> @@ -14,13 +15,29 @@ #include <rpc/request.h> #include <script/script.h> #include <script/sign.h> +#include <uint256.h> #include <univalue.h> #include <util/check.h> +#include <cstddef> +#include <cstdint> +#include <functional> +#include <initializer_list> +#include <map> +#include <optional> #include <string> +#include <type_traits> +#include <utility> #include <variant> #include <vector> +class JSONRPCRequest; +enum ServiceFlags : uint64_t; +enum class OutputType; +enum class TransactionError; +struct FlatSigningProvider; +struct bilingual_str; + static constexpr bool DEFAULT_RPC_DOC_CHECK{ #ifdef RPC_DOC_CHECK true @@ -383,6 +400,44 @@ public: RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples, RPCMethodImpl fun); UniValue HandleRequest(const JSONRPCRequest& request) const; + /** + * Helper to get a request argument. + * This function only works during m_fun(), i.e. it should only be used in + * RPC method implementations. The helper internally checks whether the + * user-passed argument isNull() and parses (from JSON) and returns the + * user-passed argument, or the default value derived from the RPCArg + * documention, or a falsy value if no default was given. + * + * Use Arg<Type>(i) to get the argument or its default value. Otherwise, + * use MaybeArg<Type>(i) to get the optional argument or a falsy value. + * + * The Type passed to this helper must match the corresponding + * RPCArg::Type. + */ + template <typename R> + auto Arg(size_t i) const + { + // Return argument (required or with default value). + if constexpr (std::is_integral_v<R> || std::is_floating_point_v<R>) { + // Return numbers by value. + return ArgValue<R>(i); + } else { + // Return everything else by reference. + return ArgValue<const R&>(i); + } + } + template <typename R> + auto MaybeArg(size_t i) const + { + // Return optional argument (without default). + if constexpr (std::is_integral_v<R> || std::is_floating_point_v<R>) { + // Return numbers by value, wrapped in optional. + return ArgValue<std::optional<R>>(i); + } else { + // Return other types by pointer. + return ArgValue<const R*>(i); + } + } std::string ToString() const; /** Return the named args that need to be converted from string to another JSON type */ UniValue GetArgMap() const; @@ -399,6 +454,9 @@ private: const std::vector<RPCArg> m_args; const RPCResults m_results; const RPCExamples m_examples; + mutable const JSONRPCRequest* m_req{nullptr}; // A pointer to the request for the duration of m_fun() + template <typename R> + R ArgValue(size_t i) const; }; /** diff --git a/src/wallet/rpc/coins.cpp b/src/wallet/rpc/coins.cpp index 6f0623cdf5..fdc6ee055d 100644 --- a/src/wallet/rpc/coins.cpp +++ b/src/wallet/rpc/coins.cpp @@ -194,8 +194,8 @@ RPCHelpMan getbalance() LOCK(pwallet->cs_wallet); - const UniValue& dummy_value = request.params[0]; - if (!dummy_value.isNull() && dummy_value.get_str() != "*") { + const auto dummy_value{self.MaybeArg<std::string>(0)}; + if (dummy_value && *dummy_value != "*") { throw JSONRPCError(RPC_METHOD_DEPRECATED, "dummy first argument must be excluded or set to \"*\"."); } diff --git a/src/wallet/rpc/wallet.cpp b/src/wallet/rpc/wallet.cpp index fb4b642bba..3774e6a3ef 100644 --- a/src/wallet/rpc/wallet.cpp +++ b/src/wallet/rpc/wallet.cpp @@ -478,7 +478,7 @@ static RPCHelpMan unloadwallet() // Release the "main" shared pointer and prevent further notifications. // Note that any attempt to load the same wallet would fail until the wallet // is destroyed (see CheckUniqueFileid). - std::optional<bool> load_on_start = request.params[1].isNull() ? std::nullopt : std::optional<bool>(request.params[1].get_bool()); + std::optional<bool> load_on_start{self.MaybeArg<bool>(1)}; if (!RemoveWallet(context, wallet, load_on_start, warnings)) { throw JSONRPCError(RPC_MISC_ERROR, "Requested wallet already unloaded"); } |