addpeeraddress:往节点地址库里塞一条地址的测试开关 图 1
addpeeraddress:往节点地址库里塞一条地址的测试开关 · 图 1

比特币节点的邻居发现机制里有一个常被忽略的细节:你连上的对端是怎么知道”这世上还有哪些节点”的?答案是一张叫 addrman 的地址管理表,节点之间靠 addr 消息互相交换观察结果。地址库的冷启动依赖内置种子节点,长期运行则完全由 gossip 喂养。围绕这张表有一条被源码标注为”仅供测试”的 RPC:addpeeraddress,能把一个 IP 和端口直接塞进地址管理表。本文按 v31.0 源码拆它的参数、写入 new/tried 两张表的语义,以及为什么生产节点不该拿它补邻居。

new 表与 tried 表:地址库的双表结构

addrman 分两层:new 表装着”听说过但还没成功连过”的地址,tried 表装着”确实成功建立过连接”的地址。节点调度新连接时从两张表按策略抽取候选。addpeeraddress 的第三个参数 tried 默认 false——写进 new 表;true 时先写入再尝试迁移进 tried 表。源码处理值得细看:调用 AddrMan::Add 失败时返回 success: falsefailed-adding-to-new 错误描述;Add 成功但 Good()(迁移 tried)失败时,success 同样置 false 并带 failed-adding-to-tried——迁移失败不会回滚 new 表里的条目,地址仍然留在候选池里等待被选中验证。返回结构里没有数字评分字段,只有 success 布尔加 error 字符串,这对脚本的启示是:不要指望它给出”为什么没进去”的结构化细节,失败原因粒度到这一步为止。命令返回体没有副作用回读通道,验证是否真的进了表,只能事后靠日志与后续连接行为间接确认。

地址对象带的是什么身份

源码构造地址对象时把服务标志写成 NODE_NETWORK 与 NODE_WITNESS 的并集,时间戳取当前时刻,注释里还有一句值得玩味的描述:源地址被设置为等于该地址本身,“等价于对端自我宣告”。真实网络里源地址决定可信度权重——从越可靠的对端学来的地址越优先。这条命令写进去的条目相当于”自报家门”,不具备任何第三方见证背书。此外它只接收 IP 字面量,LookupHost 解析失败直接报参数错误——不解析域名,不接受 onion 之外花样的间接写法,这是测试工具刻意收窄的输入面。

为什么不能用它对抗”连不上网”

排障场景里常见诱惑:节点孤立、连接数低,想用 addpeeraddress 手动喂几个大矿池节点 IP 续命。这条路的问题在于它只改了候选池,绕不过真实握手——连不上的根因(防火墙、NAT 超时、版本过旧被 MIN_PEER_PROTO_VERSION 拒绝、DNS 种子失败)一个都不解决,喂进去的地址握手失败后照样沉回 new 表或淘汰。正确的检查顺序是:getpeerinfo 看现有连接、getnetworkinfo 看本地网络可达性宣告、日志看握手失败原因,再决定动哪个配置项(onlynetbindconnectproxy)。这条命令的正当用途在它的标签里:集成测试里需要让节点对一个”不存在但格式正确”的地址发起连接行为时,用它构造前置状态。测试框架自己就是这么用的。

隐藏分类的工程含义

addpeeraddresssendmsgtopeer 一样躺在 hidden 帮助分类里:命令注册在生产代码中、受持续回归覆盖,但文档语义、参数宽容度和返回粒度都表明不承诺生产用途。地址发现的正路设计——种子节点、ADDRv2、指数退避重连、按子网多样性采样——是一个不依赖人工干预的闭环;任何”手动补一个地址”的操作在闭环里都活不过下一轮清理策略。理解它存在的原因(测试需要构造地址库状态),比记住它的参数更重要。

风险提示:本文是节点网络机制说明,提供的防御性排障思路不构成任何网络干扰行为的建议,也不构成投资建议。