aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWladimir J. van der Laan <laanwj@protonmail.com>2021-01-28 19:25:07 +0100
committerWladimir J. van der Laan <laanwj@protonmail.com>2021-01-28 19:25:12 +0100
commit80e16cadd56ca73b94b01c5cd98a325fe9d89bf3 (patch)
treed4ed4cb5d4c8f208d9f3f8e19faacef803c666f9
parent6ba2ffd28b6bf15e3f70982ccd45256a657c5f70 (diff)
parentfa04f9b4ddffc5ef23c2ee7f3cc72a7c2ae49204 (diff)
downloadbitcoin-80e16cadd56ca73b94b01c5cd98a325fe9d89bf3.tar.xz
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
-rwxr-xr-xci/lint/06_script.sh1
-rw-r--r--src/qt/test/rpcnestedtests.cpp2
-rw-r--r--src/rpc/blockchain.cpp66
-rw-r--r--src/rpc/client.cpp11
-rw-r--r--src/rpc/mining.cpp28
-rw-r--r--src/rpc/misc.cpp30
-rw-r--r--src/rpc/net.cpp36
-rw-r--r--src/rpc/rawtransaction.cpp42
-rw-r--r--src/rpc/server.cpp31
-rw-r--r--src/rpc/server.h8
-rw-r--r--src/rpc/util.cpp18
-rw-r--r--src/rpc/util.h2
-rw-r--r--src/wallet/rpcwallet.cpp126
-rw-r--r--src/zmq/zmqrpc.cpp6
-rwxr-xr-xtest/functional/rpc_help.py64
-rwxr-xr-xtest/lint/check-rpc-mappings.py162
16 files changed, 284 insertions, 349 deletions
diff --git a/ci/lint/06_script.sh b/ci/lint/06_script.sh
index de89a09d6a..e38cfe8eef 100755
--- a/ci/lint/06_script.sh
+++ b/ci/lint/06_script.sh
@@ -21,7 +21,6 @@ test/lint/git-subtree-check.sh src/univalue
test/lint/git-subtree-check.sh src/leveldb
test/lint/git-subtree-check.sh src/crc32c
test/lint/check-doc.py
-test/lint/check-rpc-mappings.py .
test/lint/lint-all.sh
if [ "$CIRRUS_REPO_FULL_NAME" = "bitcoin/bitcoin" ] && [ -n "$CIRRUS_CRON" ]; then
diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp
index 24d7735447..0d9928d363 100644
--- a/src/qt/test/rpcnestedtests.cpp
+++ b/src/qt/test/rpcnestedtests.cpp
@@ -33,7 +33,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 7a336c1ad6..e41a78f917 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -2480,41 +2480,41 @@ 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", "getblockcount", &getblockcount, {} },
- { "blockchain", "getblock", &getblock, {"blockhash","verbosity|verbose"} },
- { "blockchain", "getblockhash", &getblockhash, {"height"} },
- { "blockchain", "getblockheader", &getblockheader, {"blockhash","verbose"} },
- { "blockchain", "getchaintips", &getchaintips, {} },
- { "blockchain", "getdifficulty", &getdifficulty, {} },
- { "blockchain", "getmempoolancestors", &getmempoolancestors, {"txid","verbose"} },
- { "blockchain", "getmempooldescendants", &getmempooldescendants, {"txid","verbose"} },
- { "blockchain", "getmempoolentry", &getmempoolentry, {"txid"} },
- { "blockchain", "getmempoolinfo", &getmempoolinfo, {} },
- { "blockchain", "getrawmempool", &getrawmempool, {"verbose", "mempool_sequence"} },
- { "blockchain", "gettxout", &gettxout, {"txid","n","include_mempool"} },
- { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, {"hash_type"} },
- { "blockchain", "pruneblockchain", &pruneblockchain, {"height"} },
- { "blockchain", "savemempool", &savemempool, {} },
- { "blockchain", "verifychain", &verifychain, {"checklevel","nblocks"} },
-
- { "blockchain", "preciousblock", &preciousblock, {"blockhash"} },
- { "blockchain", "scantxoutset", &scantxoutset, {"action", "scanobjects"} },
- { "blockchain", "getblockfilter", &getblockfilter, {"blockhash", "filtertype"} },
+{ // category actor (function)
+ // --------------------- ------------------------
+ { "blockchain", &getblockchaininfo, },
+ { "blockchain", &getchaintxstats, },
+ { "blockchain", &getblockstats, },
+ { "blockchain", &getbestblockhash, },
+ { "blockchain", &getblockcount, },
+ { "blockchain", &getblock, },
+ { "blockchain", &getblockhash, },
+ { "blockchain", &getblockheader, },
+ { "blockchain", &getchaintips, },
+ { "blockchain", &getdifficulty, },
+ { "blockchain", &getmempoolancestors, },
+ { "blockchain", &getmempooldescendants, },
+ { "blockchain", &getmempoolentry, },
+ { "blockchain", &getmempoolinfo, },
+ { "blockchain", &getrawmempool, },
+ { "blockchain", &gettxout, },
+ { "blockchain", &gettxoutsetinfo, },
+ { "blockchain", &pruneblockchain, },
+ { "blockchain", &savemempool, },
+ { "blockchain", &verifychain, },
+
+ { "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 042005b9a6..d1eb849b7e 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -210,14 +210,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/mining.cpp b/src/rpc/mining.cpp
index 0e96beb6db..c723f1d7ea 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -1232,24 +1232,24 @@ 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","dummy","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, },
- { "generating", "generatetoaddress", &generatetoaddress, {"nblocks","address","maxtries"} },
- { "generating", "generatetodescriptor", &generatetodescriptor, {"num_blocks","descriptor","maxtries"} },
- { "generating", "generateblock", &generateblock, {"output","transactions"} },
+ { "generating", &generatetoaddress, },
+ { "generating", &generatetodescriptor, },
+ { "generating", &generateblock, },
- { "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 215e48ca26..b75a7b8d26 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -699,23 +699,23 @@ void RegisterMiscRPCCommands(CRPCTable &t)
{
// clang-format off
static const CRPCCommand commands[] =
-{ // category name actor (function) argNames
- // --------------------- ------------------------ ----------------------- ----------
- { "control", "getmemoryinfo", &getmemoryinfo, {"mode"} },
- { "control", "logging", &logging, {"include", "exclude"}},
- { "util", "validateaddress", &validateaddress, {"address"} },
- { "util", "createmultisig", &createmultisig, {"nrequired","keys","address_type"} },
- { "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"} },
+{ // category actor (function)
+ // --------------------- ------------------------
+ { "control", &getmemoryinfo, },
+ { "control", &logging, },
+ { "util", &validateaddress, },
+ { "util", &createmultisig, },
+ { "util", &deriveaddresses, },
+ { "util", &getdescriptorinfo, },
+ { "util", &verifymessage, },
+ { "util", &signmessagewithprivkey, },
+ { "util", &getindexinfo, },
/* 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", &setmocktime, },
+ { "hidden", &mockscheduler, },
+ { "hidden", &echo, },
+ { "hidden", &echojson, },
};
// clang-format on
for (const auto& c : commands) {
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index cfca8b4ad4..47d77b341a 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -941,24 +941,24 @@ 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", "setnetworkactive", &setnetworkactive, {"state"} },
- { "network", "getnodeaddresses", &getnodeaddresses, {"count"} },
-
- { "hidden", "addconnection", &addconnection, {"address", "connection_type"} },
- { "hidden", "addpeeraddress", &addpeeraddress, {"address", "port"} },
+{ // 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", &setnetworkactive, },
+ { "network", &getnodeaddresses, },
+
+ { "hidden", &addconnection, },
+ { "hidden", &addpeeraddress, },
};
// clang-format on
for (const auto& c : commands) {
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index e94d554e3c..031ed31da1 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -1859,27 +1859,27 @@ void RegisterRawTransactionRPCCommands(CRPCTable &t)
{
// clang-format off
static const CRPCCommand commands[] =
-{ // category name actor (function) argNames
- // --------------------- ------------------------ ----------------------- ----------
- { "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose","blockhash"} },
- { "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime","replaceable"} },
- { "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring","iswitness"} },
- { "rawtransactions", "decodescript", &decodescript, {"hexstring"} },
- { "rawtransactions", "sendrawtransaction", &sendrawtransaction, {"hexstring","maxfeerate"} },
- { "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","replaceable"} },
- { "rawtransactions", "converttopsbt", &converttopsbt, {"hexstring","permitsigdata","iswitness"} },
- { "rawtransactions", "utxoupdatepsbt", &utxoupdatepsbt, {"psbt", "descriptors"} },
- { "rawtransactions", "joinpsbts", &joinpsbts, {"txs"} },
- { "rawtransactions", "analyzepsbt", &analyzepsbt, {"psbt"} },
-
- { "blockchain", "gettxoutproof", &gettxoutproof, {"txids", "blockhash"} },
- { "blockchain", "verifytxoutproof", &verifytxoutproof, {"proof"} },
+{ // category actor (function)
+ // --------------------- -----------------------
+ { "rawtransactions", &getrawtransaction, },
+ { "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, },
+ { "blockchain", &verifytxoutproof, },
};
// clang-format on
for (const auto& c : commands) {
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index f32d9abac6..9a9b3713f3 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -144,8 +144,13 @@ static RPCHelpMan help()
[&](const RPCHelpMan& self, const JSONRPCRequest& jsonRequest) -> UniValue
{
std::string strCommand;
- if (jsonRequest.params.size() > 0)
+ if (jsonRequest.params.size() > 0) {
strCommand = jsonRequest.params[0].get_str();
+ }
+ if (strCommand == "dump_all_command_conversions") {
+ // Used for testing only, undocumented
+ return tableRPC.dumpArgMap();
+ }
return tableRPC.help(strCommand, jsonRequest);
},
@@ -244,13 +249,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"} },
- { "control", "stop", &stop, {"wait"} },
- { "control", "uptime", &uptime, {} },
+ { "control", &getrpcinfo, },
+ { "control", &help, },
+ { "control", &stop, },
+ { "control", &uptime, },
};
// clang-format on
@@ -479,6 +484,18 @@ std::vector<std::string> 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 040192b455..fe5a791e1e 100644
--- a/src/rpc/server.h
+++ b/src/rpc/server.h
@@ -103,7 +103,7 @@ public:
}
//! 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(
category,
fn().m_name,
@@ -111,8 +111,6 @@ public:
fn().GetArgNames(),
intptr_t(fn))
{
- CHECK_NONFATAL(fn().m_name == name_in);
- CHECK_NONFATAL(fn().GetArgNames() == args_in);
}
std::string category;
@@ -147,6 +145,10 @@ public:
*/
std::vector<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.
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index 31072114da..bfdba5253c 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -549,6 +549,24 @@ 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<std::string> arg_names;
+ boost::split(arg_names, arg.m_names, boost::is_any_of("|"));
+ 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 942c243718..444a013ca1 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -336,6 +336,8 @@ public:
RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> 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 75888cfd66..46de273d63 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -4557,69 +4557,69 @@ Span<const CRPCCommand> GetWalletRPCCommands()
{
// clang-format off
static const CRPCCommand commands[] =
-{ // category name actor (function) argNames
- // --------------------- ------------------------ ----------------------- ----------
- { "rawtransactions", "fundrawtransaction", &fundrawtransaction, {"hexstring","options","iswitness"} },
- { "wallet", "abandontransaction", &abandontransaction, {"txid"} },
- { "wallet", "abortrescan", &abortrescan, {} },
- { "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","label","address_type"} },
- { "wallet", "backupwallet", &backupwallet, {"destination"} },
- { "wallet", "bumpfee", &bumpfee, {"txid", "options"} },
- { "wallet", "psbtbumpfee", &psbtbumpfee, {"txid", "options"} },
- { "wallet", "createwallet", &createwallet, {"wallet_name", "disable_private_keys", "blank", "passphrase", "avoid_reuse", "descriptors", "load_on_startup"} },
- { "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","include_watchonly","avoid_reuse"} },
- { "wallet", "getnewaddress", &getnewaddress, {"label","address_type"} },
- { "wallet", "getrawchangeaddress", &getrawchangeaddress, {"address_type"} },
- { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, {"address","minconf"} },
- { "wallet", "getreceivedbylabel", &getreceivedbylabel, {"label","minconf"} },
- { "wallet", "gettransaction", &gettransaction, {"txid","include_watchonly","verbose"} },
- { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, {} },
- { "wallet", "getbalances", &getbalances, {} },
- { "wallet", "getwalletinfo", &getwalletinfo, {} },
- { "wallet", "importaddress", &importaddress, {"address","label","rescan","p2sh"} },
- { "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", "listaddressgroupings", &listaddressgroupings, {} },
- { "wallet", "listdescriptors", &listdescriptors, {} },
- { "wallet", "listlabels", &listlabels, {"purpose"} },
- { "wallet", "listlockunspent", &listlockunspent, {} },
- { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, {"minconf","include_empty","include_watchonly","address_filter"} },
- { "wallet", "listreceivedbylabel", &listreceivedbylabel, {"minconf","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","fee_rate","options"} },
- { "wallet", "sendmany", &sendmany, {"dummy","amounts","minconf","comment","subtractfeefrom","replaceable","conf_target","estimate_mode","fee_rate","verbose"} },
- { "wallet", "sendtoaddress", &sendtoaddress, {"address","amount","comment","comment_to","subtractfeefromamount","replaceable","conf_target","estimate_mode","avoid_reuse","fee_rate","verbose"} },
- { "wallet", "sethdseed", &sethdseed, {"newkeypool","seed"} },
- { "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", "walletcreatefundedpsbt", &walletcreatefundedpsbt, {"inputs","outputs","locktime","options","bip32derivs"} },
- { "wallet", "walletlock", &walletlock, {} },
- { "wallet", "walletpassphrase", &walletpassphrase, {"passphrase","timeout"} },
- { "wallet", "walletpassphrasechange", &walletpassphrasechange, {"oldpassphrase","newpassphrase"} },
- { "wallet", "walletprocesspsbt", &walletprocesspsbt, {"psbt","sign","sighashtype","bip32derivs"} },
+{ // category actor (function)
+ // ------------------ ------------------------
+ { "rawtransactions", &fundrawtransaction, },
+ { "wallet", &abandontransaction, },
+ { "wallet", &abortrescan, },
+ { "wallet", &addmultisigaddress, },
+ { "wallet", &backupwallet, },
+ { "wallet", &bumpfee, },
+ { "wallet", &psbtbumpfee, },
+ { "wallet", &createwallet, },
+ { "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", &importdescriptors, },
+ { "wallet", &importmulti, },
+ { "wallet", &importprivkey, },
+ { "wallet", &importprunedfunds, },
+ { "wallet", &importpubkey, },
+ { "wallet", &importwallet, },
+ { "wallet", &keypoolrefill, },
+ { "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", &setlabel, },
+ { "wallet", &settxfee, },
+ { "wallet", &setwalletflag, },
+ { "wallet", &signmessage, },
+ { "wallet", &signrawtransactionwithwallet, },
+ { "wallet", &unloadwallet, },
+ { "wallet", &upgradewallet, },
+ { "wallet", &walletcreatefundedpsbt, },
+ { "wallet", &walletlock, },
+ { "wallet", &walletpassphrase, },
+ { "wallet", &walletpassphrasechange, },
+ { "wallet", &walletprocesspsbt, },
};
// clang-format on
return MakeSpan(commands);
diff --git a/src/zmq/zmqrpc.cpp b/src/zmq/zmqrpc.cpp
index 1dd751b493..81859f924f 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 c83157a843..1eefd109f8 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 0a4cc875d0..0000000000
--- a/test/lint/check-rpc-mappings.py
+++ /dev/null
@@ -1,162 +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/mining.cpp",
- "src/rpc/misc.cpp",
- "src/rpc/net.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)
- 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()