Skip to content

分叉生成

Solana 协议在生成新的区块时不会等待所有验证者达成共识后才生成下一个区块。因此,经常会出现两个不同的区块链接到同一个父区块的情况。这些冲突的链被称为“分叉”

Solana 验证者需要在这些分叉中投票,并通过共识算法达成一致,选择其中一个分叉继续(共识算法的具体细节不在本文的讨论范围)。需要记住的主要一点是,当存在竞争分叉时,只有一个分叉会被集群最终选定,而竞争分叉中的区块将被丢弃。

本节描述了由于领导者轮换而自然发生的分叉。

概述

节点将轮流作为领导者,生成编码状态变化的历史证明(PoH)。即使集群中的某个领导者失去连接,集群也能通过合成该领导者本应生成但未包含任何状态变化的数据来处理这种情况。

可能的分叉数量将被领导者轮换时间边界可能出现的“存在/不存在”跳跃列表限制。在任何给定的槽(slot)中,只有单个领导者的交易会被接受。

分叉示例

下表展示了可能出现的竞争分叉。时间从左到右推进,每个槽会被分配给一个验证者,该验证者将暂时成为集群的“领导者”,并可能为该槽生成一个区块。

在这个示例中,槽(slot) 3 的领导者选择将其“区块 3”直接链接到“区块 1”,跳过了“区块 2”。同样地,槽 5 的领导者选择将“区块 5”直接链接到“区块 3”,跳过了“区块 4”。

注意,在不同的分叉中,为给定槽生成的区块 始终 相同,因为为同一个槽生成两个不同的区块将会受到被惩罚。因此,上述冲突分叉可以通过它们 跳过 的槽来区分。

槽 1槽 2槽 3槽 4槽 5
分叉 1区块 1区块 3区块 5
分叉 2区块 1区块 3区块 4
分叉 3区块 1区块 2

信息流

  1. 交易由当前领导者接收。
  2. 领导者过滤,保留有效交易。
  3. 领导者执行有效交易,更新相应状态。
  4. 领导者根据其当前 PoH 槽将交易打包成条目。
  5. 领导者将条目传输给验证节点(以签名的分片形式)。
    1. PoH 流包含条目(ticks);这些空条目表明领导者的活跃状态以及集群中的时间流逝。
    2. 领导者的流从完成 PoH 所需的 tick 条目开始,直到领导者最近观察到的前一个领导者槽。
  6. 验证者将条目重新传输给其集合中的同伴以及进一步下游的节点。
  7. 验证者验证交易并在他们存储的状态上执行这些交易。
  8. 验证者计算状态的哈希值。
  9. 在特定时间点,即特定 PoH tick 计数时,验证者向领导者传输投票。
    1. 投票是对特定 PoH tick 计数时计算的状态哈希的签名。
    2. 投票也通过 gossip 协议传播。
  10. 领导者执行投票,与其他交易一样,并将其广播到集群。
  11. 验证者观察自己的投票和来自集群的所有投票。

分区、分叉

在对应于投票的 PoH tick 计数处可能会出现分叉。下一个领导者可能没有观察到最后的投票槽,从而使用生成的虚拟 PoH 条目开始。这些空 ticks 由集群中的所有节点以集群配置的哈希/每 tick 速率 Z 生成。

在投票槽期间,PoH 只有两种可能的版本:当前领导者生成的带有 T 个 ticks 和条目的 PoH,或者仅有 ticks(ticks only) 的 PoH。仅有 ticks 的 PoH 版本可以被视为虚拟账本,集群中的所有节点都可以从前一个槽中的最后一个 tick 导出这种 PoH。

验证者可以忽略其他可能(例如,来自错误领导者)的分叉,或惩罚导致分叉的领导者。

验证者基于贪婪选择的原则来进行投票,从而最大化他们在 Tower BFT 中描述的奖励。

验证者的视角

时间进展

下图表示验证者视角的 PoH 流,随着时间推移可能出现的分叉。L1、L2 等是领导者槽,E 表示在该领导者槽期间来自该领导者的条目。x 表示仅包含 ticks 的条目(虚拟条目),时间在图中是自上而下的。

                                                                validator action
               +----+                                           ----------------
         |     | L1 |                  E1
         |     +----+            /             \                vote(E1)
         |     | L2 |          E2                x
         |     +----+        /    \           /     \           vote(E2)
  time   |     | L3 |     E3        x        E3'      x
         |     +----+    /  \     /   \     /  \     /  \       slash(E3)
         |     | L4 |  x     x  E4     x   x    x   x    x
         |     +----+  |     |  |      |   |    |   |    |      vote(E4)
         v     | L5 |  xx   xx  xx    E5  xx   xx  xx   xx
               +----+                                           hang on to E4 and E5 for more...

注意,在同一个槽上出现的两个分叉中的 E 是一种可被惩罚的情况,因此,观察到 E3E3' 的验证者可以惩罚 L3 并安全地选择该槽的 x(ticks only)。一旦验证者承诺于一个分叉,其他分叉可以在该 tick 计数下被丢弃。对于任何槽,验证者只需考虑由领导者提出的单个“有条目(has entry)”链或“仅有 ticks(ticks only)”链即可。但是,当多个虚拟条目链接回前一个槽时,它们可能会重叠。

时间分割

考虑领导者轮换的 PoH tick 计数作为编码集群状态工作的时间分割是有很用的。下表展示了上述分叉树作为时间分割账本。

领导者槽L1L2L3L4L5
数据E1E2E3E4E5
自前一次以来的 tickxxx

请注意,只有来自 L3 领导者的数据会在 L3 槽被接受。如果 L3 未观察到 L2 的数据,L3 中的数据可能包括除L2外其他槽的“补充” ticks。L4 和 L5 的传递包括“回到之前(ticks to prev)”的 PoH 条目。

这种网络数据流的排列允许节点将其精确保存到账本,以供再运行、重启和作为检查点使用。

领导者的视角

当新领导者开始一个槽时,必须首先传输任何链接新槽与最近观察到并投票的槽所需的 PoH(ticks)。领导者提议的分叉会将当前槽链接到领导者已投票的先前带有虚拟 ticks 的分叉。