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
This commit is contained in:
Wladimir J. van der Laan 2021-01-28 19:25:07 +01:00 committed by Konstantin Akimov
parent 1d87ce4e86
commit d55759fa79
No known key found for this signature in database
GPG Key ID: 2176C4A5D01EA524
22 changed files with 418 additions and 488 deletions

View File

@ -24,8 +24,6 @@ if [ "$CHECK_DOC" = 1 ]; then
#test/lint/git-subtree-check.sh src/leveldb #test/lint/git-subtree-check.sh src/leveldb
# TODO: Check docs (re-enable after all Bitcoin PRs have been merged and docs fully fixed) # TODO: Check docs (re-enable after all Bitcoin PRs have been merged and docs fully fixed)
#test/lint/check-doc.py #test/lint/check-doc.py
# Check rpc consistency
test/lint/check-rpc-mappings.py .
# Run all linters # Run all linters
test/lint/lint-all.sh test/lint/lint-all.sh
test/lint/extended-lint-all.sh test/lint/extended-lint-all.sh

View File

@ -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/univalue
test/lint/git-subtree-check.sh src/leveldb test/lint/git-subtree-check.sh src/leveldb
test/lint/check-doc.py test/lint/check-doc.py
test/lint/check-rpc-mappings.py .
test/lint/lint-all.sh test/lint/lint-all.sh
if [ "$CIRRUS_REPO_FULL_NAME" = "dashpay/dash" ] && [ -n "$CIRRUS_CRON" ]; then if [ "$CIRRUS_REPO_FULL_NAME" = "dashpay/dash" ] && [ -n "$CIRRUS_CRON" ]; then

View File

@ -35,7 +35,7 @@ static RPCHelpMan rpcNestedTest_rpc()
} }
static const CRPCCommand vRPCCommands[] = { static const CRPCCommand vRPCCommands[] = {
{"test", "rpcNestedTest", &rpcNestedTest_rpc, {"arg1", "arg2", "arg3"}}, {"test", &rpcNestedTest_rpc},
}; };
void RPCNestedTests::rpcNestedTests() void RPCNestedTests::rpcNestedTests()

View File

@ -3038,47 +3038,47 @@ void RegisterBlockchainRPCCommands(CRPCTable &t)
{ {
// clang-format off // clang-format off
static const CRPCCommand commands[] = static const CRPCCommand commands[] =
{ // category name actor (function) argNames { // category actor (function)
// --------------------- ------------------------ ----------------------- ---------- // --------------------- ------------------------
{ "blockchain", "getblockchaininfo", &getblockchaininfo, {} }, { "blockchain", &getblockchaininfo, },
{ "blockchain", "getchaintxstats", &getchaintxstats, {"nblocks", "blockhash"} }, { "blockchain", &getchaintxstats, },
{ "blockchain", "getblockstats", &getblockstats, {"hash_or_height", "stats"} }, { "blockchain", &getblockstats, },
{ "blockchain", "getbestblockhash", &getbestblockhash, {} }, { "blockchain", &getbestblockhash, },
{ "blockchain", "getbestchainlock", &getbestchainlock, {} }, { "blockchain", &getbestchainlock, },
{ "blockchain", "getblockcount", &getblockcount, {} }, { "blockchain", &getblockcount, },
{ "blockchain", "getblock", &getblock, {"blockhash","verbosity|verbose"} }, { "blockchain", &getblock, },
{ "blockchain", "getblockfrompeer", &getblockfrompeer, {"block_hash", "peer_id"}}, { "blockchain", &getblockfrompeer, },
{ "blockchain", "getblockhashes", &getblockhashes, {"high","low"} }, { "blockchain", &getblockhashes, },
{ "blockchain", "getblockhash", &getblockhash, {"height"} }, { "blockchain", &getblockhash, },
{ "blockchain", "getblockheader", &getblockheader, {"blockhash","verbose"} }, { "blockchain", &getblockheader, },
{ "blockchain", "getblockheaders", &getblockheaders, {"blockhash","count","verbose"} }, { "blockchain", &getblockheaders, },
{ "blockchain", "getmerkleblocks", &getmerkleblocks, {"filter","blockhash","count"} }, { "blockchain", &getmerkleblocks, },
{ "blockchain", "getchaintips", &getchaintips, {"count","branchlen"} }, { "blockchain", &getchaintips, },
{ "blockchain", "getdifficulty", &getdifficulty, {} }, { "blockchain", &getdifficulty, },
{ "blockchain", "getmempoolancestors", &getmempoolancestors, {"txid","verbose"} }, { "blockchain", &getmempoolancestors, },
{ "blockchain", "getmempooldescendants", &getmempooldescendants, {"txid","verbose"} }, { "blockchain", &getmempooldescendants, },
{ "blockchain", "getmempoolentry", &getmempoolentry, {"txid"} }, { "blockchain", &getmempoolentry, },
{ "blockchain", "getmempoolinfo", &getmempoolinfo, {} }, { "blockchain", &getmempoolinfo, },
{ "blockchain", "getrawmempool", &getrawmempool, {"verbose"} }, { "blockchain", &getrawmempool, },
{ "blockchain", "getspecialtxes", &getspecialtxes, {"blockhash", "type", "count", "skip", "verbosity"} }, { "blockchain", &getspecialtxes, },
{ "blockchain", "gettxout", &gettxout, {"txid","n","include_mempool"} }, { "blockchain", &gettxout, },
{ "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, {"hash_type", "hash_or_height", "use_index"} }, { "blockchain", &gettxoutsetinfo, },
{ "blockchain", "pruneblockchain", &pruneblockchain, {"height"} }, { "blockchain", &pruneblockchain, },
{ "blockchain", "savemempool", &savemempool, {} }, { "blockchain", &savemempool, },
{ "blockchain", "verifychain", &verifychain, {"checklevel","nblocks"} }, { "blockchain", &verifychain, },
{ "blockchain", "preciousblock", &preciousblock, {"blockhash"} }, { "blockchain", &preciousblock, },
{ "blockchain", "scantxoutset", &scantxoutset, {"action", "scanobjects"} }, { "blockchain", &scantxoutset, },
{ "blockchain", "getblockfilter", &getblockfilter, {"blockhash", "filtertype"} }, { "blockchain", &getblockfilter, },
/* Not shown in help */ /* Not shown in help */
{ "hidden", "invalidateblock", &invalidateblock, {"blockhash"} }, { "hidden", &invalidateblock, },
{ "hidden", "reconsiderblock", &reconsiderblock, {"blockhash"} }, { "hidden", &reconsiderblock, },
{ "hidden", "waitfornewblock", &waitfornewblock, {"timeout"} }, { "hidden", &waitfornewblock, },
{ "hidden", "waitforblock", &waitforblock, {"blockhash","timeout"} }, { "hidden", &waitforblock, },
{ "hidden", "waitforblockheight", &waitforblockheight, {"height","timeout"} }, { "hidden", &waitforblockheight, },
{ "hidden", "syncwithvalidationinterfacequeue", &syncwithvalidationinterfacequeue, {} }, { "hidden", &syncwithvalidationinterfacequeue, },
{ "hidden", "dumptxoutset", &dumptxoutset, {"path"} }, { "hidden", &dumptxoutset, },
}; };
// clang-format on // clang-format on
for (const auto& c : commands) { for (const auto& c : commands) {

View File

@ -251,14 +251,9 @@ public:
CRPCConvertTable::CRPCConvertTable() CRPCConvertTable::CRPCConvertTable()
{ {
const unsigned int n_elem = for (const auto& cp : vRPCConvertParams) {
(sizeof(vRPCConvertParams) / sizeof(vRPCConvertParams[0])); members.emplace(cp.methodName, cp.paramIdx);
membersByName.emplace(cp.methodName, cp.paramName);
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));
} }
} }

View File

@ -281,15 +281,15 @@ void RegisterCoinJoinRPCCommands(CRPCTable &t)
{ {
// clang-format off // clang-format off
static const CRPCCommand commands[] = static const CRPCCommand commands[] =
{ // category name actor (function) argNames { // category actor (function)
// ------------------------------------------------------------------------------------------------------ // --------------------- -----------------------
{ "dash", "getpoolinfo", &getpoolinfo, {} }, { "dash", &getpoolinfo, },
{ "dash", "getcoinjoininfo", &getcoinjoininfo, {} }, { "dash", &getcoinjoininfo, },
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
{ "dash", "coinjoin", &coinjoin, {"command"} }, { "dash", &coinjoin, },
{ "dash", "coinjoin", "reset", &coinjoin_reset, {} }, { "dash", &coinjoin_reset, },
{ "dash", "coinjoin", "start", &coinjoin_start, {} }, { "dash", &coinjoin_start, },
{ "dash", "coinjoin", "stop", &coinjoin_stop, {} }, { "dash", &coinjoin_stop, },
#endif // ENABLE_WALLET #endif // ENABLE_WALLET
}; };
// clang-format on // clang-format on

View File

@ -1869,37 +1869,37 @@ void RegisterEvoRPCCommands(CRPCTable &tableRPC)
{ {
// clang-format off // clang-format off
static const CRPCCommand commands[] = static const CRPCCommand commands[] =
{ // category name actor (function) { // category actor (function)
// --------------------- ------------------------ ----------------------- // --------------------- -----------------------
{ "evo", "bls", &bls_help, {"command"} }, { "evo", &bls_help, },
{ "evo", "bls", "generate", &bls_generate, {"legacy"} }, { "evo", &bls_generate, },
{ "evo", "bls", "fromsecret", &bls_fromsecret, {"secret", "legacy"} }, { "evo", &bls_fromsecret, },
{ "evo", "protx", &protx_help, {"command"} }, { "evo", &protx_help, },
#ifdef ENABLE_WALLET #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", &protx_register_evo, {"collateralHash", "collateralIndex", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "platformNodeID", "platformP2PPort", "platformHTTPPort", "feeSourceAddress", "submit"} }, { "evo", &protx_register_evo, },
{ "evo", "protx", "register_hpmn", &protx_register_hpmn, {"collateralHash", "collateralIndex", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "platformNodeID", "platformP2PPort", "platformHTTPPort", "feeSourceAddress", "submit"} }, { "evo", &protx_register_hpmn, },
{ "evo", "protx", "register_legacy", &protx_register_legacy, {"collateralHash", "collateralIndex", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "feeSourceAddress", "submit"} }, { "evo", &protx_register_legacy, },
{ "evo", "protx", "register_fund", &protx_register_fund, {"collateralAddress", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "fundAddress", "submit"} }, { "evo", &protx_register_fund, },
{ "evo", "protx", "register_fund_legacy", &protx_register_fund_legacy, {"collateralAddress", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "fundAddress", "submit"} }, { "evo", &protx_register_fund_legacy, },
{ "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_evo, },
{ "evo", "protx", "register_fund_hpmn", &protx_register_fund_hpmn, {"collateralAddress", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "platformNodeID", "platformP2PPort", "platformHTTPPort", "fundAddress", "submit"} }, { "evo", &protx_register_fund_hpmn, },
{ "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", &protx_register_prepare_evo, {"collateralHash", "collateralIndex", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "platformNodeID", "platformP2PPort", "platformHTTPPort", "feeSourceAddress"} }, { "evo", &protx_register_prepare_evo, },
{ "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_hpmn, },
{ "evo", "protx", "register_prepare_legacy", &protx_register_prepare_legacy, {"collateralHash", "collateralIndex", "ipAndPort", "ownerAddress", "operatorPubKey", "votingAddress", "operatorReward", "payoutAddress", "feeSourceAddress"} }, { "evo", &protx_register_prepare_legacy, },
{ "evo", "protx", "update_service", &protx_update_service, {"proTxHash", "ipAndPort", "operatorKey", "operatorPayoutAddress", "feeSourceAddress"} }, { "evo", &protx_update_service, },
{ "evo", "protx", "update_service_evo", &protx_update_service_evo, {"proTxHash", "ipAndPort", "operatorKey", "platformNodeID", "platformP2PPort", "platformHTTPPort", "operatorPayoutAddress", "feeSourceAddress"} }, { "evo", &protx_update_service_evo, },
{ "evo", "protx", "update_service_hpmn", &protx_update_service_hpmn, {"proTxHash", "ipAndPort", "operatorKey", "platformNodeID", "platformP2PPort", "platformHTTPPort", "operatorPayoutAddress", "feeSourceAddress"} }, { "evo", &protx_update_service_hpmn, },
{ "evo", "protx", "register_submit", &protx_register_submit, {"tx", "sig"} }, { "evo", &protx_register_submit, },
{ "evo", "protx", "update_registrar", &protx_update_registrar, {"proTxHash", "operatorPubKey", "votingAddress", "payoutAddress", "feeSourceAddress"} }, { "evo", &protx_update_registrar, },
{ "evo", "protx", "update_registrar_legacy", &protx_update_registrar_legacy, {"proTxHash", "operatorPubKey", "votingAddress", "payoutAddress", "feeSourceAddress"} }, { "evo", &protx_update_registrar_legacy, },
{ "evo", "protx", "revoke", &protx_revoke, {"proTxHash", "operatorKey", "reason", "feeSourceAddress"} }, { "evo", &protx_revoke, },
#endif #endif
{ "evo", "protx", "list", &protx_list, {"type", "detailed", "height"} }, { "evo", &protx_list, },
{ "evo", "protx", "info", &protx_info, {"proTxHash", "blockHash"} }, { "evo", &protx_info, },
{ "evo", "protx", "diff", &protx_diff, {"baseBlock", "block", "extended"} }, { "evo", &protx_diff, },
{ "evo", "protx", "listdiff", &protx_listdiff, {"baseBlock", "block"} }, { "evo", &protx_listdiff, },
}; };
// clang-format on // clang-format on
for (const auto& command : commands) { for (const auto& command : commands) {

View File

@ -1057,28 +1057,27 @@ void RegisterGovernanceRPCCommands(CRPCTable &t)
{ {
// clang-format off // clang-format off
static const CRPCCommand commands[] = static const CRPCCommand commands[] =
{ // category name actor (function) argNames { // category actor (function)
// --------------------- ------------------------ ----------------------- ---------- // --------------------- -----------------------
/* Dash features */ /* Dash features */
{ "dash", "getgovernanceinfo", &getgovernanceinfo, {} }, { "dash", &getgovernanceinfo, },
{ "dash", "getsuperblockbudget", &getsuperblockbudget, {"index"} }, { "dash", &getsuperblockbudget, },
{ "dash", "gobject", &gobject, {"command"} }, { "dash", &gobject, },
{ "dash", "gobject", "count", &gobject_count, {"mode"} }, { "dash", &gobject_count, },
{ "dash", "gobject", "deserialize", &gobject_deserialize, {"hex_data"} }, { "dash", &gobject_deserialize, },
{ "dash", "gobject", "check", &gobject_check, {"hex_data"} }, { "dash", &gobject_check, },
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
{ "dash", "gobject", "prepare", &gobject_prepare, {"parent-hash", "revision", "time", "data-hex", "use-IS", "outputHash", "outputIndex"} }, { "dash", &gobject_prepare, },
{ "dash", "gobject", "list-prepared", &gobject_list_prepared, {"count"} }, { "dash", &gobject_list_prepared, },
{ "dash", "gobject", "vote-many", &gobject_vote_many, {"governance-hash", "vote", "vote-outcome"} }, { "dash", &gobject_vote_many, },
{ "dash", "gobject", "vote-alias", &gobject_vote_alias, {"governance-hash", "vote", "vote-outcome", "protx-hash"} }, { "dash", &gobject_vote_alias, },
#endif #endif
{ "dash", "gobject", "submit", &gobject_submit, {"parent-hash", "revision", "time", "data-hex", "fee-txid"} }, { "dash", &gobject_submit, },
{ "dash", "gobject", "list", &gobject_list, {"signal", "type"} }, { "dash", &gobject_list, },
{ "dash", "gobject", "diff", &gobject_diff, {"signal", "type"} }, { "dash", &gobject_diff, },
{ "dash", "gobject", "get", &gobject_get, {"governance-hash"} }, { "dash", &gobject_get, },
{ "dash", "gobject", "getcurrentvotes", &gobject_getcurrentvotes, {"governance-hash", "txid", "vout"} }, { "dash", &gobject_getcurrentvotes, },
{ "dash", "voteraw", &voteraw, {"mn-collateral-tx-hash","mn-collateral-tx-index","governance-hash","vote-signal","vote-outcome","time","vote-sig"} }, { "dash", &voteraw, },
}; };
// clang-format on // clang-format on
for (const auto& command : commands) { for (const auto& command : commands) {

View File

@ -722,21 +722,21 @@ void RegisterMasternodeRPCCommands(CRPCTable &t)
{ {
// clang-format off // clang-format off
static const CRPCCommand commands[] = static const CRPCCommand commands[] =
{ // category name actor (function) argNames { // category actor (function)
// --------------------- ------------------------ ----------------------- ---------- // --------------------- -----------------------
{ "dash", "masternode", &masternode_help, {"command"} }, { "dash", &masternode_help, },
{ "dash", "masternode", "list", &masternodelist_composite, {"mode", "filter"} }, { "dash", &masternodelist_composite, },
{ "dash", "masternodelist", &masternodelist, {"mode", "filter"} }, { "dash", &masternodelist, },
{ "dash", "masternode", "connect", &masternode_connect, {"address"} }, { "dash", &masternode_connect, },
{ "dash", "masternode", "count", &masternode_count, {} }, { "dash", &masternode_count, },
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
{ "dash", "masternode", "outputs", &masternode_outputs, {} }, { "dash", &masternode_outputs, },
#endif // ENABLE_WALLET #endif // ENABLE_WALLET
{ "dash", "masternode", "status", &masternode_status, {} }, { "dash", &masternode_status, },
{ "dash", "masternode", "payments", &masternode_payments, {"blockhash", "count"} }, { "dash", &masternode_payments, },
{ "dash", "masternode", "winners", &masternode_winners, {"count", "filter"} }, { "dash", &masternode_winners, },
{ "dash", "masternode", "current", &masternode_current, {} }, { "dash", &masternode_current, },
{ "dash", "masternode", "winner", &masternode_winner, {} }, { "dash", &masternode_winner, },
}; };
// clang-format on // clang-format on
for (const auto& command : commands) { for (const auto& command : commands) {

View File

@ -416,17 +416,24 @@ static RPCHelpMan generateblock()
}; };
} }
#else #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 #endif // ENABLE_MINER
@ -1295,29 +1302,30 @@ void RegisterMiningRPCCommands(CRPCTable &t)
{ {
// clang-format off // clang-format off
static const CRPCCommand commands[] = static const CRPCCommand commands[] =
{ // category name actor (function) argNames { // category actor (function)
// --------------------- ------------------------ ----------------------- ---------- // --------------------- -----------------------
{ "mining", "getnetworkhashps", &getnetworkhashps, {"nblocks","height"} }, { "mining", &getnetworkhashps, },
{ "mining", "getmininginfo", &getmininginfo, {} }, { "mining", &getmininginfo, },
{ "mining", "prioritisetransaction", &prioritisetransaction, {"txid","fee_delta"} }, { "mining", &prioritisetransaction, },
{ "mining", "getblocktemplate", &getblocktemplate, {"template_request"} }, { "mining", &getblocktemplate, },
{ "mining", "submitblock", &submitblock, {"hexdata","dummy"} }, { "mining", &submitblock, },
{ "mining", "submitheader", &submitheader, {"hexdata"} }, { "mining", &submitheader, },
#if ENABLE_MINER #if ENABLE_MINER
{ "generating", "generatetoaddress", &generatetoaddress, {"nblocks","address","maxtries"} }, { "generating", &generatetoaddress, },
{ "generating", "generatetodescriptor", &generatetodescriptor, {"num_blocks","descriptor","maxtries"} }, { "generating", &generatetodescriptor, },
{ "generating", "generateblock", &generateblock, {"output","transactions"} }, { "generating", &generateblock, },
#else #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 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", &generatetoaddress, },
{ "hidden", "generateblock", &generateblock, {"address","transactions"} }, { "hidden", &generatetodescriptor, },
{ "hidden", &generateblock, },
#endif // ENABLE_MINER #endif // ENABLE_MINER
{ "util", "estimatesmartfee", &estimatesmartfee, {"conf_target", "estimate_mode"} }, { "util", &estimatesmartfee, },
{ "hidden", "estimaterawfee", &estimaterawfee, {"conf_target", "threshold"} }, { "hidden", &estimaterawfee, },
{ "hidden", "generate", &generate, {} }, { "hidden", &generate, },
}; };
// clang-format on // clang-format on
for (const auto& c : commands) { for (const auto& c : commands) {

View File

@ -1458,38 +1458,38 @@ static RPCHelpMan echojson() { return echo("echojson"); }
void RegisterMiscRPCCommands(CRPCTable &t) void RegisterMiscRPCCommands(CRPCTable &t)
{ {
static const CRPCCommand commands[] = static const CRPCCommand commands[] =
{ // category name actor (function) argNames { // category actor (function)
// --------------------- ------------------------ ----------------------- ---------- // --------------------- ------------------------
{ "control", "debug", &debug, {"category"} }, { "control", &debug, },
{ "control", "getmemoryinfo", &getmemoryinfo, {"mode"} }, { "control", &getmemoryinfo, },
{ "control", "logging", &logging, {"include", "exclude"}}, { "control", &logging, },
{ "util", "validateaddress", &validateaddress, {"address"} }, { "util", &validateaddress, },
{ "util", "createmultisig", &createmultisig, {"nrequired","keys"} }, { "util", &createmultisig, },
{ "util", "deriveaddresses", &deriveaddresses, {"descriptor", "range"} }, { "util", &deriveaddresses, },
{ "util", "getdescriptorinfo", &getdescriptorinfo, {"descriptor"} }, { "util", &getdescriptorinfo, },
{ "util", "verifymessage", &verifymessage, {"address","signature","message"} }, { "util", &verifymessage, },
{ "util", "signmessagewithprivkey", &signmessagewithprivkey, {"privkey","message"} }, { "util", &signmessagewithprivkey, },
{ "util", "getindexinfo", &getindexinfo, {"index_name"} }, { "util", &getindexinfo, },
{ "blockchain", "getspentinfo", &getspentinfo, {"request"} }, { "blockchain", &getspentinfo, },
/* Address index */ /* Address index */
{ "addressindex", "getaddressmempool", &getaddressmempool, {"addresses"} }, { "addressindex", &getaddressmempool, },
{ "addressindex", "getaddressutxos", &getaddressutxos, {"addresses"} }, { "addressindex", &getaddressutxos, },
{ "addressindex", "getaddressdeltas", &getaddressdeltas, {"addresses"} }, { "addressindex", &getaddressdeltas, },
{ "addressindex", "getaddresstxids", &getaddresstxids, {"addresses"} }, { "addressindex", &getaddresstxids, },
{ "addressindex", "getaddressbalance", &getaddressbalance, {"addresses"} }, { "addressindex", &getaddressbalance, },
/* Dash features */ /* Dash features */
{ "dash", "mnsync", &mnsync, {"mode"} }, { "dash", &mnsync, },
{ "dash", "spork", &spork, {"command"} }, { "dash", &spork, },
{ "dash", "sporkupdate", &sporkupdate, {"name","value"} }, { "dash", &sporkupdate, },
/* Not shown in help */ /* Not shown in help */
{ "hidden", "setmocktime", &setmocktime, {"timestamp"}}, { "hidden", &setmocktime, },
{ "hidden", "mockscheduler", &mockscheduler, {"delta_time"}}, { "hidden", &mockscheduler, },
{ "hidden", "echo", &echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}}, { "hidden", &echo, },
{ "hidden", "echojson", &echojson, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}}, { "hidden", &echojson, },
{ "hidden", "mnauth", &mnauth, {"nodeId", "proTxHash", "publicKey"}}, { "hidden", &mnauth, },
}; };
// clang-format on // clang-format on
for (const auto& c : commands) { for (const auto& c : commands) {

View File

@ -1013,25 +1013,25 @@ void RegisterNetRPCCommands(CRPCTable &t)
{ {
// clang-format off // clang-format off
static const CRPCCommand commands[] = static const CRPCCommand commands[] =
{ // category name actor (function) argNames { // category actor
// --------------------- ------------------------ ----------------------- ---------- // --------------------- -----------------------
{ "network", "getconnectioncount", &getconnectioncount, {} }, { "network", &getconnectioncount, },
{ "network", "ping", &ping, {} }, { "network", &ping, },
{ "network", "getpeerinfo", &getpeerinfo, {} }, { "network", &getpeerinfo, },
{ "network", "addnode", &addnode, {"node","command"} }, { "network", &addnode, },
{ "network", "disconnectnode", &disconnectnode, {"address", "nodeid"} }, { "network", &disconnectnode, },
{ "network", "getaddednodeinfo", &getaddednodeinfo, {"node"} }, { "network", &getaddednodeinfo, },
{ "network", "getnettotals", &getnettotals, {} }, { "network", &getnettotals, },
{ "network", "getnetworkinfo", &getnetworkinfo, {} }, { "network", &getnetworkinfo, },
{ "network", "setban", &setban, {"subnet", "command", "bantime", "absolute"} }, { "network", &setban, },
{ "network", "listbanned", &listbanned, {} }, { "network", &listbanned, },
{ "network", "clearbanned", &clearbanned, {} }, { "network", &clearbanned, },
{ "network", "cleardiscouraged", &cleardiscouraged, {} }, { "network", &cleardiscouraged, },
{ "network", "setnetworkactive", &setnetworkactive, {"state"} }, { "network", &setnetworkactive, },
{ "network", "getnodeaddresses", &getnodeaddresses, {"count", "network"} }, { "network", &getnodeaddresses, },
{ "hidden", "addconnection", &addconnection, {"address", "connection_type"} }, { "hidden", &addconnection, },
{ "hidden", "addpeeraddress", &addpeeraddress, {"address", "port"} }, { "hidden", &addpeeraddress, },
}; };
// clang-format on // clang-format on
for (const auto& c : commands) { for (const auto& c : commands) {

View File

@ -1132,28 +1132,28 @@ void RegisterQuorumsRPCCommands(CRPCTable &tableRPC)
{ {
// clang-format off // clang-format off
static const CRPCCommand commands[] = static const CRPCCommand commands[] =
{ // category name actor (function) { // category actor (function)
// --------------------- ------------------------ ----------------------- // --------------------- -----------------------
{ "evo", "quorum", &quorum_help, {"command"} }, { "evo", &quorum_help, },
{ "evo", "quorum", "list", &quorum_list, {"count"} }, { "evo", &quorum_list, },
{ "evo", "quorum", "listextended", &quorum_list_extended, {"height"} }, { "evo", &quorum_list_extended, },
{ "evo", "quorum", "info", &quorum_info, {"llmqType", "quorumHash", "includeSkShare"} }, { "evo", &quorum_info, },
{ "evo", "quorum", "dkginfo", &quorum_dkginfo, {} }, { "evo", &quorum_dkginfo, },
{ "evo", "quorum", "dkgstatus", &quorum_dkgstatus, {"detail_level"} }, { "evo", &quorum_dkgstatus, },
{ "evo", "quorum", "memberof", &quorum_memberof, {"proTxHash", "scanQuorumsCount"} }, { "evo", &quorum_memberof, },
{ "evo", "quorum", "sign", &quorum_sign, {"llmqType", "id", "msgHash", "quorumHash", "submit"} }, { "evo", &quorum_sign, },
{ "evo", "quorum", "platformsign", &quorum_platformsign, {"id", "msgHash", "quorumHash", "submit"} }, { "evo", &quorum_platformsign, },
{ "evo", "quorum", "verify", &quorum_verify, {"llmqType", "id", "msgHash", "signature", "quorumHash", "signHeight"} }, { "evo", &quorum_verify, },
{ "evo", "quorum", "hasrecsig", &quorum_hasrecsig, {"llmqType", "id", "msgHash"} }, { "evo", &quorum_hasrecsig, },
{ "evo", "quorum", "getrecsig", &quorum_getrecsig, {"llmqType", "id", "msgHash"} }, { "evo", &quorum_getrecsig, },
{ "evo", "quorum", "isconflicting",&quorum_isconflicting, {"llmqType", "id", "msgHash"} }, { "evo", &quorum_isconflicting, },
{ "evo", "quorum", "selectquorum", &quorum_selectquorum, {"llmqType", "id"} }, { "evo", &quorum_selectquorum, },
{ "evo", "quorum", "dkgsimerror", &quorum_dkgsimerror, {"type", "rate"} }, { "evo", &quorum_dkgsimerror, },
{ "evo", "quorum", "getdata", &quorum_getdata, {"nodeId", "llmqType", "quorumHash", "dataMask", "proTxHash"} }, { "evo", &quorum_getdata, },
{ "evo", "quorum", "rotationinfo", &quorum_rotationinfo, {"blockRequestHash", "extraShare", "baseBlockHash..."} }, { "evo", &quorum_rotationinfo, },
{ "evo", "submitchainlock", &submitchainlock, {"blockHash", "signature", "blockHeight"} }, { "evo", &submitchainlock, },
{ "evo", "verifychainlock", &verifychainlock, {"blockHash", "signature", "blockHeight"} }, { "evo", &verifychainlock, },
{ "evo", "verifyislock", &verifyislock, {"id", "txid", "signature", "maxHeight"} }, { "evo", &verifyislock, },
}; };
// clang-format on // clang-format on
for (const auto& command : commands) { for (const auto& command : commands) {

View File

@ -2079,30 +2079,30 @@ void RegisterRawTransactionRPCCommands(CRPCTable &t)
{ {
// clang-format off // clang-format off
static const CRPCCommand commands[] = static const CRPCCommand commands[] =
{ // category name actor (function) argNames { // category actor (function)
// --------------------- ------------------------ ----------------------- ---------- // --------------------- -----------------------
{ "rawtransactions", "getassetunlockstatuses", &getassetunlockstatuses, {"indexes","height"} }, { "rawtransactions", &getassetunlockstatuses, },
{ "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose","blockhash"} }, { "rawtransactions", &getrawtransaction, },
{ "rawtransactions", "getrawtransactionmulti", &getrawtransactionmulti, {"transactions","verbose"} }, { "rawtransactions", &getrawtransactionmulti, },
{ "rawtransactions", "gettxchainlocks", &gettxchainlocks, {"txids"} }, { "rawtransactions", &gettxchainlocks, },
{ "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime"} }, { "rawtransactions", &createrawtransaction, },
{ "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring"} }, { "rawtransactions", &decoderawtransaction, },
{ "rawtransactions", "decodescript", &decodescript, {"hexstring"} }, { "rawtransactions", &decodescript, },
{ "rawtransactions", "sendrawtransaction", &sendrawtransaction, {"hexstring","maxfeerate","instantsend","bypasslimits"} }, { "rawtransactions", &sendrawtransaction, },
{ "rawtransactions", "combinerawtransaction", &combinerawtransaction, {"txs"} }, { "rawtransactions", &combinerawtransaction, },
{ "rawtransactions", "signrawtransactionwithkey", &signrawtransactionwithkey, {"hexstring","privkeys","prevtxs","sighashtype"} }, { "rawtransactions", &signrawtransactionwithkey, },
{ "rawtransactions", "testmempoolaccept", &testmempoolaccept, {"rawtxs","maxfeerate"} }, { "rawtransactions", &testmempoolaccept, },
{ "rawtransactions", "decodepsbt", &decodepsbt, {"psbt"} }, { "rawtransactions", &decodepsbt, },
{ "rawtransactions", "combinepsbt", &combinepsbt, {"txs"} }, { "rawtransactions", &combinepsbt, },
{ "rawtransactions", "finalizepsbt", &finalizepsbt, {"psbt", "extract"} }, { "rawtransactions", &finalizepsbt, },
{ "rawtransactions", "createpsbt", &createpsbt, {"inputs","outputs","locktime"} }, { "rawtransactions", &createpsbt, },
{ "rawtransactions", "converttopsbt", &converttopsbt, {"hexstring","permitsigdata"} }, { "rawtransactions", &converttopsbt, },
{ "rawtransactions", "utxoupdatepsbt", &utxoupdatepsbt, {"psbt", "descriptors"} }, { "rawtransactions", &utxoupdatepsbt, },
{ "rawtransactions", "joinpsbts", &joinpsbts, {"txs"} }, { "rawtransactions", &joinpsbts, },
{ "rawtransactions", "analyzepsbt", &analyzepsbt, {"psbt"} }, { "rawtransactions", &analyzepsbt, },
{ "blockchain", "gettxoutproof", &gettxoutproof, {"txids", "blockhash"} }, { "blockchain", &gettxoutproof, },
{ "blockchain", "verifytxoutproof", &verifytxoutproof, {"proof"} }, { "blockchain", &verifytxoutproof, },
}; };
// clang-format on // clang-format on
for (const auto& c : commands) { for (const auto& c : commands) {

View File

@ -176,10 +176,16 @@ static RPCHelpMan help()
[&](const RPCHelpMan& self, const JSONRPCRequest& jsonRequest) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& jsonRequest) -> UniValue
{ {
std::string strCommand, strSubCommand; std::string strCommand, strSubCommand;
if (jsonRequest.params.size() > 0) if (jsonRequest.params.size() > 0) {
strCommand = jsonRequest.params[0].get_str(); strCommand = jsonRequest.params[0].get_str();
if (jsonRequest.params.size() > 1) }
if (jsonRequest.params.size() > 1) {
strSubCommand = jsonRequest.params[1].get_str(); 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); return tableRPC.help(strCommand, strSubCommand, jsonRequest);
}, },
@ -280,13 +286,13 @@ static RPCHelpMan getrpcinfo()
} }
// clang-format off // clang-format off
static const CRPCCommand vRPCCommands[] = static const CRPCCommand vRPCCommands[] =
{ // category name actor (function) argNames { // category actor (function)
// --------------------- ------------------------ ----------------------- ---------- // --------------------- -----------------------
/* Overall control/query calls */ /* Overall control/query calls */
{ "control", "getrpcinfo", &getrpcinfo, {} }, { "control", &getrpcinfo, },
{ "control", "help", &help, {"command","subcommand"} }, { "control", &help, },
{ "control", "stop", &stop, {"wait"} }, { "control", &stop, },
{ "control", "uptime", &uptime, {} }, { "control", &uptime, },
}; };
// clang-format on // clang-format on
@ -622,6 +628,18 @@ std::vector<std::pair<std::string, std::string>> CRPCTable::listCommands() const
return commandList; 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) void RPCSetTimerInterfaceIfUnset(RPCTimerInterface *iface)
{ {
if (!timerInterface) if (!timerInterface)

View File

@ -101,7 +101,7 @@ public:
} }
//! Simplified constructor taking plain RpcMethodFnType function pointer. //! Simplified constructor taking plain RpcMethodFnType function pointer.
CRPCCommand(std::string category, std::string name_in, RpcMethodFnType fn, std::vector<std::string> args_in) CRPCCommand(std::string category, RpcMethodFnType fn)
: CRPCCommand( : CRPCCommand(
category, category,
fn().m_name, fn().m_name,
@ -110,8 +110,6 @@ public:
fn().GetArgNames(), fn().GetArgNames(),
intptr_t(fn)) 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. //! Simplified constructor taking plain RpcMethodFnType function pointer with sub-command.
@ -169,6 +167,11 @@ public:
*/ */
std::vector<std::pair<std::string, std::string>> listCommands() const; std::vector<std::pair<std::string, std::string>> 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. * Appends a CRPCCommand to the dispatch table.
* *

View File

@ -566,6 +566,23 @@ std::string RPCHelpMan::ToString() const
return ret; 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<std::string> 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 std::string RPCArg::GetFirstName() const
{ {
return m_names.substr(0, m_names.find("|")); return m_names.substr(0, m_names.find("|"));

View File

@ -341,6 +341,8 @@ public:
RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples, RPCMethodImpl fun); RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples, RPCMethodImpl fun);
std::string ToString() const; 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) UniValue HandleRequest(const JSONRPCRequest& request)
{ {
Check(request); Check(request);

View File

@ -4718,76 +4718,76 @@ Span<const CRPCCommand> GetWalletRPCCommands()
{ {
// clang-format off // clang-format off
static const CRPCCommand commands[] = static const CRPCCommand commands[] =
{ // category name actor (function) argNames { // category actor (function)
// --------------------- ------------------------ ----------------------- ---------- // ------------------ ------------------------
{ "hidden", "instantsendtoaddress", &instantsendtoaddress, {} }, { "hidden", &instantsendtoaddress, },
{ "rawtransactions", "fundrawtransaction", &fundrawtransaction, {"hexstring","options"} }, { "rawtransactions", &fundrawtransaction, },
{ "wallet", "abandontransaction", &abandontransaction, {"txid"} }, { "wallet", &abandontransaction, },
{ "wallet", "abortrescan", &abortrescan, {} }, { "wallet", &abortrescan, },
{ "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","label"} }, { "wallet", &addmultisigaddress, },
{ "wallet", "backupwallet", &backupwallet, {"destination"} }, { "wallet", &backupwallet, },
{ "wallet", "createwallet", &createwallet, {"wallet_name", "disable_private_keys", "blank", "passphrase", "avoid_reuse", "descriptors", "load_on_startup"} }, { "wallet", &createwallet, },
{ "wallet", "restorewallet", &restorewallet, {"wallet_name","backup_file", "load_on_startup"} }, { "wallet", &restorewallet, },
{ "wallet", "dumphdinfo", &dumphdinfo, {} }, { "wallet", &dumphdinfo, },
{ "wallet", "dumpprivkey", &dumpprivkey, {"address"} }, { "wallet", &dumpprivkey, },
{ "wallet", "dumpwallet", &dumpwallet, {"filename"} }, { "wallet", &dumpwallet, },
{ "wallet", "encryptwallet", &encryptwallet, {"passphrase"} }, { "wallet", &encryptwallet, },
{ "wallet", "getaddressesbylabel", &getaddressesbylabel, {"label"} }, { "wallet", &getaddressesbylabel, },
{ "wallet", "getaddressinfo", &getaddressinfo, {"address"} }, { "wallet", &getaddressinfo, },
{ "wallet", "getbalance", &getbalance, {"dummy","minconf","addlocked","include_watchonly", "avoid_reuse"} }, { "wallet", &getbalance, },
{ "wallet", "getnewaddress", &getnewaddress, {"label"} }, { "wallet", &getnewaddress, },
{ "wallet", "getrawchangeaddress", &getrawchangeaddress, {} }, { "wallet", &getrawchangeaddress, },
{ "wallet", "getreceivedbyaddress", &getreceivedbyaddress, {"address","minconf","addlocked"} }, { "wallet", &getreceivedbyaddress, },
{ "wallet", "getreceivedbylabel", &getreceivedbylabel, {"label","minconf","addlocked"} }, { "wallet", &getreceivedbylabel, },
{ "wallet", "gettransaction", &gettransaction, {"txid","include_watchonly","verbose"} }, { "wallet", &gettransaction, },
{ "wallet", "getunconfirmedbalance", &getunconfirmedbalance, {} }, { "wallet", &getunconfirmedbalance, },
{ "wallet", "getbalances", &getbalances, {} }, { "wallet", &getbalances, },
{ "wallet", "getwalletinfo", &getwalletinfo, {} }, { "wallet", &getwalletinfo, },
{ "wallet", "importaddress", &importaddress, {"address","label","rescan","p2sh"} }, { "wallet", &importaddress, },
{ "wallet", "importelectrumwallet", &importelectrumwallet, {"filename", "index"} }, { "wallet", &importelectrumwallet, },
{ "wallet", "importdescriptors", &importdescriptors, {"requests"} }, { "wallet", &importdescriptors, },
{ "wallet", "importmulti", &importmulti, {"requests","options"} }, { "wallet", &importmulti, },
{ "wallet", "importprivkey", &importprivkey, {"privkey","label","rescan"} }, { "wallet", &importprivkey, },
{ "wallet", "importprunedfunds", &importprunedfunds, {"rawtransaction","txoutproof"} }, { "wallet", &importprunedfunds, },
{ "wallet", "importpubkey", &importpubkey, {"pubkey","label","rescan"} }, { "wallet", &importpubkey, },
{ "wallet", "importwallet", &importwallet, {"filename"} }, { "wallet", &importwallet, },
{ "wallet", "keypoolrefill", &keypoolrefill, {"newsize"} }, { "wallet", &keypoolrefill, },
{ "wallet", "listaddressbalances", &listaddressbalances, {"minamount"} }, { "wallet", &listaddressbalances, },
{ "wallet", "listaddressgroupings", &listaddressgroupings, {} }, { "wallet", &listaddressgroupings, },
{ "wallet", "listdescriptors", &listdescriptors, {} }, { "wallet", &listdescriptors, },
{ "wallet", "listlabels", &listlabels, {"purpose"} }, { "wallet", &listlabels, },
{ "wallet", "listlockunspent", &listlockunspent, {} }, { "wallet", &listlockunspent, },
{ "wallet", "listreceivedbyaddress", &listreceivedbyaddress, {"minconf","addlocked","include_empty","include_watchonly","address_filter"} }, { "wallet", &listreceivedbyaddress, },
{ "wallet", "listreceivedbylabel", &listreceivedbylabel, {"minconf","addlocked","include_empty","include_watchonly"} }, { "wallet", &listreceivedbylabel, },
{ "wallet", "listsinceblock", &listsinceblock, {"blockhash","target_confirmations","include_watchonly","include_removed"} }, { "wallet", &listsinceblock, },
{ "wallet", "listtransactions", &listtransactions, {"label|dummy","count","skip","include_watchonly"} }, { "wallet", &listtransactions, },
{ "wallet", "listunspent", &listunspent, {"minconf","maxconf","addresses","include_unsafe","query_options"} }, { "wallet", &listunspent, },
{ "wallet", "listwalletdir", &listwalletdir, {} }, { "wallet", &listwalletdir, },
{ "wallet", "listwallets", &listwallets, {} }, { "wallet", &listwallets, },
{ "wallet", "loadwallet", &loadwallet, {"filename", "load_on_startup"} }, { "wallet", &loadwallet, },
{ "wallet", "lockunspent", &lockunspent, {"unlock","transactions"} }, { "wallet", &lockunspent, },
{ "wallet", "removeprunedfunds", &removeprunedfunds, {"txid"} }, { "wallet", &removeprunedfunds, },
{ "wallet", "rescanblockchain", &rescanblockchain, {"start_height", "stop_height"} }, { "wallet", &rescanblockchain, },
{ "wallet", "send", &send, {"outputs","conf_target","estimate_mode","options"} }, { "wallet", &send, },
{ "wallet", "sendmany", &sendmany, {"dummy","amounts","minconf","addlocked","comment","subtractfeefrom","use_is","use_cj","conf_target","estimate_mode","verbose"} }, { "wallet", &sendmany, },
{ "wallet", "sendtoaddress", &sendtoaddress, {"address","amount","comment","comment_to","subtractfeefromamount","use_is","use_cj","conf_target","estimate_mode", "avoid_reuse","verbose"} }, { "wallet", &sendtoaddress, },
{ "wallet", "sethdseed", &sethdseed, {"newkeypool","seed"} }, { "wallet", &sethdseed, },
{ "wallet", "setcoinjoinrounds", &setcoinjoinrounds, {"rounds"} }, { "wallet", &setcoinjoinrounds, },
{ "wallet", "setcoinjoinamount", &setcoinjoinamount, {"amount"} }, { "wallet", &setcoinjoinamount, },
{ "wallet", "setlabel", &setlabel, {"address","label"} }, { "wallet", &setlabel, },
{ "wallet", "settxfee", &settxfee, {"amount"} }, { "wallet", &settxfee, },
{ "wallet", "setwalletflag", &setwalletflag, {"flag","value"} }, { "wallet", &setwalletflag, },
{ "wallet", "signmessage", &signmessage, {"address","message"} }, { "wallet", &signmessage, },
{ "wallet", "signrawtransactionwithwallet", &signrawtransactionwithwallet, {"hexstring","prevtxs","sighashtype"} }, { "wallet", &signrawtransactionwithwallet, },
{ "wallet", "unloadwallet", &unloadwallet, {"wallet_name", "load_on_startup"} }, { "wallet", &unloadwallet, },
{ "wallet", "upgradewallet", &upgradewallet, {"version"} }, { "wallet", &upgradewallet, },
{ "wallet", "upgradetohd", &upgradetohd, {"mnemonic", "mnemonicpassphrase", "walletpassphrase", "rescan"} }, { "wallet", &upgradetohd, },
{ "wallet", "walletlock", &walletlock, {} }, { "wallet", &walletlock, },
{ "wallet", "walletpassphrasechange", &walletpassphrasechange, {"oldpassphrase","newpassphrase"} }, { "wallet", &walletpassphrasechange, },
{ "wallet", "walletpassphrase", &walletpassphrase, {"passphrase","timeout","mixingonly"} }, { "wallet", &walletpassphrase, },
{ "wallet", "walletprocesspsbt", &walletprocesspsbt, {"psbt","sign","sighashtype","bip32derivs"} }, { "wallet", &walletprocesspsbt, },
{ "wallet", "walletcreatefundedpsbt", &walletcreatefundedpsbt, {"inputs","outputs","locktime","options","bip32derivs"} }, { "wallet", &walletcreatefundedpsbt, },
{ "wallet", "wipewallettxes", &wipewallettxes, {"keep_confirmed"} }, { "wallet", &wipewallettxes, },
}; };
// clang-format on // clang-format on
return commands; return commands;

View File

@ -52,9 +52,9 @@ static RPCHelpMan getzmqnotifications()
} }
const CRPCCommand commands[] = const CRPCCommand commands[] =
{ // category name actor (function) argNames { // category actor (function)
// ----------------- ------------------------ ----------------------- ---------- // ----------------- -----------------------
{ "zmq", "getzmqnotifications", &getzmqnotifications, {} }, { "zmq", &getzmqnotifications, },
}; };
} // anonymous namespace } // anonymous namespace

View File

@ -7,7 +7,39 @@
from test_framework.test_framework import BitcoinTestFramework from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error from test_framework.util import assert_equal, assert_raises_rpc_error
from collections import defaultdict
import os 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): class HelpRpcTest(BitcoinTestFramework):
@ -16,11 +48,43 @@ class HelpRpcTest(BitcoinTestFramework):
self.supports_cli = False self.supports_cli = False
def run_test(self): def run_test(self):
self.test_client_conversion_table()
self.test_categories() self.test_categories()
self.dump_help() self.dump_help()
if self.is_wallet_compiled(): if self.is_wallet_compiled():
self.wallet_help() 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): def test_categories(self):
node = self.nodes[0] node = self.nodes[0]

View File

@ -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()