diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index f3e1330b449..c2582cabead 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -540,7 +540,7 @@ static RPCMethod getblockfrompeer() RPCResult{RPCResult::Type::OBJ, "", /*optional=*/false, "", {}}, RPCExamples{ HelpExampleCli("getblockfrompeer", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" 0") - + HelpExampleRpc("getblockfrompeer", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" 0") + + HelpExampleRpc("getblockfrompeer", R"("00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09", 0)") }, [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue { diff --git a/src/rpc/mempool.cpp b/src/rpc/mempool.cpp index c262332942f..cb8d837d3d6 100644 --- a/src/rpc/mempool.cpp +++ b/src/rpc/mempool.cpp @@ -868,7 +868,7 @@ static RPCMethod getmempoolcluster() RPCResult::Type::OBJ, "", "", ClusterDescription()}, RPCExamples{ HelpExampleCli("getmempoolcluster", "txid") - + HelpExampleRpc("getmempoolcluster", "txid") + + HelpExampleRpc("getmempoolcluster", R"("txid")") }, [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue { @@ -1168,7 +1168,7 @@ static RPCMethod importmempool() RPCArgOptions{.oneline_description = "options"}}, }, RPCResult{RPCResult::Type::OBJ, "", "", std::vector{}}, - RPCExamples{HelpExampleCli("importmempool", "/path/to/mempool.dat") + HelpExampleRpc("importmempool", "/path/to/mempool.dat")}, + RPCExamples{HelpExampleCli("importmempool", "/path/to/mempool.dat") + HelpExampleRpc("importmempool", R"("/path/to/mempool.dat")")}, [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue { const NodeContext& node{EnsureAnyNodeContext(request.context)}; diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index da57ed14a2b..8d143fe6e60 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -335,7 +335,7 @@ static RPCMethod addnode() RPCResult{RPCResult::Type::NONE, "", ""}, RPCExamples{ HelpExampleCli("addnode", "\"192.168.0.6:8333\" \"onetry\" true") - + HelpExampleRpc("addnode", "\"192.168.0.6:8333\", \"onetry\" true") + + HelpExampleRpc("addnode", R"("192.168.0.6:8333", "onetry", true)") }, [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue { @@ -410,7 +410,7 @@ static RPCMethod addconnection() }}, RPCExamples{ HelpExampleCli("addconnection", "\"192.168.0.6:8333\" \"outbound-full-relay\" true") - + HelpExampleRpc("addconnection", "\"192.168.0.6:8333\" \"outbound-full-relay\" true") + + HelpExampleRpc("addconnection", R"("192.168.0.6:8333", "outbound-full-relay", true)") }, [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue { @@ -1083,7 +1083,7 @@ static RPCMethod sendmsgtopeer() }, RPCResult{RPCResult::Type::OBJ, "", "", std::vector{}}, RPCExamples{ - HelpExampleCli("sendmsgtopeer", "0 \"addr\" \"ffffff\"") + HelpExampleRpc("sendmsgtopeer", "0 \"addr\" \"ffffff\"")}, + HelpExampleCli("sendmsgtopeer", "0 \"addr\" \"ffffff\"") + HelpExampleRpc("sendmsgtopeer", R"(0, "addr", "ffffff")")}, [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue { const NodeId peer_id{request.params[0].getInt()}; const auto msg_type{self.Arg("msg_type")}; diff --git a/src/rpc/node.cpp b/src/rpc/node.cpp index 6d0d5511e33..507eaad4120 100644 --- a/src/rpc/node.cpp +++ b/src/rpc/node.cpp @@ -378,7 +378,7 @@ static RPCMethod getindexinfo() HelpExampleCli("getindexinfo", "") + HelpExampleRpc("getindexinfo", "") + HelpExampleCli("getindexinfo", "txindex") - + HelpExampleRpc("getindexinfo", "txindex") + + HelpExampleRpc("getindexinfo", R"("txindex")") }, [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue { diff --git a/src/wallet/rpc/addresses.cpp b/src/wallet/rpc/addresses.cpp index 02d59a79188..c07ea876f05 100644 --- a/src/wallet/rpc/addresses.cpp +++ b/src/wallet/rpc/addresses.cpp @@ -643,7 +643,7 @@ RPCMethod listlabels() "\nList labels that have sending addresses\n" + HelpExampleCli("listlabels", "send") + "\nAs a JSON-RPC call\n" - + HelpExampleRpc("listlabels", "receive") + + HelpExampleRpc("listlabels", R"("receive")") }, [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue { diff --git a/src/wallet/rpc/backup.cpp b/src/wallet/rpc/backup.cpp index 9ffea19c474..c0cc7f173f8 100644 --- a/src/wallet/rpc/backup.cpp +++ b/src/wallet/rpc/backup.cpp @@ -625,9 +625,9 @@ RPCMethod restorewallet() }, RPCExamples{ HelpExampleCli("restorewallet", "\"testwallet\" \"home\\backups\\backup-file.bak\"") - + HelpExampleRpc("restorewallet", "\"testwallet\" \"home\\backups\\backup-file.bak\"") - + HelpExampleCliNamed("restorewallet", {{"wallet_name", "testwallet"}, {"backup_file", "home\\backups\\backup-file.bak\""}, {"load_on_startup", true}}) - + HelpExampleRpcNamed("restorewallet", {{"wallet_name", "testwallet"}, {"backup_file", "home\\backups\\backup-file.bak\""}, {"load_on_startup", true}}) + + HelpExampleRpc("restorewallet", R"("testwallet", "home\\backups\\backup-file.bak")") + + HelpExampleCliNamed("restorewallet", {{"wallet_name", "testwallet"}, {"backup_file", "home\\backups\\backup-file.bak"}, {"load_on_startup", true}}) + + HelpExampleRpcNamed("restorewallet", {{"wallet_name", "testwallet"}, {"backup_file", "home\\backups\\backup-file.bak"}, {"load_on_startup", true}}) }, [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue { diff --git a/src/wallet/rpc/coins.cpp b/src/wallet/rpc/coins.cpp index e38cb17851d..ed12aef5af3 100644 --- a/src/wallet/rpc/coins.cpp +++ b/src/wallet/rpc/coins.cpp @@ -514,7 +514,7 @@ RPCMethod listunspent() RPCExamples{ HelpExampleCli("listunspent", "") + HelpExampleCli("listunspent", "6 9999999 \"[\\\"" + EXAMPLE_ADDRESS[0] + "\\\",\\\"" + EXAMPLE_ADDRESS[1] + "\\\"]\"") - + HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"" + EXAMPLE_ADDRESS[0] + "\\\",\\\"" + EXAMPLE_ADDRESS[1] + "\\\"]\"") + + HelpExampleRpc("listunspent", strprintf(R"(6, 9999999, ["%s","%s"])", EXAMPLE_ADDRESS[0], EXAMPLE_ADDRESS[1])) + HelpExampleCli("listunspent", "6 9999999 '[]' true '{ \"minimumAmount\": 0.005 }'") + HelpExampleRpc("listunspent", "6, 9999999, [] , true, { \"minimumAmount\": 0.005 } ") }, diff --git a/src/wallet/rpc/wallet.cpp b/src/wallet/rpc/wallet.cpp index ed3c8bfd8e4..1a08bf72f18 100644 --- a/src/wallet/rpc/wallet.cpp +++ b/src/wallet/rpc/wallet.cpp @@ -243,7 +243,7 @@ static RPCMethod loadwallet() + HelpExampleRpc("loadwallet", "\"/path/to/walletname/\"") + "\nLoad wallet using absolute path (Windows):\n" + HelpExampleCli("loadwallet", "\"DriveLetter:\\path\\to\\walletname\\\"") - + HelpExampleRpc("loadwallet", "\"DriveLetter:\\path\\to\\walletname\\\"") + + HelpExampleRpc("loadwallet", R"("DriveLetter:\\path\\to\\walletname")") }, [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue { @@ -451,7 +451,7 @@ static RPCMethod unloadwallet() }}, RPCExamples{ HelpExampleCli("unloadwallet", "wallet_name") - + HelpExampleRpc("unloadwallet", "wallet_name") + + HelpExampleRpc("unloadwallet", R"("wallet_name")") }, [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue { @@ -771,7 +771,7 @@ static RPCMethod createwalletdescriptor() }, RPCExamples{ HelpExampleCli("createwalletdescriptor", "bech32m") - + HelpExampleRpc("createwalletdescriptor", "bech32m") + + HelpExampleRpc("createwalletdescriptor", R"("bech32m")") }, [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue { @@ -861,7 +861,7 @@ RPCMethod addhdkey() }, }, RPCExamples{ - HelpExampleCli("addhdkey", "xprv") + HelpExampleRpc("addhdkey", "xprv") + HelpExampleCli("addhdkey", "xprv") + HelpExampleRpc("addhdkey", R"("xprv")") }, [&](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue { diff --git a/test/functional/rpc_help.py b/test/functional/rpc_help.py index d76a6fa6af5..5a09169974f 100755 --- a/test/functional/rpc_help.py +++ b/test/functional/rpc_help.py @@ -8,6 +8,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_raises_rpc_error from collections import defaultdict +import json import os import re @@ -153,9 +154,17 @@ class HelpRpcTest(BitcoinTestFramework): os.mkdir(dump_dir) calls = [line.split(' ', 1)[0] for line in self.nodes[0].help().splitlines() if line and not line.startswith('==')] for call in calls: + help_text = self.nodes[0].help(call) with open(os.path.join(dump_dir, call), 'w') as f: # Make sure the node can generate the help at runtime without crashing - f.write(self.nodes[0].help(call)) + f.write(help_text) + # Make sure any curl examples have a JSON-RPC payload that is valid JSON + for match in re.finditer(r"--data-binary '(.*)' -H", help_text): + payload = match.group(1) + try: + json.loads(payload) + except json.JSONDecodeError as e: + raise AssertionError(f"HelpExampleRpc for '{call}' is not valid JSON: {payload!r}\n{e}") def wallet_help(self): assert 'getnewaddress ( "label" "address_type" )' in self.nodes[0].help('getnewaddress')