Elea Notes.

词条 · 加密与分布式 · 核心

分叉与孤块

两人几乎同时出块,链短暂分成两支,落败那支被丢弃

也称:分叉、孤块、临时分叉、fork、orphan、orphan block、stale block、reorg

一句话

两个矿工几乎同时出块时,网络会短暂看到两条竞争的分支;下一个块接到哪一支,另一支就被丢弃,里面的块成为孤块。这是设计预期内的正常现象,不是故障。

先看这个现象为什么必然出现。

先看一个麻烦:消息传得再快也不是瞬间

一百个人分布在全球,每人手上一份账本。规则很简单:谁先写完新的一页,就喊一声,其他人抄下来接着往后写。

问题出在”喊一声”这个动作上:它需要时间。从北京喊到圣保罗,消息要经过一串中继,几秒才能到齐。

在这几秒里,圣保罗那个人并不知道北京已经写完了。如果他刚好也在这几秒里写完,他会理所当然地喊出自己那一页。

现在网络里有两页,都声称是第 901 页,都是诚实写的,都合法。没有人作弊,也没有人出错。

只要信息传播不是瞬时的,这种情况就一定会发生——它不是可以修掉的 bug,它是光速的后果。

朴素尝试为什么都不够

招数一:谁先写完谁算,让后写完的人作废。 听起来公平,但”先”由谁认定?北京的人看到自己先,圣保罗的人看到自己先。这正是最长链规则里讲过的毛病:“先收到”是私人经验,不是网络的共同事实。

招数二:加一个中央协调员,由他裁定。 能解决冲突,代价是引入必须被信任的单点,也就是整套设计要消掉的东西。

招数三:让大家先举手抢锁,抢到才能写。 抢锁本身也是消息,同样要传几秒,同样会撞。而且锁需要一个仲裁者来发,又回到招数二。

招数四:把出块间隔拉长,比如一天一页,撞的概率就极低。 方向是对的——概率确实随间隔变长而下降。但代价难以接受:一笔交易要等一天才能上链。这暴露出一个真实的权衡,出块间隔同时决定了确认速度和分叉频率,不能只优化一头。

招数五:干脆两页都留下,让链变成一棵树。 那就等于没有唯一的账本,甲的那笔钱在两个分支里归属不同,双花重新出现。

四次失败逼出一个不同的思路:别再试图阻止冲突,改成让冲突自己消退。 需要的不是预防机制,而是一个所有人独立执行、结果必然收敛的收敛规则。

机制:让分歧衰减,而不是禁止分歧

节点收到两条竞争分支时,先在自己先收到的那支上继续挖,同时保留另一支。谁也不必宣布放弃。真正的裁决交给下一个块:

        ┌─[901a]        ← 北京写的
[900]───┤
        └─[901b]        ← 圣保罗写的,同时存在,两边都合法

下一个块出来(假设接在 901a 后面):

        ┌─[901a]─[902]  ← 累积工作量更大,成为主链
[900]───┤
        └─[901b]        ← 被丢弃,成为孤块

901b 里的交易并没有消失:它们回到待打包队列,绝大多数会被收进 902 或之后的块。写它的矿工损失的只是那一份出块奖励。

算一下这事多常见。若一个块传遍全网约需 t 秒,出块间隔 600 秒,那么”另一个人刚好在这 t 秒窗口内也出块”的概率约为 t/600:

t =  2 秒 → 约 0.33%   每天 144 个块 → 约 0.48 次分叉
t =  5 秒 → 约 0.83%   → 约 1.20 次/天
t = 10 秒 → 约 1.67%   → 约 2.40 次/天
t = 20 秒 → 约 3.33%   → 约 4.80 次/天

一天几次,量级对得上现实。再看连续撞两次(也就是两条分支各自都长到 2 个块)的概率,以 t=5 秒为例:

0.00833 × 0.00833 ≈ 0.0000694 ≈ 1/14,400
按每天 144 个块算,平均 100 天才出现一次

深度每加一层,概率就乘一次小数——这就是”分歧指数衰减”的具体样子。也解释了确认数的意义:等待不是形式,是让替换概率乘上一个小于 1 的数,一次一次乘下去。

回头看:这里能安心丢块,靠的是前面两条铺好的路

被丢弃的块能被安全地丢掉,前提是两件已经学过的事:

  • 交易可以重新打包,因为交易的有效性由签名决定,跟它待在哪个块里无关。这是数字签名那一条的性质在这里生效。
  • 哪一支胜出不需要任何人裁定,因为累积工作量是从链上数据算出的,人人得同一答案。这是最长链规则第 1 条要求的直接兑现。

还有一处直觉要推翻。招数一到招数三都把冲突当成需要排除的异常,默认一个正确的系统不该出现两个答案。这套设计换了个立场:冲突是常态,唯一性是逐渐得到的结果,不是随时保持的状态。 账本在任一瞬间都可能有两个版本;它只保证越往后看,版本分歧的概率越小。

顺带看清一件事:出块间隔为什么定在十分钟。招数四暴露的权衡在这里被具体化了——间隔太短,分叉频繁,算力被浪费在互相作废的分支上;间隔太长,确认太慢。十分钟是对这个权衡的取值,不是什么自然常数。

边界与常见误解

“孤块”这个词被历史用法搞乱了。 严格说,orphan block 早期指”收到了但找不到父块”的块,而竞争落败的块应叫 stale block。中文里两者都被叫做孤块。现在多数场合说的孤块是后者,读旧文档时要留意。

临时分叉不是软/硬分叉。 这里说的分叉是同一套规则下的短暂竞争,几分钟内自动消失。软分叉、硬分叉指的是规则本身发生变更,那是协议升级问题,除了名字相同以外没有关系。

孤块里的交易没有被撤销。 它们回到内存池等待重新打包。但有一个例外要小心:如果某笔交易和胜出分支里的另一笔冲突(同一笔钱花给了不同人),那它就永远进不去了。这正是双花攻击利用的窗口,也是确认数存在的原因。

零确认交易不安全,一次确认也不算稳。 一笔刚上链的交易随时可能因为分支替换而回到未确认状态。等待多少个块取决于金额:小额一到两个,大额通常六个(按前面的指数衰减,六层之后概率已经很小)。

分叉频率反映网络传播质量,不反映算力多少。 分叉变多说明块传得慢(区块变大、网络变差),不说明矿工变多了。降低分叉率靠的是改进传播(紧凑区块、专用中继网络),不是提高算力。