一枚玩笑补丁锁死 2100 万:BIP-42 与补贴右移的未定义行为 图 1
一枚玩笑补丁锁死 2100 万:BIP-42 与补贴右移的未定义行为 · 图 1

「比特币总量 2100 万枚」几乎是加密世界传播度最高的数字。有趣的是,这个数字在最初十几年的代码里并不是一个显式常数,而是藏在一行靠右移位实现的补贴公式里;这行代码在数学上并不像看起来那么收敛,而给这件事收口的,是 2014 年 4 月 1 日发布、措辞从头到尾都在讲段子的 BIP-42。

先看那行著名的代码:nSubsidy >>= (nHeight / 210000);,起点是 int64_t nSubsidy = 50 * COIN;。含义直白:每过 21 万个区块,把 50 个币的补贴右移一位,等效除以二,这就是「每四年减半」的实现本体。BIP-42 的正文用反讽的笔法提醒:这段代码「精心依赖了 C++ 规范中的未定义行为」。问题出在移位量上——int64_t 只有 64 位,当区块高度不断增长、移位量 nHeight / 210000 达到 64 及以上时,C++ 标准并不保证结果是 0,实际行为取决于具体平台和编译器。文档点破:在当时支持的平台上,由于「新金矿发现周期恰好是减半周期的 64 倍」,这条曲线会在第 64 次减半(约区块高度 13,440,000)之后整体循环,补贴回到 50 个币的量级。另一份坑同时埋给跨语言实现者:文档特意点名 Python 对超宽移位直接返回 0,行为与 C++ 相反——谁重写客户端谁踩雷。

BIP-42 给出的「修复方案」是标准的愚人文体:既然中本聪把发行曲线建模为「每 mibillennium(1024 年)发现四座金矿、每座开采 140 年耗尽」,那不如在一场软分叉里一了百了——2214 年 4 月 1 日,把区块补贴永久设为零,总供应量定格在「42 halfmillion」(42 个半百万,正好 2100 万)枚,并且贴心地注明包含创世区块那笔实际花不掉的 coinbase 输出。文档顺带嘲讽了其他方案:浮点近似「自从金融危机之后人人觉得带小数点的数字可疑」;字符串截零方案则「更符合核心团队 PHP 程序员的身份」。(这几句请当作社区幽默读,而不是技术评估。)正经的部分是:它引用的实现 PR 3842 给出了确定性的处理,让补贴在右移语义上安全地归零并停留在零,BIP-42 的元数据状态也随之标成了 Deployed——一个愚人节提案,以最正式的姿态完成了它的历史使命。

这篇文档真正的信息密度在笑点之下:共识系统里「数学上显然」的事实,必须由规范和实现共同保证,而不能靠平台行为兜底。发行曲线是比特币最强的叙事锚点,而它在代码层的保证长期是一行未定义行为的表达式;如果哪天主流编译器换了一种移位语义,或者一个新客户端用别的语言「正确地」实现了移位,两条链就会在千万高度之后分道扬镳。把隐式约定写成显式规则——这正是 BIP 流程存在的意义,哪怕触发它的是一份玩笑。

常见误区有三条。第一,「2100 万」被当成精确到枚的硬顶:按现行曲线,减半的整数除法会让最后一次非零补贴发生在第 33 次减半前,实际发行总量的尾数是聪级的细节,各篇文章口径应以发行曲线推算为准。第二,把 BIP-42 说成已激活的共识变更:它是一次玩笑立项加一笔真实的防御性修复,别把它与任何硬分叉事件挂钩。第三,以为「每四年」是协议常数:协议只规定 210,000 块的间隔,四年只是 10 分钟出块目标的换算,实际节奏随算力与块时间浮动。

快速问答。问:移位问题真的会在主网咬人吗?答:要等到高度 13,440,000 附近才可能显形,按当前节奏远在下一代人的账本里,但共识实现不能赌「到时候再说」。问:修复改了什么语义?答:让补贴在足够多次减半后确定性地归零,不再依赖移位未定义行为。问:为什么说它「以 Deployed 收尾」不奇怪?答:BIP 的状态词表里 Deployed 描述的是变更被部署采纳的事实,社区用这个标签给一个玩笑提案发了毕业礼,属于 BIP 史上一则名梗。

风险提示:本文为机制科普,不构成投资建议;涉及发行总量的任何推算都应以区块补贴公式实算为准,勿凭记忆引用整数结论。