每秒只放行十分之一条:比特币节点给addr消息装的令牌桶 图 1
每秒只放行十分之一条:比特币节点给addr消息装的令牌桶 · 图 1

比特币节点连上网络后收到的第一波消息里,addr 占了相当大的比例:对端会主动把自己认识的可达地址推过来,一条接一条,像互相递名片。这个机制让新节点的地址库迅速丰满,但也留下了一个明显的攻击面——任何人都可以构造海量 addr 消息,既淹没你的带宽,也污染你的连接选择。比特币核心对这件事的处理不是简单的“来多少收多少”,而是在解析之后加了一个流量闸门,v31.0 里那几个常量恰好拼出完整的防御设计。

闸门的核心是一个令牌桶,参数在 net_processing.cpp 里各占一行。流速是 MAX_ADDR_RATE_PER_SECOND,值为 0.1:普通对端平均每秒只能让你消化十分之一条地址消息,反过来说,一条持续高速塞地址的连接,其增量会被压到每十秒一条的速率线上。桶容量是 MAX_ADDR_PROCESSING_TOKEN_BUCKET,等于 1000:低速累积的“配额”最多攒一千条,攒够后突发不再有效。为什么要桶:网络里有合法的突发场景——你发一条 getaddr,对方按约定可以在响应里一次性回大量地址,用 MAX_ADDR_TO_SEND(一千条)封顶,而且源码注释写明这一段不受速率限制约束。桶的意义就是把这种一次性大批量与细水长流的刷屏区分开:前者消耗一次性配额,后者持续被限速。

被限速的代价只落在处理速度上,节点不会因此断线,这正是防御的聪明处:刷屏者得不到任何拒绝服务收益,你的带宽与内存安然无恙,只是它的地址不再进库。而如果你自己配了白名单权限并且给到 Addr 权限位,这条流量线可以整体旁路——可信对端的批量同步不该被通用规则卡住。

地址的另一半故事在“往外说”这一侧。同样在 v31.0 里,节点响应一条 getaddr 时最多交出 addrman 库存的百分之二十三(MAX_PCT_ADDR_TO_SEND 等于 23),单条 ADDR 消息的条目上限是一千。百分之二十三是有意选择的分数:低到任何单一对端都难以通过反复询问拼出你的完整邻居图谱,高到仍能支撑正常的地址扩散。此外主动中继有固定的目的地轮转周期(源码里 ROTATE_ADDR_RELAY_DEST_INTERVAL 是二十四小时),同一批地址向同一批对端的重复投递会按时间重新分桶,避免固定广播模式暴露网络拓扑。

这些规则对普通节点运营者的实际含义有三条。第一,节点地址库的增长速度取决于它主动发 getaddr 的时机,而不是等别人推送——所以“新节点头几分钟连接恢复特别快,之后越来越慢”不是软件变慢了,而是配额制下合法信息的优先到达。第二,如果你怀疑自己的节点在向网络扩散异常地址(比如错误绑定了不该公开的地址),对照这些常量可以定位方向:主动中继受轮转与比例双重控制,问题多半出在地址可达性判定而非刷屏逻辑;第三,为网关或爬虫类特殊角色配置 Addr 权限时要有自觉:那是在对某条连接说“你的地址消息我全收”,权限换的是便利,也让出了这层防御,值得只给确有需要且网络位置可信的对端。

顺带补一个常被混淆的细节:addr 消息的载荷里除了 IP 与端口,还有服务位与新式网络类型字段。对端是否支持隔离见证中继、是否宣称提供区块过滤器、是 Onion v3 还是 I2P 地址,都写在条目内部;闸门统计的是条目数而不是字节数,所以一条塞满冷门网络类型条目的消息并不比常见的 IPv4 批次更“重”。换句话说,刷屏者也无法通过换协议族来绕过配额——桶对所有条目一视同仁。这个按条目计费的口径让防御的可预测性更强:无论对方怎么组织载荷,每分钟能进入你地址库的上限就是那个流速乘以时间,加上至多一千条的桶深突发。

把这套设计与更早的年份对比,能看到比特币网络防御思路的一贯轨迹:早期策略靠断连和扣分,代价是误伤与可预测;addr 闸门则选择了另一条路——不断开、不判罚,只让无效信息不再产生效果。攻击的回报被清零,防御就不需要任何情报。地址管理器的 new 与 tried 两区结构、按 IP 与子网聚类的防关联分组,与这个令牌桶配合,构成了节点对端选择的底层秩序。你平时感觉不到它的存在,是因为它的全部工作就是让“洪水般的地址”退化成“涓涓细流级的噪声”,安静地在解析队列里流走。

风险提示:本文为网络机制说明,不构成任何投资建议;文中常量以比特币核心 v31.0 源码为准,后续版本可能调整,请以对应版本为准。

每秒只放行十分之一条:比特币节点给addr消息装的令牌桶 图 2
每秒只放行十分之一条:比特币节点给addr消息装的令牌桶 · 图 2