From 5d5a90e819d23a302f9bec6b995a3116ead6ae94 Mon Sep 17 00:00:00 2001 From: Ivan Metlushko Date: Wed, 3 Mar 2021 09:32:14 +0100 Subject: rpc: Add HelpExampleRpcNamed --- src/rpc/util.cpp | 11 +++++++++++ src/rpc/util.h | 3 +++ 2 files changed, 14 insertions(+) (limited to 'src') diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index e890c0108a..b4533c14d0 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -124,6 +124,17 @@ std::string HelpExampleRpc(const std::string& methodname, const std::string& arg "\"method\": \"" + methodname + "\", \"params\": [" + args + "]}' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n"; } +std::string HelpExampleRpcNamed(const std::string& methodname, const RPCArgList& args) +{ + UniValue params(UniValue::VOBJ); + for (const auto& param: args) { + params.pushKV(param.first, param.second); + } + + return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\": \"curltest\", " + "\"method\": \"" + methodname + "\", \"params\": " + params.write() + "}' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n"; +} + // Converts a hex string to a public key if possible CPubKey HexToPubKey(const std::string& hex_in) { diff --git a/src/rpc/util.h b/src/rpc/util.h index c54ce85f60..184dfc2940 100644 --- a/src/rpc/util.h +++ b/src/rpc/util.h @@ -78,8 +78,11 @@ extern std::vector ParseHexV(const UniValue& v, std::string strNa extern std::vector ParseHexO(const UniValue& o, std::string strKey); extern CAmount AmountFromValue(const UniValue& value); + +using RPCArgList = std::vector>; extern std::string HelpExampleCli(const std::string& methodname, const std::string& args); extern std::string HelpExampleRpc(const std::string& methodname, const std::string& args); +extern std::string HelpExampleRpcNamed(const std::string& methodname, const RPCArgList& args); CPubKey HexToPubKey(const std::string& hex_in); CPubKey AddrToPubKey(const FillableSigningProvider& keystore, const std::string& addr_in); -- cgit v1.2.3 From 591735ef0bf13b94643b794518406f981fa5dcb7 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 26 Feb 2021 15:03:50 +0100 Subject: rpc: Add HelpExampleCliNamed and use it for `createwallet` doc --- src/rpc/util.cpp | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ src/rpc/util.h | 1 + src/wallet/rpcwallet.cpp | 2 ++ 3 files changed, 55 insertions(+) (limited to 'src') diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index b4533c14d0..d844074601 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -113,11 +113,63 @@ std::vector ParseHexO(const UniValue& o, std::string strKey) return ParseHexV(find_value(o, strKey), strKey); } +namespace { + +/** + * Quote an argument for shell. + * + * @note This is intended for help, not for security-sensitive purposes. + */ +std::string ShellQuote(const std::string& s) +{ + std::string result; + result.reserve(s.size() * 2); + for (const char ch: s) { + if (ch == '\'') { + result += "'\''"; + } else { + result += ch; + } + } + return "'" + result + "'"; +} + +/** + * Shell-quotes the argument if it needs quoting, else returns it literally, to save typing. + * + * @note This is intended for help, not for security-sensitive purposes. + */ +std::string ShellQuoteIfNeeded(const std::string& s) +{ + for (const char ch: s) { + if (ch == ' ' || ch == '\'' || ch == '"') { + return ShellQuote(s); + } + } + + return s; +} + +} + std::string HelpExampleCli(const std::string& methodname, const std::string& args) { return "> bitcoin-cli " + methodname + " " + args + "\n"; } +std::string HelpExampleCliNamed(const std::string& methodname, const RPCArgList& args) +{ + std::string result = "> bitcoin-cli -named " + methodname; + for (const auto& argpair: args) { + const auto& value = argpair.second.isStr() + ? argpair.second.get_str() + : argpair.second.write(); + result += " " + argpair.first + "=" + ShellQuoteIfNeeded(value); + } + result += "\n"; + return result; +} + std::string HelpExampleRpc(const std::string& methodname, const std::string& args) { return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\": \"curltest\", " diff --git a/src/rpc/util.h b/src/rpc/util.h index 184dfc2940..c87c3db6f0 100644 --- a/src/rpc/util.h +++ b/src/rpc/util.h @@ -81,6 +81,7 @@ extern CAmount AmountFromValue(const UniValue& value); using RPCArgList = std::vector>; extern std::string HelpExampleCli(const std::string& methodname, const std::string& args); +extern std::string HelpExampleCliNamed(const std::string& methodname, const RPCArgList& args); extern std::string HelpExampleRpc(const std::string& methodname, const std::string& args); extern std::string HelpExampleRpcNamed(const std::string& methodname, const RPCArgList& args); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index bfc42ac1b0..f065332c99 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2738,6 +2738,8 @@ static RPCHelpMan createwallet() RPCExamples{ HelpExampleCli("createwallet", "\"testwallet\"") + HelpExampleRpc("createwallet", "\"testwallet\"") + + HelpExampleCliNamed("createwallet", {{"wallet_name", "descriptors"}, {"avoid_reuse", true}, {"descriptors", true}, {"load_on_startup", true}}) + + HelpExampleRpcNamed("createwallet", {{"wallet_name", "descriptors"}, {"avoid_reuse", true}, {"descriptors", true}, {"load_on_startup", true}}) }, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { -- cgit v1.2.3 From 5039e0e55a7c9dd63a38c12fa60d244592de69e0 Mon Sep 17 00:00:00 2001 From: Ivan Metlushko Date: Wed, 3 Mar 2021 09:32:39 +0100 Subject: test: HelpExampleCliNamed and HelpExampleRpcNamed --- src/test/rpc_tests.cpp | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'src') diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index b54cbb3f00..946f993536 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -421,4 +421,39 @@ BOOST_AUTO_TEST_CASE(rpc_getblockstats_calculate_percentiles_by_weight) } } +BOOST_AUTO_TEST_CASE(help_example) +{ + // test different argument types + const RPCArgList& args = {{"foo", "bar"}, {"b", true}, {"n", 1}}; + BOOST_CHECK_EQUAL(HelpExampleCliNamed("test", args), "> bitcoin-cli -named test foo=bar b=true n=1\n"); + BOOST_CHECK_EQUAL(HelpExampleRpcNamed("test", args), "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\": \"curltest\", \"method\": \"test\", \"params\": {\"foo\":\"bar\",\"b\":true,\"n\":1}}' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n"); + + // test shell escape + BOOST_CHECK_EQUAL(HelpExampleCliNamed("test", {{"foo", "b'ar"}}), "> bitcoin-cli -named test foo='b'''ar'\n"); + BOOST_CHECK_EQUAL(HelpExampleCliNamed("test", {{"foo", "b\"ar"}}), "> bitcoin-cli -named test foo='b\"ar'\n"); + BOOST_CHECK_EQUAL(HelpExampleCliNamed("test", {{"foo", "b ar"}}), "> bitcoin-cli -named test foo='b ar'\n"); + + // test object params + UniValue obj_value(UniValue::VOBJ); + obj_value.pushKV("foo", "bar"); + obj_value.pushKV("b", false); + obj_value.pushKV("n", 1); + BOOST_CHECK_EQUAL(HelpExampleCliNamed("test", {{"name", obj_value}}), "> bitcoin-cli -named test name='{\"foo\":\"bar\",\"b\":false,\"n\":1}'\n"); + BOOST_CHECK_EQUAL(HelpExampleRpcNamed("test", {{"name", obj_value}}), "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\": \"curltest\", \"method\": \"test\", \"params\": {\"name\":{\"foo\":\"bar\",\"b\":false,\"n\":1}}}' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n"); + + // test array params + UniValue arr_value(UniValue::VARR); + arr_value.push_back("bar"); + arr_value.push_back(false); + arr_value.push_back(1); + BOOST_CHECK_EQUAL(HelpExampleCliNamed("test", {{"name", arr_value}}), "> bitcoin-cli -named test name='[\"bar\",false,1]'\n"); + BOOST_CHECK_EQUAL(HelpExampleRpcNamed("test", {{"name", arr_value}}), "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\": \"curltest\", \"method\": \"test\", \"params\": {\"name\":[\"bar\",false,1]}}' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n"); + + // test types don't matter for shell + BOOST_CHECK_EQUAL(HelpExampleCliNamed("foo", {{"arg", true}}), HelpExampleCliNamed("foo", {{"arg", "true"}})); + + // test types matter for Rpc + BOOST_CHECK_NE(HelpExampleRpcNamed("foo", {{"arg", true}}), HelpExampleRpcNamed("foo", {{"arg", "true"}})); +} + BOOST_AUTO_TEST_SUITE_END() -- cgit v1.2.3