协议版本的两套数字:70016 的自报与 31800 的断连线 图 1
协议版本的两套数字:70016 的自报与 31800 的断连线 · 图 1

比特币核心在 v31.0 的 node/protocol_version.h 里只放了两行数字:本软件自报的协议版本 70016,和一条断连线 31800。后者常被误读成”只兼容 70016 附近的新节点”——恰恰相反,它低得惊人,挡住的是比协议编号体系还古老的实现。本文把两套编号、这条线的真实门槛效应,以及收到 version 消息后的处理顺序讲清楚。

一、两套编号各管什么。软件版本(31.0)给人看;协议版本是 version 消息里的一个整数,给机器做协商:规则取双方较小值(源码里 greatest_common_version = min(对方声明, PROTOCOL_VERSION)),协商结果决定后续消息能用哪些扩展。子版本字符串(形如 /Satoshi:x.y/ 的 user agent)只做统计与识别,没有任何准入效力,改它不影响兼容性判断。

二、70016 与 31800 的历史错位。协议编号在历史上有过几次大跨度:几万个数量级的旧编号时代之后,主干软件把编号推到了六万、七万段。断连线 31800 停在旧时代的刻度上,v30 与 v31 源码里同为 31800、没有上调。翻译成白话:只要你自报的协议版本不低于当年 0.3.x 时代的那条线,数字这一关就放行;核心并不打算靠这个数字把”老但无害”的替代实现关在门外——那靠的是功能协商:每条扩展消息(如 wtxid 中继、sendheaders、feefilter)另有用生效版本号的条件,版本够但没协商到扩展的邻居,对应功能静默降级而不是断连。

三、收到 version 消息后的前三道检查。net_processing 的处理顺序值得按源码记:先比对协议版本,低于 31800 就断开,日志留一句 peer using obsolete version;再做自连接检测(对方带来的 nonce 与自家出站连接撞车就断,另一篇专门讲);随后比较服务位——对方声明的能力不满足本连接类型的要求(比如全中继外连却声称不当全节点),同样断开。三道的日志措辞互不相同,排查时别混为一谈:obsolete version 是版本问题,does not offer expected services 是角色问题,第三条才是网络问题。

四、“取较小值”规则的隐性成本。降级对话的边界由协议版本号钉住,而每代新功能都同时有独立协商通道,所以网络里长期存在三代实现混跑:新节点对老邻居逐个关闭扩展,而不是拉黑。这也解释了为什么”节点越来越难找邻居”通常与协议版本无关(70016 与数年前的实现都互相兼容),而更可能是可达性、服务位或网络类别问题——拿协议版本当替罪羊会浪费排障时间。

五、边界与误读纠正。第一,断连线是共识外的网络策略,改动不需要软分叉;仅就 v28、v30、v31 三代源码核对,这条线都钉在 31800 纹丝不动——它不是逐代上调的门槛,而是一次性划定的远古下限。第二,软分叉信号与这条线无关:区块里的 versionbits 字段管规则激活投票,P2P 准入线管”能不能说话”,两本账。第三,用伪造协议版本骗功能会失败:协商到的功能在消息层有各自的行为前提,谎报版本换来的能力一用就穿帮。给运维的一句话总结:这个文件里的两个数字,一个说明你对外宣称多新,一个说明你不在乎多旧——只要对方不低于上古门槛,先谈得来再说。

六、再加一个实操核对路径。想验证上面的结论,办法都在本机:getpeerinfo 里每条连接的 version 字段是对端自报的协议版本,对照自家 getnetworkinfo 的 version 即为自报值 70016,两列数字并排看,能直观看到网络里邻居们实际声明的版本分布;再开 -debug=net 跑一段,obsolete version 与 service 不匹配的断连日志会各自留下可区分的句子。这些观测都只读不改,适合把版本焦虑变成一次几分钟的核对,而不是凭印象升级或改配置。

风险提示:本文内容为技术机制科普,不构成任何投资建议、收益承诺或买卖时机判断。涉及协议规则与软件行为的描述以对应软件版本(文中已标注)的官方源码与规范为准。涉及资金操作的,请先在测试网或小额环境验证。

协议版本的两套数字:70016 的自报与 31800 的断连线 图 2
协议版本的两套数字:70016 的自报与 31800 的断连线 · 图 2