From d55759fa791a72ad15340ef2c4825652a630d2c6 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 28 Jan 2021 19:25:07 +0100 Subject: [PATCH] Merge #20012: rpc: Remove duplicate name and argNames from CRPCCommand fa04f9b4ddffc5ef23c2ee7f3cc72a7c2ae49204 rpc: Remove duplicate name and argNames from CRPCCommand (MarcoFalke) fa92912b4bb4629addcbfdfb7cc000be701614af rpc: Use RPCHelpMan for check-rpc-mappings linter (MarcoFalke) faf835680be39811827504f77005b6603165f53e rpc: [refactor] Use concise C++11 code in CRPCConvertTable constructor (MarcoFalke) Pull request description: Currently, the RPC argument names are specified twice to simplify consistency linting. To avoid having to specify the argnames twice when adding new arguments, remove the linter and add an equivalent test based on RPCHelpMan. ACKs for top commit: laanwj: ACK fa04f9b4ddffc5ef23c2ee7f3cc72a7c2ae49204 Tree-SHA512: 3f5f32f5a09b22d879f24aa67031639d2612cff481d6aebc6cfe6fd757cafb3e7bf72120b30466f59292a260747b71e57322c189d5478b668519b9f32fcde31a --- ci/dash/build_src.sh | 2 - ci/lint/06_script.sh | 1 - src/qt/test/rpcnestedtests.cpp | 2 +- src/rpc/blockchain.cpp | 76 +++++++------- src/rpc/client.cpp | 11 +- src/rpc/coinjoin.cpp | 16 +-- src/rpc/evo.cpp | 58 +++++------ src/rpc/governance.cpp | 37 ++++--- src/rpc/masternode.cpp | 26 ++--- src/rpc/mining.cpp | 54 +++++----- src/rpc/misc.cpp | 52 +++++----- src/rpc/net.cpp | 36 +++---- src/rpc/quorums.cpp | 44 ++++---- src/rpc/rawtransaction.cpp | 46 ++++----- src/rpc/server.cpp | 34 +++++-- src/rpc/server.h | 9 +- src/rpc/util.cpp | 17 ++++ src/rpc/util.h | 2 + src/wallet/rpcwallet.cpp | 140 +++++++++++++------------- src/zmq/zmqrpc.cpp | 6 +- test/functional/rpc_help.py | 64 ++++++++++++ test/lint/check-rpc-mappings.py | 173 -------------------------------- 22 files changed, 418 insertions(+), 488 deletions(-) delete mode 100755 test/lint/check-rpc-mappings.py diff --git a/ci/dash/build_src.sh b/ci/dash/build_src.sh index 3a2f108909..8cf4116c7c 100755 --- a/ci/dash/build_src.sh +++ b/ci/dash/build_src.sh @@ -24,8 +24,6 @@ if [ "$CHECK_DOC" = 1 ]; then #test/lint/git-subtree-check.sh src/leveldb # TODO: Check docs (re-enable after all Bitcoin PRs have been merged and docs fully fixed) #test/lint/check-doc.py - # Check rpc consistency - test/lint/check-rpc-mappings.py . # Run all linters test/lint/lint-all.sh test/lint/extended-lint-all.sh diff --git a/ci/lint/06_script.sh b/ci/lint/06_script.sh index 2b7a0751c0..1c3f589883 100755 --- a/ci/lint/06_script.sh +++ b/ci/lint/06_script.sh @@ -20,7 +20,6 @@ test/lint/git-subtree-check.sh src/secp256k1 test/lint/git-subtree-check.sh src/univalue test/lint/git-subtree-check.sh src/leveldb test/lint/check-doc.py -test/lint/check-rpc-mappings.py . test/lint/lint-all.sh if [ "$CIRRUS_REPO_FULL_NAME" = "dashpay/dash" ] && [ -n "$CIRRUS_CRON" ]; then diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp index 65780c2996..36f4ffc136 100644 --- a/src/qt/test/rpcnestedtests.cpp +++ b/src/qt/test/rpcnestedtests.cpp @@ -35,7 +35,7 @@ static RPCHelpMan rpcNestedTest_rpc() } static const CRPCCommand vRPCCommands[] = { - {"test", "rpcNestedTest", &rpcNestedTest_rpc, {"arg1", "arg2", "arg3"}}, + {"test", &rpcNestedTest_rpc}, }; void RPCNestedTests::rpcNestedTests() diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 524736d560..8b4e7b40f5 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -3038,47 +3038,47 @@ void RegisterBlockchainRPCCommands(CRPCTable &t) { // clang-format off static const CRPCCommand commands[] = -{ // category name actor (function) argNames - // --------------------- ------------------------ ----------------------- ---------- - { "blockchain", "getblockchaininfo", &getblockchaininfo, {} }, - { "blockchain", "getchaintxstats", &getchaintxstats, {"nblocks", "blockhash"} }, - { "blockchain", "getblockstats", &getblockstats, {"hash_or_height", "stats"} }, - { "blockchain", "getbestblockhash", &getbestblockhash, {} }, - { "blockchain", "getbestchainlock", &getbestchainlock, {} }, - { "blockchain", "getblockcount", &getblockcount, {} }, - { "blockchain", "getblock", &getblock, {"blockhash","verbosity|verbose"} }, - { "blockchain", "getblockfrompeer", &getblockfrompeer, {"block_hash", "peer_id"}}, - { "blockchain", "getblockhashes", &getblockhashes, {"high","low"} }, - { "blockchain", "getblockhash", &getblockhash, {"height"} }, - { "blockchain", "getblockheader", &getblockheader, {"blockhash","verbose"} }, - { "blockchain", "getblockheaders", &getblockheaders, {"blockhash","count","verbose"} }, - { "blockchain", "getmerkleblocks", &getmerkleblocks, {"filter","blockhash","count"} }, - { "blockchain", "getchaintips", &getchaintips, {"count","branchlen"} }, - { "blockchain", "getdifficulty", &getdifficulty, {} }, - { "blockchain", "getmempoolancestors", &getmempoolancestors, {"txid","verbose"} }, - { "blockchain", "getmempooldescendants", &getmempooldescendants, {"txid","verbose"} }, - { "blockchain", "getmempoolentry", &getmempoolentry, {"txid"} }, - { "blockchain", "getmempoolinfo", &getmempoolinfo, {} }, - { "blockchain", "getrawmempool", &getrawmempool, {"verbose"} }, - { "blockchain", "getspecialtxes", &getspecialtxes, {"blockhash", "type", "count", "skip", "verbosity"} }, - { "blockchain", "gettxout", &gettxout, {"txid","n","include_mempool"} }, - { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, {"hash_type", "hash_or_height", "use_index"} }, - { "blockchain", "pruneblockchain", &pruneblockchain, {"height"} }, - { "blockchain", "savemempool", &savemempool, {} }, - { "blockchain", "verifychain", &verifychain, {"checklevel","nblocks"} }, +{ // category actor (function) + // --------------------- ------------------------ + { "blockchain", &getblockchaininfo, }, + { "blockchain", &getchaintxstats, }, + { "blockchain", &getblockstats, }, + { "blockchain", &getbestblockhash, }, + { "blockchain", &getbestchainlock, }, + { "blockchain", &getblockcount, }, + { "blockchain", &getblock, }, + { "blockchain", &getblockfrompeer, }, + { "blockchain", &getblockhashes, }, + { "blockchain", &getblockhash, }, + { "blockchain", &getblockheader, }, + { "blockchain", &getblockheaders, }, + { "blockchain", &getmerkleblocks, }, + { "blockchain", &getchaintips, }, + { "blockchain", &getdifficulty, }, + { "blockchain", &getmempoolancestors, }, + { "blockchain", &getmempooldescendants, }, + { "blockchain", &getmempoolentry, }, + { "blockchain", &getmempoolinfo, }, + { "blockchain", &getrawmempool, }, + { "blockchain", &getspecialtxes, }, + { "blockchain", &gettxout, }, + { "blockchain", &gettxoutsetinfo, }, + { "blockchain", &pruneblockchain, }, + { "blockchain", &savemempool, }, + { "blockchain", &verifychain, }, - { "blockchain", "preciousblock", &preciousblock, {"blockhash"} }, - { "blockchain", "scantxoutset", &scantxoutset, {"action", "scanobjects"} }, - { "blockchain", "getblockfilter", &getblockfilter, {"blockhash", "filtertype"} }, + { "blockchain", &preciousblock, }, + { "blockchain", &scantxoutset, }, + { "blockchain", &getblockfilter, }, /* Not shown in help */ - { "hidden", "invalidateblock", &invalidateblock, {"blockhash"} }, - { "hidden", "reconsiderblock", &reconsiderblock, {"blockhash"} }, - { "hidden", "waitfornewblock", &waitfornewblock, {"timeout"} }, - { "hidden", "waitforblock", &waitforblock, {"blockhash","timeout"} }, - { "hidden", "waitforblockheight", &waitforblockheight, {"height","timeout"} }, - { "hidden", "syncwithvalidationinterfacequeue", &syncwithvalidationinterfacequeue, {} }, - { "hidden", "dumptxoutset", &dumptxoutset, {"path"} }, + { "hidden", &invalidateblock, }, + { "hidden", &reconsiderblock, }, + { "hidden", &waitfornewblock, }, + { "hidden", &waitforblock, }, + { "hidden", &waitforblockheight, }, + { "hidden", &syncwithvalidationinterfacequeue, }, + { "hidden", &dumptxoutset, }, }; // clang-format on for (const auto& c : commands) { diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 4adc8953b7..b7b2e79604 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -251,14 +251,9 @@ public: CRPCConvertTable::CRPCConvertTable() { - const unsigned int n_elem = - (sizeof(vRPCConvertParams) / sizeof(vRPCConvertParams[0])); - - for (unsigned int i = 0; i < n_elem; i++) { - members.insert(std::make_pair(vRPCConvertParams[i].methodName, - vRPCConvertParams[i].paramIdx)); - membersByName.insert(std::make_pair(vRPCConvertParams[i].methodName, - vRPCConvertParams[i].paramName)); + for (const auto& cp : vRPCConvertParams) { + members.emplace(cp.methodName, cp.paramIdx); + membersByName.emplace(cp.methodName, cp.paramName); } } diff --git a/src/rpc/coinjoin.cpp b/src/rpc/coinjoin.cpp index 29cf8fbb41..45f4e52f93 100644 --- a/src/rpc/coinjoin.cpp +++ b/src/rpc/coinjoin.cpp @@ -281,15 +281,15 @@ void RegisterCoinJoinRPCCommands(CRPCTable &t) { // clang-format off static const CRPCCommand commands[] = - { // category name actor (function) argNames - // ------------------------------------------------------------------------------------------------------ - { "dash", "getpoolinfo", &getpoolinfo, {} }, - { "dash", "getcoinjoininfo", &getcoinjoininfo, {} }, +{ // category actor (function) + // --------------------- ----------------------- + { "dash", &getpoolinfo, }, + { "dash", &getcoinjoininfo, }, #ifdef ENABLE_WALLET - { "dash", "coinjoin", &coinjoin, {"command"} }, - { "dash", "coinjoin", "reset", &coinjoin_reset, {} }, - { "dash", "coinjoin", "start", &coinjoin_start, {} }, - { "dash", "coinjoin", "stop", &coinjoin_stop, {} }, + { "dash", &coinjoin, }, + { "dash", &coinjoin_reset, }, + { "dash", &coinjoin_start, }, + { "dash", &coinjoin_stop, }, #endif // ENABLE_WALLET }; // clang-format on diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index 66f435dafd..a0e2ffdb97 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -1869,37 +1869,37 @@ void RegisterEvoRPCCommands(CRPCTable &tableRPC) { // clang-format off static const CRPCCommand commands[] = -{ // category name actor (function) - // --------------------- ------------------------ ----------------------- - { "evo", "bls", &bls_help, {"command"} }, - { "evo", "bls", "generate", &bls_generate, {"legacy"} }, - { "evo", "bls", "fromsecret", &bls_fromsecret, {"secret", "legacy"} }, - { "evo", "protx", &protx_help, {"command"} }, +{ // category actor (function) + // --------------------- ----------------------- + { "evo", &bls_help, }, + { "evo", &bls_generate, }, + { "evo", &bls_fromsecret, }, + { "evo", &protx_help, }, #ifdef ENABLE_WALLET - { "evo", "protx", "register", &protx_register, {"collateralHash", "collateralIndex", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "feeSourceAddress", "submit"} }, - { "evo", "protx", "register_evo", &protx_register_evo, {"collateralHash", "collateralIndex", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "platformNodeID", "platformP2PPort", "platformHTTPPort", "feeSourceAddress", "submit"} }, - { "evo", "protx", "register_hpmn", &protx_register_hpmn, {"collateralHash", "collateralIndex", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "platformNodeID", "platformP2PPort", "platformHTTPPort", "feeSourceAddress", "submit"} }, - { "evo", "protx", "register_legacy", &protx_register_legacy, {"collateralHash", "collateralIndex", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "feeSourceAddress", "submit"} }, - { "evo", "protx", "register_fund", &protx_register_fund, {"collateralAddress", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "fundAddress", "submit"} }, - { "evo", "protx", "register_fund_legacy", &protx_register_fund_legacy, {"collateralAddress", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "fundAddress", "submit"} }, - { "evo", "protx", "register_fund_evo", &protx_register_fund_evo, {"collateralAddress", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "platformNodeID", "platformP2PPort", "platformHTTPPort", "fundAddress", "submit"} }, - { "evo", "protx", "register_fund_hpmn", &protx_register_fund_hpmn, {"collateralAddress", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "platformNodeID", "platformP2PPort", "platformHTTPPort", "fundAddress", "submit"} }, - { "evo", "protx", "register_prepare", &protx_register_prepare, {"collateralHash", "collateralIndex", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "feeSourceAddress"} }, - { "evo", "protx", "register_prepare_evo", &protx_register_prepare_evo, {"collateralHash", "collateralIndex", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "platformNodeID", "platformP2PPort", "platformHTTPPort", "feeSourceAddress"} }, - { "evo", "protx", "register_prepare_hpmn", &protx_register_prepare_hpmn, {"collateralHash", "collateralIndex", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "platformNodeID", "platformP2PPort", "platformHTTPPort", "feeSourceAddress"} }, - { "evo", "protx", "register_prepare_legacy", &protx_register_prepare_legacy, {"collateralHash", "collateralIndex", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "feeSourceAddress"} }, - { "evo", "protx", "update_service", &protx_update_service, {"proTxHash", "ipAndPort", "operatorKey", "operatorPayoutAddress", "feeSourceAddress"} }, - { "evo", "protx", "update_service_evo", &protx_update_service_evo, {"proTxHash", "ipAndPort", "operatorKey", "platformNodeID", "platformP2PPort", "platformHTTPPort", "operatorPayoutAddress", "feeSourceAddress"} }, - { "evo", "protx", "update_service_hpmn", &protx_update_service_hpmn, {"proTxHash", "ipAndPort", "operatorKey", "platformNodeID", "platformP2PPort", "platformHTTPPort", "operatorPayoutAddress", "feeSourceAddress"} }, - { "evo", "protx", "register_submit", &protx_register_submit, {"tx", "sig"} }, - { "evo", "protx", "update_registrar", &protx_update_registrar, {"proTxHash", "operatorPubKey", "votingAddress", "payoutAddress", "feeSourceAddress"} }, - { "evo", "protx", "update_registrar_legacy", &protx_update_registrar_legacy, {"proTxHash", "operatorPubKey", "votingAddress", "payoutAddress", "feeSourceAddress"} }, - { "evo", "protx", "revoke", &protx_revoke, {"proTxHash", "operatorKey", "reason", "feeSourceAddress"} }, + { "evo", &protx_register, }, + { "evo", &protx_register_evo, }, + { "evo", &protx_register_hpmn, }, + { "evo", &protx_register_legacy, }, + { "evo", &protx_register_fund, }, + { "evo", &protx_register_fund_legacy, }, + { "evo", &protx_register_fund_evo, }, + { "evo", &protx_register_fund_hpmn, }, + { "evo", &protx_register_prepare, }, + { "evo", &protx_register_prepare_evo, }, + { "evo", &protx_register_prepare_hpmn, }, + { "evo", &protx_register_prepare_legacy, }, + { "evo", &protx_update_service, }, + { "evo", &protx_update_service_evo, }, + { "evo", &protx_update_service_hpmn, }, + { "evo", &protx_register_submit, }, + { "evo", &protx_update_registrar, }, + { "evo", &protx_update_registrar_legacy, }, + { "evo", &protx_revoke, }, #endif - { "evo", "protx", "list", &protx_list, {"type", "detailed", "height"} }, - { "evo", "protx", "info", &protx_info, {"proTxHash", "blockHash"} }, - { "evo", "protx", "diff", &protx_diff, {"baseBlock", "block", "extended"} }, - { "evo", "protx", "listdiff", &protx_listdiff, {"baseBlock", "block"} }, + { "evo", &protx_list, }, + { "evo", &protx_info, }, + { "evo", &protx_diff, }, + { "evo", &protx_listdiff, }, }; // clang-format on for (const auto& command : commands) { diff --git a/src/rpc/governance.cpp b/src/rpc/governance.cpp index 4b1af3dc3d..8c92e8c08b 100644 --- a/src/rpc/governance.cpp +++ b/src/rpc/governance.cpp @@ -1057,28 +1057,27 @@ void RegisterGovernanceRPCCommands(CRPCTable &t) { // clang-format off static const CRPCCommand commands[] = -{ // category name actor (function) argNames - // --------------------- ------------------------ ----------------------- ---------- +{ // category actor (function) + // --------------------- ----------------------- /* Dash features */ - { "dash", "getgovernanceinfo", &getgovernanceinfo, {} }, - { "dash", "getsuperblockbudget", &getsuperblockbudget, {"index"} }, - { "dash", "gobject", &gobject, {"command"} }, - { "dash", "gobject", "count", &gobject_count, {"mode"} }, - { "dash", "gobject", "deserialize", &gobject_deserialize, {"hex_data"} }, - { "dash", "gobject", "check", &gobject_check, {"hex_data"} }, + { "dash", &getgovernanceinfo, }, + { "dash", &getsuperblockbudget, }, + { "dash", &gobject, }, + { "dash", &gobject_count, }, + { "dash", &gobject_deserialize, }, + { "dash", &gobject_check, }, #ifdef ENABLE_WALLET - { "dash", "gobject", "prepare", &gobject_prepare, {"parent-hash", "revision", "time", "data-hex", "use-IS", "outputHash", "outputIndex"} }, - { "dash", "gobject", "list-prepared", &gobject_list_prepared, {"count"} }, - { "dash", "gobject", "vote-many", &gobject_vote_many, {"governance-hash", "vote", "vote-outcome"} }, - { "dash", "gobject", "vote-alias", &gobject_vote_alias, {"governance-hash", "vote", "vote-outcome", "protx-hash"} }, + { "dash", &gobject_prepare, }, + { "dash", &gobject_list_prepared, }, + { "dash", &gobject_vote_many, }, + { "dash", &gobject_vote_alias, }, #endif - { "dash", "gobject", "submit", &gobject_submit, {"parent-hash", "revision", "time", "data-hex", "fee-txid"} }, - { "dash", "gobject", "list", &gobject_list, {"signal", "type"} }, - { "dash", "gobject", "diff", &gobject_diff, {"signal", "type"} }, - { "dash", "gobject", "get", &gobject_get, {"governance-hash"} }, - { "dash", "gobject", "getcurrentvotes", &gobject_getcurrentvotes, {"governance-hash", "txid", "vout"} }, - { "dash", "voteraw", &voteraw, {"mn-collateral-tx-hash","mn-collateral-tx-index","governance-hash","vote-signal","vote-outcome","time","vote-sig"} }, - + { "dash", &gobject_submit, }, + { "dash", &gobject_list, }, + { "dash", &gobject_diff, }, + { "dash", &gobject_get, }, + { "dash", &gobject_getcurrentvotes, }, + { "dash", &voteraw, }, }; // clang-format on for (const auto& command : commands) { diff --git a/src/rpc/masternode.cpp b/src/rpc/masternode.cpp index 923cdc0993..bc2550ea0c 100644 --- a/src/rpc/masternode.cpp +++ b/src/rpc/masternode.cpp @@ -722,21 +722,21 @@ void RegisterMasternodeRPCCommands(CRPCTable &t) { // clang-format off static const CRPCCommand commands[] = -{ // category name actor (function) argNames - // --------------------- ------------------------ ----------------------- ---------- - { "dash", "masternode", &masternode_help, {"command"} }, - { "dash", "masternode", "list", &masternodelist_composite, {"mode", "filter"} }, - { "dash", "masternodelist", &masternodelist, {"mode", "filter"} }, - { "dash", "masternode", "connect", &masternode_connect, {"address"} }, - { "dash", "masternode", "count", &masternode_count, {} }, +{ // category actor (function) + // --------------------- ----------------------- + { "dash", &masternode_help, }, + { "dash", &masternodelist_composite, }, + { "dash", &masternodelist, }, + { "dash", &masternode_connect, }, + { "dash", &masternode_count, }, #ifdef ENABLE_WALLET - { "dash", "masternode", "outputs", &masternode_outputs, {} }, + { "dash", &masternode_outputs, }, #endif // ENABLE_WALLET - { "dash", "masternode", "status", &masternode_status, {} }, - { "dash", "masternode", "payments", &masternode_payments, {"blockhash", "count"} }, - { "dash", "masternode", "winners", &masternode_winners, {"count", "filter"} }, - { "dash", "masternode", "current", &masternode_current, {} }, - { "dash", "masternode", "winner", &masternode_winner, {} }, + { "dash", &masternode_status, }, + { "dash", &masternode_payments, }, + { "dash", &masternode_winners, }, + { "dash", &masternode_current, }, + { "dash", &masternode_winner, }, }; // clang-format on for (const auto& command : commands) { diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index dd83686da7..9e75dabfe3 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -416,17 +416,24 @@ static RPCHelpMan generateblock() }; } #else -static UniValue generatetoaddress(const JSONRPCRequest& request) +static RPCHelpMan generatetoaddress() { - throw JSONRPCError(RPC_METHOD_NOT_FOUND, "This call is not available because RPC miner isn't compiled"); + return RPCHelpMan{"generatetoaddress", "This call is not available because RPC miner isn't compiled", {}, {}, RPCExamples{""}, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "This call is not available because RPC miner isn't compiled"); + }}; } -static UniValue generatetodescriptor(const JSONRPCRequest& request) + +static RPCHelpMan generatetodescriptor() { - throw JSONRPCError(RPC_METHOD_NOT_FOUND, "This call is not available because RPC miner isn't compiled"); + return RPCHelpMan{"generatetodescriptor", "This call is not available because RPC miner isn't compiled", {}, {}, RPCExamples{""}, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "This call is not available because RPC miner isn't compiled"); + }}; } -static UniValue generateblock(const JSONRPCRequest& request) +static RPCHelpMan generateblock() { - throw JSONRPCError(RPC_METHOD_NOT_FOUND, "This call is not available because RPC miner isn't compiled"); + return RPCHelpMan{"generateblock", "This call is not available because RPC miner isn't compiled", {}, {}, RPCExamples{""}, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "This call is not available because RPC miner isn't compiled"); + }}; } #endif // ENABLE_MINER @@ -1295,29 +1302,30 @@ void RegisterMiningRPCCommands(CRPCTable &t) { // clang-format off static const CRPCCommand commands[] = -{ // category name actor (function) argNames - // --------------------- ------------------------ ----------------------- ---------- - { "mining", "getnetworkhashps", &getnetworkhashps, {"nblocks","height"} }, - { "mining", "getmininginfo", &getmininginfo, {} }, - { "mining", "prioritisetransaction", &prioritisetransaction, {"txid","fee_delta"} }, - { "mining", "getblocktemplate", &getblocktemplate, {"template_request"} }, - { "mining", "submitblock", &submitblock, {"hexdata","dummy"} }, - { "mining", "submitheader", &submitheader, {"hexdata"} }, +{ // category actor (function) + // --------------------- ----------------------- + { "mining", &getnetworkhashps, }, + { "mining", &getmininginfo, }, + { "mining", &prioritisetransaction, }, + { "mining", &getblocktemplate, }, + { "mining", &submitblock, }, + { "mining", &submitheader, }, #if ENABLE_MINER - { "generating", "generatetoaddress", &generatetoaddress, {"nblocks","address","maxtries"} }, - { "generating", "generatetodescriptor", &generatetodescriptor, {"num_blocks","descriptor","maxtries"} }, - { "generating", "generateblock", &generateblock, {"output","transactions"} }, + { "generating", &generatetoaddress, }, + { "generating", &generatetodescriptor, }, + { "generating", &generateblock, }, #else - { "hidden", "generatetoaddress", &generatetoaddress, {"nblocks","address","maxtries"} }, // Hidden as it isn't functional, just an error to let people know if miner isn't compiled - { "hidden", "generatetodescriptor", &generatetodescriptor, {"num_blocks","descriptor","maxtries"} }, - { "hidden", "generateblock", &generateblock, {"address","transactions"} }, +// Hidden as it isn't functional, just an error to let people know if miner isn't compiled + { "hidden", &generatetoaddress, }, + { "hidden", &generatetodescriptor, }, + { "hidden", &generateblock, }, #endif // ENABLE_MINER - { "util", "estimatesmartfee", &estimatesmartfee, {"conf_target", "estimate_mode"} }, + { "util", &estimatesmartfee, }, - { "hidden", "estimaterawfee", &estimaterawfee, {"conf_target", "threshold"} }, - { "hidden", "generate", &generate, {} }, + { "hidden", &estimaterawfee, }, + { "hidden", &generate, }, }; // clang-format on for (const auto& c : commands) { diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 8ceedf0da2..9706c94bda 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -1458,38 +1458,38 @@ static RPCHelpMan echojson() { return echo("echojson"); } void RegisterMiscRPCCommands(CRPCTable &t) { static const CRPCCommand commands[] = -{ // category name actor (function) argNames - // --------------------- ------------------------ ----------------------- ---------- - { "control", "debug", &debug, {"category"} }, - { "control", "getmemoryinfo", &getmemoryinfo, {"mode"} }, - { "control", "logging", &logging, {"include", "exclude"}}, - { "util", "validateaddress", &validateaddress, {"address"} }, - { "util", "createmultisig", &createmultisig, {"nrequired","keys"} }, - { "util", "deriveaddresses", &deriveaddresses, {"descriptor", "range"} }, - { "util", "getdescriptorinfo", &getdescriptorinfo, {"descriptor"} }, - { "util", "verifymessage", &verifymessage, {"address","signature","message"} }, - { "util", "signmessagewithprivkey", &signmessagewithprivkey, {"privkey","message"} }, - { "util", "getindexinfo", &getindexinfo, {"index_name"} }, - { "blockchain", "getspentinfo", &getspentinfo, {"request"} }, +{ // category actor (function) + // --------------------- ------------------------ + { "control", &debug, }, + { "control", &getmemoryinfo, }, + { "control", &logging, }, + { "util", &validateaddress, }, + { "util", &createmultisig, }, + { "util", &deriveaddresses, }, + { "util", &getdescriptorinfo, }, + { "util", &verifymessage, }, + { "util", &signmessagewithprivkey, }, + { "util", &getindexinfo, }, + { "blockchain", &getspentinfo, }, /* Address index */ - { "addressindex", "getaddressmempool", &getaddressmempool, {"addresses"} }, - { "addressindex", "getaddressutxos", &getaddressutxos, {"addresses"} }, - { "addressindex", "getaddressdeltas", &getaddressdeltas, {"addresses"} }, - { "addressindex", "getaddresstxids", &getaddresstxids, {"addresses"} }, - { "addressindex", "getaddressbalance", &getaddressbalance, {"addresses"} }, + { "addressindex", &getaddressmempool, }, + { "addressindex", &getaddressutxos, }, + { "addressindex", &getaddressdeltas, }, + { "addressindex", &getaddresstxids, }, + { "addressindex", &getaddressbalance, }, /* Dash features */ - { "dash", "mnsync", &mnsync, {"mode"} }, - { "dash", "spork", &spork, {"command"} }, - { "dash", "sporkupdate", &sporkupdate, {"name","value"} }, + { "dash", &mnsync, }, + { "dash", &spork, }, + { "dash", &sporkupdate, }, /* Not shown in help */ - { "hidden", "setmocktime", &setmocktime, {"timestamp"}}, - { "hidden", "mockscheduler", &mockscheduler, {"delta_time"}}, - { "hidden", "echo", &echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}}, - { "hidden", "echojson", &echojson, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}}, - { "hidden", "mnauth", &mnauth, {"nodeId", "proTxHash", "publicKey"}}, + { "hidden", &setmocktime, }, + { "hidden", &mockscheduler, }, + { "hidden", &echo, }, + { "hidden", &echojson, }, + { "hidden", &mnauth, }, }; // clang-format on for (const auto& c : commands) { diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index acbb5230e4..8a33030510 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -1013,25 +1013,25 @@ void RegisterNetRPCCommands(CRPCTable &t) { // clang-format off static const CRPCCommand commands[] = -{ // category name actor (function) argNames - // --------------------- ------------------------ ----------------------- ---------- - { "network", "getconnectioncount", &getconnectioncount, {} }, - { "network", "ping", &ping, {} }, - { "network", "getpeerinfo", &getpeerinfo, {} }, - { "network", "addnode", &addnode, {"node","command"} }, - { "network", "disconnectnode", &disconnectnode, {"address", "nodeid"} }, - { "network", "getaddednodeinfo", &getaddednodeinfo, {"node"} }, - { "network", "getnettotals", &getnettotals, {} }, - { "network", "getnetworkinfo", &getnetworkinfo, {} }, - { "network", "setban", &setban, {"subnet", "command", "bantime", "absolute"} }, - { "network", "listbanned", &listbanned, {} }, - { "network", "clearbanned", &clearbanned, {} }, - { "network", "cleardiscouraged", &cleardiscouraged, {} }, - { "network", "setnetworkactive", &setnetworkactive, {"state"} }, - { "network", "getnodeaddresses", &getnodeaddresses, {"count", "network"} }, +{ // category actor + // --------------------- ----------------------- + { "network", &getconnectioncount, }, + { "network", &ping, }, + { "network", &getpeerinfo, }, + { "network", &addnode, }, + { "network", &disconnectnode, }, + { "network", &getaddednodeinfo, }, + { "network", &getnettotals, }, + { "network", &getnetworkinfo, }, + { "network", &setban, }, + { "network", &listbanned, }, + { "network", &clearbanned, }, + { "network", &cleardiscouraged, }, + { "network", &setnetworkactive, }, + { "network", &getnodeaddresses, }, - { "hidden", "addconnection", &addconnection, {"address", "connection_type"} }, - { "hidden", "addpeeraddress", &addpeeraddress, {"address", "port"} }, + { "hidden", &addconnection, }, + { "hidden", &addpeeraddress, }, }; // clang-format on for (const auto& c : commands) { diff --git a/src/rpc/quorums.cpp b/src/rpc/quorums.cpp index c0494a2d7a..311d90cf7b 100644 --- a/src/rpc/quorums.cpp +++ b/src/rpc/quorums.cpp @@ -1132,28 +1132,28 @@ void RegisterQuorumsRPCCommands(CRPCTable &tableRPC) { // clang-format off static const CRPCCommand commands[] = -{ // category name actor (function) - // --------------------- ------------------------ ----------------------- - { "evo", "quorum", &quorum_help, {"command"} }, - { "evo", "quorum", "list", &quorum_list, {"count"} }, - { "evo", "quorum", "listextended", &quorum_list_extended, {"height"} }, - { "evo", "quorum", "info", &quorum_info, {"llmqType", "quorumHash", "includeSkShare"} }, - { "evo", "quorum", "dkginfo", &quorum_dkginfo, {} }, - { "evo", "quorum", "dkgstatus", &quorum_dkgstatus, {"detail_level"} }, - { "evo", "quorum", "memberof", &quorum_memberof, {"proTxHash", "scanQuorumsCount"} }, - { "evo", "quorum", "sign", &quorum_sign, {"llmqType", "id", "msgHash", "quorumHash", "submit"} }, - { "evo", "quorum", "platformsign", &quorum_platformsign, {"id", "msgHash", "quorumHash", "submit"} }, - { "evo", "quorum", "verify", &quorum_verify, {"llmqType", "id", "msgHash", "signature", "quorumHash", "signHeight"} }, - { "evo", "quorum", "hasrecsig", &quorum_hasrecsig, {"llmqType", "id", "msgHash"} }, - { "evo", "quorum", "getrecsig", &quorum_getrecsig, {"llmqType", "id", "msgHash"} }, - { "evo", "quorum", "isconflicting",&quorum_isconflicting, {"llmqType", "id", "msgHash"} }, - { "evo", "quorum", "selectquorum", &quorum_selectquorum, {"llmqType", "id"} }, - { "evo", "quorum", "dkgsimerror", &quorum_dkgsimerror, {"type", "rate"} }, - { "evo", "quorum", "getdata", &quorum_getdata, {"nodeId", "llmqType", "quorumHash", "dataMask", "proTxHash"} }, - { "evo", "quorum", "rotationinfo", &quorum_rotationinfo, {"blockRequestHash", "extraShare", "baseBlockHash..."} }, - { "evo", "submitchainlock", &submitchainlock, {"blockHash", "signature", "blockHeight"} }, - { "evo", "verifychainlock", &verifychainlock, {"blockHash", "signature", "blockHeight"} }, - { "evo", "verifyislock", &verifyislock, {"id", "txid", "signature", "maxHeight"} }, +{ // category actor (function) + // --------------------- ----------------------- + { "evo", &quorum_help, }, + { "evo", &quorum_list, }, + { "evo", &quorum_list_extended, }, + { "evo", &quorum_info, }, + { "evo", &quorum_dkginfo, }, + { "evo", &quorum_dkgstatus, }, + { "evo", &quorum_memberof, }, + { "evo", &quorum_sign, }, + { "evo", &quorum_platformsign, }, + { "evo", &quorum_verify, }, + { "evo", &quorum_hasrecsig, }, + { "evo", &quorum_getrecsig, }, + { "evo", &quorum_isconflicting, }, + { "evo", &quorum_selectquorum, }, + { "evo", &quorum_dkgsimerror, }, + { "evo", &quorum_getdata, }, + { "evo", &quorum_rotationinfo, }, + { "evo", &submitchainlock, }, + { "evo", &verifychainlock, }, + { "evo", &verifyislock, }, }; // clang-format on for (const auto& command : commands) { diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index da0010acc6..6c7111ccd5 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -2079,30 +2079,30 @@ void RegisterRawTransactionRPCCommands(CRPCTable &t) { // clang-format off static const CRPCCommand commands[] = -{ // category name actor (function) argNames - // --------------------- ------------------------ ----------------------- ---------- - { "rawtransactions", "getassetunlockstatuses", &getassetunlockstatuses, {"indexes","height"} }, - { "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose","blockhash"} }, - { "rawtransactions", "getrawtransactionmulti", &getrawtransactionmulti, {"transactions","verbose"} }, - { "rawtransactions", "gettxchainlocks", &gettxchainlocks, {"txids"} }, - { "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime"} }, - { "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring"} }, - { "rawtransactions", "decodescript", &decodescript, {"hexstring"} }, - { "rawtransactions", "sendrawtransaction", &sendrawtransaction, {"hexstring","maxfeerate","instantsend","bypasslimits"} }, - { "rawtransactions", "combinerawtransaction", &combinerawtransaction, {"txs"} }, - { "rawtransactions", "signrawtransactionwithkey", &signrawtransactionwithkey, {"hexstring","privkeys","prevtxs","sighashtype"} }, - { "rawtransactions", "testmempoolaccept", &testmempoolaccept, {"rawtxs","maxfeerate"} }, - { "rawtransactions", "decodepsbt", &decodepsbt, {"psbt"} }, - { "rawtransactions", "combinepsbt", &combinepsbt, {"txs"} }, - { "rawtransactions", "finalizepsbt", &finalizepsbt, {"psbt", "extract"} }, - { "rawtransactions", "createpsbt", &createpsbt, {"inputs","outputs","locktime"} }, - { "rawtransactions", "converttopsbt", &converttopsbt, {"hexstring","permitsigdata"} }, - { "rawtransactions", "utxoupdatepsbt", &utxoupdatepsbt, {"psbt", "descriptors"} }, - { "rawtransactions", "joinpsbts", &joinpsbts, {"txs"} }, - { "rawtransactions", "analyzepsbt", &analyzepsbt, {"psbt"} }, +{ // category actor (function) + // --------------------- ----------------------- + { "rawtransactions", &getassetunlockstatuses, }, + { "rawtransactions", &getrawtransaction, }, + { "rawtransactions", &getrawtransactionmulti, }, + { "rawtransactions", &gettxchainlocks, }, + { "rawtransactions", &createrawtransaction, }, + { "rawtransactions", &decoderawtransaction, }, + { "rawtransactions", &decodescript, }, + { "rawtransactions", &sendrawtransaction, }, + { "rawtransactions", &combinerawtransaction, }, + { "rawtransactions", &signrawtransactionwithkey, }, + { "rawtransactions", &testmempoolaccept, }, + { "rawtransactions", &decodepsbt, }, + { "rawtransactions", &combinepsbt, }, + { "rawtransactions", &finalizepsbt, }, + { "rawtransactions", &createpsbt, }, + { "rawtransactions", &converttopsbt, }, + { "rawtransactions", &utxoupdatepsbt, }, + { "rawtransactions", &joinpsbts, }, + { "rawtransactions", &analyzepsbt, }, - { "blockchain", "gettxoutproof", &gettxoutproof, {"txids", "blockhash"} }, - { "blockchain", "verifytxoutproof", &verifytxoutproof, {"proof"} }, + { "blockchain", &gettxoutproof, }, + { "blockchain", &verifytxoutproof, }, }; // clang-format on for (const auto& c : commands) { diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 194fa9c094..08de516717 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -176,10 +176,16 @@ static RPCHelpMan help() [&](const RPCHelpMan& self, const JSONRPCRequest& jsonRequest) -> UniValue { std::string strCommand, strSubCommand; - if (jsonRequest.params.size() > 0) + if (jsonRequest.params.size() > 0) { strCommand = jsonRequest.params[0].get_str(); - if (jsonRequest.params.size() > 1) + } + if (jsonRequest.params.size() > 1) { strSubCommand = jsonRequest.params[1].get_str(); + } + if (strCommand == "dump_all_command_conversions") { + // Used for testing only, undocumented + return tableRPC.dumpArgMap(); + } return tableRPC.help(strCommand, strSubCommand, jsonRequest); }, @@ -280,13 +286,13 @@ static RPCHelpMan getrpcinfo() } // clang-format off static const CRPCCommand vRPCCommands[] = -{ // category name actor (function) argNames - // --------------------- ------------------------ ----------------------- ---------- +{ // category actor (function) + // --------------------- ----------------------- /* Overall control/query calls */ - { "control", "getrpcinfo", &getrpcinfo, {} }, - { "control", "help", &help, {"command","subcommand"} }, - { "control", "stop", &stop, {"wait"} }, - { "control", "uptime", &uptime, {} }, + { "control", &getrpcinfo, }, + { "control", &help, }, + { "control", &stop, }, + { "control", &uptime, }, }; // clang-format on @@ -622,6 +628,18 @@ std::vector> CRPCTable::listCommands() const return commandList; } +UniValue CRPCTable::dumpArgMap() const +{ + UniValue ret{UniValue::VARR}; + for (const auto& cmd : mapCommands) { + for (const auto& c : cmd.second) { + const auto help = RpcMethodFnType(c->unique_id)(); + help.AppendArgMap(ret); + } + } + return ret; +} + void RPCSetTimerInterfaceIfUnset(RPCTimerInterface *iface) { if (!timerInterface) diff --git a/src/rpc/server.h b/src/rpc/server.h index 56744fd47f..5cb93c1fc6 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -101,7 +101,7 @@ public: } //! Simplified constructor taking plain RpcMethodFnType function pointer. - CRPCCommand(std::string category, std::string name_in, RpcMethodFnType fn, std::vector args_in) + CRPCCommand(std::string category, RpcMethodFnType fn) : CRPCCommand( category, fn().m_name, @@ -110,8 +110,6 @@ public: fn().GetArgNames(), intptr_t(fn)) { - CHECK_NONFATAL(fn().m_name == name_in); - CHECK_NONFATAL(fn().GetArgNames() == args_in); } //! Simplified constructor taking plain RpcMethodFnType function pointer with sub-command. @@ -169,6 +167,11 @@ public: */ std::vector> listCommands() const; + /** + * Return all named arguments that need to be converted by the client from string to another JSON type + */ + UniValue dumpArgMap() const; + /** * Appends a CRPCCommand to the dispatch table. * diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index c9292461c1..5afb7efa94 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -566,6 +566,23 @@ std::string RPCHelpMan::ToString() const return ret; } +void RPCHelpMan::AppendArgMap(UniValue& arr) const +{ + for (int i{0}; i < int(m_args.size()); ++i) { + const auto& arg = m_args.at(i); + std::vector arg_names = SplitString(arg.m_names, '|'); + for (const auto& arg_name : arg_names) { + UniValue map{UniValue::VARR}; + map.push_back(m_name); + map.push_back(i); + map.push_back(arg_name); + map.push_back(arg.m_type == RPCArg::Type::STR || + arg.m_type == RPCArg::Type::STR_HEX); + arr.push_back(map); + } + } +} + std::string RPCArg::GetFirstName() const { return m_names.substr(0, m_names.find("|")); diff --git a/src/rpc/util.h b/src/rpc/util.h index 57759a2a11..958995ec8c 100644 --- a/src/rpc/util.h +++ b/src/rpc/util.h @@ -341,6 +341,8 @@ public: RPCHelpMan(std::string name, std::string description, std::vector args, RPCResults results, RPCExamples examples, RPCMethodImpl fun); std::string ToString() const; + /** Append the named args that need to be converted from string to another JSON type */ + void AppendArgMap(UniValue& arr) const; UniValue HandleRequest(const JSONRPCRequest& request) { Check(request); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 4a70b3cfbc..b4cd4a1146 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -4718,76 +4718,76 @@ Span GetWalletRPCCommands() { // clang-format off static const CRPCCommand commands[] = -{ // category name actor (function) argNames - // --------------------- ------------------------ ----------------------- ---------- - { "hidden", "instantsendtoaddress", &instantsendtoaddress, {} }, - { "rawtransactions", "fundrawtransaction", &fundrawtransaction, {"hexstring","options"} }, - { "wallet", "abandontransaction", &abandontransaction, {"txid"} }, - { "wallet", "abortrescan", &abortrescan, {} }, - { "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","label"} }, - { "wallet", "backupwallet", &backupwallet, {"destination"} }, - { "wallet", "createwallet", &createwallet, {"wallet_name", "disable_private_keys", "blank", "passphrase", "avoid_reuse", "descriptors", "load_on_startup"} }, - { "wallet", "restorewallet", &restorewallet, {"wallet_name","backup_file", "load_on_startup"} }, - { "wallet", "dumphdinfo", &dumphdinfo, {} }, - { "wallet", "dumpprivkey", &dumpprivkey, {"address"} }, - { "wallet", "dumpwallet", &dumpwallet, {"filename"} }, - { "wallet", "encryptwallet", &encryptwallet, {"passphrase"} }, - { "wallet", "getaddressesbylabel", &getaddressesbylabel, {"label"} }, - { "wallet", "getaddressinfo", &getaddressinfo, {"address"} }, - { "wallet", "getbalance", &getbalance, {"dummy","minconf","addlocked","include_watchonly", "avoid_reuse"} }, - { "wallet", "getnewaddress", &getnewaddress, {"label"} }, - { "wallet", "getrawchangeaddress", &getrawchangeaddress, {} }, - { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, {"address","minconf","addlocked"} }, - { "wallet", "getreceivedbylabel", &getreceivedbylabel, {"label","minconf","addlocked"} }, - { "wallet", "gettransaction", &gettransaction, {"txid","include_watchonly","verbose"} }, - { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, {} }, - { "wallet", "getbalances", &getbalances, {} }, - { "wallet", "getwalletinfo", &getwalletinfo, {} }, - { "wallet", "importaddress", &importaddress, {"address","label","rescan","p2sh"} }, - { "wallet", "importelectrumwallet", &importelectrumwallet, {"filename", "index"} }, - { "wallet", "importdescriptors", &importdescriptors, {"requests"} }, - { "wallet", "importmulti", &importmulti, {"requests","options"} }, - { "wallet", "importprivkey", &importprivkey, {"privkey","label","rescan"} }, - { "wallet", "importprunedfunds", &importprunedfunds, {"rawtransaction","txoutproof"} }, - { "wallet", "importpubkey", &importpubkey, {"pubkey","label","rescan"} }, - { "wallet", "importwallet", &importwallet, {"filename"} }, - { "wallet", "keypoolrefill", &keypoolrefill, {"newsize"} }, - { "wallet", "listaddressbalances", &listaddressbalances, {"minamount"} }, - { "wallet", "listaddressgroupings", &listaddressgroupings, {} }, - { "wallet", "listdescriptors", &listdescriptors, {} }, - { "wallet", "listlabels", &listlabels, {"purpose"} }, - { "wallet", "listlockunspent", &listlockunspent, {} }, - { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, {"minconf","addlocked","include_empty","include_watchonly","address_filter"} }, - { "wallet", "listreceivedbylabel", &listreceivedbylabel, {"minconf","addlocked","include_empty","include_watchonly"} }, - { "wallet", "listsinceblock", &listsinceblock, {"blockhash","target_confirmations","include_watchonly","include_removed"} }, - { "wallet", "listtransactions", &listtransactions, {"label|dummy","count","skip","include_watchonly"} }, - { "wallet", "listunspent", &listunspent, {"minconf","maxconf","addresses","include_unsafe","query_options"} }, - { "wallet", "listwalletdir", &listwalletdir, {} }, - { "wallet", "listwallets", &listwallets, {} }, - { "wallet", "loadwallet", &loadwallet, {"filename", "load_on_startup"} }, - { "wallet", "lockunspent", &lockunspent, {"unlock","transactions"} }, - { "wallet", "removeprunedfunds", &removeprunedfunds, {"txid"} }, - { "wallet", "rescanblockchain", &rescanblockchain, {"start_height", "stop_height"} }, - { "wallet", "send", &send, {"outputs","conf_target","estimate_mode","options"} }, - { "wallet", "sendmany", &sendmany, {"dummy","amounts","minconf","addlocked","comment","subtractfeefrom","use_is","use_cj","conf_target","estimate_mode","verbose"} }, - { "wallet", "sendtoaddress", &sendtoaddress, {"address","amount","comment","comment_to","subtractfeefromamount","use_is","use_cj","conf_target","estimate_mode", "avoid_reuse","verbose"} }, - { "wallet", "sethdseed", &sethdseed, {"newkeypool","seed"} }, - { "wallet", "setcoinjoinrounds", &setcoinjoinrounds, {"rounds"} }, - { "wallet", "setcoinjoinamount", &setcoinjoinamount, {"amount"} }, - { "wallet", "setlabel", &setlabel, {"address","label"} }, - { "wallet", "settxfee", &settxfee, {"amount"} }, - { "wallet", "setwalletflag", &setwalletflag, {"flag","value"} }, - { "wallet", "signmessage", &signmessage, {"address","message"} }, - { "wallet", "signrawtransactionwithwallet", &signrawtransactionwithwallet, {"hexstring","prevtxs","sighashtype"} }, - { "wallet", "unloadwallet", &unloadwallet, {"wallet_name", "load_on_startup"} }, - { "wallet", "upgradewallet", &upgradewallet, {"version"} }, - { "wallet", "upgradetohd", &upgradetohd, {"mnemonic", "mnemonicpassphrase", "walletpassphrase", "rescan"} }, - { "wallet", "walletlock", &walletlock, {} }, - { "wallet", "walletpassphrasechange", &walletpassphrasechange, {"oldpassphrase","newpassphrase"} }, - { "wallet", "walletpassphrase", &walletpassphrase, {"passphrase","timeout","mixingonly"} }, - { "wallet", "walletprocesspsbt", &walletprocesspsbt, {"psbt","sign","sighashtype","bip32derivs"} }, - { "wallet", "walletcreatefundedpsbt", &walletcreatefundedpsbt, {"inputs","outputs","locktime","options","bip32derivs"} }, - { "wallet", "wipewallettxes", &wipewallettxes, {"keep_confirmed"} }, +{ // category actor (function) + // ------------------ ------------------------ + { "hidden", &instantsendtoaddress, }, + { "rawtransactions", &fundrawtransaction, }, + { "wallet", &abandontransaction, }, + { "wallet", &abortrescan, }, + { "wallet", &addmultisigaddress, }, + { "wallet", &backupwallet, }, + { "wallet", &createwallet, }, + { "wallet", &restorewallet, }, + { "wallet", &dumphdinfo, }, + { "wallet", &dumpprivkey, }, + { "wallet", &dumpwallet, }, + { "wallet", &encryptwallet, }, + { "wallet", &getaddressesbylabel, }, + { "wallet", &getaddressinfo, }, + { "wallet", &getbalance, }, + { "wallet", &getnewaddress, }, + { "wallet", &getrawchangeaddress, }, + { "wallet", &getreceivedbyaddress, }, + { "wallet", &getreceivedbylabel, }, + { "wallet", &gettransaction, }, + { "wallet", &getunconfirmedbalance, }, + { "wallet", &getbalances, }, + { "wallet", &getwalletinfo, }, + { "wallet", &importaddress, }, + { "wallet", &importelectrumwallet, }, + { "wallet", &importdescriptors, }, + { "wallet", &importmulti, }, + { "wallet", &importprivkey, }, + { "wallet", &importprunedfunds, }, + { "wallet", &importpubkey, }, + { "wallet", &importwallet, }, + { "wallet", &keypoolrefill, }, + { "wallet", &listaddressbalances, }, + { "wallet", &listaddressgroupings, }, + { "wallet", &listdescriptors, }, + { "wallet", &listlabels, }, + { "wallet", &listlockunspent, }, + { "wallet", &listreceivedbyaddress, }, + { "wallet", &listreceivedbylabel, }, + { "wallet", &listsinceblock, }, + { "wallet", &listtransactions, }, + { "wallet", &listunspent, }, + { "wallet", &listwalletdir, }, + { "wallet", &listwallets, }, + { "wallet", &loadwallet, }, + { "wallet", &lockunspent, }, + { "wallet", &removeprunedfunds, }, + { "wallet", &rescanblockchain, }, + { "wallet", &send, }, + { "wallet", &sendmany, }, + { "wallet", &sendtoaddress, }, + { "wallet", &sethdseed, }, + { "wallet", &setcoinjoinrounds, }, + { "wallet", &setcoinjoinamount, }, + { "wallet", &setlabel, }, + { "wallet", &settxfee, }, + { "wallet", &setwalletflag, }, + { "wallet", &signmessage, }, + { "wallet", &signrawtransactionwithwallet, }, + { "wallet", &unloadwallet, }, + { "wallet", &upgradewallet, }, + { "wallet", &upgradetohd, }, + { "wallet", &walletlock, }, + { "wallet", &walletpassphrasechange, }, + { "wallet", &walletpassphrase, }, + { "wallet", &walletprocesspsbt, }, + { "wallet", &walletcreatefundedpsbt, }, + { "wallet", &wipewallettxes, }, }; // clang-format on return commands; diff --git a/src/zmq/zmqrpc.cpp b/src/zmq/zmqrpc.cpp index 5abb2d0304..5a425430aa 100644 --- a/src/zmq/zmqrpc.cpp +++ b/src/zmq/zmqrpc.cpp @@ -52,9 +52,9 @@ static RPCHelpMan getzmqnotifications() } const CRPCCommand commands[] = -{ // category name actor (function) argNames - // ----------------- ------------------------ ----------------------- ---------- - { "zmq", "getzmqnotifications", &getzmqnotifications, {} }, +{ // category actor (function) + // ----------------- ----------------------- + { "zmq", &getzmqnotifications, }, }; } // anonymous namespace diff --git a/test/functional/rpc_help.py b/test/functional/rpc_help.py index 9df7a934ae..2e821e5939 100755 --- a/test/functional/rpc_help.py +++ b/test/functional/rpc_help.py @@ -7,7 +7,39 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_raises_rpc_error +from collections import defaultdict import os +import re + + +def parse_string(s): + assert s[0] == '"' + assert s[-1] == '"' + return s[1:-1] + + +def process_mapping(fname): + """Find and parse conversion table in implementation file `fname`.""" + cmds = [] + in_rpcs = False + with open(fname, "r", encoding="utf8") as f: + for line in f: + line = line.rstrip() + if not in_rpcs: + if line == 'static const CRPCConvertParam vRPCConvertParams[] =': + in_rpcs = True + else: + if line.startswith('};'): + in_rpcs = False + elif '{' in line and '"' in line: + m = re.search(r'{ *("[^"]*"), *([0-9]+) *, *("[^"]*") *},', line) + assert m, 'No match to table expression: %s' % line + name = parse_string(m.group(1)) + idx = int(m.group(2)) + argname = parse_string(m.group(3)) + cmds.append((name, idx, argname)) + assert not in_rpcs and cmds + return cmds class HelpRpcTest(BitcoinTestFramework): @@ -16,11 +48,43 @@ class HelpRpcTest(BitcoinTestFramework): self.supports_cli = False def run_test(self): + self.test_client_conversion_table() self.test_categories() self.dump_help() if self.is_wallet_compiled(): self.wallet_help() + def test_client_conversion_table(self): + file_conversion_table = os.path.join(self.config["environment"]["SRCDIR"], 'src', 'rpc', 'client.cpp') + mapping_client = process_mapping(file_conversion_table) + # Ignore echojson in client table + mapping_client = [m for m in mapping_client if m[0] != 'echojson'] + + mapping_server = self.nodes[0].help("dump_all_command_conversions") + # Filter all RPCs whether they need conversion + mapping_server_conversion = [tuple(m[:3]) for m in mapping_server if not m[3]] + + # Only check if all RPC methods have been compiled (i.e. wallet is enabled) + if self.is_wallet_compiled() and sorted(mapping_client) != sorted(mapping_server_conversion): + raise AssertionError("RPC client conversion table ({}) and RPC server named arguments mismatch!\n{}".format( + file_conversion_table, + set(mapping_client).symmetric_difference(mapping_server_conversion), + )) + + # Check for conversion difference by argument name. + # It is preferable for API consistency that arguments with the same name + # have the same conversion, so bin by argument name. + all_methods_by_argname = defaultdict(list) + converts_by_argname = defaultdict(list) + for m in mapping_server: + all_methods_by_argname[m[2]].append(m[0]) + converts_by_argname[m[2]].append(m[3]) + + for argname, convert in converts_by_argname.items(): + if all(convert) != any(convert): + # Only allow dummy to fail consistency check + assert argname == 'dummy', ('WARNING: conversion mismatch for argument named %s (%s)' % (argname, list(zip(all_methods_by_argname[argname], converts_by_argname[argname])))) + def test_categories(self): node = self.nodes[0] diff --git a/test/lint/check-rpc-mappings.py b/test/lint/check-rpc-mappings.py deleted file mode 100755 index c9596dbc77..0000000000 --- a/test/lint/check-rpc-mappings.py +++ /dev/null @@ -1,173 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) 2017-2019 The Bitcoin Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. -"""Check RPC argument consistency.""" - -from collections import defaultdict -import os -import re -import sys - -# Source files (relative to root) to scan for dispatch tables -SOURCES = [ - "src/rpc/server.cpp", - "src/rpc/blockchain.cpp", - "src/rpc/coinjoin.cpp", - "src/rpc/evo.cpp", - "src/rpc/governance.cpp", - "src/rpc/masternode.cpp", - "src/rpc/mining.cpp", - "src/rpc/misc.cpp", - "src/rpc/net.cpp", - "src/rpc/quorums.cpp", - "src/rpc/rawtransaction.cpp", - "src/wallet/rpcwallet.cpp", -] -# Source file (relative to root) containing conversion mapping -SOURCE_CLIENT = 'src/rpc/client.cpp' -# Argument names that should be ignored in consistency checks -IGNORE_DUMMY_ARGS = {'dummy', 'arg0', 'arg1', 'arg2', 'arg3', 'arg4', 'arg5', 'arg6', 'arg7', 'arg8', 'arg9'} - -class RPCCommand: - def __init__(self, name, args): - self.name = name - self.args = args - -class RPCArgument: - def __init__(self, names, idx): - self.names = names - self.idx = idx - self.convert = False - -def parse_string(s): - assert s[0] == '"' - assert s[-1] == '"' - return s[1:-1] - -def process_commands(fname): - """Find and parse dispatch table in implementation file `fname`.""" - cmds = [] - in_rpcs = False - with open(fname, "r", encoding="utf8") as f: - for line in f: - line = line.rstrip() - if not in_rpcs: - if re.match(r"static const CRPCCommand .*\[\] =", line): - in_rpcs = True - else: - if line.startswith('};'): - in_rpcs = False - elif '{' in line and '"' in line: - m = re.search(r'{ *("[^"]*"), *("[^"]*"), *&([^,]*), *{([^}]*)} *},', line) - # that's a quick fix for composite command - # no proper implementation is needed so far as this linter would be removed soon with bitcoin#20012 - if not m: - m = re.search(r'{ *("[^"]*"), *("[^"]*"), *("[^"]*"), *&([^,]*), *{([^}]*)} *},', line) - if m: - continue - assert m, 'No match to table expression: %s' % line - name = parse_string(m.group(2)) - args_str = m.group(4).strip() - if args_str: - args = [RPCArgument(parse_string(x.strip()).split('|'), idx) for idx, x in enumerate(args_str.split(','))] - else: - args = [] - cmds.append(RPCCommand(name, args)) - assert not in_rpcs and cmds, "Something went wrong with parsing the C++ file: update the regexps" - return cmds - -def process_mapping(fname): - """Find and parse conversion table in implementation file `fname`.""" - cmds = [] - in_rpcs = False - with open(fname, "r", encoding="utf8") as f: - for line in f: - line = line.rstrip() - if not in_rpcs: - if line == 'static const CRPCConvertParam vRPCConvertParams[] =': - in_rpcs = True - else: - if line.startswith('};'): - in_rpcs = False - elif '{' in line and '"' in line: - m = re.search(r'{ *("[^"]*"), *([0-9]+) *, *("[^"]*") *},', line) - assert m, 'No match to table expression: %s' % line - name = parse_string(m.group(1)) - idx = int(m.group(2)) - argname = parse_string(m.group(3)) - cmds.append((name, idx, argname)) - assert not in_rpcs and cmds - return cmds - -def main(): - if len(sys.argv) != 2: - print('Usage: {} ROOT-DIR'.format(sys.argv[0]), file=sys.stderr) - sys.exit(1) - - root = sys.argv[1] - - # Get all commands from dispatch tables - cmds = [] - for fname in SOURCES: - cmds += process_commands(os.path.join(root, fname)) - - cmds_by_name = {} - for cmd in cmds: - cmds_by_name[cmd.name] = cmd - - # Get current convert mapping for client - client = SOURCE_CLIENT - mapping = set(process_mapping(os.path.join(root, client))) - - print('* Checking consistency between dispatch tables and vRPCConvertParams') - - # Check mapping consistency - errors = 0 - for (cmdname, argidx, argname) in mapping: - try: - rargnames = cmds_by_name[cmdname].args[argidx].names - except IndexError: - print('ERROR: %s argument %i (named %s in vRPCConvertParams) is not defined in dispatch table' % (cmdname, argidx, argname)) - errors += 1 - continue - if argname not in rargnames: - print('ERROR: %s argument %i is named %s in vRPCConvertParams but %s in dispatch table' % (cmdname, argidx, argname, rargnames), file=sys.stderr) - errors += 1 - - # Check for conflicts in vRPCConvertParams conversion - # All aliases for an argument must either be present in the - # conversion table, or not. Anything in between means an oversight - # and some aliases won't work. - for cmd in cmds: - for arg in cmd.args: - convert = [((cmd.name, arg.idx, argname) in mapping) for argname in arg.names] - if any(convert) != all(convert): - print('ERROR: %s argument %s has conflicts in vRPCConvertParams conversion specifier %s' % (cmd.name, arg.names, convert)) - errors += 1 - arg.convert = all(convert) - - # Check for conversion difference by argument name. - # It is preferable for API consistency that arguments with the same name - # have the same conversion, so bin by argument name. - all_methods_by_argname = defaultdict(list) - converts_by_argname = defaultdict(list) - for cmd in cmds: - for arg in cmd.args: - for argname in arg.names: - all_methods_by_argname[argname].append(cmd.name) - converts_by_argname[argname].append(arg.convert) - - for argname, convert in converts_by_argname.items(): - if all(convert) != any(convert): - if argname in IGNORE_DUMMY_ARGS: - # these are testing or dummy, don't warn for them - continue - print('WARNING: conversion mismatch for argument named %s (%s)' % - (argname, list(zip(all_methods_by_argname[argname], converts_by_argname[argname])))) - - sys.exit(errors > 0) - - -if __name__ == '__main__': - main()