共識
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 狀态轉換
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; |
+--------------------------------------------------------------------+
- 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 子產品中,恢複某個塊高度的投票資訊
- 根據恢複後的狀态資訊,重複投票資訊
- 程序根據目前狀态,繼續運作