共识

CITA的共识模块主要是保证多个节点对于交易的顺序和Block的内容达成一致。在众多的分布式算法中, 我们选择实现了非拜占庭容错的Raft算法和拜占庭容错的的Tendermint算法。

共识的架构

共识主要有MQ(消息队列)通讯模块、交易池、定时模块、WAL(write ahead log)、算法逻辑模块。

+-------------+       +-------------+       +-----+
| MQ通讯模块   |<----->| 算法逻辑模块 |<----> | WAL |
+-------------+       +-------------+       +-----+
       ^                ^    ^
       |                |    |
       |----------------+    |
       |                     |
  +--------+           +-----------+
  | 交易池  |           | 定时模块  |
  +--------+           +-----------+

MQ通讯模块: CITA的消息通过MQ进行周转,MQ通讯模块负责订阅、发布基于MQ的消息。

交易池: 交易池订阅和存储交易信息,并提供交易的打包、生成Block。还进行交易的持久化,实现快速确认的功能。

定时模块: 提供算法定时服务。使用者向定时模块发送定时请求,定时模块在时间到达后,发送确认信息。

WAL: WAL提供预写日志(write ahead log)的服务,持久化各个节点的投票。用来进行节点崩溃后恢复。

算法逻辑模块: 分布式算法逻辑的实现模块,接受共识其它模块发送过来的信息,根据自身的算法要求,进行算法逻辑相应的处理。 例如对于Tendermint就需要进行一系列的状态转换。

基本前提

  1. 每个共识节点知道其它共识节点的公钥
  2. 每个共识节点发送的投票信息,都必须有自己的签名
  3. 共识节点根据公钥和签名可以验证消息的真实性
  4. 共识节点数量需要满足算法要求的基本数量

基本步骤

虽然分布式算法多种多样,具体落实在CITA中,基本上需要进行如下的步骤:

  1. 共识模块从消息队列中订阅交易信息,放入交易池。如果应用有快速确认的需求,交易池可以对交易进行持久化。
  2. 共识算法根据配置和算法要求,选择一个出块的节点。该出块节点把块的哈希值(Block.Hash)作为共识的主要信息通过MQ通讯模块向其它的节点进行广播。
  3. 当出块节点进过一轮或者多轮投票,收到算法要求的法定多数的投票返回时,向Chain模块确认出块,否则进入重新选择出块节点的计算,由下一个出块节点继续出块。
  4. 接收Chain返回的状态信息,作为出块成功的标志。
  5. 出块成功后,从交易池删除已经达成共识的交易。

Raft算法

Raft作为非拜占庭容错的算法,主要是主节点出块。主节点有故障时,从节点会启动重新选主的流程,所以Raft的机制主要分为两部分:选主流程和 主节点同步信息流程。

选主流程:

  1. 每个节点发送提升请求给其他节点。
  2. 收到1/2+的投票的节点,升为主节点。
  3. 如果有冲突,采用随机退避的方式,再次投票。

主节点同步信息:

  1. 主节点打包交易,出块,把块发送给从节点。
  2. 从节点收到后发送确认信息返回。
  3. 主节点收到1/2+确认后,发送确认信息给从节点。
  4. 主节点持久化和发送Block到Chain进行计算。

Tendermint算法

Tendermint是一种专为区块链设计的高性能共识算法,基于半同步网络假设,Tendermint在保证活性和安全性(Liveness & Safety)的前提下能够容忍1/3的拜占庭节点。 CITA共识节点通过点对点共识消息交换协议对每一个区块交换投票信息,迅速形成多数共识。投票结果最后会被记录在区块里。CITA支持独有的低延迟技术,能够实现 毫秒级交易确认延迟。

Tendermint状态转换

Tendermint是一种状态机副本复制算法(State Machine Replication),在实现上包括以下几个状态: NewHeight -> (Propose -> Prevote -> Precommit)+ -> Commit -> NewHeight ->…

                         +-------------------------------------+
                         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;                   |
+--------------------------------------------------------------------+

Tendermint是随着块的高度增长,多种状态的依次循环的过程。在决定某个高度的块的过程中,可能需要一轮或者多轮的投票。 下面介绍Tendermint在块高度H和第R轮上,进行块的共识的主要流程:

  1. proposal阶段:proposal节点打包交易池中的交易,WAL记录后,通过MQ通讯模块,发送proposal消息给共识的其它节点,然后进入prevote阶段。而非proposal节点在进行一段时间的超时后,进入prevote投票阶段。
  2. prevote阶段:每个共识节点根据收到的proposal信息,进行prevote投票。校验成功则prevote block.hash,校验失败或者没有收到proposal信息则prevote空票。
  3. prevote等待阶段:等待节点收到2/3节点以上的prevote投票,在必要的超时后,进入precommit阶段。
  4. precommit阶段:节点根据prevote阶段收到的投票进行判断,如果收到相同block.hash的prevote投票,超过2/3,则precommit block.hash,否则precommit空值
  5. precommit等待阶段:等待收到的precommit投票数超过节点数的2/3。如果收到precommit相同block.hash投票超过2/3时,进入commit阶段。否则进入新一轮的proposal的阶段。
  6. commit阶段:共识模块把共识完成的block发送给chain模块后,等待chain模块的计算完成后发送的状态信息,然后进入下一个高度 NewHeight。

Tendermint交易池操作流程

  1. 交易池启动时,尝试从KV数据库恢复数据
  2. 交易池订阅MQ的交易信息
  3. 交易池收到交易后,持久化到KV数据库
  4. 交易池收到打包请求,检查交易的有效性,输出有效交易列表
  5. 交易池根据出块的交易列表,删除已经上链的交易

Tendermint故障重启流程

  1. 从WAL模块中,恢复某个块高度的投票信息
  2. 根据恢复后的状态信息,重复投票信息
  3. 进程根据当前状态,继续运行

CITA对Tendermint的优化

Tendermint算法实践上比较优秀,CITA结合联盟链和本身的特定,对其进行了优化。 比如CITA优化了commit阶段的Block广播,使用Chain模块计算交易后的状态消息作为出块成功的标志。 避免了一次的全网的广播,减少对下个高度Block同步的影响。