基於 tendermint 實現 Hyperledger Fabric 的拜占庭容錯排序
作者簡介
何鑫銘,攜程技術中心創新研發部區塊鏈技術專家,攜程區塊鏈技術平臺技術負責人,精通當前主流區塊鏈開源技術框架,熱衷於研究區塊鏈底層設計和區塊鏈應用創新。
一、引言
HyperLedger Fabric作為一個架構靈活的企業級區塊鏈平臺,正在被越來越多的企業用於生產環境。之前我分享過一篇文章 《HyperLedger Fabric在攜程區塊鏈服務平臺的應用實戰》 介紹了一些攜程對於HyperLedger Fabric的落地經驗,並重點分享了Fabric框架在程式碼結構靈活性上的一些認識和在Fabric開源框架的基礎上做的一些延伸開發。本文將分享我們在Fabric排序服務做的一個延伸點。
二、fabric orderer服務過程分析
我們先以最簡單的solo為例,看一下Fabric的orderer節點接收排序請求後的主要處理邏輯。
首先,orderer的排序服務需要實現consensus包的Consenter(排序引擎)interface,Consenter介面只需要實現一個HandleChain方法,該方法需要返回一個Chain物件。
Chain物件需要實現Chaininterface,實現Order(普通交易排序)、Configure(配置交易排序)、Start等主要方法。那我們現在來看一下當一個orderer節點啟動後,將會經過怎樣的步驟,如何實現對交易的排序。
1、當我們在fabric網路定義排序型別為solo的情況時,orderer節點啟動會初始化一個solo consenter物件(參考程式碼orderer/common/server/main.go中的方法initializeMultichannelRegistrar)。
2、當orderer啟動後,orderer節點會檢查本地賬本中存在的通道,此時發現只有一個testchainid通道(瞭解fabric的話,我們會知道當區塊鏈網路創世時,會有一條預設名為testchainid的系統通道),solo consenter會為系統通道testchainid建立一個chain物件儲存在orderer記憶體中,並啟動監聽來自orderer節點接收到的系統通道配置交易(testchainid只會接受配置類交易,如建立新的通道請求)。
chain物件請求監聽的原理其實是在chain的start方法中會啟動一個goroutine監聽一個名為sendChan的go chan。
3、此時我們假設有一個建立名為mychannel新通道的交易被orderer接收到,只能是testchainid系統通道對應的chain物件來進行處理,此時orderer節點會判斷該交易型別為配置型別交易,則呼叫chain物件的configure方法,configure方法將交易寫入chain.sendChan中。
4、此時,會觸發sendChan的監聽服務,監聽服務會檢查交易並將交易通過ch.support.BlockCutter().Ordered方法放入本地佇列中等待出塊,出塊任務在啟動orderer節點時時會啟動一個chan timer,每隔固定時間(設定的batchTimeOut)會從佇列中取出一定的交易數量(不超過設定的每個塊最大的交易量)出塊,並寫入orderer本地賬本,當mychannel的建立交易被成功受理出塊,即意味著名為mychannel的新通道已經被建立。
5、mychannel通道建立後,solo consenter會通過HandleChain方法為之建立一個新的chain物件,mychannel chain物件會受理mychannel通道的交易排序,原理與以上同。
以上只是以solo的order type為例,fabric截止到目前的1.4版本,官方推薦使用更穩定的kafka排序。
kafka排序與上述例子中solo排序的區別是:可以支援多個orderer節點,所有的交易可以請求任何一個orderer節點,請求的orderer節點本地排序出塊後會通過kafka叢集將資料同步給其他的orderer節點,意味著排序服務可以實現更高的可用性。
我們在檢視orderer原始碼時,發現了官方已經在做raft排序。簡單測試了一下,過程是能夠走通了,大家可以耐心等release版本。
三、為什麼要做pbft排序服務
我們認為目前已經release的kafka排序是能夠滿足初級的聯盟鏈需求的。
背景是,現在的聯盟鏈更多是一強多弱型企業聯盟,如一個大型公司主導區塊鏈業務與技術,其上下游機構合作參與;如一個集團性企業佈道區塊鏈,其分子公司合作參與;如aws、阿里雲等雲廠商提供baas雲服務,企業使用只需要向baas申請節點資源。
以上情況中,kafka叢集大都需要部署到大型公司、集團總部或者雲廠商,保證高計算能力和高可用,是可以支援比較高併發的上鍊請求的(官方資料是TPS可以達到3000以上)。
為什麼說是初級呢?我們可以發現以上列舉的情況實際是有偏離區塊鏈初衷的,區塊鏈在聯盟中更應該作為聯盟企業間平等、互信合作的基礎設施。當聯盟間各個企業不存在一強多弱,真正是幾家平等的企業在合作時,在技術設計中就會存在一個比較大的疑惑,kafka排序服務應該部署在哪裡呢?也許部署到公有云上是一個選擇,但當雲廠商本身是利益的相關方呢?
所以我們認為,無論是官方正在開發的raft排序還是我們做的pbft排序,最重要的目的就是首先要允許orderer節點部署到不同的企業,每個企業都參與到fabric的排序服務,而不是像kafka排序一樣需要將排序服務部署到一箇中心化的機構。
四、基於tendermint的abci實現fabric排序服務
Tendermint提供了一個高效能、一致的、安全的BFT共識引擎,嚴格的分叉問責保證能夠控制作惡者的行為。
Tendermint非常適合用於擴充套件異構區塊鏈,包括公有鏈以及注重的效能的許可鏈/聯盟鏈,像Ethermint 就是一次對Ethereum以太坊POS機制的快速實現。使用Tendermint在區塊鏈領域中的成功案例包括Hyperledger Burrow、cosmos等著名專案 。
tendermint專案的團隊是正在進行著名跨鏈專案Cosmos研發的團隊(相信很多同學一定關注過這個明星專案),而tendermint也是作為共識協議用於在Cosmos Hub上構建第一個分割槽。
Tendermint獨有的abci定義了區塊鏈執行的標準介面,可以允許使用者自定義實現介面內容,不需要修改tendermint原始碼來整合他,並支援跨語言通過上層介面進行呼叫。這就為許多其他技術棧甚至不同語言的區塊鏈底層的整合提供了思路。關於更多tendermint的介紹這裡不再贅述。
這裡,我們通過tendermint的abci來實現fabric的orderer服務。
fabric的排序方式,需要peer節點將交易proposal 傳送給任一orderer節點,kafka排序是orderer節點藉助kafka訊息佇列,而raft排序是orderer節點藉助etcd實現區塊傳遞給其他orderer節點。而在這裡,我們讓orderer節點藉助其內部的tendermint節點服務,將訊息傳遞給其他orderer節點,並能夠相容其中的拜占庭節點。
下面我們藉助tendermint的abci介面,實現程式碼不侵入,完成orderer的tendermint pbft排序。
首先,新建一個全新的package實現fabric consensus的所有介面方法。這裡可以判斷,每次handleChain方法觸發(一個新的fabric channel建立)時,嘗試呼叫tendermint包的node.NewNode方法啟動tendermint服務,可以多個channel只啟動一個tendermint服務。
然後,每當有新的交易傳遞到orderer時,envelope型別的交易都會通過order方法和configure方法傳遞進來,這裡我們只需要在兩個方法中,將交易序列化為tendermint可以傳遞的資料型別如byte陣列,呼叫tendermint節點的Mempool.checkTx方法,將交易打包到tendermint的記憶體池中即可。
之後的事情,打包到tendermint記憶體池中的交易,將進行多個orderer節點的pbft共識,這裡會執行tendermint的標準p2p通訊和多輪共識。
完成共識之後,需要我們通過tendermint的abci的DeliverTx和commit方法獲取到共識後的交易,並呼叫fabric的CreateNextBlock方法和WriteBlock方法打包生成區塊。即完成一個完整的交易共識並記賬。
這裡,有一個比較容易產生疑問的問題,我們知道fabric是多通道的賬本結構,而tendermint是單通道賬本,如何做到相容兩邊?
在這裡,實際我們是需要寫fabric和tendermint兩套賬本的,從上述過程我們可以看到,共識交易完成後需要每個orderer節點自行呼叫fabric自帶的寫區塊方法在對應的通道中進行寫塊,而同時我們在abci中也建立了一個tendermint的基於leveldb的賬本。即每一筆交易,我們也在tendermint的賬本中記錄了一份,只是沒有區分通道,因為本來fabric中的orderer也是記錄的全通道資料。
該賬本主要用於fabric追塊,當某個orderer節點的tendermint塊高度比其他節點小時,會觸發tendermint的追塊功能,從tendermint中讀取交易後寫入自己的tendermint賬本,同時寫入fabric對應通道的區塊中。
以上整個過程,沒有動過tendermint的原始碼,只需要擴充套件一個新的實現fabric的consensus介面的類,在類中同時實現tendermint的abci介面即可。簡單程式碼請參考 https://github.com/hxmhlt/fabric/tree/develop-1.4-tmpbft 。
程式碼一些功能還未完成,如動態新增orderer節點需要結合tendermint動態新增validator功能來做、tendermint配置檔案的自動生成、效能也未進一步優化,也或許還有一些其他問題,程式碼截至撰寫本文還未用於生產環境。開源部分的原始碼僅供參考,也歡迎各位讀者在評論區交流~
【推薦閱讀】