Page MenuHomePhabricator

D17947.id53542.diff
No OneTemporary

D17947.id53542.diff

diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -2414,7 +2414,8 @@
[&](const RPCHelpMan &self, const Config &config,
const JSONRPCRequest &request) -> UniValue {
UniValue result(UniValue::VOBJ);
- if (request.params[0].get_str() == "status") {
+ const auto action{self.Arg<std::string>("action")};
+ if (action == "status") {
CoinsViewScanReserver reserver;
if (reserver.reserve()) {
// no scan in progress
@@ -2422,7 +2423,7 @@
}
result.pushKV("progress", g_scan_progress.load());
return result;
- } else if (request.params[0].get_str() == "abort") {
+ } else if (action == "abort") {
CoinsViewScanReserver reserver;
if (reserver.reserve()) {
// reserve was possible which means no scan was running
@@ -2431,7 +2432,7 @@
// set the abort flag
g_should_abort_scan = true;
return true;
- } else if (request.params[0].get_str() == "start") {
+ } else if (action == "start") {
CoinsViewScanReserver reserver;
if (!reserver.reserve()) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
@@ -2512,7 +2513,8 @@
result.pushKV("unspents", unspents);
result.pushKV("total_amount", total_in);
} else {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid command");
+ throw JSONRPCError(RPC_INVALID_PARAMETER,
+ strprintf("Invalid action '%s'", action));
}
return result;
},
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -127,7 +127,8 @@
const JSONRPCRequest &request) -> UniValue {
ChainstateManager &chainman = EnsureAnyChainman(request.context);
LOCK(cs_main);
- return GetNetworkHashPS(self.Arg<int>(0), self.Arg<int>(1),
+ return GetNetworkHashPS(self.Arg<int>("nblocks"),
+ self.Arg<int>("height"),
chainman.ActiveChain());
},
};
@@ -265,12 +266,12 @@
HelpExampleCli("generatetodescriptor", "11 \"mydesc\"")},
[&](const RPCHelpMan &self, const Config &config,
const JSONRPCRequest &request) -> UniValue {
- const int num_blocks{self.Arg<int>(0)};
- const auto max_tries{self.Arg<uint64_t>(2)};
+ const int num_blocks{self.Arg<int>("num_blocks")};
+ const auto max_tries{self.Arg<uint64_t>("maxtries")};
CScript coinbase_script;
std::string error;
- if (!getScriptFromDescriptor(self.Arg<std::string>(1),
+ if (!getScriptFromDescriptor(self.Arg<std::string>("descriptor"),
coinbase_script, error)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
}
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -370,9 +370,9 @@
const JSONRPCRequest &request) -> UniValue {
LOCK(cs_main);
- std::string strAddress = request.params[0].get_str();
- std::string strSign = request.params[1].get_str();
- std::string strMessage = request.params[2].get_str();
+ std::string strAddress = self.Arg<std::string>("address");
+ std::string strSign = self.Arg<std::string>("signature");
+ std::string strMessage = self.Arg<std::string>("message");
switch (MessageVerify(config.GetChainParams(), strAddress, strSign,
strMessage)) {
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -364,7 +364,7 @@
HelpExampleRpc("addnode", "\"192.168.0.6:8333\", \"onetry\"")},
[&](const RPCHelpMan &self, const Config &config,
const JSONRPCRequest &request) -> UniValue {
- const std::string command{request.params[1].get_str()};
+ const auto command{self.Arg<std::string>("command")};
if (command != "onetry" && command != "add" &&
command != "remove") {
throw std::runtime_error(self.ToString());
@@ -373,7 +373,8 @@
NodeContext &node = EnsureAnyNodeContext(request.context);
CConnman &connman = EnsureConnman(node);
- const std::string node_arg{request.params[0].get_str()};
+ const auto node_arg{self.Arg<std::string>("node")};
+ // TODO: apply core#29277 when backporting the "v2transport" arg
if (command == "onetry") {
CAddress addr;
diff --git a/src/rpc/util.h b/src/rpc/util.h
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -383,18 +383,29 @@
UniValue HandleRequest(const Config &config,
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.
+ * @brief Helper to get a required or default-valued request argument.
*
- * 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.
+ * Use this function when the argument is required or when it has a default
+ * value. If the argument is optional and may not be provided, use MaybeArg
+ * instead.
*
- * The Type passed to this helper must match the corresponding
- * RPCArg::Type.
+ * This function only works during m_fun(), i.e., it should only be used in
+ * RPC method implementations. It 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 documentation.
+ *
+ * There are two overloads of this function:
+ * - Use Arg<Type>(size_t i) to get the argument (or the default value) by
+ * index.
+ * - Use Arg<Type>(const std::string& key) to get the argument (or the
+ * default value) by key.
+ *
+ * The Type passed to this helper must match the corresponding RPCArg::Type.
+ *
+ * @return The value of the RPC argument (or the default value) cast to
+ * type Type.
+ *
+ * @see MaybeArg for handling optional arguments without default values.
*/
template <typename R> auto Arg(size_t i) const {
// Return argument (required or with default value).
@@ -406,6 +417,36 @@
return ArgValue<const R &>(i);
}
}
+ template <typename R> auto Arg(std::string_view key) const {
+ return Arg<R>(GetParamIndex(key));
+ }
+ /**
+ * @brief Helper to get an optional request argument.
+ *
+ * Use this function when the argument is optional and does not have a
+ * default value. If the argument is required or has a default value, use
+ * Arg instead.
+ *
+ * This function only works during m_fun(), i.e., it should only be used in
+ * RPC method implementations. It internally checks whether the user-passed
+ * argument isNull() and parses (from JSON) and returns the user-passed
+ * argument, or a falsy value if no argument was passed.
+ *
+ * There are two overloads of this function:
+ * - Use MaybeArg<Type>(size_t i) to get the optional argument by index.
+ * - Use MaybeArg<Type>(const std::string& key) to get the optional
+ * argument by key.
+ *
+ * The Type passed to this helper must match the corresponding RPCArg::Type.
+ *
+ * @return For integral and floating-point types, a std::optional<Type> is
+ * returned. For other types, a Type* pointer to the argument is
+ * returned. If the argument is not provided, std::nullopt or a
+ * null pointer is returned.
+ *
+ * @see Arg for handling arguments that are required or have a default
+ * value.
+ */
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>) {
@@ -416,6 +457,9 @@
return ArgValue<const R *>(i);
}
}
+ template <typename R> auto MaybeArg(std::string_view key) const {
+ return MaybeArg<R>(GetParamIndex(key));
+ }
std::string ToString() const;
/**
* Return the named args that need to be converted from string to another
@@ -438,6 +482,8 @@
//! A pointer to the request for the duration of m_fun()
mutable const JSONRPCRequest *m_req{nullptr};
template <typename R> R ArgValue(size_t i) const;
+ //! Return positional index of a parameter using its name as key.
+ size_t GetParamIndex(std::string_view key) const;
};
#endif // BITCOIN_RPC_UTIL_H
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -16,6 +16,9 @@
#include <util/string.h>
#include <util/translation.h>
+#include <algorithm>
+#include <iterator>
+#include <string_view>
#include <tuple>
#include <variant>
@@ -722,6 +725,16 @@
return ret;
}
+size_t RPCHelpMan::GetParamIndex(std::string_view key) const {
+ auto it{std::find_if(m_args.begin(), m_args.end(), [&key](const auto &arg) {
+ return arg.GetName() == key;
+ })};
+
+ // TODO: ideally this is checked at compile time
+ CHECK_NONFATAL(it != m_args.end());
+ return std::distance(m_args.begin(), it);
+}
+
std::string RPCHelpMan::ToString() const {
std::string ret;
diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp
--- a/src/test/rpc_tests.cpp
+++ b/src/test/rpc_tests.cpp
@@ -750,4 +750,102 @@
HelpExampleRpcNamed("foo", {{"arg", "true"}}));
}
+static void CheckRpc(const std::vector<RPCArg> &params, const UniValue &args,
+ RPCHelpMan::RPCMethodImpl test_impl) {
+ auto null_result{RPCResult{RPCResult::Type::NONE, "", "None"}};
+ const RPCHelpMan rpc{"dummy", "dummy description", params,
+ null_result, RPCExamples{""}, test_impl};
+ JSONRPCRequest req;
+ req.params = args;
+
+ rpc.HandleRequest(GetConfig(), req);
+}
+
+BOOST_AUTO_TEST_CASE(rpc_arg_helper) {
+ constexpr bool DEFAULT_BOOL = true;
+ constexpr auto DEFAULT_STRING = "default";
+ constexpr uint64_t DEFAULT_UINT64_T = 3;
+
+ //! Parameters with which the RPCHelpMan is instantiated
+ const std::vector<RPCArg> params{
+ // Required arg
+ {"req_int", RPCArg::Type::NUM, RPCArg::Optional::NO, ""},
+ {"req_str", RPCArg::Type::STR, RPCArg::Optional::NO, ""},
+ // Default arg
+ {"def_uint64_t", RPCArg::Type::NUM, RPCArg::Default{DEFAULT_UINT64_T},
+ ""},
+ {"def_string", RPCArg::Type::STR, RPCArg::Default{DEFAULT_STRING}, ""},
+ {"def_bool", RPCArg::Type::BOOL, RPCArg::Default{DEFAULT_BOOL}, ""},
+ // Optional arg without default
+ {"opt_double", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, ""},
+ {"opt_string", RPCArg::Type::STR, RPCArg::Optional::OMITTED, ""}};
+
+ //! Check that `self.Arg` returns the same value as the `request.params`
+ //! accessors
+ RPCHelpMan::RPCMethodImpl check_positional =
+ [&](const RPCHelpMan &self, const Config &config,
+ const JSONRPCRequest &request) -> UniValue {
+ BOOST_CHECK_EQUAL(self.Arg<int>(0), request.params[0].getInt<int>());
+ BOOST_CHECK_EQUAL(self.Arg<std::string>(1),
+ request.params[1].get_str());
+ BOOST_CHECK_EQUAL(self.Arg<uint64_t>(2),
+ request.params[2].isNull()
+ ? DEFAULT_UINT64_T
+ : request.params[2].getInt<uint64_t>());
+ BOOST_CHECK_EQUAL(self.Arg<std::string>(3),
+ request.params[3].isNull()
+ ? DEFAULT_STRING
+ : request.params[3].get_str());
+ BOOST_CHECK_EQUAL(self.Arg<bool>(4),
+ request.params[4].isNull()
+ ? DEFAULT_BOOL
+ : request.params[4].get_bool());
+ if (!request.params[5].isNull()) {
+ BOOST_CHECK_EQUAL(self.MaybeArg<double>(5).value(),
+ request.params[5].get_real());
+ } else {
+ BOOST_CHECK(!self.MaybeArg<double>(5));
+ }
+ if (!request.params[6].isNull()) {
+ BOOST_CHECK(self.MaybeArg<std::string>(6));
+ BOOST_CHECK_EQUAL(*self.MaybeArg<std::string>(6),
+ request.params[6].get_str());
+ } else {
+ BOOST_CHECK(!self.MaybeArg<std::string>(6));
+ }
+ return UniValue{};
+ };
+ CheckRpc(params,
+ UniValue{JSON(R"([5, "hello", null, null, null, null, null])")},
+ check_positional);
+ CheckRpc(params,
+ UniValue{JSON(R"([5, "hello", 4, "test", true, 1.23, "world"])")},
+ check_positional);
+
+ //! Check that `self.Arg` returns the same value when using index and key
+ RPCHelpMan::RPCMethodImpl check_named =
+ [&](const RPCHelpMan &self, const Config &config,
+ const JSONRPCRequest &request) -> UniValue {
+ BOOST_CHECK_EQUAL(self.Arg<int>(0), self.Arg<int>("req_int"));
+ BOOST_CHECK_EQUAL(self.Arg<std::string>(1),
+ self.Arg<std::string>("req_str"));
+ BOOST_CHECK_EQUAL(self.Arg<uint64_t>(2),
+ self.Arg<uint64_t>("def_uint64_t"));
+ BOOST_CHECK_EQUAL(self.Arg<std::string>(3),
+ self.Arg<std::string>("def_string"));
+ BOOST_CHECK_EQUAL(self.Arg<bool>(4), self.Arg<bool>("def_bool"));
+ BOOST_CHECK(self.MaybeArg<double>(5) ==
+ self.MaybeArg<double>("opt_double"));
+ BOOST_CHECK(self.MaybeArg<std::string>(6) ==
+ self.MaybeArg<std::string>("opt_string"));
+ return UniValue{};
+ };
+ CheckRpc(params,
+ UniValue{JSON(R"([5, "hello", null, null, null, null, null])")},
+ check_named);
+ CheckRpc(params,
+ UniValue{JSON(R"([5, "hello", 4, "test", true, 1.23, "world"])")},
+ check_named);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -669,17 +669,14 @@
LOCK(pwallet->cs_wallet);
- const auto dummy_value{self.MaybeArg<std::string>(0)};
+ const auto dummy_value{self.MaybeArg<std::string>("dummy")};
if (dummy_value && *dummy_value != "*") {
throw JSONRPCError(
RPC_METHOD_DEPRECATED,
"dummy first argument must be excluded or set to \"*\".");
}
- int min_depth = 0;
- if (!request.params[1].isNull()) {
- min_depth = request.params[1].getInt<int>();
- }
+ const auto min_depth{self.Arg<int>("minconf")};
bool include_watchonly =
ParseIncludeWatchonly(request.params[2], *pwallet);

File Metadata

Mime Type
text/plain
Expires
Sat, Apr 26, 10:36 (5 h, 26 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5573296
Default Alt Text
D17947.id53542.diff (15 KB)

Event Timeline