Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F13711149
D17947.id53542.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
15 KB
Subscribers
None
D17947.id53542.diff
View Options
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> ¶ms, 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
Details
Attached
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)
Attached To
D17947: RPC: access RPC arguments by name
Event Timeline
Log In to Comment