Merge 244daa4821 into merged_master (Bitcoin PR #18607)

This commit is contained in:
Andrew Poelstra 2020-11-26 01:08:23 +00:00
commit 86082b9846
7 changed files with 48 additions and 21 deletions

View file

@ -869,7 +869,7 @@ static UniValue getblock(const JSONRPCRequest& request)
"If verbosity is 2, returns an Object with information about block <hash> 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",

View file

@ -297,19 +297,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{
@ -1699,7 +1699,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"} },

View file

@ -16,6 +16,9 @@
#include <tuple>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
const std::string UNIX_EPOCH_TIME = "UNIX epoch time";
const std::string EXAMPLE_ADDRESS[2] = {"bc1q09vm5lfy0j5reeulh4x5752q25uqqvz34hufdl", "bc1q02ad21edsxd23d32dfgqqsz4vv4nmtfzuklhy3"};
@ -341,7 +344,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);
}
@ -352,7 +355,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);
}
@ -364,7 +367,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});
@ -430,8 +433,12 @@ RPCHelpMan::RPCHelpMan(std::string name, std::string description, std::vector<RP
{
std::set<std::string> named_args;
for (const auto& arg : m_args) {
std::vector<std::string> 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);
}
}
}
@ -500,7 +507,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
@ -517,6 +524,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) {
@ -692,7 +710,7 @@ std::string RPCArg::ToStringObj(const bool oneline) const
{
std::string res;
res += "\"";
res += m_name;
res += GetFirstName();
if (oneline) {
res += "\":";
} else {
@ -734,13 +752,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: {

View file

@ -147,7 +147,7 @@ struct RPCArg {
OMITTED,
};
using Fallback = boost::variant<Optional, /* default value for optional args */ std::string>;
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<RPCArg> m_inner; //!< Only used for arrays or dicts
const Fallback m_fallback;
@ -162,7 +162,7 @@ struct RPCArg {
const std::string description,
const std::string oneline_description = "",
const std::vector<std::string> 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)},
@ -180,7 +180,7 @@ struct RPCArg {
const std::vector<RPCArg> inner,
const std::string oneline_description = "",
const std::vector<std::string> 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)},
@ -193,6 +193,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).

View file

@ -1622,8 +1622,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')"},

View file

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

View file

@ -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")},