diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index c34abb1486b..a27fe56f8b5 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -809,7 +809,7 @@ static UniValue getblock(const JSONRPCRequest& request) "If verbosity is 2, returns an Object with information about block and information about each transaction. \n", { {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"}, - {"verbosity", RPCArg::Type::NUM, /* default */ "1", "0 for hex-encoded data, 1 for a json object, and 2 for json object with transaction data"}, + {"verbosity|verbose", RPCArg::Type::NUM, /* default */ "1", "0 for hex-encoded data, 1 for a json object, and 2 for json object with transaction data"}, }, { RPCResult{"for verbosity = 0", diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index b812f3005f5..05d3fd6afb5 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -277,19 +277,19 @@ static UniValue generateblock(const JSONRPCRequest& request) RPCHelpMan{"generateblock", "\nMine a block with a set of ordered transactions immediately to a specified address or descriptor (before the RPC call returns)\n", { - {"address/descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The address or descriptor to send the newly generated bitcoin to."}, + {"output", RPCArg::Type::STR, RPCArg::Optional::NO, "The address or descriptor to send the newly generated bitcoin to."}, {"transactions", RPCArg::Type::ARR, RPCArg::Optional::NO, "An array of hex strings which are either txids or raw transactions.\n" "Txids must reference transactions currently in the mempool.\n" "All transactions must be valid and in valid order, otherwise the block will be rejected.", { {"rawtx/txid", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""}, }, - } + }, }, RPCResult{ RPCResult::Type::OBJ, "", "", { - {RPCResult::Type::STR_HEX, "hash", "hash of generated block"} + {RPCResult::Type::STR_HEX, "hash", "hash of generated block"}, } }, RPCExamples{ @@ -1188,7 +1188,7 @@ static const CRPCCommand commands[] = { "generating", "generatetoaddress", &generatetoaddress, {"nblocks","address","maxtries"} }, { "generating", "generatetodescriptor", &generatetodescriptor, {"num_blocks","descriptor","maxtries"} }, - { "generating", "generateblock", &generateblock, {"address","transactions"} }, + { "generating", "generateblock", &generateblock, {"output","transactions"} }, { "util", "estimatesmartfee", &estimatesmartfee, {"conf_target", "estimate_mode"} }, diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index d03facf87e1..860fa198d55 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -13,6 +13,9 @@ #include +#include +#include + const std::string UNIX_EPOCH_TIME = "UNIX epoch time"; const std::string EXAMPLE_ADDRESS[2] = {"bc1q09vm5lfy0j5reeulh4x5752q25uqqvz34hufdl", "bc1q02ad21edsxd23d32dfgqqsz4vv4nmtfzuklhy3"}; @@ -330,7 +333,7 @@ struct Sections { if (outer_type == OuterType::NONE) return; // Nothing more to do for non-recursive types on first recursion auto left = indent; if (arg.m_type_str.size() != 0 && push_name) { - left += "\"" + arg.m_name + "\": " + arg.m_type_str.at(0); + left += "\"" + arg.GetName() + "\": " + arg.m_type_str.at(0); } else { left += push_name ? arg.ToStringObj(/* oneline */ false) : arg.ToString(/* oneline */ false); } @@ -341,7 +344,7 @@ struct Sections { case RPCArg::Type::OBJ: case RPCArg::Type::OBJ_USER_KEYS: { const auto right = outer_type == OuterType::NONE ? "" : arg.ToDescriptionString(); - PushSection({indent + (push_name ? "\"" + arg.m_name + "\": " : "") + "{", right}); + PushSection({indent + (push_name ? "\"" + arg.GetName() + "\": " : "") + "{", right}); for (const auto& arg_inner : arg.m_inner) { Push(arg_inner, current_indent + 2, OuterType::OBJ); } @@ -353,7 +356,7 @@ struct Sections { } case RPCArg::Type::ARR: { auto left = indent; - left += push_name ? "\"" + arg.m_name + "\": " : ""; + left += push_name ? "\"" + arg.GetName() + "\": " : ""; left += "["; const auto right = outer_type == OuterType::NONE ? "" : arg.ToDescriptionString(); PushSection({left, right}); @@ -419,8 +422,12 @@ RPCHelpMan::RPCHelpMan(std::string name, std::string description, std::vector named_args; for (const auto& arg : m_args) { + std::vector names; + boost::split(names, arg.m_names, boost::is_any_of("|")); // Should have unique named arguments - CHECK_NONFATAL(named_args.insert(arg.m_name).second); + for (const std::string& name : names) { + CHECK_NONFATAL(named_args.insert(name).second); + } } } @@ -489,7 +496,7 @@ std::string RPCHelpMan::ToString() const if (i == 0) ret += "\nArguments:\n"; // Push named argument name and description - sections.m_sections.emplace_back(::ToString(i + 1) + ". " + arg.m_name, arg.ToDescriptionString()); + sections.m_sections.emplace_back(::ToString(i + 1) + ". " + arg.GetFirstName(), arg.ToDescriptionString()); sections.m_max_pad = std::max(sections.m_max_pad, sections.m_sections.back().m_left.size()); // Recursively push nested args @@ -506,6 +513,17 @@ std::string RPCHelpMan::ToString() const return ret; } +std::string RPCArg::GetFirstName() const +{ + return m_names.substr(0, m_names.find("|")); +} + +std::string RPCArg::GetName() const +{ + CHECK_NONFATAL(std::string::npos == m_names.find("|")); + return m_names; +} + bool RPCArg::IsOptional() const { if (m_fallback.which() == 1) { @@ -681,7 +699,7 @@ std::string RPCArg::ToStringObj(const bool oneline) const { std::string res; res += "\""; - res += m_name; + res += GetFirstName(); if (oneline) { res += "\":"; } else { @@ -723,13 +741,13 @@ std::string RPCArg::ToString(const bool oneline) const switch (m_type) { case Type::STR_HEX: case Type::STR: { - return "\"" + m_name + "\""; + return "\"" + GetFirstName() + "\""; } case Type::NUM: case Type::RANGE: case Type::AMOUNT: case Type::BOOL: { - return m_name; + return GetFirstName(); } case Type::OBJ: case Type::OBJ_USER_KEYS: { diff --git a/src/rpc/util.h b/src/rpc/util.h index 11b806a0ffa..53dce2c3972 100644 --- a/src/rpc/util.h +++ b/src/rpc/util.h @@ -142,7 +142,7 @@ struct RPCArg { OMITTED, }; using Fallback = boost::variant; - const std::string m_name; //!< The name of the arg (can be empty for inner args) + const std::string m_names; //!< The name of the arg (can be empty for inner args, can contain multiple aliases separated by | for named request arguments) const Type m_type; const std::vector m_inner; //!< Only used for arrays or dicts const Fallback m_fallback; @@ -157,7 +157,7 @@ struct RPCArg { const std::string description, const std::string oneline_description = "", const std::vector type_str = {}) - : m_name{std::move(name)}, + : m_names{std::move(name)}, m_type{std::move(type)}, m_fallback{std::move(fallback)}, m_description{std::move(description)}, @@ -175,7 +175,7 @@ struct RPCArg { const std::vector inner, const std::string oneline_description = "", const std::vector type_str = {}) - : m_name{std::move(name)}, + : m_names{std::move(name)}, m_type{std::move(type)}, m_inner{std::move(inner)}, m_fallback{std::move(fallback)}, @@ -188,6 +188,12 @@ struct RPCArg { bool IsOptional() const; + /** Return the first of all aliases */ + std::string GetFirstName() const; + + /** Return the name, throws when there are aliases */ + std::string GetName() const; + /** * Return the type string of the argument. * Set oneline to allow it to be overridden by a custom oneline type string (m_oneline_description). diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index ae3134b89ae..703365b612b 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1400,8 +1400,8 @@ UniValue listtransactions(const JSONRPCRequest& request) "\nIf a label name is provided, this will return only incoming transactions paying to addresses with the specified label.\n" "\nReturns up to 'count' most recent transactions skipping the first 'from' transactions.\n", { - {"label", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "If set, should be a valid label name to return only incoming transactions\n" - " with the specified label, or \"*\" to disable filtering and return all transactions."}, + {"label|dummy", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "If set, should be a valid label name to return only incoming transactions\n" + "with the specified label, or \"*\" to disable filtering and return all transactions."}, {"count", RPCArg::Type::NUM, /* default */ "10", "The number of transactions to return"}, {"skip", RPCArg::Type::NUM, /* default */ "0", "The number of transactions to skip"}, {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Include transactions to watch-only addresses (see 'importaddress')"}, diff --git a/test/functional/rpc_generateblock.py b/test/functional/rpc_generateblock.py index f23d9ec5569..aa58c0af9d4 100755 --- a/test/functional/rpc_generateblock.py +++ b/test/functional/rpc_generateblock.py @@ -11,6 +11,7 @@ from test_framework.util import ( assert_raises_rpc_error, ) + class GenerateBlockTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 @@ -23,14 +24,14 @@ class GenerateBlockTest(BitcoinTestFramework): self.log.info('Generate an empty block to address') address = node.getnewaddress() - hash = node.generateblock(address, [])['hash'] - block = node.getblock(hash, 2) + hash = node.generateblock(output=address, transactions=[])['hash'] + block = node.getblock(blockhash=hash, verbose=2) assert_equal(len(block['tx']), 1) assert_equal(block['tx'][0]['vout'][0]['scriptPubKey']['addresses'][0], address) self.log.info('Generate an empty block to a descriptor') hash = node.generateblock('addr(' + address + ')', [])['hash'] - block = node.getblock(hash, 2) + block = node.getblock(blockhash=hash, verbosity=2) assert_equal(len(block['tx']), 1) assert_equal(block['tx'][0]['vout'][0]['scriptPubKey']['addresses'][0], address) diff --git a/test/functional/wallet_listtransactions.py b/test/functional/wallet_listtransactions.py index 8c44a070b80..8ff663ccd23 100755 --- a/test/functional/wallet_listtransactions.py +++ b/test/functional/wallet_listtransactions.py @@ -97,6 +97,8 @@ class ListTransactionsTest(BitcoinTestFramework): txid = self.nodes[1].sendtoaddress(multisig["address"], 0.1) self.nodes[1].generate(1) self.sync_all() + assert_equal(len(self.nodes[0].listtransactions(label="watchonly", include_watchonly=True)), 1) + assert_equal(len(self.nodes[0].listtransactions(dummy="watchonly", include_watchonly=True)), 1) assert len(self.nodes[0].listtransactions(label="watchonly", count=100, include_watchonly=False)) == 0 assert_array_result(self.nodes[0].listtransactions(label="watchonly", count=100, include_watchonly=True), {"category": "receive", "amount": Decimal("0.1")},