HotStuff共识算法详解

1. 前言

HotStuff提出了一个三阶段投票的BFT类共识协议,该协议实现了safety、liveness、responsiveness特性。经过在投票过程当中引入门限签名实现了O(n的消息验证复杂度。Hotstuff总结出对比了目前主流的BFT共识协议,构建了基于经典BFT共识实现pipeline BFT共识的模式。html

HotStuff是基于View的的共识协议,View表示一个共识单元,共识过程是由一个接一个的View组成。在一个View中,存在一个肯定Leader来主导共识协议,并通过三阶段投票达成共识,而后切换到下一个View继续进行共识。假如遇到异常情况,某个View超时未能达成共识,也是切换到下一个View继续进行共识。算法

Basic hotStuff基础版本的共识协议,一个区块的确认须要三阶段投票达成后再进入下一个区块的共识。pipeline hotStuff是流水线的共识协议,提升了共识的效率。安全

2. 协议内容

2.1. 协议基础

2.1.1. 名词解释

  • BFT: 全称是Byzantine Fault tolerance, 表示系统能够容纳任意类型的错误,包括宕机、做恶等等
  • SMR: 全称是State Machine Replication, 一个状态机系统,系统的每一个节点都有着相同的状态副本
  • BFT SMR protocol: 用来保证SMR中的各个正常节点都按照相同的顺序执行命令的一套协议
  • View: 表示一个共识单元,共识过程是由一个接一个的View组成的,每一个View中都有一个ViewNumber表示,每一个ViewNumber对应一个Leader
  • QC(quorum certificate): 表示一个被(nf)个节点签名确认的数据包及viewNumber。好比,对某个区块的(nf)个投票集合。
  • prepareQC: 对于某个prepare消息,Leader收集齐(nf)个节点签名所生成的证据(聚合签名或者是消息集合),能够视为第一轮投票达成的证据
  • lockedQC: 对于某个precommit消息,Leader收集齐(nf)个节点签名所生成的证据(聚合签名或者是消息集合),能够视为第二轮投票达成的证据。

2.1.2. 副本状态机 | State Machine Replication

副本状态机(SMR, State Machine Replication)指的是状态机由多个副本组成,在执行命令时,各个副本上的状态经过共识达成一致。网络

假如各个副本的初始状态是一致的,那么经过共识机制使得输入命令的顺序达成全局一致,就能够实现各个副本上状态的一致。并发

在SMR中,存在一个Leader节点发送proposal,而后各个节点参与投票达成共识。异步

系统输入为tx,网络节点负责将这些tx,打包成一个block,每一个block都包含其父block的哈希索引。async

2.1.3. 网络假设

在实际的分布式系统中,因为网络延时、分区等因素,系统不是同步的系统。分布式

在异步的网络系统,由FLP原理可知,各个节点不可能达成共识,所以对于分布式系统的分析,通常是基于部分同步假设的。ide

  • 同步(synchrony):正常节点发出的消息,在已知的时间间隔内能够送达目标节点,即最大消息延迟是肯定。
  • 异步(asynchrony):正常节点发出消息,在一个时间间隔内能够送达目标节点,可是该时间间隔未知,即最大消息延迟未知。
  • 部分同步(partially synchrony): 系统存在一个不肯定的GST(global stable time)和一个Δ,使得在GST结束后的Δ时间内,系统处于一个同步状态。

2.2. basic HotStuff 三阶段流程

2.2.1. Prepare阶段

每一个View开始时,新的Leader收集由(nf)个副本节点发送的NEW-VIEW消息,每一个NEW-VIEW消息中包含了发送节点上高度最高的prepareQC(若是没有则设为空)。区块链

prepareQC能够看作是对于某个区块(nf)个节点的投票集合,共识共识过程当中第一轮投票达成的证据

Leader从收到的NewView消息中,选取高度最高的preparedQC做为highQC。由于highQC是viewNumber最大的,因此不会有比它更高的区块获得确认,该区块所在的分支是安全的。

下图是Leader节点本地的区块树, #71是Leader节点收到的highQC, 那么阴影所表示的分支就是一个安全分支,基于该分支建立新的区块不会产生冲突。

Leader节点会在highQC所在的安全分支来建立一个新的区块,并广播proposal,proposal中包含了新的区块和highQC,其中highQC做为proposal的安全性验证。

其余节点(replica)一旦收到当前View对应Leader的Proposal消息,Replica会根据会safeNode-predicate规则检查Proposal是否合法。若是Proposal合法,Replica会向Leader发送一个Prepare-vote(根据本身私钥份额对Proposal的签名)。

Replica对于Proposal的验证遵循以下的规则:

1). Proposal消息中的区块是从本机lockQC的区块扩展产生(即m.block是lockQC.block的子孙区块)

2). 为了保证liveness, 除了上一条以外,当Proposal.highQC高于本地lockQC中的view_number时也会接收该proposal。

 

safety判断规则对比的是lockQC,而不是第一轮投票的结果,因此即便在上一轮针对A投了prepare票,假如A没有commit,那么下一轮依然能够对A’投票,因此说第一轮投票能够反悔。

2.2.2. Precommit

Leader发出proposal消息之后,等待(nf)个节点对于该proposal的签名,集齐签名后会将这些签名组合成一个新的签名,以生成prepare-QC保存在本地,而后将其放入PRECOMMIT消息中广播给Replica节点。

prepare-QC能够代表有(nf)个节点对相应的proposal进行了签名确认。

digraph prepare { rankdir=LR; Leader -> Replica1 [label="PRECOMMIT"] Leader -> Replica2 Leader -> Replica3 Leader -> Replica4 } 
  • 在PBFT、Tendermint中,签名(投票)消息是节点间相互广播,各个节点都要作投票收集工做,因此对于每轮投票,Replica都须要至少验证(nf)个签名。

  • 在HotStuff中引入了阈值签名方案,Replica利用各自的私钥份额签名,由Leader收集签名,Replica只须要将签名消息发送给Leader就能够。Leader将Replica的签名组装后,广播给Replica。这样HotStuff的一轮投票每一个Replica只须要验证一次签名。

  • 在HotStuff中,一轮投票的过程,是经过replica与Leader的交互完成

    • replica收到proposal,对其签名后,发送给Leader
    • Leader集齐签名(投票)后,将签名(投票)组装,广播precommit消息
    • replica收到Precommit,验证其中签名,验证经过则表示第一轮投票成功。

LibraBFT是基于hotStuff的共识协议,可是并无采用hotStuff中的阈值签名方案

当Replica收到Precommit消息时,会对其签名,而后回复给leader。

2.2.3. Commit

commit阶段与precommit阶段相似,也是Leader先收集(n-f)个precommit-vote,而后将其组合为precommit-QC,并将其放在COMMIT消息中广播。

当Leader收到当前Proposal的(n-f)个precommit-vote时,会将这些投票组合成precommit-QC,而后将其放入COMMIT消息中广播。

当Replica收到COMMIT消息时,会对其签名commit-vote,而后回复给leader。更为重要的是,在此时,replica锁定在precommitQC上,将本地的lockQC更新成收到的precommitQC.

  • 从Replica发出precommit-vote到Leader集齐消息并发出commit消息,这个过程至关于pbft、tendermint中的第二轮投票。
  • Replica收到了commit消息,验证成功后,表示第二轮投票达成。此时Replica回复给Leader,而且保存precommitQC到lockedQC.

2.2.4. Decide

当Leader收到了(n-f)个commit-vote投票,将他们组合成commitQC,广播DECIDE消息。

Replica收到DECIDE消息中的commitQC后,认为当前proposal是一个肯定的消息,而后执行已经肯定的分支上的tx。Viewnumber加1,开始新的阶段。

  • Note: 这里也是针对输入作共识,共识后再执行已经肯定共识分支上的交易。

2.3. Safety

2.3.1. Safety性证实

2.3.1.1. 同一个View下,不会对冲突的区块,产生相同类型的QC

证实思路: 反证法,假如在同一个view下,产生了相同类型的QC,并且最多存在f个做恶节点,那么就会有一个诚实节点双投了,这与前提假设矛盾。

  • Lemma1: 对于任意两个有效的qc1qc2,假如qc1.type==qc2.type,且qc1.blockqc2.block冲突,那么必然有qc1.viewNumber!=qc2.viewNumber.

证实(反证法):

假设qc1.viewNumber==qc2.viewNumber

那么,在相同的view中,有2f+1个replica对qc1.block进行签名投票,一样有2f+1qc2.block投票,这样的话,就存在一个正常节点在算法流程中投了针对某个消息投了两票,这与算法流程冲突。

2.3.1.2. 正常Replica不会commit冲突的区块

证实思路: 反证法,假如正常节点commit了冲突的区块,咱们追踪到最先出现的冲突区块的位置,则这个冲突的位置确定与两条safety规则相矛盾。

证实:

1. 根据**Lemma1**, 在相同的view下,正常的replica不会对冲突的区块产生commitQC,因此不会commit冲突的区块。
2. 下面证实在不一样的view下,正常的replica也不会对冲突的区块产生commit

证实(反证法):

假设viewNumber在v1和v2时(v1 < v2),commit了冲突的区块,即存在commitQC_1 = {block1, v1}, commitQC_2={block2, v2},且block1与block2冲突。为了简化证实,咱们同时假设v1与v2之间不存在其余的commitQC了,即commitQC_2是commit_1以后的第一个commitQC.

在v1和v2之间,确定存在一个最小的v_s(v1 < v_s <= v2),使得v_s下存在有效的prepareQC_s{block_s, v_s},其中block_s与block1冲突.

当含有block_s的prepare被广播后,节点会对该消息作safety验证,因为block_s与block1冲突,因此显然,不符合safety规则1.

那么是否会符合规则2呢?
假如block_s.parent.viewNumber > block_1.viewNumber,那么显然block_s.parent与block_1冲突,因此block_s.parent是更早的与block1冲突的,这与v_s最小矛盾。

有2f+1个节点对于block_s的prepare消息投了票,那么这些节点在收到Prepare_s时,会进行safeNode验证,正常状况下,因为block_s与block1冲突,那么正常节点不会投出prepare_vote票,故而根本不会产生prepareQC_s, v_s根本不会存在. 这与上述假定冲突,所以在不一样的view下,不可能对相同的block产生commit.

2.4. chained hotStuff

在basic hotStuff中,三阶段投票每一阶段无非都是发出消息而后收集投票,那么可使用以下的方式简化协议。

在Prepare阶段的投票由当前view对应的leader1收集,集齐后生成prepareQC。而后将prepareQC发送到下一个view的leader2那里,leader2基于prepareQC开始新的prepare阶段,这是leader2的prepare阶段,同时也是leader1的precommit阶段。以此类推,leader2产生新的prepareQC,而后发送给下一个view的leader3,leader3开始本身的prepare阶段,同时也是leader1的commit阶段、leader2的precommit阶段。

协议简化为以下过程:

  • Leader节点
    • 等待NewView消息,而后发出Proposal
    • 发出Proposal后,等待其余节点的投票
    • 向下一个Leader发出NewView消息
  • 非Leader节点
    • 等待来自Leader的Proposal消息
    • 收到Leader的Proposal消息后,检查消息中的QC,更新本地的prepareQC、lockedQC等变量,发出投票
    • 向下一Leader发出NewView消息

2.4.1. Dummy block

正常状况下,每一个View中都有一个区块产生并集齐签名,可是状况不会老是这么完美,有时不会有新的区块产生。为了保持区块高度与viewNumber的一致,hotStuff中引入了Dummy block的概念。假如在一个View中,不能达成共识,那么就在为该View添加一个Dummy block

2.4.2. k-chain

一个区块中的QC是对其直接父区块的确认,那么咱们称之为1-chain。同理,一个区块b后面没有Dummy block的状况下,连续产生了k个区块,则称这段区块链分支是对区块b的k-chain。

若是b’对b造成了1-chain,那么b’至关于b的prepare阶段达成(第一轮投票成功),节点会将本地的prepareQC更新。

每当一个新的区块造成,节点都会检查是否会造成1-chain,2-chian,3-chain.

  • 1-chain: 有新的prepareQC造成,更新本地的prepareQC
  • 2-chain: 有新的precommitQC造成,更新本地的lockedQC
  • 3-chian: 有新的commitQC造成,有新的区块分支进入commit状态,执行确认的区块分支

2.4.3. Pacemaker

把hotstuff抽象成一个事件驱动的协议,能够将liveness相关的功能抽离出来,成为单独的pacemaker模块。safety与liveness在实现上解耦,safety是协议的核心保证安全性,liveness由pacemaker保证。

  • Pacemaker实现以下几部分功能
    • Leader检查
    • 收集NewView消息,对齐View并更新highQC

3. Q&A

  • BFT类共识算法研究对比: PBFT - Tendermint - hosStuff - Casper - GRANDPA

    • PBFT: 两阶段投票,每一个view有超时,viewchange经过一轮投票来完成,viewchange消息中包含了prepared消息(即达成了第一阶段投票的消息)。
    • Tendermint: 两阶段投票,一个round中的各个阶段都有超时时间,roundchange经过超时触发(而不是投票),网络节点保存本身已经达成第一阶段投票的消息(即polka消息)。
    • hotStuff: 三阶段投票,每一个view有超时,采用阈值签名减少消息复杂度。liveness与safety解耦为两个部分
    • GRANDPA: 将出块与共识确认分离,用来对已经产生的区块链进行投票确认,两阶段投票,可是投票是针对区块分支(对一个区块投票也至关于对其全部父区块投票),而不是特定区块,各个节点能够针对不一样高度的区块投票
  • 第三阶段投票的意义?(对比pbft、tendermint)
    • 表面上看,第三轮投票是为了在确认大多数节点(2f+1)达成前两轮投票后,再发出NewView消息。这是为了经过用一轮投票来保证大多数节点均可以进入下一个高度,经过一轮投票让各个节点保持视图对齐。

    • 反过来看,假如去掉第三轮投票,达成第二轮投票后就发出NewView消息,会出现各个节点步调不一致,若是新的Leader自身运行的慢一点,第二轮投票尚未达成,那么收到NewView时,校验时会通不过。(可是经过几轮超时切换,共识流程依然会正常进行)

    • 我认为,假如没有第三阶段,不会影响节点的liveness和safety特性。

    • 在PBFT中的view change也是经过一轮投票实现的,与hotstuff中同样,只是pbft中只有在leader不能工做时候才会启动view change,在hotstuff中每一个区块都会切换leader。加入pbft也是每次都切换Leader,那么pbft算上view change的话,也是三阶段投票。

    • 在tendermint中,视图切换没有经过投票来完成,而是经过固定的时间间隔来实现的,即便集齐了两轮投票也须要等待本轮view的时间耗尽才会进入下一个view,经过时间的等待,确保节点的视图对齐。tendermint这样的优势是少了一轮投票,可是牺牲了responsiveness。responsiveness指的是一个区块被leader发出后,到达成共识的时间间隔只与实际的网络延时有关。而Tendermint中,即便网络状态无缺,依然须要等待6秒左右的时间才能达成共识。hotstuff使用一轮投票,保持了responsive特性。

  • 达成第二阶段投票后,区块就不可逆转了,假如在此时执行交易是否有问题?
    • 我认为能够,可是会形成各个节点的临时状态新旧不一,而放在第三阶段投票后执行,能够保证大多数节点的状态一致性,与视图对齐的效果差很少。
  • 相比于Tendermint, NewView消息的意义是什么?
    • NewView消息中包含了Replica节点的prepareQC,这能够保证,新的Leader可以基于全局最新状态进行下一轮共识(而不是只根据本身本地的状态)。
      • hotStuff新区块不是在已经commit的区块上添加,而是在highQC所在的安全分支上添加新区块,因此NewView消息是有必要的,不然会广播冲突或者无效的prepare。
      • 在Tendermint中,新的Leader多是不知道其余节点存在锁定的区块,因此只根据节点本地的状态打包新的区块,这可能形成不一样节点锁定在不一样区块上。而为了保证Liveness,又引入了解锁机制(Istanbul中也采用了相似的解锁方案)。
  • Responsiveness: block的确认时间,只取决于网络的实际延时,而不是取决于某个预先肯定的时间限制。
    • Tendermint在网络正常状况下,也是6秒左右一个块
    • hotStuff中区块的确认时间只与实际的网络延迟有关
  • Responsiveness在区块链的世界中是否重要?

原文出处:https://www.cnblogs.com/gexin/p/12031954.html

相关文章
相关标签/搜索