PBFT

HydraChain Consensus
Tendermint Byzantine Consensus Algorithm

HydraChain BFT

HydraChain的共识是一种拜占庭容错协议,用于协调关于区块链系统事务顺序的共识。

特点

  • 最后,没有状态重组
  • 正常运行时低开销

协议依赖于一组验证者,其中不超过1/3的验证者必须是拜占庭式的。

在每个区块的高度,一个或多个回合被用来商定一个提议区块的高度。区块的每个高度和回合的提议者是从验证者集合中选择的确定的轮询器。新回合只能启动一次+2/3的节点在最后一回合投票中投票,这使得分布式系统保持同步。

正常运行的开销很低,因为提议的区块在最后高度的区块上有法定数量的签名。

propose H -> commit H-1 -> vote H

符号

  • HR:区块高度,回合
  • B:区块
  • +1/n:超过1/n的投票

回合

  • 序列propose H -> vote H称为一个回合R
  • 在一个给定高度可能有多余一个回合
  • 超时没有收到投票则投上次的票或者表明超时

投票/锁
投票是验证者发送的签名消息,验证者的签名为:(H, R, [B])

两种投票:

  • Lock(H, R, B):验证者对锁定在H,R中的区块的投票
  • NotLocked(H, R):验证者没有锁定在H中的区块并承诺不锁定H,R

验证者每回合都会发送他们的投票(必须发送一次投票),或者是响应提议者(Lock)的消息,或者是超时(NotLocked或Lock)。

LockSet(R)
LockSet(R):至少+2/3的合格投票的集合。
每个验证者都根据收到的投票收集自己的LockSet。一个有效的LockSet必须包含+2/3的合格投票。投票可以锁定在一个区块上,也可以不锁定(在超时的情况下)。有效的LockSet允许节点进入下一回合。

三种LockSet:

  • Quorum:有超过2/3对同一个区块的合格投票
  • QuorumPossible:有超过1/3对同一个区块的合格投票
  • NoQuorum:有最多1/3对同一区块的合格投票

VoteInstruction(R, B)

R>R0的提议包括一个QuorumPossible(R-1)以及至少一个非拜占庭投票。

共识协议

为了就区块链的下一个高度达成一致,将运行基于回合的协议来确定下一个区块。每一回合至少由两步组成(提议和投票)。

如果一个提议区块存在一个Quroum则意味着回合成功

在正常操作中,一个验证者在区块高度H的步骤如下:

  1. 收到提议B(H,R0)包括在B(H-1,R)Quorum
  2. 提交B(H-1, R)
  3. 投票B(H,R0)

注意:H-1的提交是隐含的,因为H中的提议必须包含H-1上的区块的Quorum。

由于节点也需要维护自己的LockSet,它们可以在看到Quorum之后立即提交。这通常是在它们收到关于新区块高度的提议之前。因此,大多数时候的顺序看起来是这样的:

  1. 接收提议B(H,R0)
  2. 投票B(H,R0)
  3. 截获Quorum B(H, R0)
  4. 提交B(H,R0)

这个异步提交是安全的,因为如果曾经有一个Quorum-LockSet,提交者至少已经学会(并且必须包括)一个QuorumPossible,这将导致对该区块的共识。

为了避免对相互冲突的提议投赞成票,每回合投票中合格的选民在发送投票以后被锁定,即:

  • Locked(H, R, B):如果对一个区块进行了投票
  • NotLocked(H, R):如果没有在一个区块上投票

只要一个节点被锁定,它就不能在H上为另一个区块投票。只有节点为一个不同的区块(像下面描述的情况)投票时,它才被允许解锁。

  • 它处于NotLocked(也就是说,它没有看到有效的提案,并且在上一轮投票中超时)
  • 它接收到VoteInstruction(超过1/3的节点被锁在不同区块)

节点必须在每一回合投票中投票(例如重复旧的投票,但在新一回合中签名)

如果指定提议者有一个LockSet(R-1) 并不是Quorum,则必须

  • NoQuorum:提议一个新区块
  • QuorumPossible:广播一条引用区块的VoteInstruction消息

在这两种情况下,消息都包含LockSet(R-1),它允许节点最终解锁并再次投票(在R中提议的区块上或在R-n中提议并指示在R中投票)

NewRound(H, R)

如果一个节点在R中收到并投票赞成一个有效的提案,或者在R中收到+2/3的投票时,那么该节点就会进入到下一回合R+1。更高的回合有更高的超时,timeout(R) = t_base * t_inc ^ R

Propose(H, R)

H,R处的block的提议者是从验证者集合中选择的轮询器。只有一个验证者必须在H,R上指定一个区块。
提议者必须在R-1中获得至少2/3的投票,才能有一个有效的LockSet(R-1)
如果是QuorumPossible,那么提议者广播一个VoteInstruction(R, B)指令。

否则,它会广播一个新的提案,如果它知道是Quorum,就会广播一个新的区块高度。

H,R0的有效新提案是区块表示为:

  • block H-1包含一个Quorum-LockSet
  • 描述一个H-1 > H的有效状态变化

H, R > R0的有效新提案是另外的区块:

  • 包括一个NoQuorum(R-1)

提议会尽快被广播,因为在最后一回合投票中,可以是下面任何一个LockSet:

  • Quorum:共识 ->广播新区块B(H, R0)
  • NoQuorum:最多只有1/3的选票锁定在同一个block上 -> 广播新区块B(H, R)
  • QuorumPossible:超过1/3的投票锁定在区块B(H, Rn < R)上 -> 广播VoteInstruction B(H, Rn < R)

Commit(H, R)

当节点第一次得知Quorum(H,R)时,进入Commit回合。这可以通过在提案中获得一个LockSet,或者通过在其本地LockSet收集足够的投票来实现。

如果区块的父区块未知,节点进入同步模式并请求丢失的区块。

如果父区块可用:

  • Commit引用Quorum的区块
  • unlock
  • 隐式移动到新的区块高度H+1

On Timeout(H, R)

  • 验证者在前一个回合被锁定在区块B,广播Lock(H, R, B)
  • 验证者未被锁定,广播NotLocked(H, R)
  • 当前高度timeout增加

Vote(H, R)

案例:验证者从H,R收到合法提议

  • 如果被锁定在一个区块:解锁
  • 将新提议锁定
  • 广播一个新的Lock(H, R, B)

案例:验证者从H, R收到非法提议

  • 如果被锁住,广播Locked(H, R, B)
  • 如果没被锁住,广播NotLocked(H, R)

案例:验证者收到一个合法的VoteInstruction(H, R, B)

  • 如果被锁定在一个区块:解锁
  • 从VoteInstruction锁定B
  • 广播Lock(H, R, B)

有效的Proposals/VoteInstructions必须由H,R指定提议者签署。它们必须为R>0提供一个有效的LockSet(R-1)

在投票之前,验证所提议区块的状态转换。

无效的提议被忽略。

Tendermint PBFT

Byzantine Consensus Algorithm

状态机的状态:NewHeightProposePrevotePrecommit,和Commit

在区块链的每个高度处,运行基于回合的协议以确定下一个区块。

每一个回合:

  • 三个基本步骤:ProposePrevotePrecommit
  • 两个特殊步骤:CommitNewHeight

在最佳方案中,步骤顺序为:

NewHeight -> (Propose -> Prevote -> Precommit)+ -> Commit -> NewHeight ->...

(Propose -> Prevote -> Precommit)被称为一个回合。

每一个区块高度可能需要不止一个回合,原因:

  • 指定提议者不在线
  • 指定提议者提议的区块无效
  • 指定提议者提议的区块没有及时广播
  • 提议的区块有效,但当到达Precommit步骤时,没有及时收到超过2/3的prevotes
  • 提议的区块有效,并且接收到足够的+2/3的prevotes,但是验证者节点没有收到超过2/3的precommit

状态机图:

                         +-------------------------------------+
                         v                                     |(Wait til `CommmitTime+timeoutCommit`)
                   +-----------+                         +-----+-----+
      +----------> |  Propose  +--------------+          | NewHeight |
      |            +-----------+              |          +-----------+
      |                                       |                ^
      |(Else, after timeoutPrecommit)         v                |
+-----+-----+                           +-----------+          |
| Precommit |  <------------------------+  Prevote  |          |
+-----+-----+                           +-----------+          |
      |(When +2/3 Precommits for block found)                  |
      v                                                        |
+--------------------------------------------------------------------+
|  Commit                                                            |
|                                                                    |
|  * Set CommitTime = now;                                           |
|  * Wait for block, then stage/save/commit block;                   |
+--------------------------------------------------------------------+

状态机说明:

Propose Step (height:H,round:R)
进入Propose:

  • 指定节点在(H, R)提议一个区块

结束propose:

  • 进入Propose的timeoutProposeR后 --> 进入Prevote(H,R)
  • 接收到提议的区块之后,在PoLC-Round进行所有prevotes --> 进入Prevote(H,R)
  • 常见退出条件

Prevote Step (height:H,round:R)

进入Prevote,每个验证者广播它的prevote投票:

  • 首先,如果从LastLockRound开始验证者就锁定在一个区块上,但是现在在PoLC- round回合上有一个PoLC,在那里LastLockRound < PoLC- round < R,然后它就会解锁。
  • 如果验证者仍然锁定在一个区块上,那么执行prevotes
  • 否则,如果Propose(H,R)提议的区块是好的,那就prevotes
  • 否则,如果提案无效或没有按时收到,它将prevotes

结束Prevote:

  • 对一个区块超过2/3的prevote或者nil,进入Precommit(H,R)
  • 接收到超过2/3的prevote时timeoutPrevote,进入Precommit(H,R)。
  • 常见退出条件

Precommit Step (height:H,round:R)

进入Precommit,每个验证者广播它的Precommit投票

  • 如果验证者对某一个区块在H,BPoLC,那么锁定区块B,设置LastLockRound = R。
  • 如果验证者在H,BPoLC,解锁它并precommits
  • 否则保持锁定提交precommits

对于Precommit 意味着“我在这一轮没有看到PoLC,但我确实得到了+2/3的prevotes,并等待了一会儿”。

结束Precommit:

  • 超过2/3的precommit为空,进入Propose(H,R+1)
  • 接收到2/3的precommit的timeoutPrecommit,进入Propose(H,R+1)
  • 常见退出条件

常见退出条件

  • 对一个区块有+2/3的precommits,进入commit(H)
  • 在(H,R+x)接收到+2/3的prevotes,进入Prevote(H,R+x)
  • 在(H,R+x)接收到+2/3的precommits,进入Precommit(H,R+x)

Commit Step (height:H)

  1. 设置CommitTime = now()
  2. 接收到新区块,进入NewHeight(H+1)

NewHeight Step (height:H)

  1. 从PreCommits转到LastCommit,增加高度
  2. 设置StartTime = CommitTime+timeoutCommit
  3. deng

你可能感兴趣的:(PBFT)