Bitcoin Core:bitcoin-cli -addrinfo 行为变更与 getaddrmaninfo 依赖链详解(PR 26988)
本文以 Bitcoin Core 的发布说明 release-notes-26988.md 为主体,深入讲解 bitcoin-cli -addrinfo 命令在地址计数行为上的关键变更:为什么从"经过质量与新鲜度过滤的地址集合"改为"返回全部已知地址"、该命令为何从此依赖 getaddrmaninfo RPC(要求 bitcoind v26.0+),并逐层拆解 src/bitcoin-cli.cpp 中的请求处理链、src/rpc/net.cpp 中的 RPC 实现,直至 src/addrman.h 的地址管理器(AddrMan)底层计数逻辑。读完本文,你能准确判断 -addrinfo 输出与节点实际 peer 选择逻辑的一致性,并能自行排查版本不匹配时的报错。
一、变更核心:-addrinfo 不再过滤地址
PR #26988 对 CLI 的 -addrinfo 选项做出的修改可以概括为一句话(来自 release-notes-26988.md 原文):
CLI
-addrinfonow returns the full set of known addresses. In previous versions (v22.0 - v30.0) the set of returned addresses was filtered for quality and recency. This was changed since it does not match the logic for selecting peers to connect to, which does not filter.
拆开来看包含三层信息:
- 变更前(v22.0 – v30.0):
bitcoin-cli -addrinfo返回的地址集合经过了质量(quality)和新鲜度(recency)过滤,即统计的是"经过筛选后认为仍然有效"的地址。 - 变更后:
-addrinfo返回节点地址管理器中全量已知地址的计数,与网络无关的过滤条件一律不施加。 - 变更动机:节点在挑选连接对等点(peer)时的选择逻辑本身不做这类过滤。旧行为给出的数字与节点实际"可以从哪些地址里挑 peer"的口径不一致,容易造成运维误判——例如看到
-addrinfo数字偏小,误以为节点缺乏连接能力,而实际上 peer 选择逻辑能看到全部地址。
这一变更的价值在于让诊断命令的统计口径与真实选路逻辑对齐:-addrinfo 报出的数字,现在就是节点选 peer 时"池子里"的地址规模。
二、版本约束:为什么 -addrinfo 要求 bitcoind v26.0+
同一份发布说明中给出了一个明确的兼容性约束:
Note: CLI
-addrinfonow requires bitcoind v26.0 or later, as it uses the getaddrmaninfo RPC internally. Users querying older, unmaintained node versions would need to use an older bitcoin-cli version.
这里的关键依赖是 getaddrmaninfo RPC。该 RPC 是 Bitcoin Core v26.0 新增的,官方 26.0 发布说明 release-notes-26.0.md 中记载:
A new RPC
getaddrmaninfohas been added to view the distribution of addresses in the new and tried table of the node's address manager across different networks(ipv4, ipv6, onion, i2p, cjdns). (#27511)
也就是说,-addrinfo 的"全量计数"实现方式是在 CLI 内部把请求转写成 getaddrmaninfo RPC 调用,而该 RPC 只存在于 v26.0 及之后的 bitcoind。因此:
- 用新版
bitcoin-cli(含本变更)去连 v26.0 之前的旧节点:RPC 不存在,命令会失败; - 官方给出的解决方案是:查询旧节点请使用与节点同版本的旧
bitcoin-cli,或升级节点本身。
这一约束在源码中有直接对应。src/bitcoin-cli.cpp 中 -addrinfo 专用的请求处理器 AddrinfoRequestHandler 在处理 RPC 响应时,会专门检查"方法不存在"这一种错误:
/** Process addrinfo requests */
struct AddrinfoRequestHandler : BaseRequestHandler {
UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override
{
if (!args.empty()) {
throw std::runtime_error("-addrinfo takes no arguments");
}
return JSONRPCRequestObj("getaddrmaninfo", NullUniValue, 1);
}
UniValue ProcessReply(const UniValue& reply) override
{
if (!reply["error"].isNull()) {
if (reply["error"]["code"].getInt<int>() == RPC_METHOD_NOT_FOUND) {
throw std::runtime_error("-addrinfo requires bitcoind v26.0 or later which supports getaddrmaninfo RPC. Please upgrade your node or use bitcoin-cli from the same version.");
}
return reply;
}
...
可以看出两点实现事实:
PrepareRequest阶段把-addrinfo固定转写为getaddrmaninfo调用,且不接受任何参数(传入参数直接报错-addrinfo takes no arguments);ProcessReply阶段对RPC_METHOD_NOT_FOUND(RPC 方法不存在)单独拦截,抛出与发布说明措辞一致的引导性错误信息,帮助运维直接定位到"节点版本太旧"这一根因,而不是抛出一个晦涩的 JSON-RPC 错误。
CLI 主流程在解析命令行参数后,检测到 -addrinfo 开关即切换到该处理器(见 src/bitcoin-cli.cpp):
} else if (gArgs.GetBoolArg("-addrinfo", false)) {
rh.reset(new AddrinfoRequestHandler());
}
三、实现链路:从 -addrinfo 到 AddrMan 的三层调用
-addrinfo 的完整数据链路可以归纳为三层:CLI 处理器 → getaddrmaninfo RPC → AddrMan 计数接口。
3.1 CLI 层:把 RPC 结果压缩成 addresses_known
AddrinfoRequestHandler::ProcessReply 的后半段(src/bitcoin-cli.cpp)遍历 getaddrmaninfo 返回的每个网络键,只抽取每个网络的 total 字段,组装成精简输出:
// Process getaddrmaninfo reply
const std::vector<std::string>& network_types{reply["result"].getKeys()};
const std::vector<UniValue>& addrman_counts{reply["result"].getValues()};
// Prepare result to return to user.
UniValue result{UniValue::VOBJ}, addresses{UniValue::VOBJ};
for (size_t i = 0; i < network_types.size(); ++i) {
int addr_count = addrman_counts[i]["total"].getInt<int>();
if (network_types[i] == "all_networks") {
addresses.pushKV("total", addr_count);
} else {
addresses.pushKV(network_types[i], addr_count);
}
}
result.pushKV("addresses_known", std::move(addresses));
从这段源码可以确认:CLI 输出结构为 {"addresses_known": {<network>: total, ..., "total": <all_networks 的 total>}},其中 total 键来自 all_networks 条目。CLI 只做"取 total 再展平"的转换,任何计数逻辑都不在 CLI 本地发生——这也正是它能"无过滤"的原因:数据全部来自节点侧 RPC。
3.2 RPC 层:getaddrmaninfo 遍历各网络的新表/尝试表
getaddrmaninfo 的实现位于 src/rpc/net.cpp,核心逻辑是遍历所有 Network 枚举值(跳过 NET_UNROUTABLE 与 NET_INTERNAL),对每个网络调用 AddrMan::Size 三种重载组合,输出 new/tried/total 三个计数,最后再追加一个 all_networks 汇总项:
[](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue {
AddrMan& addrman = EnsureAnyAddrman(request.context);
UniValue ret(UniValue::VOBJ);
for (int n = 0; n < NET_MAX; ++n) {
enum Network network = static_cast<enum Network>(n);
if (network == NET_UNROUTABLE || network == NET_INTERNAL) continue;
UniValue obj(UniValue::VOBJ);
obj.pushKV("new", addrman.Size(network, true));
obj.pushKV("tried", addrman.Size(network, false));
obj.pushKV("total", addrman.Size(network));
ret.pushKV(GetNetworkName(network), std::move(obj));
}
UniValue obj(UniValue::VOBJ);
obj.pushKV("new", addrman.Size(std::nullopt, true));
obj.pushKV("tried", addrman.Size(std::nullopt, false));
obj.pushKV("total", addrman.Size());
ret.pushKV("all_networks", std::move(obj));
return ret;
},
RPC 文档字符串对返回字段的官方解释也值得注意(同文件):
new:new 表中的地址数,"represent potential peers the node has discovered but hasn't yet successfully connected to"(节点发现但尚未成功连接过的候选 peer);tried:tried 表中的地址数,"represent peers the node has successfully connected to in the past"(历史上成功连接过的 peer);total:两张表之和。
该 RPC 归属 network 命令组(注册处见 src/rpc/net.cpp)。
3.3 AddrMan 层:Size 是纯计数,不含过滤
getaddrmaninfo 依赖的计数接口声明于 src/addrman.h:
size_t Size(std::optional<Network> net = std::nullopt, std::optional<bool> in_new = std::nullopt) const;
两个可选参数分别限定"按哪个网络统计"与"按 new 表还是 tried 表统计";实现见 src/addrman.cpp,是一个直接委托给内部实现(m_impl->Size(net, in_new))的薄封装。从源码结构看,Size 就是对地址管理器内部表容量的直接读取,没有施加质量评分或新鲜度阈值——这与 PR #26988 发布说明中"peer 选择逻辑不过滤"的描述相互印证:-addrinfo 现在与节点选 peer 时看到的地址池同源。
四、实操:命令用法与输出解读
在节点版本满足要求(bitcoind ≥ v26.0)的前提下,用法与历史版本一致,无需任何额外参数:
bitcoin-cli -addrinfo
- 该选项不接受任何位置参数,多余参数会触发
-addrinfo takes no arguments错误(见 src/bitcoin-cli.cpp); - 选项在 CLI 参数表中的注册说明为 "Get the number of addresses known to the node, per network and total."(见 src/bitcoin-cli.cpp);
- 输出按前述 3.1 节的组装逻辑,形如(数值为示意结构,实际数量取决于节点运行状态):
{
"addresses_known": {
"ipv4": 0,
"ipv6": 0,
"onion": 0,
"i2p": 0,
"cjdns": 0,
"total": 0
}
}
其中各网络键来自 getaddrmaninfo 返回的网络名(跳过 unroutable/internal),total 键对应 all_networks 的 total。
排查要点:若命令报出
-addrinfo requires bitcoind v26.0 or later which supports getaddrmaninfo RPC.
Please upgrade your node or use bitcoin-cli from the same version.
即可确定原因是节点侧不支持 getaddrmaninfo(即节点版本早于 v26.0),应按错误提示升级节点或改用与节点同版本的旧 bitcoin-cli。
需要区分的是:-addrinfo(CLI 开关,精简计数输出)与 getaddrmaninfo(RPC,含 new/tried/total 明细)是两个观测入口,底层数据同源。如果还需要查看地址在 new/tried 两表中的分布细节,应直接使用 RPC:
bitcoin-cli getaddrmaninfo
其行为的自动化验证可参考功能测试 test/functional/rpc_net.py(其中包含对 getaddrmaninfo 的调用与断言),fuzz 侧则覆盖于 src/test/fuzz/rpc.cpp。
五、小结
| 要点 | 变更前(v22.0 – v30.0) | 变更后(PR #26988) |
|---|---|---|
-addrinfo 统计口径 |
地址集合按质量与新鲜度过滤后再统计 | 返回全量已知地址计数,与 peer 选择逻辑口径一致 |
| 底层数据源 | 旧版过滤逻辑 | 内部转写 getaddrmaninfo RPC |
| 节点版本要求 | 无此约束 | 要求 bitcoind ≥ v26.0(getaddrmaninfo 于 v26.0 引入) |
| 版本不匹配时的表现 | — | 明确的 RPC_METHOD_NOT_FOUND 拦截与升级提示 |
PR #26988 的实质是一次"诊断命令口径对齐"的修正:让运维通过 -addrinfo 看到的世界与节点真正用于选 peer 的地址池保持一致,同时把兼容性代价(依赖 v26.0+ 的 getaddrmaninfo)通过清晰的错误提示显式化。阅读 src/bitcoin-cli.cpp 中 AddrinfoRequestHandler 与 src/rpc/net.cpp 中 getaddrmaninfo 的实现,可以完整复核本文所述的每一处行为。
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 StartedRust0624
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