事件回放:一个区块把网络劈成两半
2013年3月11日,一名运行比特币核心0.8.0版本的矿工在高度225430挖出一个新区块。这个区块没有违反任何当时已知的共识规则,但它的交易输入总数超过了此前所有区块曾经达到过的规模。结果同样一份数据,在不同版本的软件面前命运相反:0.8的节点顺利验证并接受,仍在使用0.7及更早版本的节点却直接拒绝。网络从这一高度起分裂成两条平行的账本史,一边是接受了它的所谓零八点八链,另一边是拒绝它的旧版本链,商户、用户和矿工各自认可各自那条,短期内谁也说服不了谁。当年的官方公告承认局面混乱,同时向普通用户保证,无论运行哪个版本,软件最终都会自动切换到正确的那条链。
根因:藏在数据库锁的数量上限里
事故复盘文档BIP50由核心开发者加文·安德森撰写,结论出乎意料:问题不在区块体积,而在锁的数量。旧版本用伯克利DB保存区块和交易索引,验证一笔交易要为涉及的每条输入数据加数据库锁,软件对同一时刻可持有的锁总数设有硬上限。那个区块虽然远没有触到体积红线,但输入条数极多,旧节点验证时需要的锁超出了上限,只能以错误告终并拒绝区块。0.8版为了性能已经把索引引擎换成LevelDB,没有同款上限,于是照常处理。更隐蔽的是,测试网此前成功跑通过接近体积上限的大区块,让大家形成体积不超就安全的错觉,而更小但锁数更多的情形从来没人测过。0.7版还取消了旧版自设的五十万字节出块上限,事发前一周矿池正被鼓励上调目标体积,风险就这样被一天天喂大。
为什么没有自动愈合
平时偶发的孤块竞争会随下一条区块自然分出胜负,这次却不行。BIP50记载,零八点八链当时聚集了约六成挖矿算力,旧链的累计工作量追不上去,分裂便僵持住。为了尽快恢复唯一账本,BTCGuild与Slush两家头部矿池把节点降回0.7版本,主动放弃继续在被接受的0.8链上挣钱,让旧链重新掌握多数算力,其他0.8节点随后也重组回旧链。被放弃的225430及其后续区块整段作废,相当于比特币史上一次人为协调的链回滚。分裂窗口内还出现了一笔跨链双花,复盘认定操作者是在试验可能性而非蓄意行窃,但商家在这段窗口收币的风险被演示得淋漓尽致。
善后:临时规则与检查点
0.8.1版本很快发布,内置两道保险。第一道是窗口期规则:从2013年3月21日到5月15日,节点额外拒收任何在旧版本上验证不通过的区块,堵住同类事故重演。第二道是永久检查点:把分裂起点高度225430直接编译进程序,宣告那条被放弃的分支永远不会被承认。同年8月16日,高度252451的区块携带BIP30规定的两条历史豁免被主网接受,没有打补丁的旧节点被永久甩出主链,事件正式收口。
对普通用户的三点提醒
第一,版本异构本身就是共识风险。比特币的规则靠软件实现,同一份数据被不同实现读出不同结论时,账本就没有唯一答案,及时升级是每个人对网络稳定性的义务。第二,链的历史排序并非物理定律,2013年证明它可以被人为协调回滚,代价由矿工的收益和生态的信誉支付,所以重大网络事件期间延迟确认、暂缓大额收付款,是最朴素也最有效的自保动作。第三,读历史事件要盯一手材料:本事件全部关键细节都来自BIP50复盘、当年公告和0.8.1发行说明,转述版本与史实出入很多,值得留意。本文是历史与技术科普,不构成投资建议。
发表评论
还没有评论,来说两句吧。
评论区为展示样式,提交不会被处理。