Inside Bitcoin
Opening the selected file…
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.
751const std::vector<std::string>& keys = in.params.getKeys();752const std::vector<UniValue>& values = in.params.getValues();753std::unordered_map<std::string, const UniValue*> argsIn;754for (size_t i=0; i<keys.size(); ++i) {755auto [_, inserted] = argsIn.emplace(keys[i], &values[i]);756if (!inserted) {757throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter " + keys[i] + " specified multiple times");758}759}760// Process expected parameters. If any parameters were left unspecified in761// the request before a parameter that was specified, null values need to be762// inserted at the unspecified parameter positions, and the "hole" variable763// below tracks the number of null values that need to be inserted.764// The "initial_hole_size" variable stores the size of the initial hole,765// i.e. how many initial positional arguments were left unspecified. This is766// used after the for-loop to add initial positional arguments from the767// "args" parameter, if present.768int hole = 0;769int initial_hole_size = 0;770const std::string* initial_param = nullptr;771UniValue options{UniValue::VOBJ};772for (const auto& [argNamePattern, named_only]: argNames) {773std::vector<std::string> vargNames = SplitString(argNamePattern, '|');774auto fr = argsIn.end();775for (const std::string & argName : vargNames) {776fr = argsIn.find(argName);777if (fr != argsIn.end()) {778break;779}780}782// Handle named-only parameters by pushing them into a temporary options783// object, and then pushing the accumulated options as the next784// positional argument.785if (named_only) {786if (fr != argsIn.end()) {787if (options.exists(fr->first)) {788throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter " + fr->first + " specified multiple times");789}790options.pushKVEnd(fr->first, *fr->second);791argsIn.erase(fr);792}793continue;794}796if (!options.empty() || fr != argsIn.end()) {797for (int i = 0; i < hole; ++i) {798// Fill hole between specified parameters with JSON nulls,799// but not at the end (for backwards compatibility with calls800// that act based on number of specified parameters).801out.params.push_back(UniValue());802}803hole = 0;804if (!initial_param) initial_param = &argNamePattern;805} else {806hole += 1;807if (out.params.empty()) initial_hole_size = hole;808}810// If named input parameter "fr" is present, push it onto out.params. If811// options are present, push them onto out.params. If both are present,812// throw an error.813if (fr != argsIn.end()) {814if (!options.empty()) {815throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter " + fr->first + " conflicts with parameter " + options.getKeys().front());816}817out.params.push_back(*fr->second);818argsIn.erase(fr);819}820if (!options.empty()) {821out.params.push_back(std::move(options));822options = UniValue{UniValue::VOBJ};823}824}825// If leftover "args" param was found, use it as a source of positional826// arguments and add named arguments after. This is a convenience for827// clients that want to pass a combination of named and positional828// arguments as described in doc/JSON-RPC-interface.md#parameter-passing829auto positional_args{argsIn.extract("args")};830if (positional_args && positional_args.mapped()->isArray()) {831if (initial_hole_size < (int)positional_args.mapped()->size() && initial_param) {832throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter " + *initial_param + " specified twice both as positional and named argument");833}834// Assign positional_args to out.params and append named_args after.835UniValue named_args{std::move(out.params)};836out.params = *positional_args.mapped();837for (size_t i{out.params.size()}; i < named_args.size(); ++i) {838out.params.push_back(named_args[i]);839}840}841// If there are still arguments in the argsIn map, this is an error.842if (!argsIn.empty()) {843throw JSONRPCError(RPC_INVALID_PARAMETER, "Unknown named parameter " + argsIn.begin()->first);844}845// Return request with named arguments transformed to positional arguments846return out;847}849static bool ExecuteCommands(const std::vector<const CRPCCommand*>& commands, const JSONRPCRequest& request, UniValue& result)850{851for (const auto& command : commands) {852if (ExecuteCommand(*command, request, result, &command == &commands.back())) {853return true;854}855}856return false;857}859UniValue CRPCTable::execute(const JSONRPCRequest &request) const860{861// Return immediately if in warmup862{863LOCK(g_rpc_warmup_mutex);864if (fRPCInWarmup)865throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus);866}868// Find method869auto it = mapCommands.find(request.strMethod);870if (it != mapCommands.end()) {871UniValue result;872if (ExecuteCommands(it->second, request, result)) {873return result;874}875}876throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found");877}879static bool ExecuteCommand(const CRPCCommand& command, const JSONRPCRequest& request, UniValue& result, bool last_handler)880{881try {882RPCCommandExecution execution(request.strMethod);883// Execute, convert arguments to array if necessary884if (request.params.isObject()) {885return command.actor(transformNamedArguments(request, command.argNames), result, last_handler);886} else {887return command.actor(request, result, last_handler);888}889} catch (const UniValue::type_error& e) {890throw JSONRPCError(RPC_TYPE_ERROR, e.what());891} catch (const std::exception& e) {892throw JSONRPCError(RPC_MISC_ERROR, e.what());893}894}896std::vector<std::string> CRPCTable::listCommands() const897{898std::vector<std::string> commandList;899commandList.reserve(mapCommands.size());900for (const auto& i : mapCommands) commandList.emplace_back(i.first);901return commandList;902}904UniValue CRPCTable::buildOpenRPCDoc(bool include_hidden) const905{906std::vector<std::string> method_names;907for (const auto& [name, cmds] : mapCommands) {908if (cmds.empty()) continue;909const CRPCCommand* cmd{cmds.front()};910if ((!include_hidden && cmd->category == "hidden") || !cmd->metadata_fn) continue;911method_names.push_back(name);912}913std::sort(method_names.begin(), method_names.end());915UniValue methods{UniValue::VARR};916for (const auto& method_name : method_names) {917const CRPCCommand* cmd{mapCommands.at(method_name).front()};918RPCMethod helpman{cmd->metadata_fn()};920UniValue params{UniValue::VARR};921for (const auto& arg : helpman.GetArgs()) {922if (!include_hidden && arg.m_opts.hidden) continue;923UniValue param{UniValue::VOBJ};924param.pushKV("name", arg.GetFirstName());925param.pushKV("required", !arg.IsOptional());926param.pushKV("schema", OpenRPCArgSchema(arg, include_hidden, /*in_skip_type_check=*/false));928std::vector<std::string> names{SplitString(arg.m_names, '|')};929if (names.size() > 1) {930UniValue aliases{UniValue::VARR};931for (size_t i{1}; i < names.size(); ++i) aliases.push_back(names[i]);932param.pushKV("x-bitcoin-aliases", std::move(aliases));933}934if (arg.m_opts.placeholder) param.pushKV("x-bitcoin-placeholder", true);935if (arg.m_opts.also_positional) param.pushKV("x-bitcoin-also-positional", true);936if (!arg.m_description.empty()) param.pushKV("description", arg.m_description);937params.push_back(std::move(param));938}940UniValue result_schema{UniValue::VOBJ};941const auto& results{helpman.GetResults().m_results};942if (results.size() == 1 && results[0].m_type != RPCResult::Type::ANY) {943result_schema = OpenRPCResultSchema(results[0]);944} else if (results.size() > 1) {945UniValue one_of{UniValue::VARR};946for (const auto& r : results) {947if (r.m_type == RPCResult::Type::ANY) continue;948UniValue schema{OpenRPCResultSchema(r)};949if (!r.m_cond.empty()) schema.pushKV("description", r.m_cond);950one_of.push_back(std::move(schema));951}952if (one_of.size() == 1) {953result_schema = one_of[0];954} else if (one_of.size() > 1) {955result_schema.pushKV("oneOf", std::move(one_of));956}957}959UniValue method{UniValue::VOBJ};960method.pushKV("name", method_name);961method.pushKV("description", util::TrimString(helpman.GetDescription()));962method.pushKV("params", std::move(params));963UniValue result{UniValue::VOBJ};964result.pushKV("name", "result");965result.pushKV("schema", std::move(result_schema));966method.pushKV("result", std::move(result));967method.pushKV("x-bitcoin-category", cmd->category);968methods.push_back(std::move(method));969}971std::string version{"v" CLIENT_VERSION_STRING};972if (!CLIENT_VERSION_IS_RELEASE) version += "-dev";974UniValue info{UniValue::VOBJ};975info.pushKV("title", CLIENT_NAME " JSON-RPC");976info.pushKV("version", version);977info.pushKV("description", "Autogenerated from " CLIENT_NAME " RPC metadata.");979UniValue doc{UniValue::VOBJ};980doc.pushKV("openrpc", "1.4.1");981doc.pushKV("info", std::move(info));982doc.pushKV("methods", std::move(methods));983return doc;984}986UniValue CRPCTable::dumpArgMap(const JSONRPCRequest& args_request) const987{988JSONRPCRequest request = args_request;989request.mode = JSONRPCRequest::GET_ARGS;991UniValue ret{UniValue::VARR};992for (const auto& cmd : mapCommands) {993UniValue result;994if (ExecuteCommands(cmd.second, request, result)) {995for (const auto& values : result.getValues()) {996ret.push_back(values);997}998}999}1000return ret;