Page MenuHomePhabricator

No OneTemporary

diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index 37ea99e40..7668850cf 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -1,685 +1,703 @@
// 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.
#include <rpc/util.h>
#include <key_io.h>
#include <pubkey.h>
#include <script/signingprovider.h>
#include <tinyformat.h>
#include <util/strencodings.h>
#include <util/string.h>
#include <univalue.h>
#include <boost/variant/static_visitor.hpp>
void RPCTypeCheck(const UniValue &params,
const std::list<UniValueType> &typesExpected,
bool fAllowNull) {
unsigned int i = 0;
for (const UniValueType &t : typesExpected) {
if (params.size() <= i) {
break;
}
const UniValue &v = params[i];
if (!(fAllowNull && v.isNull())) {
RPCTypeCheckArgument(v, t);
}
i++;
}
}
void RPCTypeCheckArgument(const UniValue &value,
const UniValueType &typeExpected) {
if (!typeExpected.typeAny && value.type() != typeExpected.type) {
throw JSONRPCError(RPC_TYPE_ERROR,
strprintf("Expected type %s, got %s",
uvTypeName(typeExpected.type),
uvTypeName(value.type())));
}
}
void RPCTypeCheckObj(const UniValue &o,
const std::map<std::string, UniValueType> &typesExpected,
bool fAllowNull, bool fStrict) {
for (const auto &t : typesExpected) {
const UniValue &v = find_value(o, t.first);
if (!fAllowNull && v.isNull()) {
throw JSONRPCError(RPC_TYPE_ERROR,
strprintf("Missing %s", t.first));
}
if (!(t.second.typeAny || v.type() == t.second.type ||
(fAllowNull && v.isNull()))) {
std::string err = strprintf("Expected type %s for %s, got %s",
uvTypeName(t.second.type), t.first,
uvTypeName(v.type()));
throw JSONRPCError(RPC_TYPE_ERROR, err);
}
}
if (fStrict) {
for (const std::string &k : o.getKeys()) {
if (typesExpected.count(k) == 0) {
std::string err = strprintf("Unexpected key %s", k);
throw JSONRPCError(RPC_TYPE_ERROR, err);
}
}
}
}
Amount AmountFromValue(const UniValue &value) {
if (!value.isNum() && !value.isStr()) {
throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string");
}
int64_t n;
if (!ParseFixedPoint(value.getValStr(), 8, &n)) {
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
}
Amount amt = n * SATOSHI;
if (!MoneyRange(amt)) {
throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range");
}
return amt;
}
uint256 ParseHashV(const UniValue &v, std::string strName) {
std::string strHex(v.get_str());
if (64 != strHex.length()) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
strprintf("%s must be of length %d (not %d, for '%s')", strName, 64,
strHex.length(), strHex));
}
// Note: IsHex("") is false
if (!IsHex(strHex)) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
strName + " must be hexadecimal string (not '" +
strHex + "')");
}
return uint256S(strHex);
}
uint256 ParseHashO(const UniValue &o, std::string strKey) {
return ParseHashV(find_value(o, strKey), strKey);
}
std::vector<uint8_t> ParseHexV(const UniValue &v, std::string strName) {
std::string strHex;
if (v.isStr()) {
strHex = v.get_str();
}
if (!IsHex(strHex)) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
strName + " must be hexadecimal string (not '" +
strHex + "')");
}
return ParseHex(strHex);
}
std::vector<uint8_t> ParseHexO(const UniValue &o, std::string strKey) {
return ParseHexV(find_value(o, strKey), strKey);
}
std::string HelpExampleCli(const std::string &methodname,
const std::string &args) {
return "> bitcoin-cli " + methodname + " " + args + "\n";
}
std::string HelpExampleRpc(const std::string &methodname,
const std::string &args) {
return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", "
"\"id\":\"curltest\", "
"\"method\": \"" +
methodname + "\", \"params\": [" + args +
"] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n";
}
// Converts a hex string to a public key if possible
CPubKey HexToPubKey(const std::string &hex_in) {
if (!IsHex(hex_in)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Invalid public key: " + hex_in);
}
CPubKey vchPubKey(ParseHex(hex_in));
if (!vchPubKey.IsFullyValid()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Invalid public key: " + hex_in);
}
return vchPubKey;
}
// Retrieves a public key for an address from the given FillableSigningProvider
CPubKey AddrToPubKey(const CChainParams &chainparams,
FillableSigningProvider *const keystore,
const std::string &addr_in) {
CTxDestination dest = DecodeDestination(addr_in, chainparams);
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Invalid address: " + addr_in);
}
CKeyID key = GetKeyForDestination(*keystore, dest);
if (key.IsNull()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
strprintf("%s does not refer to a key", addr_in));
}
CPubKey vchPubKey;
if (!keystore->GetPubKey(key, vchPubKey)) {
throw JSONRPCError(
RPC_INVALID_ADDRESS_OR_KEY,
strprintf("no full public key for address %s", addr_in));
}
if (!vchPubKey.IsFullyValid()) {
throw JSONRPCError(RPC_INTERNAL_ERROR,
"Wallet contains an invalid public key");
}
return vchPubKey;
}
// Creates a multisig address from a given list of public keys, number of
// signatures required, and the address type
CTxDestination AddAndGetMultisigDestination(const int required,
const std::vector<CPubKey> &pubkeys,
OutputType type,
FillableSigningProvider &keystore,
CScript &script_out) {
// Gather public keys
if (required < 1) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
"a multisignature address must require at least one key to redeem");
}
if ((int)pubkeys.size() < required) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
strprintf("not enough keys supplied (got %u keys, "
"but need at least %d to redeem)",
pubkeys.size(), required));
}
if (pubkeys.size() > 16) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Number of keys involved in the multisignature "
"address creation > 16\nReduce the number");
}
script_out = GetScriptForMultisig(required, pubkeys);
if (script_out.size() > MAX_SCRIPT_ELEMENT_SIZE) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
(strprintf("redeemScript exceeds size limit: %d > %d",
script_out.size(), MAX_SCRIPT_ELEMENT_SIZE)));
}
// Check if any keys are uncompressed. If so, the type is legacy
for (const CPubKey &pk : pubkeys) {
if (!pk.IsCompressed()) {
type = OutputType::LEGACY;
break;
}
}
// Make the address
CTxDestination dest =
AddAndGetDestinationForScript(keystore, script_out, type);
return dest;
}
class DescribeAddressVisitor : public boost::static_visitor<UniValue> {
public:
explicit DescribeAddressVisitor() {}
UniValue operator()(const CNoDestination &dest) const {
return UniValue(UniValue::VOBJ);
}
UniValue operator()(const PKHash &keyID) const {
UniValue obj(UniValue::VOBJ);
obj.pushKV("isscript", false);
return obj;
}
UniValue operator()(const ScriptHash &scriptID) const {
UniValue obj(UniValue::VOBJ);
obj.pushKV("isscript", true);
return obj;
}
};
UniValue DescribeAddress(const CTxDestination &dest) {
return boost::apply_visitor(DescribeAddressVisitor(), dest);
}
RPCErrorCode RPCErrorFromTransactionError(TransactionError terr) {
switch (terr) {
case TransactionError::MEMPOOL_REJECTED:
return RPC_TRANSACTION_REJECTED;
case TransactionError::ALREADY_IN_CHAIN:
return RPC_TRANSACTION_ALREADY_IN_CHAIN;
case TransactionError::P2P_DISABLED:
return RPC_CLIENT_P2P_DISABLED;
case TransactionError::INVALID_PSBT:
case TransactionError::PSBT_MISMATCH:
return RPC_INVALID_PARAMETER;
case TransactionError::SIGHASH_MISMATCH:
return RPC_DESERIALIZATION_ERROR;
default:
break;
}
return RPC_TRANSACTION_ERROR;
}
UniValue JSONRPCTransactionError(TransactionError terr,
const std::string &err_string) {
if (err_string.length() > 0) {
return JSONRPCError(RPCErrorFromTransactionError(terr), err_string);
} else {
return JSONRPCError(RPCErrorFromTransactionError(terr),
TransactionErrorString(terr));
}
}
/**
* A pair of strings that can be aligned (through padding) with other Sections
* later on
*/
struct Section {
Section(const std::string &left, const std::string &right)
: m_left{left}, m_right{right} {}
const std::string m_left;
const std::string m_right;
};
/**
* Keeps track of RPCArgs by transforming them into sections for the purpose
* of serializing everything to a single string
*/
struct Sections {
std::vector<Section> m_sections;
size_t m_max_pad{0};
void PushSection(const Section &s) {
m_max_pad = std::max(m_max_pad, s.m_left.size());
m_sections.push_back(s);
}
/**
* Serializing RPCArgs depends on the outer type. Only arrays and
* dictionaries can be nested in json. The top-level outer type is "named
* arguments", a mix between a dictionary and arrays.
*/
enum class OuterType {
ARR,
OBJ,
// Only set on first recursion
NAMED_ARG,
};
/**
* Recursive helper to translate an RPCArg into sections
*/
void Push(const RPCArg &arg, const size_t current_indent = 5,
const OuterType outer_type = OuterType::NAMED_ARG) {
const auto indent = std::string(current_indent, ' ');
const auto indent_next = std::string(current_indent + 2, ' ');
// Dictionary keys must have a name
const bool push_name{outer_type == OuterType::OBJ};
switch (arg.m_type) {
case RPCArg::Type::STR_HEX:
case RPCArg::Type::STR:
case RPCArg::Type::NUM:
case RPCArg::Type::AMOUNT:
case RPCArg::Type::BOOL: {
// Nothing more to do for non-recursive types on first recursion
if (outer_type == OuterType::NAMED_ARG) {
return;
}
auto left = indent;
if (arg.m_type_str.size() != 0 && push_name) {
left += "\"" + arg.m_name + "\": " + arg.m_type_str.at(0);
} else {
left += push_name ? arg.ToStringObj(/* oneline */ false)
: arg.ToString(/* oneline */ false);
}
left += ",";
PushSection({left, arg.ToDescriptionString()});
break;
}
case RPCArg::Type::OBJ:
case RPCArg::Type::OBJ_USER_KEYS: {
const auto right = outer_type == OuterType::NAMED_ARG
? ""
: arg.ToDescriptionString();
PushSection({indent +
(push_name ? "\"" + arg.m_name + "\": " : "") +
"{",
right});
for (const auto &arg_inner : arg.m_inner) {
Push(arg_inner, current_indent + 2, OuterType::OBJ);
}
if (arg.m_type != RPCArg::Type::OBJ) {
PushSection({indent_next + "...", ""});
}
PushSection(
{indent + "}" +
(outer_type != OuterType::NAMED_ARG ? "," : ""),
""});
break;
}
case RPCArg::Type::ARR: {
auto left = indent;
left += push_name ? "\"" + arg.m_name + "\": " : "";
left += "[";
const auto right = outer_type == OuterType::NAMED_ARG
? ""
: arg.ToDescriptionString();
PushSection({left, right});
for (const auto &arg_inner : arg.m_inner) {
Push(arg_inner, current_indent + 2, OuterType::ARR);
}
PushSection({indent_next + "...", ""});
PushSection(
{indent + "]" +
(outer_type != OuterType::NAMED_ARG ? "," : ""),
""});
break;
}
// no default case, so the compiler can warn about missing cases
}
}
/**
* Concatenate all sections with proper padding
*/
std::string ToString() const {
std::string ret;
const size_t pad = m_max_pad + 4;
for (const auto &s : m_sections) {
if (s.m_right.empty()) {
ret += s.m_left;
ret += "\n";
continue;
}
std::string left = s.m_left;
left.resize(pad, ' ');
ret += left;
// Properly pad after newlines
std::string right;
size_t begin = 0;
size_t new_line_pos = s.m_right.find_first_of('\n');
while (true) {
right += s.m_right.substr(begin, new_line_pos - begin);
if (new_line_pos == std::string::npos) {
// No new line
break;
}
right += "\n" + std::string(pad, ' ');
begin = s.m_right.find_first_not_of(' ', new_line_pos + 1);
if (begin == std::string::npos) {
break; // Empty line
}
new_line_pos = s.m_right.find_first_of('\n', begin + 1);
}
ret += right;
ret += "\n";
}
return ret;
}
};
RPCHelpMan::RPCHelpMan(std::string name, std::string description,
std::vector<RPCArg> args, RPCResults results,
RPCExamples examples)
: m_name{std::move(name)}, m_description{std::move(description)},
m_args{std::move(args)}, m_results{std::move(results)},
m_examples{std::move(examples)} {
std::set<std::string> named_args;
for (const auto &arg : m_args) {
// Should have unique named arguments
CHECK_NONFATAL(named_args.insert(arg.m_name).second);
}
}
std::string RPCResults::ToDescriptionString() const {
std::string result;
for (const auto &r : m_results) {
if (r.m_cond.empty()) {
result += "\nResult:\n";
} else {
result += "\nResult (" + r.m_cond + "):\n";
}
result += r.m_result;
}
return result;
}
std::string RPCExamples::ToDescriptionString() const {
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;
// Oneline summary
ret += m_name;
bool was_optional{false};
for (const auto &arg : m_args) {
const bool optional = arg.IsOptional();
ret += " ";
if (optional) {
if (!was_optional) {
ret += "( ";
}
was_optional = true;
} else {
if (was_optional) {
ret += ") ";
}
was_optional = false;
}
ret += arg.ToString(/* oneline */ true);
}
if (was_optional) {
ret += " )";
}
ret += "\n";
// Description
ret += m_description;
// Arguments
Sections sections;
for (size_t i{0}; i < m_args.size(); ++i) {
const auto &arg = m_args.at(i);
if (i == 0) {
ret += "\nArguments:\n";
}
// Push named argument name and description
sections.m_sections.emplace_back(std::to_string(i + 1) + ". " +
arg.m_name,
arg.ToDescriptionString());
sections.m_max_pad = std::max(sections.m_max_pad,
sections.m_sections.back().m_left.size());
// Recursively push nested args
sections.Push(arg);
}
ret += sections.ToString();
// Result
ret += m_results.ToDescriptionString();
// Examples
ret += m_examples.ToDescriptionString();
return ret;
}
bool RPCArg::IsOptional() const {
if (m_fallback.which() == 1) {
return true;
} else {
return RPCArg::Optional::NO != boost::get<RPCArg::Optional>(m_fallback);
}
}
std::string RPCArg::ToDescriptionString() const {
std::string ret;
ret += "(";
if (m_type_str.size() != 0) {
ret += m_type_str.at(1);
} else {
switch (m_type) {
case Type::STR_HEX:
case Type::STR: {
ret += "string";
break;
}
case Type::NUM: {
ret += "numeric";
break;
}
case Type::AMOUNT: {
ret += "numeric or string";
break;
}
case Type::BOOL: {
ret += "boolean";
break;
}
case Type::OBJ:
case Type::OBJ_USER_KEYS: {
ret += "json object";
break;
}
case Type::ARR: {
ret += "json array";
break;
}
// no default case, so the compiler can warn about missing cases
}
}
if (m_fallback.which() == 1) {
ret += ", optional, default=" + boost::get<std::string>(m_fallback);
} else {
switch (boost::get<RPCArg::Optional>(m_fallback)) {
case RPCArg::Optional::OMITTED: {
// nothing to do. Element is treated as if not present and has
// no default value
break;
}
case RPCArg::Optional::OMITTED_NAMED_ARG: {
ret += ", optional"; // Default value is "null"
break;
}
case RPCArg::Optional::NO: {
ret += ", required";
break;
}
// no default case, so the compiler can warn about missing cases
}
}
ret += ")";
ret += m_description.empty() ? "" : " " + m_description;
return ret;
}
std::string RPCArg::ToStringObj(const bool oneline) const {
std::string res;
res += "\"";
res += m_name;
if (oneline) {
res += "\":";
} else {
res += "\": ";
}
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(oneline) + ",";
}
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 bool oneline) const {
if (oneline && !m_oneline_description.empty()) {
return m_oneline_description;
}
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: {
const std::string res = Join(m_inner, ",", [&](const RPCArg &i) {
return i.ToStringObj(oneline);
});
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(oneline) + ",";
}
return "[" + res + "...]";
}
// no default case, so the compiler can warn about missing cases
}
assert(false);
}
+
+std::pair<int64_t, int64_t> ParseRange(const UniValue &value) {
+ if (value.isNum()) {
+ return {0, value.get_int64()};
+ }
+ if (value.isArray() && value.size() == 2 && value[0].isNum() &&
+ value[1].isNum()) {
+ int64_t low = value[0].get_int64();
+ int64_t high = value[1].get_int64();
+ if (low > high)
+ throw JSONRPCError(
+ RPC_INVALID_PARAMETER,
+ "Range specified as [begin,end] must not have begin after end");
+ return {low, high};
+ }
+ throw JSONRPCError(RPC_INVALID_PARAMETER,
+ "Range must be specified as end or as [begin,end]");
+}
diff --git a/src/rpc/util.h b/src/rpc/util.h
index 1f5cfc594..7d4323ec6 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -1,252 +1,255 @@
// 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.
#ifndef BITCOIN_RPC_UTIL_H
#define BITCOIN_RPC_UTIL_H
#include <node/transaction.h>
#include <outputtype.h>
#include <rpc/protocol.h>
#include <rpc/request.h>
#include <script/standard.h> // For CTxDestination
#include <univalue.h>
#include <util/check.h>
#include <boost/variant.hpp>
#include <string>
#include <vector>
class CChainParams;
class FillableSigningProvider;
class CPubKey;
class CScript;
class UniValue;
/**
* Wrapper for UniValue::VType, which includes typeAny: used to denote don't
* care type.
*/
struct UniValueType {
UniValueType(UniValue::VType _type) : typeAny(false), type(_type) {}
UniValueType() : typeAny(true) {}
bool typeAny;
UniValue::VType type;
};
/**
* Type-check arguments; throws JSONRPCError if wrong type given. Does not check
* that the right number of arguments are passed, just that any passed are the
* correct type.
*/
void RPCTypeCheck(const UniValue &params,
const std::list<UniValueType> &typesExpected,
bool fAllowNull = false);
/**
* Type-check one argument; throws JSONRPCError if wrong type given.
*/
void RPCTypeCheckArgument(const UniValue &value,
const UniValueType &typeExpected);
/**
* Check for expected keys/value types in an Object.
*/
void RPCTypeCheckObj(const UniValue &o,
const std::map<std::string, UniValueType> &typesExpected,
bool fAllowNull = false, bool fStrict = false);
/**
* Utilities: convert hex-encoded values (throws error if not hex).
*/
extern uint256 ParseHashV(const UniValue &v, std::string strName);
extern uint256 ParseHashO(const UniValue &o, std::string strKey);
extern std::vector<uint8_t> ParseHexV(const UniValue &v, std::string strName);
extern std::vector<uint8_t> ParseHexO(const UniValue &o, std::string strKey);
extern Amount AmountFromValue(const UniValue &value);
extern std::string HelpExampleCli(const std::string &methodname,
const std::string &args);
extern std::string HelpExampleRpc(const std::string &methodname,
const std::string &args);
CPubKey HexToPubKey(const std::string &hex_in);
CPubKey AddrToPubKey(const CChainParams &chainparams,
FillableSigningProvider *const keystore,
const std::string &addr_in);
CTxDestination AddAndGetMultisigDestination(const int required,
const std::vector<CPubKey> &pubkeys,
OutputType type,
FillableSigningProvider &keystore,
CScript &script_out);
UniValue DescribeAddress(const CTxDestination &dest);
RPCErrorCode RPCErrorFromTransactionError(TransactionError terr);
UniValue JSONRPCTransactionError(TransactionError terr,
const std::string &err_string = "");
+//! Parse a JSON range specified as int64, or [int64, int64]
+std::pair<int64_t, int64_t> ParseRange(const UniValue &value);
+
struct RPCArg {
enum class Type {
OBJ,
ARR,
STR,
NUM,
BOOL,
//! 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
OBJ_USER_KEYS,
//! Special type representing a floating point amount (can be either NUM
//! or STR)
AMOUNT,
//! Special type that is a STR with only hex chars
STR_HEX,
};
enum class Optional {
/** Required arg */
NO,
/**
* 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,
/**
* Optional argument with default value omitted because they are
* implicitly clear. That is, elements in an array or object may not
* exist by default.
* When possible, the default value should be specified.
*/
OMITTED,
};
using Fallback =
boost::variant<Optional,
/* default value for optional args */ std::string>;
//! The name of the arg (can be empty for inner args)
const std::string m_name;
const Type m_type;
//! Only used for arrays or dicts
const std::vector<RPCArg> m_inner;
const Fallback m_fallback;
const std::string m_description;
//! Should be empty unless it is supposed to override the auto-generated
//! summary line
const std::string m_oneline_description;
//! Should be empty unless it is supposed to override the
//! auto-generated type strings. Vector length is either 0
//! or 2, m_type_str.at(0) will override the type of the
//! value in a key-value pair, m_type_str.at(1) will
//! override the type in the argument description.
const std::vector<std::string> m_type_str;
RPCArg(const std::string &name, const Type &type, const Fallback &fallback,
const std::string &description,
const std::string &oneline_description = "",
const std::vector<std::string> &type_str = {})
: m_name{name}, m_type{type}, m_fallback{fallback},
m_description{description},
m_oneline_description{oneline_description}, m_type_str{type_str} {
CHECK_NONFATAL(type != Type::ARR && type != Type::OBJ);
}
RPCArg(const std::string &name, const Type &type, const Fallback &fallback,
const std::string &description, const std::vector<RPCArg> &inner,
const std::string &oneline_description = "",
const std::vector<std::string> &type_str = {})
: m_name{name}, m_type{type}, m_inner{inner}, m_fallback{fallback},
m_description{description},
m_oneline_description{oneline_description}, m_type_str{type_str} {
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
* (m_oneline_description).
*/
std::string ToString(bool oneline) const;
/**
* Return the type string of the argument when it is in an object (dict).
* Set oneline to get the oneline representation (less whitespace)
*/
std::string ToStringObj(bool oneline) const;
/**
* Return the description string, including the argument type and whether
* the argument is required.
*/
std::string ToDescriptionString() const;
};
struct RPCResult {
const std::string m_cond;
const std::string m_result;
explicit RPCResult(std::string result)
: m_cond{}, m_result{std::move(result)} {
CHECK_NONFATAL(!m_result.empty());
}
RPCResult(std::string cond, std::string result)
: m_cond{std::move(cond)}, m_result{std::move(result)} {
CHECK_NONFATAL(!m_cond.empty());
CHECK_NONFATAL(!m_result.empty());
}
};
struct RPCResults {
const std::vector<RPCResult> m_results;
RPCResults() : m_results{} {}
RPCResults(RPCResult result) : m_results{{result}} {}
RPCResults(std::initializer_list<RPCResult> results) : m_results{results} {}
/**
* Return the description string.
*/
std::string ToDescriptionString() const;
};
struct RPCExamples {
const std::string m_examples;
explicit RPCExamples(std::string examples)
: m_examples(std::move(examples)) {}
RPCExamples() : m_examples(std::move("")) {}
std::string ToDescriptionString() const;
};
class RPCHelpMan {
public:
RPCHelpMan(std::string name, std::string description,
std::vector<RPCArg> args, RPCResults results,
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;
/**
* Check if the given request is valid according to this command or if
* the user is asking for help information, and throw help when appropriate.
*/
inline void Check(const JSONRPCRequest &request) const {
if (request.fHelp || !IsValidNumArgs(request.params.size())) {
throw std::runtime_error(ToString());
}
}
private:
const std::string m_name;
const std::string m_description;
const std::vector<RPCArg> m_args;
const RPCResults m_results;
const RPCExamples m_examples;
};
#endif // BITCOIN_RPC_UTIL_H

File Metadata

Mime Type
text/x-diff
Expires
Sun, Apr 27, 11:20 (1 d, 11 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5573393
Default Alt Text
(32 KB)

Event Timeline