协议演进 / BIP/比特币改进提案 / BIP-50

BIP-50 2013 年 3 月链分叉事后分析

复盘 2013 年 3 月因旧版客户端 Berkeley DB 锁配置不足引发的意外链分叉:发生了什么、根因是什么、当时如何恢复,以及事后定下的行动项。本 BIP 目前处于已部署状态。

一、档案卡

档案卡
编号BIP-50
标题March 2013 Chain Fork Post-Mortem
作者Gavin Andresen <[email protected]>
指定日期2013-03-20
类型Informational(信息类)
状态Deployed(已部署)
阅读版https://bips.dev/50
官方原文https://github.com/bitcoin/bips/blob/master/bip-0050.mediawiki

二、放在哪一族

信息类事后分析(post-mortem),不提出新规则,只记录一次真实事故。它与一般规范类 BIP 不同:读它是为了理解早期比特币在实现层面出过什么问题、社区当时如何处置。

它记录的事件牵涉两个版本世代的差异:0.8 之前的客户端使用 Berkeley DB,0.8 改用了 LevelDB。也因此,它常被放在“实现差异如何变成共识风险”这一主题下阅读。

三、它要解决什么

3.1 出了什么事

一个“交易输入总数比以往见过的都多”的区块被挖出并广播。0.8 节点能处理它,但部分 0.8 之前的节点拒绝了它,于是区块链出现了意料之外的分叉。

当时 0.8 一侧的链约占 60% 算力,分叉因此不会自动消解——若 0.8 之前的链在总工作量上反超,0.8 节点本会重组过去,但算力分布让这件事没有自然发生。

3.2 当时如何恢复

为了尽快恢复一条公认的链,BTCGuild 与 Slush 两家矿池把自家 0.8 节点降级回 0.7,让它们的矿池也拒绝那个大区块。这样,多数算力回到了不含该区块的链上,最终 0.8 节点重组到了 0.8 之前的链上。

期间至少发生了一笔大额双花。原文同时说明:实施者只是试验双花是否可行,并无恶意盗取的意图。

3.3 哪些地方做对了

原文列出的“做对了”包括:

四、规则本身

本 BIP 不定新规则,以下是原文给出的根因与行动项,属于事后结论。

4.1 根因

0.8 之前的版本为 Berkeley DB 配置的锁数量不足,处理不了较大但本身有效的区块。Berkeley DB 的锁必须由使用其 API 的一方按预期负载手工配置;手册建议在压力条件下测出实际用到的锁、locker 与锁对象数量,再翻倍留作安全余量。

锁配置不足带来一个隐蔽后果:它事实上变成了一条决定区块有效性的网络共识规则,而且是一条不一致、不安全的规则——因为每个节点实际需要多少锁,取决于其本地数据库的具体状态。

几个叠加因素让问题此前没有暴露:

0.8 不再使用 Berkeley DB,改用 LevelDB,没有同样的锁限制,因此能成功处理那个引发分叉的区块。

原文还指出:BDB 锁在处理链重组时同样需要,0.8 之前的版本可能也处理不了某些有效的重组。而且即便全网都跑 0.7.2,问题依然存在:各节点 blkindex.dat 内容不完全相同、锁按页获取,一台 0.7.2 节点造出的区块,另一台可能验证不了。

4.2 行动项(原文记录,多项标注为已完成)

立即项:

警报系统:复查警报系统密钥的持有者名单、确保彼此有联系方式且时区覆盖良好;实现 -alertnotify=<command>,让服务在收到适用于自己的警报时运行命令;预先生成 52 条测试警报,每周在测试网上定时广播以便演练。

安全模式设想:检测到足够长的侧链时触发警报(哪怕它不是主链),矿池可据此自动暂停 payouts。

测试:在测试网上运行机器人,从水龙头取币、生成大量随机拆分/合并交易再发回,以随机化在线测试更早暴露这类病态区块。

双花问题:此次双花能成功,很可能是矿池节点降级时内存池被清空所致;原文提出的解法方向是节点启动时彼此同步内存池,但这需要先有内存池过期策略。

4.3 结局

原文 Resolution 一节记录:2013 年 8 月 16 日,区块 252,451 被主网接受,未打补丁的节点由此被分叉出网络。

五、谁必须动

普通持币人:本文本身不要求任何操作。它记录的是历史事件与当时的处置。

钱包与商家服务:原文肯定了事发时暂停充值与支付、事后迅速恢复的做法,并提出让商家/交易所(乃至矿池)在跟随网络多数版本的 bitcoind 后面运行新代码的想法(来自 Michael Gronager)。

全节点与矿工:当时的行动项直接指向他们:升级或打补丁、按页面说明调整锁配置、关注警报。

开发者与实现维护者:教训在实现层——数据库这类底层组件的配置上限,可能在事实上变成共识规则;多实现、多版本共存时,这类隐性上限最危险。

六、现在算不算数,以及一个常见误读

状态为 Deployed,但它是信息类文档:价值在于留档,不在于现行效力。今天的客户端早已不使用当年那套 Berkeley DB 配置,文中具体的锁数、版本与补丁只对理解这段历史有意义。

常见误读:

“2013 年分叉是协议规则本身写错了、或是有人蓄意攻击造成的。”

不对。原文的根因结论很明确:区块本身有效,问题出在旧实现里 Berkeley DB 锁配置不足这一实现细节,它在事实上冒充了一条不一致的共识规则。那笔双花也被原文定性为试验性质、并无盗取意图。真正的教训是:实现细节可能悄悄变成共识的一部分。

七、延伸

本页内容整理自 bitcoin/bips 仓库与 bips.dev,事实已与仓库核对;解读仅供学习参考。