Merge #14530: Use RPCHelpMan to generate RPC doc strings

fa483e13b3 rpc: Add RPCHelpMan for machine-generated help (MarcoFalke)
fa0d36f712 rpc: Include rpc/util.h where needed for RPCHelpMan (MarcoFalke)

Pull request description:

  This introduces a manager for the RPC help generation and demonstrates its use of it in some RPCs.

  It is the first non-exhaustive step toward #14378 and I will create pull requests for the next steps after this one is merged.

Tree-SHA512: 86f68322443ff01cd964aaf0ebe186be63fbebe4c47676cf7a622cc2b5305fd176bd57badfd1bbf788a036812253eb0dead74ecc3b30664c3e0d9392b2248054
This commit is contained in:
MarcoFalke 2018-11-13 12:34:43 -05:00
commit c651265c93
No known key found for this signature in database
GPG key ID: D2EA4850E7528B25
10 changed files with 346 additions and 23 deletions

View file

@ -12,8 +12,8 @@
#include <checkpoints.h> #include <checkpoints.h>
#include <coins.h> #include <coins.h>
#include <consensus/validation.h> #include <consensus/validation.h>
#include <validation.h>
#include <core_io.h> #include <core_io.h>
#include <hash.h>
#include <index/txindex.h> #include <index/txindex.h>
#include <key_io.h> #include <key_io.h>
#include <policy/feerate.h> #include <policy/feerate.h>
@ -21,14 +21,15 @@
#include <policy/rbf.h> #include <policy/rbf.h>
#include <primitives/transaction.h> #include <primitives/transaction.h>
#include <rpc/server.h> #include <rpc/server.h>
#include <rpc/util.h>
#include <script/descriptor.h> #include <script/descriptor.h>
#include <streams.h> #include <streams.h>
#include <sync.h> #include <sync.h>
#include <txdb.h> #include <txdb.h>
#include <txmempool.h> #include <txmempool.h>
#include <util/system.h>
#include <util/strencodings.h> #include <util/strencodings.h>
#include <hash.h> #include <util/system.h>
#include <validation.h>
#include <validationinterface.h> #include <validationinterface.h>
#include <versionbitsinfo.h> #include <versionbitsinfo.h>
#include <warnings.h> #include <warnings.h>

View file

@ -18,10 +18,11 @@
#include <rpc/blockchain.h> #include <rpc/blockchain.h>
#include <rpc/mining.h> #include <rpc/mining.h>
#include <rpc/server.h> #include <rpc/server.h>
#include <rpc/util.h>
#include <shutdown.h> #include <shutdown.h>
#include <txmempool.h> #include <txmempool.h>
#include <util/system.h>
#include <util/strencodings.h> #include <util/strencodings.h>
#include <util/system.h>
#include <validation.h> #include <validation.h>
#include <validationinterface.h> #include <validationinterface.h>
#include <versionbitsinfo.h> #include <versionbitsinfo.h>

View file

@ -7,17 +7,18 @@
#include <chainparams.h> #include <chainparams.h>
#include <clientversion.h> #include <clientversion.h>
#include <core_io.h> #include <core_io.h>
#include <validation.h>
#include <net.h> #include <net.h>
#include <net_processing.h> #include <net_processing.h>
#include <netbase.h> #include <netbase.h>
#include <policy/policy.h> #include <policy/policy.h>
#include <rpc/protocol.h> #include <rpc/protocol.h>
#include <rpc/util.h>
#include <sync.h> #include <sync.h>
#include <timedata.h> #include <timedata.h>
#include <ui_interface.h> #include <ui_interface.h>
#include <util/system.h>
#include <util/strencodings.h> #include <util/strencodings.h>
#include <util/system.h>
#include <validation.h>
#include <version.h> #include <version.h>
#include <warnings.h> #include <warnings.h>

View file

@ -10,10 +10,8 @@
#include <core_io.h> #include <core_io.h>
#include <index/txindex.h> #include <index/txindex.h>
#include <init.h> #include <init.h>
#include <keystore.h>
#include <validation.h>
#include <validationinterface.h>
#include <key_io.h> #include <key_io.h>
#include <keystore.h>
#include <merkleblock.h> #include <merkleblock.h>
#include <net.h> #include <net.h>
#include <policy/policy.h> #include <policy/policy.h>
@ -29,6 +27,8 @@
#include <txmempool.h> #include <txmempool.h>
#include <uint256.h> #include <uint256.h>
#include <util/strencodings.h> #include <util/strencodings.h>
#include <validation.h>
#include <validationinterface.h>
#include <future> #include <future>
#include <stdint.h> #include <stdint.h>
@ -206,7 +206,16 @@ static UniValue gettxoutproof(const JSONRPCRequest& request)
{ {
if (request.fHelp || (request.params.size() != 1 && request.params.size() != 2)) if (request.fHelp || (request.params.size() != 1 && request.params.size() != 2))
throw std::runtime_error( throw std::runtime_error(
"gettxoutproof [\"txid\",...] ( blockhash )\n" RPCHelpMan{"gettxoutproof",
{
{"txids", RPCArg::Type::ARR,
{
{"txid", RPCArg::Type::STR_HEX, false},
},
false},
{"blockhash", RPCArg::Type::STR_HEX, true},
}}
.ToString() +
"\nReturns a hex-encoded proof that \"txid\" was included in a block.\n" "\nReturns a hex-encoded proof that \"txid\" was included in a block.\n"
"\nNOTE: By default this function only works sometimes. This is when there is an\n" "\nNOTE: By default this function only works sometimes. This is when there is an\n"
"unspent output in the utxo for this transaction. To make it always work,\n" "unspent output in the utxo for this transaction. To make it always work,\n"
@ -673,10 +682,17 @@ static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::
static UniValue combinerawtransaction(const JSONRPCRequest& request) static UniValue combinerawtransaction(const JSONRPCRequest& request)
{ {
if (request.fHelp || request.params.size() != 1) if (request.fHelp || request.params.size() != 1)
throw std::runtime_error( throw std::runtime_error(
"combinerawtransaction [\"hexstring\",...]\n" RPCHelpMan{"combinerawtransaction",
{
{"txs", RPCArg::Type::ARR,
{
{"hexstring", RPCArg::Type::STR_HEX, false},
},
false},
}}
.ToString() +
"\nCombine multiple partially signed transactions into one transaction.\n" "\nCombine multiple partially signed transactions into one transaction.\n"
"The combined transaction may be another partially signed transaction or a \n" "The combined transaction may be another partially signed transaction or a \n"
"fully signed transaction." "fully signed transaction."
@ -899,7 +915,30 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
{ {
if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) if (request.fHelp || request.params.size() < 2 || request.params.size() > 4)
throw std::runtime_error( throw std::runtime_error(
"signrawtransactionwithkey \"hexstring\" [\"privatekey1\",...] ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] sighashtype )\n" RPCHelpMan{"signrawtransactionwithkey",
{
{"hexstring", RPCArg::Type::STR, false},
{"privkyes", RPCArg::Type::ARR,
{
{"privatekey", RPCArg::Type::STR_HEX, false},
},
false},
{"prevtxs", RPCArg::Type::ARR,
{
{"", RPCArg::Type::OBJ,
{
{"txid", RPCArg::Type::STR_HEX, false},
{"vout", RPCArg::Type::NUM, false},
{"scriptPubKey", RPCArg::Type::STR_HEX, false},
{"redeemScript", RPCArg::Type::STR_HEX, false},
{"amount", RPCArg::Type::AMOUNT, false},
},
true},
},
true},
{"sighashtype", RPCArg::Type::STR, true},
}}
.ToString() +
"\nSign inputs for raw transaction (serialized, hex-encoded).\n" "\nSign inputs for raw transaction (serialized, hex-encoded).\n"
"The second argument is an array of base58-encoded private\n" "The second argument is an array of base58-encoded private\n"
"keys that will be the only keys used to sign the transaction.\n" "keys that will be the only keys used to sign the transaction.\n"
@ -1454,7 +1493,15 @@ UniValue combinepsbt(const JSONRPCRequest& request)
{ {
if (request.fHelp || request.params.size() != 1) if (request.fHelp || request.params.size() != 1)
throw std::runtime_error( throw std::runtime_error(
"combinepsbt [\"psbt\",...]\n" RPCHelpMan{"combinepsbt",
{
{"txs", RPCArg::Type::ARR,
{
{"psbt", RPCArg::Type::STR_HEX, false},
},
false},
}}
.ToString() +
"\nCombine multiple partially signed Bitcoin transactions into one transaction.\n" "\nCombine multiple partially signed Bitcoin transactions into one transaction.\n"
"Implements the Combiner role.\n" "Implements the Combiner role.\n"
"\nArguments:\n" "\nArguments:\n"
@ -1571,7 +1618,37 @@ UniValue createpsbt(const JSONRPCRequest& request)
{ {
if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) if (request.fHelp || request.params.size() < 2 || request.params.size() > 4)
throw std::runtime_error( throw std::runtime_error(
"createpsbt [{\"txid\":\"id\",\"vout\":n},...] [{\"address\":amount},{\"data\":\"hex\"},...] ( locktime ) ( replaceable )\n" RPCHelpMan{"createpsbt",
{
{"inputs", RPCArg::Type::ARR,
{
{"", RPCArg::Type::OBJ,
{
{"txid", RPCArg::Type::STR_HEX, false},
{"vout", RPCArg::Type::NUM, false},
{"sequence", RPCArg::Type::NUM, true},
},
false},
},
false},
{"outputs", RPCArg::Type::ARR,
{
{"", RPCArg::Type::OBJ,
{
{"address", RPCArg::Type::AMOUNT, false},
},
true},
{"", RPCArg::Type::OBJ,
{
{"data", RPCArg::Type::STR_HEX, false},
},
true},
},
false},
{"locktime", RPCArg::Type::NUM, true},
{"replaceable", RPCArg::Type::BOOL, true},
}}
.ToString() +
"\nCreates a transaction in the Partially Signed Transaction format.\n" "\nCreates a transaction in the Partially Signed Transaction format.\n"
"Implements the Creator role.\n" "Implements the Creator role.\n"
"\nArguments:\n" "\nArguments:\n"

View file

@ -8,11 +8,12 @@
#include <fs.h> #include <fs.h>
#include <key_io.h> #include <key_io.h>
#include <random.h> #include <random.h>
#include <rpc/util.h>
#include <shutdown.h> #include <shutdown.h>
#include <sync.h> #include <sync.h>
#include <ui_interface.h> #include <ui_interface.h>
#include <util/system.h>
#include <util/strencodings.h> #include <util/strencodings.h>
#include <util/system.h>
#include <boost/bind.hpp> #include <boost/bind.hpp>
#include <boost/signals2/signal.hpp> #include <boost/signals2/signal.hpp>

View file

@ -128,3 +128,95 @@ UniValue DescribeAddress(const CTxDestination& dest)
{ {
return boost::apply_visitor(DescribeAddressVisitor(), dest); return boost::apply_visitor(DescribeAddressVisitor(), dest);
} }
std::string RPCHelpMan::ToString() const
{
std::string ret;
ret += m_name;
bool is_optional{false};
for (const auto& arg : m_args) {
ret += " ";
if (arg.m_optional) {
if (!is_optional) ret += "( ";
is_optional = true;
} else {
// Currently we still support unnamed arguments, so any argument following an optional argument must also be optional
// If support for positional arguments is deprecated in the future, remove this line
assert(!is_optional);
}
ret += arg.ToString();
}
if (is_optional) ret += " )";
ret += "\n";
return ret;
}
std::string RPCArg::ToStringObj() const
{
std::string res = "\"" + m_name + "\":";
switch (m_type) {
case Type::STR:
return res + "\"str\"";
case Type::STR_HEX:
return res + "\"hex\"";
case Type::NUM:
return res + "n";
case Type::AMOUNT:
return res + "amount";
case Type::BOOL:
return res + "bool";
case Type::ARR:
res += "[";
for (const auto& i : m_inner) {
res += i.ToString() + ",";
}
return res + "...]";
case Type::OBJ:
case Type::OBJ_USER_KEYS:
// Currently unused, so avoid writing dead code
assert(false);
// no default case, so the compiler can warn about missing cases
}
assert(false);
}
std::string RPCArg::ToString() const
{
switch (m_type) {
case Type::STR_HEX:
case Type::STR: {
return "\"" + m_name + "\"";
}
case Type::NUM:
case Type::AMOUNT:
case Type::BOOL: {
return m_name;
}
case Type::OBJ:
case Type::OBJ_USER_KEYS: {
std::string res;
for (size_t i = 0; i < m_inner.size();) {
res += m_inner[i].ToStringObj();
if (++i < m_inner.size()) res += ",";
}
if (m_type == Type::OBJ) {
return "{" + res + "}";
} else {
return "{" + res + ",...}";
}
}
case Type::ARR: {
std::string res;
for (const auto& i : m_inner) {
res += i.ToString() + ",";
}
return "[" + res + "...]";
}
// no default case, so the compiler can warn about missing cases
}
assert(false);
}

View file

@ -30,4 +30,53 @@ CScript CreateMultisigRedeemscript(const int required, const std::vector<CPubKey
UniValue DescribeAddress(const CTxDestination& dest); UniValue DescribeAddress(const CTxDestination& dest);
struct RPCArg {
enum class Type {
OBJ,
ARR,
STR,
NUM,
BOOL,
OBJ_USER_KEYS, //!< Special type where the user must set the keys e.g. to define multiple addresses; as opposed to e.g. an options object where the keys are predefined
AMOUNT, //!< Special type representing a floating point amount (can be either NUM or STR)
STR_HEX, //!< Special type that is a STR with only hex chars
};
const std::string m_name; //!< The name of the arg (can be empty for inner args)
const Type m_type;
const std::vector<RPCArg> m_inner; //!< Only used for arrays or dicts
const bool m_optional;
RPCArg(const std::string& name, const Type& type, const bool optional)
: m_name{name}, m_type{type}, m_optional{optional}
{
assert(type != Type::ARR && type != Type::OBJ);
}
RPCArg(const std::string& name, const Type& type, const std::vector<RPCArg>& inner, const bool optional)
: m_name{name}, m_type{type}, m_inner{inner}, m_optional{optional}
{
assert(type == Type::ARR || type == Type::OBJ);
}
std::string ToString() const;
private:
std::string ToStringObj() const;
};
class RPCHelpMan
{
public:
RPCHelpMan(const std::string& name, const std::vector<RPCArg>& args)
: m_name{name}, m_args{args}
{
}
std::string ToString() const;
private:
const std::string m_name;
const std::vector<RPCArg> m_args;
};
#endif // BITCOIN_RPC_UTIL_H #endif // BITCOIN_RPC_UTIL_H

View file

@ -3,18 +3,19 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chain.h> #include <chain.h>
#include <core_io.h>
#include <interfaces/chain.h> #include <interfaces/chain.h>
#include <key_io.h> #include <key_io.h>
#include <merkleblock.h>
#include <rpc/server.h> #include <rpc/server.h>
#include <validation.h> #include <rpc/util.h>
#include <script/script.h> #include <script/script.h>
#include <script/standard.h> #include <script/standard.h>
#include <sync.h> #include <sync.h>
#include <util/system.h> #include <util/system.h>
#include <util/time.h> #include <util/time.h>
#include <validation.h>
#include <wallet/wallet.h> #include <wallet/wallet.h>
#include <merkleblock.h>
#include <core_io.h>
#include <wallet/rpcwallet.h> #include <wallet/rpcwallet.h>

View file

@ -2075,7 +2075,21 @@ static UniValue lockunspent(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error( throw std::runtime_error(
"lockunspent unlock ([{\"txid\":\"txid\",\"vout\":n},...])\n" RPCHelpMan{"lockunspent",
{
{"unlock", RPCArg::Type::BOOL, false},
{"transactions", RPCArg::Type::ARR,
{
{"", RPCArg::Type::OBJ,
{
{"txid", RPCArg::Type::STR_HEX, false},
{"vout", RPCArg::Type::NUM, false},
},
true},
},
true},
}}
.ToString() +
"\nUpdates list of temporarily unspendable outputs.\n" "\nUpdates list of temporarily unspendable outputs.\n"
"Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n" "Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n"
"If no transaction outputs are specified when unlocking then all current locked transaction outputs are unlocked.\n" "If no transaction outputs are specified when unlocking then all current locked transaction outputs are unlocked.\n"
@ -2620,7 +2634,26 @@ static UniValue listunspent(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 5) if (request.fHelp || request.params.size() > 5)
throw std::runtime_error( throw std::runtime_error(
"listunspent ( minconf maxconf [\"addresses\",...] [include_unsafe] [query_options])\n" RPCHelpMan{"listunspent",
{
{"minconf", RPCArg::Type::NUM, true},
{"maxconf", RPCArg::Type::NUM, true},
{"addresses", RPCArg::Type::ARR,
{
{"address", RPCArg::Type::STR_HEX, true},
},
true},
{"include_unsafe", RPCArg::Type::BOOL, true},
{"query_options", RPCArg::Type::OBJ,
{
{"minimumAmount", RPCArg::Type::AMOUNT, true},
{"maximumAmount", RPCArg::Type::AMOUNT, true},
{"maximumCount", RPCArg::Type::NUM, true},
{"minimumSumAmount", RPCArg::Type::AMOUNT, true},
},
true},
}}
.ToString() +
"\nReturns array of unspent transaction outputs\n" "\nReturns array of unspent transaction outputs\n"
"with between minconf and maxconf (inclusive) confirmations.\n" "with between minconf and maxconf (inclusive) confirmations.\n"
"Optionally filter to only include txouts paid to specified addresses.\n" "Optionally filter to only include txouts paid to specified addresses.\n"
@ -2995,7 +3028,25 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 3) if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
throw std::runtime_error( throw std::runtime_error(
"signrawtransactionwithwallet \"hexstring\" ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] sighashtype )\n" RPCHelpMan{"signrawtransactionwithwallet",
{
{"hexstring", RPCArg::Type::STR, false},
{"prevtxs", RPCArg::Type::ARR,
{
{"", RPCArg::Type::OBJ,
{
{"txid", RPCArg::Type::STR_HEX, false},
{"vout", RPCArg::Type::NUM, false},
{"scriptPubKey", RPCArg::Type::STR_HEX, false},
{"redeemScript", RPCArg::Type::STR_HEX, false},
{"amount", RPCArg::Type::AMOUNT, false},
},
false},
},
true},
{"sighashtype", RPCArg::Type::STR, true},
}}
.ToString() +
"\nSign inputs for raw transaction (serialized, hex-encoded).\n" "\nSign inputs for raw transaction (serialized, hex-encoded).\n"
"The second optional argument (may be null) is an array of previous transaction outputs that\n" "The second optional argument (may be null) is an array of previous transaction outputs that\n"
"this transaction depends on but may not yet be in the block chain.\n" "this transaction depends on but may not yet be in the block chain.\n"
@ -3902,7 +3953,55 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 2 || request.params.size() > 5) if (request.fHelp || request.params.size() < 2 || request.params.size() > 5)
throw std::runtime_error( throw std::runtime_error(
"walletcreatefundedpsbt [{\"txid\":\"id\",\"vout\":n},...] [{\"address\":amount},{\"data\":\"hex\"},...] ( locktime ) ( replaceable ) ( options bip32derivs )\n" RPCHelpMan{"walletcreatefundedpsbt",
{
{"inputs", RPCArg::Type::ARR,
{
{"", RPCArg::Type::OBJ,
{
{"txid", RPCArg::Type::STR_HEX, false},
{"vout", RPCArg::Type::NUM, false},
{"sequence", RPCArg::Type::NUM, false},
},
false},
},
false},
{"outputs", RPCArg::Type::ARR,
{
{"", RPCArg::Type::OBJ,
{
{"address", RPCArg::Type::AMOUNT, true},
},
true},
{"", RPCArg::Type::OBJ,
{
{"data", RPCArg::Type::STR_HEX, true},
},
true},
},
false},
{"locktime", RPCArg::Type::NUM, true},
{"options", RPCArg::Type::OBJ,
{
{"changeAddress", RPCArg::Type::STR_HEX, true},
{"changePosition", RPCArg::Type::NUM, true},
{"change_type", RPCArg::Type::STR, true},
{"includeWatching", RPCArg::Type::BOOL, true},
{"lockUnspents", RPCArg::Type::BOOL, true},
{"feeRate", RPCArg::Type::NUM, true},
{"subtractFeeFromOutputs", RPCArg::Type::ARR,
{
{"int", RPCArg::Type::NUM, true},
},
true},
{"replaceable", RPCArg::Type::BOOL, true},
{"conf_target", RPCArg::Type::NUM, true},
{"estimate_mode", RPCArg::Type::STR, true},
},
true},
{"bip32derivs", RPCArg::Type::BOOL, true},
}}
.ToString() +
"\nCreates and funds a transaction in the Partially Signed Transaction format. Inputs will be added if supplied inputs are not enough\n" "\nCreates and funds a transaction in the Partially Signed Transaction format. Inputs will be added if supplied inputs are not enough\n"
"Implements the Creator and Updater roles.\n" "Implements the Creator and Updater roles.\n"
"\nArguments:\n" "\nArguments:\n"

View file

@ -5,6 +5,7 @@
#include <zmq/zmqrpc.h> #include <zmq/zmqrpc.h>
#include <rpc/server.h> #include <rpc/server.h>
#include <rpc/util.h>
#include <zmq/zmqabstractnotifier.h> #include <zmq/zmqabstractnotifier.h>
#include <zmq/zmqnotificationinterface.h> #include <zmq/zmqnotificationinterface.h>