diff options
author | W. J. van der Laan <laanwj@protonmail.com> | 2021-04-05 15:31:30 +0200 |
---|---|---|
committer | W. J. van der Laan <laanwj@protonmail.com> | 2021-04-05 15:31:41 +0200 |
commit | 5c9b06db81091e1ebadf6e96701a5f1485a5d616 (patch) | |
tree | 52700e64bb2e41bfe25349f37412b74f53ab25e9 /src | |
parent | 824eea5643a1a317c04efcbd30268e46b9165a3c (diff) | |
parent | 5039e0e55a7c9dd63a38c12fa60d244592de69e0 (diff) |
Merge #21302: wallet: createwallet examples for descriptor wallets
5039e0e55a7c9dd63a38c12fa60d244592de69e0 test: HelpExampleCliNamed and HelpExampleRpcNamed (Ivan Metlushko)
591735ef0bf13b94643b794518406f981fa5dcb7 rpc: Add HelpExampleCliNamed and use it for `createwallet` doc (Wladimir J. van der Laan)
5d5a90e819d23a302f9bec6b995a3116ead6ae94 rpc: Add HelpExampleRpcNamed (Ivan Metlushko)
Pull request description:
Rationale: make descriptor wallets more visible and just a bit easier to setup
`bitcoin-cli help createwallet`
**Before**:
```
Examples:
> bitcoin-cli createwallet "testwallet"
> curl --user myusername --data-binary '{"jsonrpc": "1.0", "id": "curltest", "method": "createwallet", "params": ["testwallet"]}' -H 'content-type: text/plain;' http://127.0.0.1:8332/
```
**After**
```
Examples:
> bitcoin-cli createwallet "testwallet"
> curl --user myusername --data-binary '{"jsonrpc": "1.0", "id": "curltest", "method": "createwallet", "params": ["testwallet"]}' -H 'content-type: text/plain;' http://127.0.0.1:8332/
> bitcoin-cli createwallet "descriptors" false false "" true true true
> curl --user myusername --data-binary '{"jsonrpc": "1.0", "id": "curltest", "method": "createwallet", "params": ["descriptors", false, false, "", true, true, true]}' -H 'content-type: text/plain;' http://127.0.0.1:8332/
```
ACKs for top commit:
laanwj:
Tested ACK 5039e0e55a7c9dd63a38c12fa60d244592de69e0
Tree-SHA512: d37210e6ce639addee881377092d8f6fb2a537a60a259c561899e24cf68a0254d7ff45a213573c938f626677e46770cd21113aae5974f26c66b9a2e137699c14
Diffstat (limited to 'src')
-rw-r--r-- | src/rpc/util.cpp | 63 | ||||
-rw-r--r-- | src/rpc/util.h | 4 | ||||
-rw-r--r-- | src/test/rpc_tests.cpp | 35 | ||||
-rw-r--r-- | src/wallet/rpcwallet.cpp | 2 |
4 files changed, 104 insertions, 0 deletions
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index 52499cd1d6..ba3105ca01 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -113,17 +113,80 @@ std::vector<unsigned char> 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\", " "\"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 db7f626e9c..6e783e19fb 100644 --- a/src/rpc/util.h +++ b/src/rpc/util.h @@ -78,8 +78,12 @@ extern std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strNa extern std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey); extern CAmount AmountFromValue(const UniValue& value); + +using RPCArgList = std::vector<std::pair<std::string, UniValue>>; 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); CPubKey HexToPubKey(const std::string& hex_in); CPubKey AddrToPubKey(const FillableSigningProvider& keystore, const std::string& addr_in); diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index 67e70b3bc3..7c50c72041 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -431,4 +431,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() diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index df26f039a0..45f292055e 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2711,6 +2711,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 { |