diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index a8c6a4086b..950f0181ad 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -206,7 +206,16 @@ static UniValue gettxoutproof(const JSONRPCRequest& request) { if (request.fHelp || (request.params.size() != 1 && request.params.size() != 2)) 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" "\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" @@ -673,10 +682,17 @@ static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std:: static UniValue combinerawtransaction(const JSONRPCRequest& request) { - if (request.fHelp || request.params.size() != 1) 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" "The combined transaction may be another partially signed transaction or a \n" "fully signed transaction." @@ -899,7 +915,30 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) 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" "The second argument is an array of base58-encoded private\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) 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" "Implements the Combiner role.\n" "\nArguments:\n" @@ -1570,7 +1617,37 @@ UniValue createpsbt(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) 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" "Implements the Creator role.\n" "\nArguments:\n" diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index ef2d14b90e..6f2450708a 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -128,3 +128,95 @@ UniValue DescribeAddress(const CTxDestination& 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); +} diff --git a/src/rpc/util.h b/src/rpc/util.h index e21b5ba22a..55ae2fa363 100644 --- a/src/rpc/util.h +++ b/src/rpc/util.h @@ -30,4 +30,53 @@ CScript CreateMultisigRedeemscript(const int required, const std::vector 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& 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& args) + : m_name{name}, m_args{args} + { + } + + std::string ToString() const; + +private: + const std::string m_name; + const std::vector m_args; +}; + #endif // BITCOIN_RPC_UTIL_H diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 5a89448e02..8261c48c77 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2075,7 +2075,21 @@ static UniValue lockunspent(const JSONRPCRequest& request) if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) 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" "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" @@ -2620,7 +2634,26 @@ static UniValue listunspent(const JSONRPCRequest& request) if (request.fHelp || request.params.size() > 5) 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" "with between minconf and maxconf (inclusive) confirmations.\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) 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" "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" @@ -3896,7 +3947,55 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request) if (request.fHelp || request.params.size() < 2 || request.params.size() > 5) 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" "Implements the Creator and Updater roles.\n" "\nArguments:\n"