参考资料:
- Yin M, Malkhi D, Reiter M K, et al. HotStuff: BFT consensus in the lens of blockchain[J]. arXiv preprint arXiv:1803.05069, 2018.
- Michael J. Fischer, Nancy A. Lynch, and Mike Paterson. Impossibility of distributed consensus with one faulty process. J. ACM, 32(2):374–382, 1985.
- 实用拜占庭协议(PBFT 99)
- HotStuff共识算法详解
- 25种区块链共识算法全面详解
- 深入理解异步拜占庭共识
文章目录
- 分布式系统
- Basic HotStuff
- 思路
- 伪代码
- Safety, Liveness, and Complexity
- Chained HotStuff
- 思路
- 伪代码
- Event-driven HotStuff
分布式系统
在分布式系统里,并发指令(不是并行)的顺序需要被各个处理器所共识。否则,并发指令顺序错乱会导致处理器状态不一致。区块链是一种分布式系统,最根本的就是共识机制,而挖矿功能一点儿也不重要。
分布式系统的共识机制要满足如下两条要求:
- 安全性(Safety),所有正确的进程都认同同一个值。
- 活性(Liveness),分布式系统最终会认同某一个值。
区块链的共识机制有很多,著名的有:工作量证明(PoW,竞争计算 Hash 碰撞)、权益证明(PoS,持币量和持币时长)、委任权益证明(DPoS,持币人选举 leader 投票)、拜占庭容错(例如 PBFT)、分布式一致性协议(例如 Raft,无法容忍随机错误)、容量证明(PoC,竞争解决 Memory Hard Function)、烧钱证明(PoBurn,烧毁其他币种)。
HotStuff 是一种使用 BFT SMR(拜占庭容错的状态机复制)共识机制的区块链,它利用区块链的链式特性改进了 PBFT,使得 leader failure 的通信复杂度从 O ( n 3 ) O(n^3) O(n3) 降低到了 O ( n ) O(n) O(n) 的线性复杂度。
不可能定理(FLP 1985):reaching consensus in full asynchrony with a single process crashing is impossible with a deterministic protocol,从理论上证明了在纯异步环境下不可能存在一种确定性的共识协议。为了绕过这个定理以达成共识,要么加强对网络的假设,要么引入随机源。
通信模型(communication model):
- synchronous:网络中发出的消息可以在已知的时间 Δ \Delta Δ 内抵达。
- partially synchronous:在未知的 global stabilization time (GST) 发生后,消息可以在已知的时间 Δ \Delta Δ 内抵达。
- Asynchronous:网络仅保证消息最终能抵达,不对到达时间做限制。
通信网络是点对点(point-to-point)、可认证(authenticated)、可靠的(reliable): one correct replica receives a message from another correct replica if and only if the latter sent that message to the former.
我们说的广播(broadcast):it involves the broadcaster, if correct, sending the same point-to-point messages to all replicas, including itself.
PBFT 是工作在 partially synchronous 网络上的 BFT SMR,要求 n ≥ 3 f + 1 n \ge 3f+1 n≥3f+1,这里 n n n 是状态机数量, f f f 是拜占庭错误状态机的数量上界。核心任务是对不断增长的客户指令请求的是否执行以及执行的顺序在所有 correct replica 上达成共识。
Basic HotStuff
思路
HotStuff 的工作环境和目标与 PBFT 一样,但 HotStuff 让状态机维护一颗树(而不是 log 日志):
- 视图序号(viewNumber)不断递增,每个 view 上有唯一确定的 leader,任意 replica 都根据 L E A D E R ( v i e w N u m b e r ) LEADER(viewNumber) LEADER(viewNumber) 计算当前 leader
- 每个 replica 都在本地维护一棵树(a tree of pending commands),每个 node 都恰好对应一个 viewNumber,由对应的 leader 提交
- 节点 node 包含:客户的(一批)指令、共识协议的元数据(matedata)、父指针(parent link)
- 分支 branch 是指,从一个节点 node 根据 parent link 一直返回到 root 的路径
- 分支 branch 单调增长,并在协议中经过 prepare、pre-commit、commit 三个阶段后被提交
- 在每个阶段里,leader 需要收集 a quorum of n − f n-f n−f replicas 的投票,来形成 quorum certificate(QC)
- HotStuff 使用 ( k , n ) (k,\, n) (k,n) threshold signature scheme(门限签名),仅当收集到了 ∣ I ∣ = k = 2 f + 1 |I| = k=2f+1 ∣I∣=k=2f+1 个 partial signature ρ i ← t s i g n i ( m ) \rho_i \leftarrow tsign_i(m) ρi←tsigni(m),才可以组合出合法签名 σ ← t c o m b i n e ( m , { ρ i } i ∈ I ) \sigma \leftarrow tcombine(m,\{\rho_i\}_{i \in I}) σ←tcombine(m,{ρi}i∈I),使得它满足 t v e r i f y ( m , σ ) = 1 tverify(m,\sigma)=1 tverify(m,σ)=1
与 PBFT 不同的是,HotStuff 每次结束三阶段,都会切换视图。它的 three phase 如下:
- prepare phase:新的 leader 收集 n − f n-f n−f 个 NEW-VIEW 消息,找出其中有最高视图的 p r e p a r e Q C prepareQC prepareQC,作为自己的 h i g h Q C highQC highQC,然后在对应的区块 node 上延长分支 c r e a t e L E A F createLEAF createLEAF(叶子上包含客户指令),广播 prepare 消息(包括 leader 自己)以发布提案(Proposal)。各个 replica(包括 leader 自己)根据 s a f e N o d e safeNode safeNode 谓词,判断是否对此提案投赞同票(仅发消息给 leader)。
- pre-commit phase:若 leader 收集到了 n − f n-f n−f 个 prepare votes,那么可以形成 p r e p a r e Q C prepareQC prepareQC 证书,广播 pre-commit 消息。各个 replica 接收到后(此时可确认大多数 correct replica 赞同延长分支),设置本地状态 p r e p a r e Q C prepareQC prepareQC,然后投票。
- commit phase:若 leader 收集到了 n − f n-f n−f 个 pre-commit votes,那么可以形成 p r e c o m m i t Q C precommitQC precommitQC 证书,广播 commit 消息。各个 replica 接收到后(此时可确认大多数 correct replica 都已知道“大多数 correct replica 赞同延长分支”这件事),设置本地状态 l o c k e d Q C lockedQC lockedQC,然后投票(同意提交提案)。
- decide phase:若 leader 收集到了 n − f n-f n−f 个 commit votes,那么可以形成 c o m m i t Q C commitQC commitQC 证书,广播 decide 消息。各个 replica 接收到后(此时确认大多数 correct replica 都同意提交这个提案,这个分支成为 committed branch),执行叶子上的客户指令。然后,各个 replica 执行视图切换。
- nextView interrupt:如果等待超时,那么会执行视图切换中断(interrupt),直接切换视图。向下一视图的 leader 发送 new-view 消息,携带本地的 prepaeQC 以辅助 new leader 识别出当前最高视图的 prepare 区块。
为了达成共识,HotStuff 设置的 safeNode predicate 如下:
- Safety rule: the branch of m . n o d e m.node m.node extends from the currently locked node l o c k e d Q C . n o d e lockedQC.node lockedQC.node(正确的 replica 仅认可在 本地的已锁定节点的所在分支 的链增长,敌手无法使系统共识另一条冲突的链)
- Liveness rule: the replica will accept m m m if m . j u s t i f y m.justify m.justify has a higher view than the current l o c k e d Q C lockedQC lockedQC(正确的 replica 仅接受 比本地的已锁定节点的视图更高 的证书,敌手无法使系统共识更短的链)
伪代码
一些基本的功能:
三阶段的共识协议:
每当 timer 超时,就会执行 new-view 中断,此时将等待时间翻倍。由于通信系统是部分同步的,因此总会使得等待时间足够长,足以接收到其他 replica 发出的消息。 L e a d e r ( ⋅ ) Leader(\cdot) Leader(⋅) 是任意的确定性算法,只要保证全部的 replica 都可以轮转成为 next leader。
Safety, Liveness, and Complexity
我们说两个 branch 是冲突的(conflicting),如果两者都不是对方的扩展(neither one is an extension of the other)。我们说两个 node 是冲突的,如果它们引导的 branch 是冲突的。
分布式系统中的 n n n 个 replica 各自维护本地的树,其中的 n − f = 2 f + 1 n-f=2f+1 n−f=2f+1 个 correct replica 维护着基本相同(除了最后的若干格区块)的树。在三阶段协议中,correct replica 仅对 存在于本地树上的拥有最高视图序号的 p r e p a r e Q C prepareQC prepareQC 证书的节点的后继分支上的安全的叶子 投赞同票。
- Safety:
- 对于任意有效的 q c 1 , q c 2 qc_1,\, qc_2 qc1,qc2,其中 q c 1 . t y p e = q c 2 . t y p e qc_1.type = qc_2.type qc1.type=qc2.type,并且 q c 1 . m o d e qc_1.mode qc1.mode 和 q c 2 . n o d e qc_2.node qc2.node 冲突,那么必然有 q c 1 . v i e w N u m b e r ≠ q c 2 . v i e w N u m b e r qc_1.viewNumber \neq qc_2.viewNumber qc1.viewNumber=qc2.viewNumber
- 如果 w , b w,\, b w,b 是冲突的节点,那么在任何的 correct replica 上,它们不会被同时 commit
- Liveness:
- 如果一个 correct replica 锁定在了 l o c k e d Q C = p r e c o m m i t Q C lockedQC = precommitQC lockedQC=precommitQC 上,那么至少有 f + 1 f+1 f+1 个 correct replica 对匹配 l o c k e d Q C lockedQC lockedQC 的一些 p r e p a r e Q C prepareQC prepareQC 投了赞同票
- 在 GST 后,存在一个有界时间段(bounded time period) T f T_f Tf,使得:如果所有的 correct replica 在时间段 T f T_f Tf 中,都同时停留在视图 v v v 里,并且视图 v v v 的 leader 是正确的,那么将会达成 decision
- The protocol is Optimistically Responsive(最优响应性)
- Complexity:
- 在 HotStuff 协议中,身份认证是最耗时的,replica 在每次投票时都需要附加上自己的签名。在每个阶段,需要 O ( n ) O(n) O(n) 次签名。
- 协议一共有常数阶段,因此总的复杂度为 O ( n ) O(n) O(n)
Chained HotStuff
思路
观察到 Basic HotStuff 中各个阶段的消息都十分相似。作者提出了链式的协议,在每个 prepare phase 都切换视图,从而每个提案都对应唯一属于自己的视图。这减少了消息类型,同时可以流水化作业。
在 Chained HotStuff 中,使用 g e n e r i c Q C genericQC genericQC 取代所有的其他证书,各个阶段度被 generic-phase 取代。因此,只需要两种类型的消息:new-view 消息、generic 消息。
在视图 v v v 上的 generic-phase,它对应于 Basic Hotstuff 里的:
- 视图 v v v 上提案的 prepare 阶段, g e n e r i c Q C genericQC genericQC 是其 h i g h Q C highQC highQC
- 视图 v − 1 v-1 v−1 上提案的 pre-commit 阶段, g e n e r i c Q C genericQC genericQC 是其 p r e p a r e Q C prepareQC prepareQC
- 视图 v − 2 v-2 v−2 上提案的 c o m m i t commit commit 阶段, g e n e r i c Q C genericQC genericQC 是其 l o c k e d Q C lockedQC lockedQC
- 视图 v − 3 v-3 v−3 上提案的 decide 阶段, g e n e r i c Q C genericQC genericQC 是其 c o m m i t Q C commitQC commitQC
在 Chained HotStuff 中,节点 b b b 的高度被设置为对应提案的视图序号 v v v,它的 parent 是视图 v − 1 v-1 v−1 的节点。如果视图 v v v 上的 leader 没能收集出 g e n e r i c Q C genericQC genericQC(提交的提案不安全、leader 崩溃、通信延迟),那么这个节点就成为了哑结点(Dummy nodes),使得 b . j u s t i f y . n o d e ≠ b . p a r e n t b.justify.node \neq b.parent b.justify.node=b.parent,也就是 p r e p a r e Q C prepareQC prepareQC 并不指向它的 parent 节点。
上图中, b ∗ . j u s t i f y . n o d e = b ∗ . p a r e n t = b ′ ′ b^*.justify.node = b^*.parent = b'' b∗.justify.node=b∗.parent=b′′,我们说 b ∗ b^* b∗ 形成了 One-Chain,此时要更新本地状态 g e n e r i c Q C ← b ∗ . j u s t i f y genericQC \leftarrow b^*.justify genericQC←b∗.justify(因为 b ∗ . j u s t i f y b^*.justify b∗.justify 是节点 b ′ ′ b'' b′′ 的提案的 p r e p a r e Q C prepareQC prepareQC)
同时, b ′ ′ . j u s t i f y . n o d e = b ′ ′ . p a r e n t = b ′ b''.justify.node = b''.parent = b' b′′.justify.node=b′′.parent=b′,我们说 b ∗ b^* b∗ 形成了 Two-Chain,此时要更新本地状态 l o c k e d Q C ← b ′ ′ . j u s t i f y lockedQC \leftarrow b''.justify lockedQC←b′′.justify(因为 b ′ ′ . j u s t i f y b''.justify b′′.justify 是节点 b ′ b' b′ 的提案的 p r e c o m m i t Q C precommitQC precommitQC)
而且, b ′ . j u s t i f y . n o d e = b ′ . p a r e n t = b b'.justify.node = b'.parent = b b′.justify.node=b′.parent=b,我们说 b ∗ b^* b∗ 形成了 Three-Chain,此时要执行客户指令 b . c m d b.cmd b.cmd(因为 b ′ . j u s t i f y b'.justify b′.justify 是节点 b b b 的提案的 c o m m i t Q C commitQC commitQC)
伪代码
Event-driven HotStuff
文章中还将 Chained HotStuff 转化为了 event-driven-style(事件驱动风格)。就是添加了一个起搏器(Pacemaker),代替 next leader 在 generic phase 的最后等待和收集 g e n r i c Q C genricQC genricQC
我没细看,但它不会单点失败么?比如起搏器 crash fault 了(非拜占庭错误),或者成为 corrupt 节点(随机错误)。