共識外掛模組的整體設計如圖:
交易池模組:主要有交易過濾和交易排序的功能,為了防止交易的重放,每一筆新接收的交易都需要透過布隆過濾器進行過濾。過濾成功後的交易,會注入到交易佇列中按交易時間進行排序,保證交易的先來後到和即時確認。
共識外掛模組:主要是共識外掛介面的實現邏輯,包含接收交易,節點共識,打包出塊,區塊確認等功能的封裝。
· 接收交易:接收來自API層傳入的交易,將交易注入交易池模組中,隨後透過節點的網路模組廣播交易;
· 節點共識:包含當前分散式節點共識狀態的確認以及網路的共識通訊,共識狀態可以判斷當前分散式網路是否已經準備就緒,準備就緒的標準之一是已經選舉出主節點。網路的共識通訊是節點的共識訊息通訊,比如節點選舉通訊、節點共識訊息通訊、節點配置變動通訊等等;
· 打包出塊:節點收到共識訊息後,根據出塊時間定時收集交易出塊。
· 區塊確認:區塊的交易執行完成並持久化之後,執行引擎通知共識外掛模組區塊已經確認。
二、介面設計
對於跨鏈場景來說,一個比較棘手的問題是不同種類共識演算法接入的適配不同。為了簡化不同共識演算法的適配問題,我們在中繼鏈中採用了外掛機制,其中共識演算法主要負責交易的打包和區塊的確認,而所有具體在共識演算法上進行操作的部分全部封裝到共識演算法外掛中,並按照中繼鏈與共識演算法互動的需求確定了一套適合的外掛介面。
這樣對於中繼鏈來說,對接任何新型別共識演算法的時候,都不需要修改自身,而是根據確定的介面開發一個新的共識演算法外掛即可。
下面以接入Raft共識演算法為例,交易經過共識模組的流程如圖所示:
需要提供的介面主要分為以下四個部分:
>Prepare介面
包含檢查共識、檢查交易和廣播交易部分,Prepare介面接收到gRPC或者Restful服務傳入的交易後,把接收的交易注入交易池中排序,然後將該交易廣播給叢集內的其它節點。如果交易在進入交易池之前就已經存在,那麼Prepare階段會拋棄該交易不進行廣播。
>Step介面
包含共識區塊部分,Step介面接收四種型別的訊息結構:
· 共識訊息(CONSENSUS)型別:由Raft傳送的共識訊息,其中訊息的內容主要有共識的日誌資訊、日誌索引以及當前的任期資訊等。
· 廣播交易(BROADCAST_TX)型別:由其它節點廣播的交易訊息,該節點接受到會將交易存在交易池中。
· 獲取交易(GET_TX)型別:由其它節點發出獲取交易的請求訊息,如果某節點在出塊階段發現部分交易不存在交易池中,則會向全網非同步傳送獲取丟失交易的請求訊息。該節點接收到訊息後發現交易確實存在交易池或者區塊的歷史資料中,則將該交易傳送給丟失交易的節點。
· 響應交易(GET_TX_ACK)型別:和獲取交易型別相對應,丟失交易的節點獲取到其它節點傳送的交易後,將交易存入交易池中進行出塊。
//訊息結構體
message RaftMessage {
enum Type {
CONSENSUS = 0; //共識訊息型別
BROADCAST_TX = 1; //廣播交易型別
GET_TX = 2; //獲取交易型別
GET_TX_ACK = 3; // 響應交易型別
}
Type type = 1; // 訊息型別
uint64 fromId = 2; //訊息來源方
bytes data = 3; //訊息資料
}
>Commit介面
包含定時出塊和區塊打包階段,Commit介面返回的是一個通道,該通道包含共識完成後的區塊結構資訊。交易在進入交易池後,叢集中的主節點會定時從交易池中收集交易,將收集好的交易組成交易雜湊列表,結合當前的塊高構建成共識訊息的結構體Ready。
//共識訊息結構體
message Ready {
repeated bytes txHashes = 1; //交易雜湊列表
uint64 height = 2; //區塊高度
}
主節點將Ready提交到Raft共識引擎中,在叢集的共識完成後的出塊階段,根據Ready結構體的交易雜湊列表和區塊高度重組區塊,當區塊構造完成後則透過Commit介面返回,打包好的區塊隨後轉入執行引擎執行區塊內部的交易,執行完成後交與儲存層進行持久化。
>ReportState介面
包含區塊確認階段,ReportState介面接收的是執行引擎剛執行完區塊的高度和雜湊,中繼鏈呼叫該介面通知外掛共識的區塊交易已經執行完成並且持久化了。透過該介面,共識演算法外掛可以完成一些區塊的收尾工作,比如記錄當前共識日誌的索引、持久化布隆過濾器的filter,刪除冗餘的區塊交易等等。
三、外掛編譯與接入
>外掛編譯
我們採用Go語言提供的外掛模式,實現中繼鏈對於外掛的動態載入。首先,編寫Makefile編譯檔案:
SHELL := /bin/bash
CURRENT_PATH = $(shell pwd)
GO = GO111MODULE=on go
plugin:
@mkdir -p build
$(GO) build --buildmode=plugin -o build/raft.so etcdraft/*.go
執行下面的命令,能夠得到raft.so檔案。
$ make plugin
>外掛接入
接入共識演算法外掛應修改節點的bitxhub.toml,該檔案在中繼鏈的配置目錄中。
[order]
plugin = "plugins/raft.so"
將你編寫的動態連結檔案和order.toml檔案,分別放到節點的plugins資料夾和配置目錄下。
./
├── api
├── bitxhub.toml
├── certs
│ ├── agency.cert
│ ├── ca.cert
│ ├── node.cert
│ └── node.priv
├── key.json
├── logs
├── network.toml
├── order.toml //共識演算法配置檔案
├── plugins
│ ├── raft.so //共識演算法外掛
├── start.sh
└── storage
結合我們提供的中繼鏈,就能接入到跨鏈平臺來。