Bitcoin Core JSON-RPC 接口:端点路由、参数传递、版本协商与安全边界深度解析
Bitcoin Core 的无头守护进程 bitcoind 默认启用 JSON-RPC API,是外部程序(矿机、交易所、钱包管理软件、监控脚本)控制节点、查询链上状态、操作钱包的核心通道。本文基于仓库中的 doc/JSON-RPC-interface.md,结合 src/httprpc.cpp、src/rpc/request.cpp、src/httpserver.cpp 等源码实现,完整讲解 RPC 的两个 HTTP 端点、按位/按名参数结构、1.1 与 2.0 协议协商机制、认证与白名单安全模型,以及 RPC 查询结果的一致性保证。读完后你将能够:正确配置并调用 / 与 /wallet/<walletname>/ 两个端点,理解 bitcoin-cli 参数如何映射为 JSON 请求体,识别 1.1/2.0 响应差异,并正确评估 RPC 凭据作为安全边界的边界。
启用方式与端点总览
无头守护进程 bitcoind 默认启用 JSON-RPC API;GUI 客户端 bitcoin-qt 默认禁用,可以通过 -server 选项改变这一默认值,并且 GUI 的 Debug Console 对话框中也可以直接执行 RPC 方法。
服务端一共注册两个 JSON-RPC 端点,这一点可以在 src/httprpc.cpp 的 StartHTTPRPC 中得到源码印证:
auto handle_rpc = context { return HTTPReq_JSONRPC(context, req); };
RegisterHTTPHandler("/", true, handle_rpc);
if (g_wallet_init_interface.HasWalletSupport()) {
RegisterHTTPHandler("/wallet/", false, handle_rpc);
}
/:第二个参数true表示精确匹配(exactMatch),该端点始终注册、始终激活;/wallet/:第二个参数false表示前缀匹配(见 src/httpserver.cpp 中RegisterHTTPHandler的exactMatch语义),仅当钱包组件被编译进二进制时才注册——这与文档“该端点只在钱包组件编译在内时激活”的描述一致。
两个端点的职责差异如下:
| 端点 | 激活条件 | 可服务的请求 |
|---|---|---|
/ |
始终激活 | 始终可以服务非钱包请求;恰好只加载了一个钱包时也可以服务钱包请求 |
/wallet/<walletname>/ |
钱包组件已编译 | 既可以服务钱包请求也可以服务非钱包请求;加载两个及以上钱包时,钱包请求必须走该端点 |
bitcoin-cli 在传入 -rpcwallet= 参数时会使用 /wallet/<walletname>/ 端点,这一点可以从 src/bitcoin-cli.cpp 的 ConnectAndCallRPC 中看到:当 rpcwallet 非空时,端点被改写为 "/wallet/" + UrlEncode(*rpcwallet)(钱包名经过 URL 编码)。该 CLI 选项的帮助文本也明确说明它“需要与传给 bitcoind 的对应 -wallet 选项完全一致,并会改变使用的 RPC 端点,例如 http://127.0.0.1:8332/wallet/”(见 src/bitcoin-cli.cpp)。
最佳实践:在多钱包场景下,所有请求都应走 /wallet/<walletname>/ 端点,从根本上消除“请求打到了哪个钱包”的歧义。
端点调用示例
以下两个 curl 示例直接来自官方文档,展示了在 rpcuser=alice、rpcport=38332 的典型环境下如何使用两个端点:
# 从 / 端点获取区块高度
$ curl --user alice --data-binary '{"jsonrpc": "2.0", "id": "0", "method": "getblockcount", "params": []}' -H 'content-type: application/json' localhost:38332/
# 从 /wallet/walletname 端点获取余额(rpcwallet=desc-wallet)
$ curl --user alice --data-binary '{"jsonrpc": "2.0", "id": "0", "method": "getbalance", "params": []}' -H 'content-type: application/json' localhost:38332/wallet/desc-wallet
需要注意的是,RPC 服务器只处理 HTTP POST 请求:src/httprpc.cpp 的 HTTPReq_JSONRPC 开头即检查 req->GetRequestMethod() != HTTPRequestMethod::POST,非 POST 一律返回 405。
参数传递:按位、按名与混合模式
JSON-RPC 服务器同时支持 JSON-RPC 规范中的 by-position(数组) 和 by-name(对象) 两种参数结构。作为额外便利,所有 RPC 方法都接受一个名为 args 的命名参数,它的值是一个数组,作为前导位置参数,与其余命名参数合并使用——这样可以避免给每个参数值都命名。文档给出的三种等价调用方式:
# "params": ["mywallet", false, false, "", false, false, true]
bitcoin-cli createwallet mywallet false false "" false false true
# "params": {"wallet_name": "mywallet", "load_on_startup": true}
bitcoin-cli -named createwallet wallet_name=mywallet load_on_startup=true
# "params": {"args": ["mywallet"], "load_on_startup": true}
bitcoin-cli -named createwallet mywallet load_on_startup=true
从 src/rpc/request.cpp 的 JSONRPCRequest::parse 可以看到服务端对 params 字段的校验规则:params 必须是数组或对象,缺失时按空数组处理,其他类型直接抛出 RPC_INVALID_REQUEST(“Params must be an array or object”)。
此外,较新的 bitcoin rpc 子命令可以作为 bitcoin-cli -named 的替代用法(纯命名参数场景),见 src/bitcoin.cpp 注册的 CLI 子命令体系。
请求体层面的解析细节同样值得了解(src/rpc/request.cpp):
id字段可选;存在则作为后续错误响应的关联 id;method必须存在且为字符串,否则报 “Missing method” / “Method must be a string”;jsonrpc字段决定协议版本(见下节)。
服务器还支持批量请求:请求体顶层是 JSON 数组时,ExecuteHTTPRPC 会逐条执行,批内某条请求失败只影响该条的响应,不会中断整批;全通知(无 id)的批次返回 HTTP 204 无响应体(src/httprpc.cpp)。
版本管理:随主版本隐式演化
RPC 接口可能在一个主版本到下一个主版本之间发生变化,因此 RPC 接口隐式地以主版本号进行版本化。可以通过 getnetworkinfo RPC 返回的 version 字段获取版本元组。
被弃用的功能通常可以在一个大版本的宽限期内通过 -deprecatedrpc= 命令行选项重新启用。新主版本的发布说明(本仓库 doc/release-notes/ 目录,如 doc/release-notes/31.1.md)会附带详细说明哪些 RPC 功能被弃用以及如何临时重新启用的指引。
JSON-RPC 1.1 与 2.0 的协议协商
服务器识别 JSON-RPC v2.0 请求并据此响应。判定依据是请求体中是否存在 "jsonrpc": "2.0" 键值对;如果请求中没有该键值对,则回退到遗留的 JSON-RPC v1.1 协议——这是 v27.0 及更早版本中唯一可用的协议。这一判定逻辑与 src/rpc/request.cpp 的实现完全吻合:默认 m_json_version = JSONRPCVersion::V1_LEGACY,遇到字符串 "2.0" 切换为 V2,"1.0" 出于向后兼容仍按 1.1 处理,其他取值则抛出 “JSON-RPC version not supported”。
两种协议的行为差异汇总(摘自文档):
| 1.1 | 2.0 | |
|---|---|---|
| 请求标记 | "version": "1.1"(或缺省) |
"jsonrpc": "2.0" |
| 响应标记 | (无) | "jsonrpc": "2.0" |
响应中的 "error" 与 "result" 字段 |
两者同时存在(其一为 null) | 只存在其中一个 |
| 响应 HTTP 状态码 | 出现任何 RPC 错误(参数无效、方法不存在等)时非 200 |
除非真正的 HTTP 层错误(请求解析失败、端点不存在等),否则恒为 200 |
| 通知(无回复的请求) | 不支持 | 支持:省略 "id" 字段的请求即为通知,返回 HTTP 204 "No Content" |
这些行为差异在源码中均有对应实现:
- 响应字段裁剪:src/rpc/request.cpp 的
JSONRPCReplyObj中,v1 同时填充result和error(空值填null),v2 只保留实际存在的那个字段并额外加上"jsonrpc": "2.0"标记; - HTTP 状态码策略:src/httprpc.cpp 中
catch_errors仅在m_json_version == JSONRPCVersion::V2时为真——2.0 下异常被捕获进响应体、HTTP 状态保持 200;1.1 下异常上抛,由JSONErrorReply映射为 HTTP 错误码(src/httprpc.cpp:RPC_INVALID_REQUEST→ 400,RPC_METHOD_NOT_FOUND→ 404,其余 → 500); - 通知支持:src/rpc/request.h 中
IsNotification()的定义是!id.has_value() && m_json_version == JSONRPCVersion::V2,即“无id且是 2.0 请求”,命中后服务器执行该请求但返回空响应体并置 HTTP 状态为 204(src/httprpc.cpp)。
HTTP 状态码常量集中定义在 src/rpc/protocol.h,RPC 错误码(如 -32600 invalid request、-32601 method not found、-32602 invalid params、-32700 parse error,以及应用层错误 -8、-25 等)同样在该头文件中定义,可作为排错时的对照表。
安全模型:认证、白名单与凭据边界
RPC 接口允许其他程序控制 Bitcoin Core,包括花费钱包资金、影响共识校验、读取私有数据等操作——文档明确警告其可造成资金、数据与隐私损失,并给出五个层面的防护建议。
保护可执行文件本身
任何拥有物理或远程访问权(计算机、容器、虚拟机)的人都可能破坏整个程序或仅破坏 RPC 接口,包括记录你解锁加密钱包时输入的口令,或篡改设置使节点谎报交易确认数。因此在非独占控制的系统(共享计算机、VPS)上不应执行安全敏感操作。
保护本地网络访问
默认情况下,RPC 只能被同一台计算机上、且能提供有效认证凭据(用户名 + 口令)的客户端访问。同一台机器上任何能接触文件系统与本地网络的程序都可能拿到这个级别的访问权;其他程序甚至可以在同一端口上伪造 RPC 接口来诱骗你泄露凭据。因此只在可信赖程序共存的环境中执行敏感操作。
RPC 凭据的安全边界
任何持有有效 RPC 凭据的客户端,都应视为对 bitcoind 进程可访问的节点与文件系统资源拥有显著控制权:RPC 命令可以加载 bitcoind 进程有权限读取路径下的钱包文件、指定操作所用的文件路径,可能获得超出预期的访问范围。Bitcoin Core 提供 -rpcwhitelist 限制特定用户可访问的命令、-rpcwhitelistdefault 控制未显式配置白名单用户的默认行为;但在多钱包或多用户共享访问场景下,这些不应被视为稳健的安全边界——持有某些命令权限的用户仍可能以非预期方式利用功能。安全敏感场景应实施系统级隔离(容器、虚拟化、受限权限的独立用户账户),而不是仅依赖 RPC 访问控制。
白名单的源码实现印证了该语义(src/httprpc.cpp):-rpcwhitelist=user:cmd1, cmd2 形式解析后存入 g_rpc_whitelist;-rpcwhitelistdefault 缺省值等于“是否传过任何 -rpcwhitelist”(!gArgs.GetArgs("-rpcwhitelist").empty())。执行时(src/httprpc.cpp):默认白名单开启且该用户无条目 → 直接 403;用户有白名单但请求方法不在其中 → 403。值得注意的是多次为同一用户配置 -rpcwhitelist 时,多个列表取的是交集(std::set_intersection),这体现了最小权限设计。
保护远程网络访问
可以通过 rpcallowip 与 rpcbind 配置参数允许其他计算机远程操控 Bitcoin Core,但文档强烈警告:不要将 RPC 暴露到公共互联网。原因是:RPC 虽有认证但没有加密,登录凭据以明文传输,网络路径上任何人可读;且 RPC 接口并未针对任意互联网流量做加固,即使经由 Tor 洋葱服务等暴露也可能带来未预见的漏洞。这些参数只适用于可信私有网络或已加固的连接(VPN、SSH/stunnel 端口转发)。可通过 bitcoind -help 查看这些设置的完整说明。
与远程访问相关的 Docker 场景:若需把容器内 RPC 端口暴露给宿主机,Docker 默认的端口映射方式会同时把端口暴露到公网,应改为仅绑定宿主机 localhost,例如 -p 127.0.0.1:8332:8332。
安全认证:cookie 优先,静态凭据次之,明文参数兜底
- 默认行为(推荐):未指定
rpcpassword时,Bitcoin Core 每次重启生成唯一登录凭据,写入配置目录下的.cookie文件,权限仅启动用户可读;拥有该文件读权限的 RPC 客户端可自动登录。 - 静态凭据:需要为程序生成稳定登录凭据时,可使用源码树中 share/rpcauth 目录下的脚本生成带盐 HMAC 凭据(
-rpcauth选项)。 - 最终兜底:手工指定
rpcuser/rpcpassword——必须选择强且唯一的口令,且仍须遵守“不使用不安全网络”的前提。
源码印证(src/httprpc.cpp 的 InitRPCAuthentication):当 -rpcpassword 为空时走 GenerateAuthCookie,生成 32 字节随机数并写入 .cookie 文件(src/rpc/request.cpp,用户名固定为 __cookie__ 以便日志辨识,权限可通过 -rpccookieperms 覆盖,默认受 umask 0077 约束);若配置了明文凭据,服务器日志会主动告警“明文配置不安全,建议改用 cookie 或哈希后的 rpcauth”,并会对明文凭据加 16 字节随机盐做 HMAC-SHA256 后再入内存(g_rpcauth 中保存 user:盐:哈希 三元组)。
验证侧的细节(src/httprpc.cpp):Authorization: Basic base64(user:pass) 解码后,用户名比较与哈希比较都使用 TimingResistantEqual 做恒定时间比较以防时序侧信道;认证失败还会记录警告日志并 UninterruptibleSleep(250ms) 来拖慢暴力破解(src/httprpc.cpp),注释同时提醒:如果这导致 DoS,说明你不该把 RPC 端口暴露出去。
安全字符串处理
RPC 接口除了 JSON 编码所必需的转义外,不保证对数据做任何转义(序列化数据通常以字节十六进制表示)。若在自己的程序中使用或透传 RPC 数据,必须自行确保问题字符串被正确转义——例如 createwallet 的 wallet_name 参数是字符串,缺乏应用层检查时可能被用于路径穿越攻击;历史上已有网站因显示包含 <script> 标签的解码十六进制字符串而被操纵。因此建议只以十六进制形式显示所有序列化数据。
RPC 一致性保证
通过 RPC 可查询的状态保证至少与调用执行之前的链状态同步;但反映内存池(mempool)状态的 RPC 返回值可能与当前 mempool 状态不同步。
内存池(Transaction Pool)
RPC 返回的 mempool 状态与调用时刻的链状态自洽:它只包含节点在 RPC 调用时认为可挖入区块(mine-able)的交易;且反映了所有早于本次调用返回的 mempool/链相关 RPC 的全部效果。
钱包(Wallet)
RPC 返回的钱包状态与调用时刻的链状态自洽,并返回与先前非钱包 RPC 一致的最新链状态:调用时刻所有区块(及其中交易)的效果都已体现在钱包交易状态中。例如区块中包含与 mempool 交易冲突的交易时,钱包状态会反映这些 mempool 交易已被移除。
但钱包状态可能落后于当前 mempool、或落后于先前某个 RPC 所见到的 mempool 状态:例如某笔钱包交易在本次 RPC 之前已在 mempool 中被 BIP-125 替换,本次响应中可能尚未体现该替换。
已知限制:文件描述符耗尽
JSON-RPC 接口存在一个已知问题:同时打开过多 HTTP 连接时,系统可能耗尽可用文件描述符并导致节点崩溃。缓解手段:提高系统允许的最大文件描述符数;在可控范围内避免同时向 JSON-RPC 接口发起过多连接。文档没有给出放之四海皆准的阈值——这取决于你的系统,但“数百个请求同时发起”时已确定处于该问题的风险区。
小结
Bitcoin Core 的 JSON-RPC 接口以两个端点(/ 与 /wallet/<walletname>/)划分通用与钱包作用域,以 jsonrpc: "2.0" 标记协商 1.1/2.0 两种响应语义,以 cookie 认证、HMAC 静态凭据与 -rpcwhitelist 分层控制访问面,并以“调用前链状态自洽”作为一致性契约。理解 doc/JSON-RPC-interface.md 给出的规则,并对照 src/httprpc.cpp、src/rpc/request.cpp、src/httpserver.cpp 的实现细节,即可在自建节点、多钱包服务与对外集成时既用对端点与参数结构,又守住凭据与网络暴露的安全底线。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00