Consensus
CITA 的共识模块主要是保证多个节点对于交易的顺序和 Block 的内容达成一致。在众多的分布式算法中, 我们选择实现了非拜占庭容错的 Raft 算法和拜占庭容错的的 CITA-BFT 算法。
共识的架构
共识主要有 MQ(消息队列)通讯模块、交易池、定时模块、WAL(write ahead log)、算法逻辑模块。
+-------------+ +-------------+ +-----+
| MQ通讯模块 |<----->| 算法逻辑模块 |<----> | WAL |
+-------------+ +-------------+ +-----+
^ ^ ^
| | |
|----------------+ |
| |
+--------+ +-----------+
| 交易池 | | 定时模块 |
+--------+ +-----------+
MQ 通讯模块: CITA 的消息通过 MQ 进行周转,MQ 通讯模块负责订阅、发布基于 MQ 的消息。
交易池: 交易池订阅和存储交易信息,并提供交易的打包、生成 Block。还进行交易的持久化,实现快速确认的功能。
定时模块: 提供算法定时服务。使用者向定时模块发送定时请求,定时模块在时间到达后,发送确认信息。
WAL: WAL 提供预写日志(write ahead log)的服务,持久化各个节点的投票。用来进行节点崩溃后恢复。
算法逻辑模块: 分布式算法逻辑的实现模块,接受共识其它模块发送过来的信息,根据自身的算法要求,进行算法逻辑相应的处理。 例如对于 CITA-BFT 就需要进行一系列的状态转换。
基本前提
- 每个共识节点知道其它共识节点的公钥
- 每个共识节点发送的投票信息,都必须有自己的签名
- 共识节点根据公钥和签名可以验证消息的真实性
- 共识节点数量需要满足算法要求的基本数量
基本步骤
虽然分布式算法多种多样,具体落实在 CITA 中,基本上需要进行如下的步骤:
- 共识模块从消息队列中订阅交易信息,放入交易池。如果应用有快速确认的需求,交易池可以对交易进行持久化。
- 共识算法根据配置和算法要求,选择一个出块的节点。该出块节点把块的哈希值(Block.Hash)作为共识的主要信息通过 MQ 通讯模块向其它的节点进行广播。
- 当出块节点进过一轮或者多轮投票,收到算法要求的法定多数的投票返回时,向 Chain 模块确认出块,否则进入重新选择出块节点的计算,由下一个出块节点继续出块。
- 接收 Chain 返回的状态信息,作为出块成功的标志。
- 出块成功后,从交易池删除已经达成共识的交易。
Raft 算法
Raft 作为非拜占庭容错的算法,主要是主节点出块。主节点有故障时,从节点会启动重新选主的流程,所以 Raft 的机制主要分为两部分:选主流程和 主节点同步信息流程。
选主流程:
- 每个节点发送提升请求给其他节点。
- 收到 1/2+的投票的节点,升为主节点。
- 如果有冲突,采用随机退避的方式,再次投票。
主节点同步信息:
- 主节点打包交易,出块,把块发送给从节点。
- 从节点收到后发送确认信息返回。
- 主节点收到 1/2+确认后,发送确认信息给从节点。
- 主节点持久化和发送 Block 到 Chain 进行计算。
CITA-BFT 算法
CITA-BFT 是一种专为区块链设计的高性能共识算法,基于半同步网络假设,CITA-BFT 在保证活性和安全性(Liveness & Safety)的前提下能够容忍 1/3 的拜占庭节点。 CITA 共识节点通过点对点共识消息交换协议对每一个区块交换投票信息,迅速形成多数共识。投票结果最后会被记录在区块里。CITA 支持独有的低延迟技术,能够实现 毫秒级交易确认延迟。
CITA-BFT 状态转换
CITA-BFT 是一种状态机副本复制算法(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; |
+--------------------------------------------------------------------+
CITA-BFT 是随着块的高度增长,多种状态的依次循环的过程。在决定某个高度的块的过程中,可能需要一轮或者多轮的投票。 下面介绍 CITA-BFT 在块高度 H 和第 R 轮上,进行块的共识的主要流程:
- proposal 阶段:proposal 节点打包交易池中的交易,WAL 记录后,通过 MQ 通讯模块,发送 proposal 消息给共识的其它节点,然后进入 prevote 阶段。而非 proposal 节点在进行一段时间的超时后,进入 prevote 投票阶段。
- prevote 阶段:每个共识节点根据收到的 proposal 信息,进行 prevote 投票。校验成功则 prevote block.hash,校验失败或者没有收到 proposal 信息则 prevote 空票。
- prevote 等待阶段:等待节点收到 2/3 节点以上的 prevote 投票,在必要的超时后,进入 precommit 阶段。
- precommit 阶段:节点根据 prevote 阶段收到的投票进行判断,如果收到相同 block.hash 的 prevote 投票,超过 2/3,则 precommit block.hash,否则 precommit 空值
- precommit 等待阶段:等待收到的 precommit 投票数超过节点数的 2/3。如果收到 precommit 相同 block.hash 投票超过 2/3 时,进入 commit 阶段。否则进入新一轮的 proposal 的阶段。
- commit 阶段:共识模块把共识完成的 block 发送给 chain 模块后,等待 chain 模块的计算完成后发送的状态信息,然后进入下一个高度 NewHeight。
CITA-BFT 交易池操作流程
- 交易池启动时,尝试从 KV 数据库恢复数据
- 交易池订阅 MQ 的交易信息
- 交易池收到交易后,持久化到 KV 数据库
- 交易池收到打包请求,检查交易的有效性,输出有效交易列表
- 交易池根据出块的交易列表,删除已经上链的交易
CITA-BFT 故障重启流程
- 从 WAL 模块中,恢复某个块高度的投票信息
- 根据恢复后的状态信息,重复投票信息
- 进程根据当前状态,继续运行