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.
751+ strprintf("> bitcoin-cli -netinfo %d outonly\n\n", NETINFO_MAX_LEVEL) +752"Full live dashboard, adjust --interval or --no-title as needed (Linux)\n"753+ strprintf("> watch --interval 1 --no-title bitcoin-cli -netinfo %d\n\n", NETINFO_MAX_LEVEL) +754"See this help\n"755"> bitcoin-cli -netinfo help\n"};756};758/** Process RPC generatetoaddress request. */759class GenerateToAddressRequestHandler : public BaseRequestHandler760{761public:762UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override763{764address_str = args.at(1);765UniValue params{RPCConvertValues("generatetoaddress", args)};766return JSONRPCRequestObj("generatetoaddress", params, 1);767}769UniValue ProcessReply(const UniValue &reply) override770{771UniValue result(UniValue::VOBJ);772result.pushKV("address", address_str);773result.pushKV("blocks", reply.get_obj()["result"]);774return JSONRPCReplyObj(std::move(result), NullUniValue, /*id=*/1, JSONRPCVersion::V2);775}776protected:777std::string address_str;778};780/** Process default single requests */781struct DefaultRequestHandler : BaseRequestHandler {782UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override783{784UniValue params;785if(gArgs.GetBoolArg("-named", DEFAULT_NAMED)) {786params = RPCConvertNamedValues(method, args);787} else {788params = RPCConvertValues(method, args);789}790UniValue id{UniValue::VSTR, gArgs.GetArg("-rpcid", DEFAULT_RPC_REQ_ID)};791return JSONRPCRequestObj(method, params, id);792}794UniValue ProcessReply(const UniValue &reply) override795{796return reply.get_obj();797}798};800static std::optional<UniValue> CallIPC(BaseRequestHandler* rh, const std::string& strMethod, const std::vector<std::string>& args, const std::string& endpoint, const std::string& username)801{802auto ipcconnect{gArgs.GetArg("-ipcconnect", "auto")};803if (ipcconnect == "0") return {}; // Do not attempt IPC if -ipcconnect is disabled.804if (gArgs.IsArgSet("-rpcconnect") && !gArgs.IsArgNegated("-rpcconnect")) {805if (ipcconnect == "auto") return {}; // Use HTTP if -ipcconnect=auto is set and -rpcconnect is enabled.806throw std::runtime_error("-rpcconnect and -ipcconnect options cannot both be enabled");807}809std::unique_ptr<interfaces::Init> local_init{interfaces::MakeBasicInit("bitcoin-cli")};810if (!local_init || !local_init->ipc()) {811if (ipcconnect == "auto") return {}; // Use HTTP if -ipcconnect=auto is set and there is no IPC support.812throw std::runtime_error("bitcoin-cli was not built with IPC support");813}815std::unique_ptr<interfaces::Init> node_init;816try {817node_init = local_init->ipc()->connectAddress(ipcconnect);818if (!node_init) return {}; // Fall back to HTTP if -ipcconnect=auto connect failed.819} catch (const std::exception& e) {820// Catch connect error if -ipcconnect=unix was specified821throw CConnectionFailed{strprintf("%s\n\n"822"Probably bitcoin-node is not running or not listening on a unix socket. Can be started with:\n\n"823" bitcoin-node -chain=%s -ipcbind=unix", e.what(), gArgs.GetChainTypeString())};824}826std::unique_ptr<interfaces::Rpc> rpc{node_init->makeRpc()};827assert(rpc);828UniValue request{rh->PrepareRequest(strMethod, args)};829UniValue reply{rpc->executeRpc(std::move(request), endpoint, username)};830return rh->ProcessReply(reply);831}833/**834* Simple synchronous HTTP client using Sock class.835*/836class HTTPClient837{838public:839static HTTPClient Connect(const std::string& host, uint16_t port, std::chrono::seconds timeout);841HTTPResponse Post(const std::string& endpoint,842std::span<const std::pair<std::string, std::string>> headers,843const std::string& body);845private:846// Signal that the peer closed the connection cleanly. Used in the read-until-close fallback.847struct RecvEOF : CConnectionFailed { using CConnectionFailed::CConnectionFailed; };849std::unique_ptr<Sock> m_socket;850std::string m_host;851std::chrono::seconds m_timeout;853HTTPClient(std::unique_ptr<Sock>&& socket, const std::string& host, std::chrono::seconds timeout)854: m_socket(std::move(socket)), m_host(host), m_timeout(timeout) {}855bool SendRequest(std::string_view request);856HTTPResponse ReadResponse();857std::optional<std::string> Recv(std::chrono::time_point<std::chrono::steady_clock> deadline);858};860HTTPClient HTTPClient::Connect(const std::string& host, uint16_t port, std::chrono::seconds timeout)861{862std::vector<CService> services = Lookup(host, port, /*fAllowLookup=*/true, /*nMaxSolutions=*/256);863if (services.empty()) {864throw CConnectionFailed(strprintf("Could not resolve host: %s", host));865}867const auto deadline{std::chrono::steady_clock::now() + timeout};868for (const CService& service : services) {869const auto time_left{std::chrono::duration_cast<std::chrono::milliseconds>(deadline - std::chrono::steady_clock::now())};870if (time_left.count() <= 0) break;872auto sock = ConnectDirectly(service, /*manual_connection=*/true, time_left);873if (sock) return HTTPClient{std::move(sock), host, timeout};874}876throw CConnectionFailed{"Could not connect to the server"};877}879HTTPResponse HTTPClient::Post(const std::string& endpoint,880std::span<const std::pair<std::string, std::string>> headers,881const std::string& body)882{883try {884// Build HTTP request885std::string request = strprintf("POST %s HTTP/1.1\r\n"886"Host: %s\r\n"887"Connection: close\r\n"888"Content-Length: %d\r\n",889endpoint, m_host, body.size());891for (const auto& [name, value] : headers) {892request += strprintf("%s: %s\r\n", name, value);893}894request += "\r\n";895request += body;897if (!SendRequest(request)) {898throw CConnectionFailed("Failed to send HTTP request");899}901return ReadResponse();902} catch (const HTTPError& e) {903throw CConnectionFailed(strprintf("HTTP error: %s", e.what()));904}905}907bool HTTPClient::SendRequest(std::string_view request)908{909const auto deadline{std::chrono::steady_clock::now() + m_timeout};911while (!request.empty()) {912Sock::Event event{0};913auto time_left = std::chrono::duration_cast<std::chrono::milliseconds>(914deadline - std::chrono::steady_clock::now());915if (time_left.count() <= 0 || !m_socket->Wait(time_left, Sock::SendEvent, &event)) {916return false;917}919if (!(event & Sock::SendEvent)) {920continue;921}923ssize_t sent = m_socket->Send(request.data(), request.size(), MSG_NOSIGNAL);924if (sent < 0) {925int err = WSAGetLastError();926if (!IOErrorIsPermanent(err)) {927std::this_thread::yield();928continue;929}930return false;931}932request.remove_prefix(sent);933}934return true;935}937HTTPResponse HTTPClient::ReadResponse()938{939HTTPResponse response;940std::string buffer;941const auto deadline{std::chrono::steady_clock::now() + m_timeout};943// Read data until we have complete headers944size_t headers_end = 0;946while (headers_end == 0) {947if (auto result{Recv(deadline)}) {948buffer.append(*result);949} else {950std::this_thread::yield();951continue;952}954// Check for header terminator955size_t pos = buffer.find("\r\n\r\n");956if (pos != std::string::npos) {957headers_end = pos + 4;958}959}961// Parse http status962util::LineReader reader(std::string_view{buffer.data(), headers_end}, headers_end);963auto status_line = reader.ReadLine();964if (!status_line) {965throw HTTPError{"Failed to read status line"};966}968const std::string_view status_str = *status_line;969// Minimum status line is "HTTP/X.Y NNN" (e.g. "HTTP/1.1 200"), 12 characters.970if (status_str.size() < 12 || !status_str.starts_with("HTTP/")) {971throw HTTPError{"Invalid status line"};972}974size_t space1 = status_str.find(' ');975if (space1 == std::string::npos || space1 + 4 > status_str.size()) {976throw HTTPError{"Invalid status line format"};977}979const std::string_view status_code_str = status_str.substr(space1 + 1, 3);980auto status_code = ToIntegral<int>(status_code_str);981if (!status_code) {982throw HTTPError{"Invalid status code"};983}984response.status = *status_code;986HTTPResponseHeaders headers;987headers.Read(reader);989// Determine body length990size_t content_length = 0;991bool chunked = false;993// RFC 9112 §6.3 says responses with both Transfer-Encoding and Content-Length994// must be rejected. We are more lenient: Transfer-Encoding takes precedence995// and Content-Length is ignored.996auto transfer_encoding = headers.FindFirst("transfer-encoding");997if (transfer_encoding && ToLower(*transfer_encoding).find("chunked") != std::string::npos) {998chunked = true;999} else {1000auto content_length_header = headers.FindFirst("content-length");