Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
501PushSection({indent_next + "...", ""});502PushSection({indent + "]" + (is_top_level_arg ? "" : ","), ""});503break;504}505} // no default case, so the compiler can warn about missing cases506}508/**509* Concatenate all sections with proper padding510*/511std::string ToString() const512{513std::string ret;514const size_t pad = m_max_pad + 4;515for (const auto& s : m_sections) {516// The left part of a section is assumed to be a single line, usually it is the name of the JSON struct or a517// brace like {, }, [, or ]518CHECK_NONFATAL(s.m_left.find('\n') == std::string::npos);519if (s.m_right.empty()) {520ret += s.m_left;521ret += "\n";522continue;523}525std::string left = s.m_left;526left.resize(pad, ' ');527ret += left;529// Properly pad after newlines530std::string right;531size_t begin = 0;532size_t new_line_pos = s.m_right.find_first_of('\n');533while (true) {534right += s.m_right.substr(begin, new_line_pos - begin);535if (new_line_pos == std::string::npos) {536break; //No new line537}538right += "\n" + std::string(pad, ' ');539begin = s.m_right.find_first_not_of(' ', new_line_pos + 1);540if (begin == std::string::npos) {541break; // Empty line542}543new_line_pos = s.m_right.find_first_of('\n', begin + 1);544}545ret += right;546ret += "\n";547}548return ret;549}550};552RPCMethod::RPCMethod(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples)553: RPCMethod{std::move(name), std::move(description), std::move(args), std::move(results), std::move(examples), nullptr} {}555RPCMethod::RPCMethod(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples, RPCMethodImpl fun)556: m_name{std::move(name)},557m_fun{std::move(fun)},558m_description{std::move(description)},559m_args{std::move(args)},560m_results{std::move(results)},561m_examples{std::move(examples)}562{563// Map of parameter names and types just used to check whether the names are564// unique. Parameter names always need to be unique, with the exception that565// there can be pairs of POSITIONAL and NAMED parameters with the same name.566enum ParamType { POSITIONAL = 1, NAMED = 2, NAMED_ONLY = 4 };567std::map<std::string, int> param_names;569for (const auto& arg : m_args) {570std::vector<std::string> names = SplitString(arg.m_names, '|');571// Should have unique named arguments572for (const std::string& name : names) {573auto& param_type = param_names[name];574CHECK_NONFATAL(!(param_type & POSITIONAL));575CHECK_NONFATAL(!(param_type & NAMED_ONLY));576param_type |= POSITIONAL;577}578if (arg.m_type == RPCArg::Type::OBJ_NAMED_PARAMS) {579for (const auto& inner : arg.m_inner) {580std::vector<std::string> inner_names = SplitString(inner.m_names, '|');581for (const std::string& inner_name : inner_names) {582auto& param_type = param_names[inner_name];583CHECK_NONFATAL(!(param_type & POSITIONAL) || inner.m_opts.also_positional);584CHECK_NONFATAL(!(param_type & NAMED));585CHECK_NONFATAL(!(param_type & NAMED_ONLY));586param_type |= inner.m_opts.also_positional ? NAMED : NAMED_ONLY;587}588}589}590// Default value type should match argument type only when defined591if (arg.m_fallback.index() == 2) {592const RPCArg::Type type = arg.m_type;593[&]() {594switch (std::get<RPCArg::Default>(arg.m_fallback).getType()) {595case UniValue::VOBJ:596CHECK_NONFATAL(type == RPCArg::Type::OBJ);597return;598case UniValue::VARR:599CHECK_NONFATAL(type == RPCArg::Type::ARR);600return;601case UniValue::VSTR:602CHECK_NONFATAL(type == RPCArg::Type::STR || type == RPCArg::Type::STR_HEX || type == RPCArg::Type::AMOUNT);603return;604case UniValue::VNUM:605CHECK_NONFATAL(type == RPCArg::Type::NUM || type == RPCArg::Type::AMOUNT || type == RPCArg::Type::RANGE);606return;607case UniValue::VBOOL:608CHECK_NONFATAL(type == RPCArg::Type::BOOL);609return;610case UniValue::VNULL:611// Null values are accepted in all arguments612return;613} // no default case, so the compiler can warn about missing cases614NONFATAL_UNREACHABLE();615}();616}617}618}620std::string RPCResults::ToDescriptionString() const621{622std::string result;623for (const auto& r : m_results) {624Sections sections;625r.ToSections(sections);626// A result can be empty via HelpElisionSkip627if (sections.m_sections.empty()) continue;629if (r.m_cond.empty()) {630result += "\nResult:\n";631} else {632result += "\nResult (" + r.m_cond + "):\n";633}634result += sections.ToString();635}636return result;637}639std::string RPCExamples::ToDescriptionString() const640{641return m_examples.empty() ? m_examples : "\nExamples:\n" + m_examples;642}644UniValue RPCMethod::HandleRequest(const JSONRPCRequest& request) const645{646if (request.mode == JSONRPCRequest::GET_ARGS) {647return GetArgMap();648}649/*650* Check if the given request is valid according to this command or if651* the user is asking for help information, and throw help when appropriate.652*/653if (request.mode == JSONRPCRequest::GET_HELP || !IsValidNumArgs(request.params.size())) {654throw HelpResult{ToString()};655}656UniValue arg_mismatch{UniValue::VOBJ};657for (size_t i{0}; i < m_args.size(); ++i) {658const auto& arg{m_args.at(i)};659UniValue match{arg.MatchesType(request.params[i])};660if (!match.isTrue()) {661arg_mismatch.pushKV(strprintf("Position %s (%s)", i + 1, arg.m_names), std::move(match));662}663}664if (!arg_mismatch.empty()) {665throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Wrong type passed:\n%s", arg_mismatch.write(4)));666}667CHECK_NONFATAL(m_req == nullptr);668m_req = &request;669UniValue ret = m_fun(*this, request);670m_req = nullptr;671if (gArgs.GetBoolArg("-rpcdoccheck", DEFAULT_RPC_DOC_CHECK)) {672UniValue mismatch{UniValue::VARR};673for (const auto& res : m_results.m_results) {674UniValue match{res.MatchesType(ret)};675if (match.isTrue()) {676mismatch.setNull();677break;678}679mismatch.push_back(std::move(match));680}681if (!mismatch.isNull()) {682std::string explain{683mismatch.empty() ? "no possible results defined" :684mismatch.size() == 1 ? mismatch[0].write(4) :685mismatch.write(4)};686throw std::runtime_error{687STR_INTERNAL_BUG(strprintf("RPC call \"%s\" returned incorrect type:\n%s", m_name, explain)),688};689}690}691return ret;692}694using CheckFn = void(const RPCArg&);695static const UniValue* DetailMaybeArg(CheckFn* check, const std::vector<RPCArg>& params, const JSONRPCRequest* req, size_t i)696{697CHECK_NONFATAL(i < params.size());698const UniValue& arg{CHECK_NONFATAL(req)->params[i]};699const RPCArg& param{params.at(i)};700if (check) check(param);702if (!arg.isNull()) return &arg;703if (!std::holds_alternative<RPCArg::Default>(param.m_fallback)) return nullptr;704return &std::get<RPCArg::Default>(param.m_fallback);705}707static void CheckRequiredOrDefault(const RPCArg& param)708{709// Must use `Arg<Type>(key)` to get the argument or its default value.710const bool required{711std::holds_alternative<RPCArg::Optional>(param.m_fallback) && RPCArg::Optional::NO == std::get<RPCArg::Optional>(param.m_fallback),712};713CHECK_NONFATAL(required || std::holds_alternative<RPCArg::Default>(param.m_fallback));714}716#define TMPL_INST(check_param, ret_type, return_code) \717template <> \718ret_type RPCMethod::ArgValue<ret_type>(size_t i) const \719{ \720const UniValue* maybe_arg{ \721DetailMaybeArg(check_param, m_args, m_req, i), \722}; \723return return_code \724} \725void force_semicolon(ret_type)727// Optional arg (without default). Can also be called on required args, if needed.728TMPL_INST(nullptr, const UniValue*, maybe_arg;);729TMPL_INST(nullptr, std::optional<double>, maybe_arg ? std::optional{maybe_arg->get_real()} : std::nullopt;);730TMPL_INST(nullptr, std::optional<bool>, maybe_arg ? std::optional{maybe_arg->get_bool()} : std::nullopt;);731TMPL_INST(nullptr, std::optional<int64_t>, maybe_arg ? std::optional{maybe_arg->getInt<int64_t>()} : std::nullopt;);732TMPL_INST(nullptr, std::optional<std::string_view>, maybe_arg ? std::optional<std::string_view>{maybe_arg->get_str()} : std::nullopt;);734// Required arg or optional arg with default value.735TMPL_INST(CheckRequiredOrDefault, const UniValue&, *CHECK_NONFATAL(maybe_arg););736TMPL_INST(CheckRequiredOrDefault, bool, CHECK_NONFATAL(maybe_arg)->get_bool(););737TMPL_INST(CheckRequiredOrDefault, int, CHECK_NONFATAL(maybe_arg)->getInt<int>(););738TMPL_INST(CheckRequiredOrDefault, uint64_t, CHECK_NONFATAL(maybe_arg)->getInt<uint64_t>(););739TMPL_INST(CheckRequiredOrDefault, uint32_t, CHECK_NONFATAL(maybe_arg)->getInt<uint32_t>(););740TMPL_INST(CheckRequiredOrDefault, std::string_view, CHECK_NONFATAL(maybe_arg)->get_str(););742bool RPCMethod::IsValidNumArgs(size_t num_args) const743{744size_t num_required_args = 0;745for (size_t n = m_args.size(); n > 0; --n) {746if (!m_args.at(n - 1).IsOptional()) {747num_required_args = n;748break;749}750}