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.
1// Copyright (c) 2010 Satoshi Nakamoto2// Copyright (c) 2009-present The Bitcoin Core developers3// Distributed under the MIT software license, see the accompanying4// file COPYING or http://www.opensource.org/licenses/mit-license.php.6#include <rpc/request.h>8#include <common/args.h>9#include <crypto/hex_base.h>10#include <logging.h>11#include <random.h>12#include <rpc/protocol.h>13#include <util/fs.h>14#include <util/fs_helpers.h>15#include <util/strencodings.h>17#include <cstddef>18#include <fstream>19#include <span>20#include <stdexcept>21#include <string>22#include <system_error>23#include <utility>24#include <vector>26/**27* JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,28* but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were29* unspecified (HTTP errors and contents of 'error').30*31* 1.0 spec: https://www.jsonrpc.org/specification_v132* 1.2 spec: https://jsonrpc.org/historical/json-rpc-over-http.html33*34* If the server receives a request with the JSON-RPC 2.0 marker `{"jsonrpc": "2.0"}`35* then Bitcoin will respond with a strictly specified response.36* It will only return an HTTP error code if an actual HTTP error is encountered37* such as the endpoint is not found (404) or the request is not formatted correctly (500).38* Otherwise the HTTP code is always OK (200) and RPC errors will be included in the39* response body.40*41* 2.0 spec: https://www.jsonrpc.org/specification42*43* Also see https://www.simple-is-better.org/rpc/#differences-between-1-0-and-2-044*/46UniValue JSONRPCRequestObj(const std::string& strMethod, const UniValue& params, const UniValue& id)47{48UniValue request(UniValue::VOBJ);49request.pushKV("method", strMethod);50request.pushKV("params", params);51request.pushKV("id", id);52request.pushKV("jsonrpc", "2.0");53return request;54}56UniValue JSONRPCReplyObj(UniValue result, UniValue error, std::optional<UniValue> id, JSONRPCVersion jsonrpc_version)57{58UniValue reply(UniValue::VOBJ);59// Add JSON-RPC version number field in v2 only.60if (jsonrpc_version == JSONRPCVersion::V2) reply.pushKV("jsonrpc", "2.0");62// Add both result and error fields in v1, even though one will be null.63// Omit the null field in v2.64if (error.isNull()) {65reply.pushKV("result", std::move(result));66if (jsonrpc_version == JSONRPCVersion::V1_LEGACY) reply.pushKV("error", NullUniValue);67} else {68if (jsonrpc_version == JSONRPCVersion::V1_LEGACY) reply.pushKV("result", NullUniValue);69reply.pushKV("error", std::move(error));70}71if (id.has_value()) reply.pushKV("id", std::move(id.value()));72return reply;73}75UniValue JSONRPCError(int code, const std::string& message)76{77UniValue error(UniValue::VOBJ);78error.pushKV("code", code);79error.pushKV("message", message);80return error;81}83/** Username used when cookie authentication is in use (arbitrary, only for84* recognizability in debugging/logging purposes)85*/86static const std::string COOKIEAUTH_USER = "__cookie__";87/** Default name for auth cookie file */88static const char* const COOKIEAUTH_FILE = ".cookie";90/** Get name of RPC authentication cookie file */91static fs::path GetAuthCookieFile(bool temp=false)92{93fs::path arg = gArgs.GetPathArg("-rpccookiefile", COOKIEAUTH_FILE);94if (arg.empty()) {95return {}; // -norpccookiefile was specified96}97if (temp) {98arg += ".tmp";99}100return AbsPathForConfigVal(gArgs, arg);101}103static bool g_generated_cookie = false;105AuthCookieResult GenerateAuthCookie(const std::optional<fs::perms>& cookie_perms,106std::string& user,107std::string& pass)108{109const size_t COOKIE_SIZE = 32;110unsigned char rand_pwd[COOKIE_SIZE];111GetRandBytes(rand_pwd);112const std::string rand_pwd_hex{HexStr(rand_pwd)};114/** the umask determines what permissions are used to create this file -115* these are set to 0077 in common/system.cpp.116*/117std::ofstream file;118fs::path filepath_tmp = GetAuthCookieFile(true);119if (filepath_tmp.empty()) {120return AuthCookieResult::Disabled; // -norpccookiefile121}122file.open(filepath_tmp.std_path());123if (!file.is_open()) {124LogWarning("Unable to open cookie authentication file %s for writing", fs::PathToString(filepath_tmp));125return AuthCookieResult::Error;126}127file << COOKIEAUTH_USER << ":" << rand_pwd_hex;128file.close();130fs::path filepath = GetAuthCookieFile(false);131if (!RenameOver(filepath_tmp, filepath)) {132LogWarning("Unable to rename cookie authentication file %s to %s", fs::PathToString(filepath_tmp), fs::PathToString(filepath));133return AuthCookieResult::Error;134}135if (cookie_perms) {136std::error_code code;137fs::permissions(filepath, cookie_perms.value(), fs::perm_options::replace, code);138if (code) {139LogWarning("Unable to set permissions on cookie authentication file %s", fs::PathToString(filepath));140return AuthCookieResult::Error;141}142}144g_generated_cookie = true;145LogInfo("Generated RPC authentication cookie %s\n", fs::PathToString(filepath));146LogInfo("Permissions used for cookie: %s\n", PermsToSymbolicString(fs::status(filepath).permissions()));148user = COOKIEAUTH_USER;149pass = rand_pwd_hex;150return AuthCookieResult::Ok;151}153AuthCookieResult GetAuthCookie(std::string& cookie_out)154{155std::ifstream file;156fs::path filepath = GetAuthCookieFile();157if (filepath.empty()) {158return AuthCookieResult::Disabled; // -norpccookiefile159}160file.open(filepath.std_path());161if (!file.is_open()) {162return AuthCookieResult::Error;163}164std::getline(file, cookie_out);165file.close();166return AuthCookieResult::Ok;167}169void DeleteAuthCookie()170{171try {172if (g_generated_cookie) {173// Delete the cookie file if it was generated by this process174fs::remove(GetAuthCookieFile());175}176} catch (const fs::filesystem_error& e) {177LogWarning("Unable to remove random auth cookie file %s: %s\n", fs::PathToString(e.path1()), e.code().message());178}179}181std::vector<UniValue> JSONRPCProcessBatchReply(const UniValue& in)182{183if (!in.isArray()) {184throw std::runtime_error("Batch must be an array");185}186const size_t num {in.size()};187std::vector<UniValue> batch(num);188for (const UniValue& rec : in.getValues()) {189if (!rec.isObject()) {190throw std::runtime_error("Batch member must be an object");191}192size_t id = rec["id"].getInt<int>();193if (id >= num) {194throw std::runtime_error("Batch member id is larger than batch size");195}196batch[id] = rec;197}198return batch;199}201void JSONRPCRequest::parse(const UniValue& valRequest)202{203// Parse request204if (!valRequest.isObject())205throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object");206const UniValue& request = valRequest.get_obj();208// Parse id now so errors from here on will have the id209if (request.exists("id")) {210id = request.find_value("id");211} else {212id = std::nullopt;213}215// Check for JSON-RPC 2.0 (default 1.1)216m_json_version = JSONRPCVersion::V1_LEGACY;217const UniValue& jsonrpc_version = request.find_value("jsonrpc");218if (!jsonrpc_version.isNull()) {219if (!jsonrpc_version.isStr()) {220throw JSONRPCError(RPC_INVALID_REQUEST, "jsonrpc field must be a string");221}222// The "jsonrpc" key was added in the 2.0 spec, but some older documentation223// incorrectly included {"jsonrpc":"1.0"} in a request object, so we224// maintain that for backwards compatibility.225if (jsonrpc_version.get_str() == "1.0") {226m_json_version = JSONRPCVersion::V1_LEGACY;227} else if (jsonrpc_version.get_str() == "2.0") {228m_json_version = JSONRPCVersion::V2;229} else {230throw JSONRPCError(RPC_INVALID_REQUEST, "JSON-RPC version not supported");231}232}234// Parse method235const UniValue& valMethod{request.find_value("method")};236if (valMethod.isNull())237throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method");238if (!valMethod.isStr())239throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string");240strMethod = valMethod.get_str();241const std::string log_id{id && !id->isNull() ? SanitizeString(id->getValStr()) : ""};242if (fLogIPs)243LogDebug(BCLog::RPC, "ThreadRPCServer method=%s user=%s peeraddr=%s id=%s", SanitizeString(strMethod),244this->authUser, this->peerAddr, log_id);245else246LogDebug(BCLog::RPC, "ThreadRPCServer method=%s user=%s id=%s", SanitizeString(strMethod), this->authUser,247log_id);249// Parse params250const UniValue& valParams{request.find_value("params")};