diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2010 Satoshi Nakamoto -// Copyright (c) 2009-2016 The Bitcoin Core developers +// Copyright (c) 2009-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. @@ -2053,100 +2053,90 @@ static UniValue getblockstats(const Config &config, const JSONRPCRequest &request) { - if (request.fHelp || request.params.size() < 1 || - request.params.size() > 4) { - throw std::runtime_error(RPCHelpMan{ - "getblockstats", - "\nCompute per block statistics for a given window. All " - "amounts are in " + - CURRENCY_UNIT + - ".\n" - "It won't work for some heights with pruning.\n" - "It won't work without -txindex for utxo_size_inc, *fee or " - "*feerate stats.\n", - { - {"hash_or_height", - RPCArg::Type::NUM, - RPCArg::Optional::NO, - "The block hash or height of the target block", - "", - {"", "string or numeric"}}, - {"stats", - RPCArg::Type::ARR, - /* default */ "all values", - "Values to plot (see result below)", - { - {"height", RPCArg::Type::STR, RPCArg::Optional::OMITTED, - "Selected statistic"}, - {"time", RPCArg::Type::STR, RPCArg::Optional::OMITTED, - "Selected statistic"}, - }, - "stats"}, - }, - RPCResult{ - "{ (json object)\n" - " \"avgfee\": x.xxx, (numeric) Average fee in the " - "block\n" - " \"avgfeerate\": x.xxx, (numeric) Average feerate (in " + - CURRENCY_UNIT + - " per byte)\n" - " \"avgtxsize\": xxxxx, (numeric) Average transaction " - "size\n" - " \"blockhash\": xxxxx, (string) The block hash (to " - "check for potential reorgs)\n" - " \"height\": xxxxx, (numeric) The height of the " - "block\n" - " \"ins\": xxxxx, (numeric) The number of inputs " - "(excluding coinbase)\n" - " \"maxfee\": xxxxx, (numeric) Maximum fee in the " - "block\n" - " \"maxfeerate\": xxxxx, (numeric) Maximum feerate (in " + - CURRENCY_UNIT + - " per byte)\n" - " \"maxtxsize\": xxxxx, (numeric) Maximum transaction " - "size\n" - " \"medianfee\": x.xxx, (numeric) Truncated median fee " - "in the block\n" - " \"medianfeerate\": x.xxx, (numeric) Truncated median " - "feerate (in " + - CURRENCY_UNIT + - " per byte)\n" - " \"mediantime\": xxxxx, (numeric) The block median time " - "past\n" - " \"mediantxsize\": xxxxx, (numeric) Truncated median " - "transaction size\n" - " \"minfee\": x.xxx, (numeric) Minimum fee in the " - "block\n" - " \"minfeerate\": xx.xx, (numeric) Minimum feerate (in " + - CURRENCY_UNIT + - " per byte)\n" - " \"mintxsize\": xxxxx, (numeric) Minimum transaction " - "size\n" - " \"outs\": xxxxx, (numeric) The number of " - "outputs\n" - " \"subsidy\": x.xxx, (numeric) The block subsidy\n" - " \"time\": xxxxx, (numeric) The block time\n" - " \"total_out\": x.xxx, (numeric) Total amount in all " - "outputs (excluding coinbase and thus reward [ie subsidy + " - "totalfee])\n" - " \"total_size\": xxxxx, (numeric) Total size of all " - "non-coinbase transactions\n" - " \"totalfee\": x.xxx, (numeric) The fee total\n" - " \"txs\": xxxxx, (numeric) The number of " - "transactions (excluding coinbase)\n" - " \"utxo_increase\": xxxxx, (numeric) The increase/decrease " - "in the number of unspent outputs\n" - " \"utxo_size_inc\": xxxxx, (numeric) The increase/decrease " - "in size for the utxo index (not discounting op_return and " - "similar)\n" - "}\n"}, - RPCExamples{ - HelpExampleCli("getblockstats", - "1000 '[\"minfeerate\",\"avgfeerate\"]'") + - HelpExampleRpc("getblockstats", - "1000 '[\"minfeerate\",\"avgfeerate\"]'")}, - } - .ToString()); + const RPCHelpMan help{ + "getblockstats", + "\nCompute per block statistics for a given window. All amounts are " + "in " + + CURRENCY_UNIT + + ".\n" + "It won't work for some heights with pruning.\n" + "It won't work without -txindex for utxo_size_inc, *fee or " + "*feerate stats.\n", + { + {"hash_or_height", + RPCArg::Type::NUM, + RPCArg::Optional::NO, + "The block hash or height of the target block", + "", + {"", "string or numeric"}}, + {"stats", + RPCArg::Type::ARR, + /* default */ "all values", + "Values to plot (see result below)", + { + {"height", RPCArg::Type::STR, RPCArg::Optional::OMITTED, + "Selected statistic"}, + {"time", RPCArg::Type::STR, RPCArg::Optional::OMITTED, + "Selected statistic"}, + }, + "stats"}, + }, + RPCResult{ + "{ (json object)\n" + " \"avgfee\": x.xxx, (numeric) Average fee in the block\n" + " \"avgfeerate\": x.xxx, (numeric) Average feerate (in " + + CURRENCY_UNIT + + " per byte)\n" + " \"avgtxsize\": xxxxx, (numeric) Average transaction size\n" + " \"blockhash\": xxxxx, (string) The block hash (to check " + "for potential reorgs)\n" + " \"height\": xxxxx, (numeric) The height of the block\n" + " \"ins\": xxxxx, (numeric) The number of inputs " + "(excluding coinbase)\n" + " \"maxfee\": xxxxx, (numeric) Maximum fee in the block\n" + " \"maxfeerate\": xxxxx, (numeric) Maximum feerate (in " + + CURRENCY_UNIT + + " per byte)\n" + " \"maxtxsize\": xxxxx, (numeric) Maximum transaction size\n" + " \"medianfee\": x.xxx, (numeric) Truncated median fee in " + "the block\n" + " \"medianfeerate\": x.xxx, (numeric) Truncated median feerate " + "(in " + + CURRENCY_UNIT + + " per byte)\n" + " \"mediantime\": xxxxx, (numeric) The block median time " + "past\n" + " \"mediantxsize\": xxxxx, (numeric) Truncated median " + "transaction size\n" + " \"minfee\": x.xxx, (numeric) Minimum fee in the block\n" + " \"minfeerate\": xx.xx, (numeric) Minimum feerate (in " + + CURRENCY_UNIT + + " per byte)\n" + " \"mintxsize\": xxxxx, (numeric) Minimum transaction size\n" + " \"outs\": xxxxx, (numeric) The number of outputs\n" + " \"subsidy\": x.xxx, (numeric) The block subsidy\n" + " \"time\": xxxxx, (numeric) The block time\n" + " \"total_out\": x.xxx, (numeric) Total amount in all " + "outputs (excluding coinbase and thus reward [ie subsidy + " + "totalfee])\n" + " \"total_size\": xxxxx, (numeric) Total size of all " + "non-coinbase transactions\n" + " \"totalfee\": x.xxx, (numeric) The fee total\n" + " \"txs\": xxxxx, (numeric) The number of " + "transactions (excluding coinbase)\n" + " \"utxo_increase\": xxxxx, (numeric) The increase/decrease in " + "the number of unspent outputs\n" + " \"utxo_size_inc\": xxxxx, (numeric) The increase/decrease in " + "size for the utxo index (not discounting op_return and similar)\n" + "}\n"}, + RPCExamples{HelpExampleCli("getblockstats", + "1000 '[\"minfeerate\",\"avgfeerate\"]'") + + HelpExampleRpc("getblockstats", + "1000 '[\"minfeerate\",\"avgfeerate\"]'")}, + }; + + if (request.fHelp || !help.IsValidNumArgs(request.params.size())) { + throw std::runtime_error(help.ToString()); } LOCK(cs_main); diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2016 The Bitcoin Core developers +// Copyright (c) 2009-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. @@ -650,41 +650,42 @@ } static UniValue setban(const Config &config, const JSONRPCRequest &request) { + const RPCHelpMan help{ + "setban", + "\nAttempts to add or remove an IP/Subnet from the " + "banned list.\n", + { + {"subnet", RPCArg::Type::STR, RPCArg::Optional::NO, + "The IP/Subnet (see getpeerinfo for nodes IP) with an optional " + "netmask (default is /32 = single IP)"}, + {"command", RPCArg::Type::STR, RPCArg::Optional::NO, + "'add' to add an IP/Subnet to the list, 'remove' to remove an " + "IP/Subnet from the list"}, + {"bantime", RPCArg::Type::NUM, /* default */ "0", + "time in seconds how long (or until when if [absolute] is set) " + "the IP is banned (0 or empty means using the default time of 24h " + "which can also be overwritten by the -bantime startup argument)"}, + {"absolute", RPCArg::Type::BOOL, /* default */ "false", + "If set, the bantime must be an absolute timestamp in seconds " + "since epoch (Jan 1 1970 GMT)"}, + }, + RPCResults{}, + RPCExamples{ + HelpExampleCli("setban", "\"192.168.0.6\" \"add\" 86400") + + HelpExampleCli("setban", "\"192.168.0.0/24\" \"add\"") + + HelpExampleRpc("setban", "\"192.168.0.6\", \"add\", 86400")}, + }; + std::string strCommand; if (!request.params[1].isNull()) { strCommand = request.params[1].get_str(); } - if (request.fHelp || request.params.size() < 2 || + if (request.fHelp || !help.IsValidNumArgs(request.params.size()) || (strCommand != "add" && strCommand != "remove")) { - throw std::runtime_error(RPCHelpMan{ - "setban", - "\nAttempts to add or remove an IP/Subnet from the " - "banned list.\n", - { - {"subnet", RPCArg::Type::STR, RPCArg::Optional::NO, - "The IP/Subnet (see getpeerinfo for nodes IP) with an " - "optional netmask (default is /32 = single IP)"}, - {"command", RPCArg::Type::STR, RPCArg::Optional::NO, - "'add' to add an IP/Subnet to the list, 'remove' to " - "remove an IP/Subnet from the list"}, - {"bantime", RPCArg::Type::NUM, /* default */ "0", - "time in seconds how long (or until when if [absolute] is " - "set) the IP is banned (0 or empty means using the default " - "time of 24h which can also be overwritten by the -bantime " - "startup argument)"}, - {"absolute", RPCArg::Type::BOOL, /* default */ "false", - "If set, the bantime must be an absolute timestamp in seconds " - "since epoch (Jan 1 1970 GMT)"}, - }, - RPCResults{}, - RPCExamples{ - HelpExampleCli("setban", "\"192.168.0.6\" \"add\" 86400") + - HelpExampleCli("setban", "\"192.168.0.0/24\" \"add\"") + - HelpExampleRpc("setban", "\"192.168.0.6\", \"add\", 86400")}, - } - .ToString()); + throw std::runtime_error(help.ToString()); } + if (!g_banman) { throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded"); diff --git a/src/rpc/util.h b/src/rpc/util.h --- a/src/rpc/util.h +++ b/src/rpc/util.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017 The Bitcoin Core developers +// 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. @@ -109,7 +109,7 @@ /** Required arg */ NO, /** - * Optinal arg that is a named argument and has a default value of + * Optional arg that is a named argument and has a default value of * `null`. When possible, the default value should be specified. */ OMITTED_NAMED_ARG, @@ -163,6 +163,8 @@ CHECK_NONFATAL(type == Type::ARR || type == Type::OBJ); } + bool IsOptional() const; + /** * Return the type string of the argument. * Set oneline to allow it to be overridden by a custom oneline type string @@ -226,6 +228,8 @@ RPCExamples examples); std::string ToString() const; + /** If the supplied number of args is neither too small nor too high */ + bool IsValidNumArgs(size_t num_args) const; private: const std::string m_name; diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017 The Bitcoin Core developers +// 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. @@ -432,6 +432,16 @@ return m_examples.empty() ? m_examples : "\nExamples:\n" + m_examples; } +bool RPCHelpMan::IsValidNumArgs(size_t num_args) const { + size_t num_required_args = 0; + for (size_t n = m_args.size(); n > 0; --n) { + if (!m_args.at(n - 1).IsOptional()) { + num_required_args = n; + break; + } + } + return num_required_args <= num_args && num_args <= m_args.size(); +} std::string RPCHelpMan::ToString() const { std::string ret; @@ -439,13 +449,7 @@ ret += m_name; bool was_optional{false}; for (const auto &arg : m_args) { - bool optional; - if (arg.m_fallback.which() == 1) { - optional = true; - } else { - optional = RPCArg::Optional::NO != - boost::get(arg.m_fallback); - } + const bool optional = arg.IsOptional(); ret += " "; if (optional) { if (!was_optional) { @@ -498,6 +502,14 @@ return ret; } +bool RPCArg::IsOptional() const { + if (m_fallback.which() == 1) { + return true; + } else { + return RPCArg::Optional::NO != boost::get(m_fallback); + } +} + std::string RPCArg::ToDescriptionString() const { std::string ret; ret += "("; diff --git a/test/functional/rpc_getblockstats.py b/test/functional/rpc_getblockstats.py --- a/test/functional/rpc_getblockstats.py +++ b/test/functional/rpc_getblockstats.py @@ -201,6 +201,17 @@ assert_raises_rpc_error(-5, 'Block not found', self.nodes[0].getblockstats, hash_or_height='000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f') + # Invalid number of args + assert_raises_rpc_error(-1, + 'getblockstats hash_or_height ( stats )', + self.nodes[0].getblockstats, + '00', + 1, + 2) + assert_raises_rpc_error(-1, + 'getblockstats hash_or_height ( stats )', + self.nodes[0].getblockstats) + if __name__ == '__main__': GetblockstatsTest().main()