握手完成之前不许办事:比特币节点在 verack 前后各挡住什么 图 1
握手完成之前不许办事:比特币节点在 verack 前后各挡住什么 · 图 1

握手完成之前不许办事:比特币节点在 verack 前后各挡住什么

两个比特币节点 TCP 连上之后,先互报家门再谈正事:version 与 verack 这对消息构成握手,握手没走完的连接在源码眼里”还不算连上”。这个状态机不是形式——功能协商必须在窗口内完成、窗口外递进来的消息会被忽略或断连、连交易广告都要等握手落幕才开始计时。本文全部以 Bitcoin Core v31.0 源码为准,拆解这条分界线的实际行为。

fSuccessfullyConnected:一条连接的状态开关

源码里每个对端对象带一个布尔量 fSuccessfullyConnected,收到对方的 verack 并被处理之后才置真。在收消息的主路径末尾有一条兜底:任何不属于握手系列的消息,如果到达时这个标志还是假,节点记一条”Unsupported message 先于 verack”的调试日志然后直接返回——不处理、不罚分、也不断线。也就是说,一个在握手完成前抢跑发 inv 的对端,消息会被静默吞掉,而不是被当成攻击。反过来,握手期间的特殊消息有专门通道:verack 本身、wtxidrelay、sendaddrv2、sendtxrcncl、sendcmpct 等都被排在兜底检查之前处理。

三条”过了村没这个店”的协商

窗口期真正的硬规则在协商类消息上。BIP339 的 wtxidrelay:如果对端在已连通状态下再发它,源码直接断线,日志写 wtxidrelay received after verack;同样,协议版本不够的老对端发的 wtxidrelay 被忽略。BIP155 的 sendaddrv2 完全同构:verack 之后到达就断连,因为地址格式协商半途切换会造成中继不一致。交易对账协议(BIP330)的 sendtxrcncl 管得更细:己方未启用 txreconciliation 时忽略;verack 后到达断线;对方在我们已声明不接受交易中继、或对方自己在 version 里关了中继还发来对账要约,同样断线。三条规则共享一个理由,源码注释说得很直白:这些协商”必须在 VERSION 与 VERACK 之间完成,避免连接建立后切换中继方式引发的中继问题”。

verack 处理时刻:从静默到放行

节点收到合法 verack 的那一刻做一串动作。先打一条新对端日志——出站连接记 INFO 级,入站只记 DEBUG 级,源码注释解释原因:入站可以被攻击者高频触发,刷满 INFO 日志本身就是攻击面。日志内容带连接类型、传输层(明文还是 BIP324 加密)、协议版本、对端声称的区块高度和 AS 编号。随后,若启用压缩区块协议版本,节点向对端递出 sendcmpct(低带宽模式,愿意提供 v2 版紧凑块但不主动要求用它收块);交易对账模块若启用,此时检查对端有没有如约完成 wtxid 注册,没注册就把对账状态遗忘掉。最后一步才把 fSuccessfullyConnected 置真,把这条连接交给常规中继与下载流水线。

一个容易忽视的时间隐私细节

源码在 verack 处理里放了一段断言加注释:握手完成之前,交易库存发送队列必须是空的、下一次公告时间必须还没初始化。原因是库存公告的随机节拍计时从 verack 之后才开始起算——如果一笔在握手窗口内到达的交易被立刻向全网喊出去,观察者就能从公告时点反推它到达本节点的精确时刻,泄露交易来源时间。把”先攒住、按涓流节拍广播”写成硬断言,说明这不是性能考虑而是隐私设计。运维排查”我的节点为什么没立刻转发某笔交易”时,这条节拍是首要嫌疑,而不是节点故障。

排障视角

日志里几种典型字符串各有明确含义:看到某消息 “received after verack” 并伴随断开,多半是对端实现把协商消息发晚了,属于协议时序错误;看到 “Unsupported message 先于 verack”,说明对端在握手未完成时抢发业务消息,常见于握手卡在半程(比如己方 version 还没被处理)的场景;连接长期停在未完成态直到超时断开,则对应 peertimeout 那套无消息计时器。三类现场对应三个不同环节,逐条归位后再谈网络问题。

风险提示:本文为 P2P 协议机制科普,消息行为以 Bitcoin Core 当前版本源码为准,可能随版本调整;不构成投资建议。