#ifndef BITCOIN_RPC_UTIL_H
#define BITCOIN_RPC_UTIL_H
#include <addresstype.h>
#include <consensus/amount.h>
#include <node/transaction.h>
#include <outputtype.h>
#include <pubkey.h>
#include <rpc/protocol.h>
#include <rpc/request.h>
#include <script/script.h>
#include <script/sign.h>
#include <uint256.h>
#include <univalue.h>
#include <util/check.h>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <initializer_list>
#include <map>
#include <optional>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>
#include <variant>
#include <vector>
class JSONRPCRequest;
enum ServiceFlags : uint64_t;
enum class OutputType;
struct FlatSigningProvider;
struct bilingual_str;
namespace common {
enum class PSBTError;
} namespace node {
enum class TransactionError;
}
static constexpr bool DEFAULT_RPC_DOC_CHECK{
#ifdef RPC_DOC_CHECK
true
#else
false
#endif
};
extern const std::string UNIX_EPOCH_TIME;
extern const std::string EXAMPLE_ADDRESS[2];
class FillableSigningProvider;
class CScript;
struct Sections;
struct HelpResult : std::runtime_error {
explicit HelpResult(const std::string& msg) : std::runtime_error{msg} {}
};
std::string GetAllOutputTypes();
struct UniValueType {
UniValueType(UniValue::VType _type) : typeAny(false), type(_type) {}
UniValueType() : typeAny(true) {}
bool typeAny;
UniValue::VType type;
};
void RPCTypeCheckObj(const UniValue& o,
const std::map<std::string, UniValueType>& typesExpected,
bool fAllowNull = false,
bool fStrict = false);
uint256 ParseHashV(const UniValue& v, std::string_view name);
uint256 ParseHashO(const UniValue& o, std::string_view strKey);
std::vector<unsigned char> ParseHexV(const UniValue& v, std::string_view name);
std::vector<unsigned char> ParseHexO(const UniValue& o, std::string_view strKey);
int ParseVerbosity(const UniValue& arg, int default_verbosity, bool allow_bool);
CAmount AmountFromValue(const UniValue& value, int decimals = 8);
CFeeRate ParseFeeRate(const UniValue& json);
using RPCArgList = std::vector<std::pair<std::string, UniValue>>;
std::string HelpExampleCli(const std::string& methodname, const std::string& args);
std::string HelpExampleCliNamed(const std::string& methodname, const RPCArgList& args);
std::string HelpExampleRpc(const std::string& methodname, const std::string& args);
std::string HelpExampleRpcNamed(const std::string& methodname, const RPCArgList& args);
CPubKey HexToPubKey(const std::string& hex_in);
CTxDestination AddAndGetMultisigDestination(const int required, const std::vector<CPubKey>& pubkeys, OutputType type, FlatSigningProvider& keystore, CScript& script_out);
UniValue DescribeAddress(const CTxDestination& dest);
std::optional<int> ParseSighashString(const UniValue& sighash);
unsigned int ParseConfirmTarget(const UniValue& value, unsigned int max_target);
RPCErrorCode RPCErrorFromTransactionError(node::TransactionError terr);
UniValue JSONRPCPSBTError(common::PSBTError err);
UniValue JSONRPCTransactionError(node::TransactionError terr, const std::string& err_string = "");
std::pair<int64_t, int64_t> ParseDescriptorRange(const UniValue& value);
std::vector<CScript> EvalDescriptorStringOrObject(const UniValue& scanobject, FlatSigningProvider& provider, const bool expand_priv = false);
enum class OuterType {
ARR,
OBJ,
NONE, };
struct RPCArgOptions {
bool skip_type_check{false};
std::string oneline_description{}; std::vector<std::string> type_str{}; bool hidden{false}; bool also_positional{false}; };
struct RPCArg {
enum class Type {
OBJ,
ARR,
STR,
NUM,
BOOL,
OBJ_NAMED_PARAMS, OBJ_USER_KEYS, AMOUNT, STR_HEX, RANGE, };
enum class Optional {
NO,
OMITTED,
};
using DefaultHint = std::string;
using Default = UniValue;
using Fallback = std::variant<Optional, DefaultHint, Default>;
const std::string m_names; const Type m_type;
const std::vector<RPCArg> m_inner; const Fallback m_fallback;
const std::string m_description;
const RPCArgOptions m_opts;
RPCArg(
std::string name,
Type type,
Fallback fallback,
std::string description,
RPCArgOptions opts = {})
: m_names{std::move(name)},
m_type{std::move(type)},
m_fallback{std::move(fallback)},
m_description{std::move(description)},
m_opts{std::move(opts)}
{
CHECK_NONFATAL(type != Type::ARR && type != Type::OBJ && type != Type::OBJ_NAMED_PARAMS && type != Type::OBJ_USER_KEYS);
}
RPCArg(
std::string name,
Type type,
Fallback fallback,
std::string description,
std::vector<RPCArg> inner,
RPCArgOptions opts = {})
: m_names{std::move(name)},
m_type{std::move(type)},
m_inner{std::move(inner)},
m_fallback{std::move(fallback)},
m_description{std::move(description)},
m_opts{std::move(opts)}
{
CHECK_NONFATAL(type == Type::ARR || type == Type::OBJ || type == Type::OBJ_NAMED_PARAMS || type == Type::OBJ_USER_KEYS);
}
bool IsOptional() const;
UniValue MatchesType(const UniValue& request) const;
std::string GetFirstName() const;
std::string GetName() const;
std::string ToString(bool oneline) const;
std::string ToStringObj(bool oneline) const;
std::string ToDescriptionString(bool is_named_arg) const;
};
struct RPCResult {
enum class Type {
OBJ,
ARR,
STR,
NUM,
BOOL,
NONE,
ANY, STR_AMOUNT, STR_HEX, OBJ_DYN, ARR_FIXED, NUM_TIME, ELISION, };
const Type m_type;
const std::string m_key_name; const std::vector<RPCResult> m_inner; const bool m_optional;
const bool m_skip_type_check;
const std::string m_description;
const std::string m_cond;
RPCResult(
std::string cond,
Type type,
std::string m_key_name,
bool optional,
std::string description,
std::vector<RPCResult> inner = {})
: m_type{std::move(type)},
m_key_name{std::move(m_key_name)},
m_inner{std::move(inner)},
m_optional{optional},
m_skip_type_check{false},
m_description{std::move(description)},
m_cond{std::move(cond)}
{
CHECK_NONFATAL(!m_cond.empty());
CheckInnerDoc();
}
RPCResult(
std::string cond,
Type type,
std::string m_key_name,
std::string description,
std::vector<RPCResult> inner = {})
: RPCResult{std::move(cond), type, std::move(m_key_name), false, std::move(description), std::move(inner)} {}
RPCResult(
Type type,
std::string m_key_name,
bool optional,
std::string description,
std::vector<RPCResult> inner = {},
bool skip_type_check = false)
: m_type{std::move(type)},
m_key_name{std::move(m_key_name)},
m_inner{std::move(inner)},
m_optional{optional},
m_skip_type_check{skip_type_check},
m_description{std::move(description)},
m_cond{}
{
CheckInnerDoc();
}
RPCResult(
Type type,
std::string m_key_name,
std::string description,
std::vector<RPCResult> inner = {},
bool skip_type_check = false)
: RPCResult{type, std::move(m_key_name), false, std::move(description), std::move(inner), skip_type_check} {}
void ToSections(Sections& sections, OuterType outer_type = OuterType::NONE, const int current_indent = 0) const;
std::string ToStringObj() const;
std::string ToDescriptionString() const;
UniValue MatchesType(const UniValue& result) const;
private:
void CheckInnerDoc() const;
};
struct RPCResults {
const std::vector<RPCResult> m_results;
RPCResults(RPCResult result)
: m_results{{result}}
{
}
RPCResults(std::initializer_list<RPCResult> results)
: m_results{results}
{
}
std::string ToDescriptionString() const;
};
struct RPCExamples {
const std::string m_examples;
explicit RPCExamples(
std::string examples)
: m_examples(std::move(examples))
{
}
std::string ToDescriptionString() const;
};
class RPCHelpMan
{
public:
RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples);
using RPCMethodImpl = std::function<UniValue(const RPCHelpMan&, const JSONRPCRequest&)>;
RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples, RPCMethodImpl fun);
UniValue HandleRequest(const JSONRPCRequest& request) const;
template <typename R>
auto Arg(std::string_view key) const
{
auto i{GetParamIndex(key)};
if constexpr (std::is_integral_v<R> || std::is_floating_point_v<R>) {
return ArgValue<R>(i);
} else {
return ArgValue<const R&>(i);
}
}
template <typename R>
auto MaybeArg(std::string_view key) const
{
auto i{GetParamIndex(key)};
if constexpr (std::is_integral_v<R> || std::is_floating_point_v<R>) {
return ArgValue<std::optional<R>>(i);
} else {
return ArgValue<const R*>(i);
}
}
std::string ToString() const;
UniValue GetArgMap() const;
bool IsValidNumArgs(size_t num_args) const;
std::vector<std::pair<std::string, bool>> GetArgNames() const;
const std::string m_name;
private:
const RPCMethodImpl m_fun;
const std::string m_description;
const std::vector<RPCArg> m_args;
const RPCResults m_results;
const RPCExamples m_examples;
mutable const JSONRPCRequest* m_req{nullptr}; template <typename R>
R ArgValue(size_t i) const;
size_t GetParamIndex(std::string_view key) const;
};
void PushWarnings(const UniValue& warnings, UniValue& obj);
void PushWarnings(const std::vector<bilingual_str>& warnings, UniValue& obj);
std::vector<RPCResult> ScriptPubKeyDoc();
uint256 GetTarget(const CBlockIndex& blockindex, const uint256 pow_limit);
#endif