關于拜占庭問題及其分析
故事起源
拜占庭問題是容錯計算中的一個老問題,有萊斯特蘭伯特等人在1982年提出。拜占廷帝國為5-15世紀的東羅馬帝國,拜占庭城邦擁有巨大的財富,令他的十個鄰邦垂涎已久,但是拜占庭高墻聳立,固若金湯,沒有任何一個單獨的鄰邦能夠成功入侵,任何單個城邦的入侵行動都會失敗,而入侵者的軍隊也會被殲滅,使得其自身反而容易遭到其它九個城邦的入侵。這十個城邦之間也互相覬覦對方的財富并經常爆發(fā)戰(zhàn)爭。
拜占庭的防御能力如此之強,非大多數人一起不能攻破。而且只要其中一個城邦背叛盟軍,那么所有進攻軍隊都會被殲滅,并隨后被其他鄰邦所劫掠。因此這是一個互不信任的各個鄰邦構成的分布式網絡。每一方都小心行事,因為稍有不慎就會給自己帶來災難。為了獲取拜占庭的巨額財富,這些鄰邦分散在拜占庭的周圍,依靠士兵傳遞消息來協(xié)商進攻目的及進攻時間,這些鄰邦將軍想要攻克拜占庭,但面臨的一個困擾,鄰邦將軍不確定他們之中是否有叛徒,叛徒是否擅自變更進攻意向或者進攻時間?在這種狀態(tài)下,將軍們能否找到一種分布式協(xié)議來進行遠程協(xié)商達成他們的共識,進而贏取拜占庭城邦的財富呢?
假設前提
在拜占庭將軍問題模型中,對于將軍們有兩個公認的假設:
假設一、所有忠誠的將軍受到相同的命令后,執(zhí)行這條命令,得到的結果一定是相同的,它的含義是所有節(jié)點對命令的解析和執(zhí)行是一樣的,這個命令必須是一個確定性的命令,不能存在隨機性,也不能依賴節(jié)點自身的狀態(tài),也就是說這個秘密不能是心情好,就攻擊敵人,心情不好就原地休息;
假設二、如果命令是正確的,那么所有忠誠的將軍必須執(zhí)行這條命令,換句話說,忠誠的將軍需要判斷,接收到的命令是不是正確的。
對于將軍們的通訊過程,在“拜占庭將軍問題”中也是有默認假設的:點對點通信是沒有問題的,也就是說在這里,我們假設A將軍要給B將軍一條命令X,那么派出去的傳令兵一定會準確的把命令X傳給B將軍。
問題
但問題在于,如果每個城邦向其他九個城邦派出一名信使,那么就是這十個城邦,每一個都派出了九名信使,也就是在任何一個時間有總計90次的信息傳輸,并且每個城市分別收到九條信息,可能每一條都寫著不同的進攻時間,除此以外,信息傳輸過程中,如果叛徒想要破壞原有的約定時間,就會自己修改相關信息,然后發(fā)給其他城邦以混淆試聽,這樣的結果是,部分城邦收到錯誤信息后,會遵循一個或者多個城邦已經修改過的攻擊時間相關信息,從而背叛發(fā)起人的本意。這樣一來,遵循錯誤信息的城邦(包含叛徒),將重新廣播超過一條信息的信息鏈,整個信息鏈會隨著他們所發(fā)送的錯誤信息,迅速變成不可信的信息和攻擊時間,變成一個相互矛盾的糾結體。
解決辦法
針對這個問題,人們主要提出了兩種解決方法,一個是口頭協(xié)議算法;另一個是書面協(xié)議算法。
口頭協(xié)議算法的核心思想:要求每一個被發(fā)送的消息都能被正確投遞,信息接收者明確知道消息發(fā)送者的身份,并且信息接收者知道信息中是否缺少信息。采用口頭協(xié)議算法,若叛徒數少于1/3時,則拜占庭將軍問題可以很容易解決。但是口頭協(xié)議算法存在著明顯的缺點,那就是消息不能溯源。
為解決該問題,提出了書面協(xié)議算法。該算法要求簽名,不可偽造,一旦被篡改即可發(fā)現,同時任何人都可以驗證簽名的可靠性。
就算是書面協(xié)議算法,也不能完全解決拜占庭將軍問題,因為該算法沒有考慮信息傳輸延遲、簽名體系難以實現的問題。且簽名消息記錄的保存,也難以擺脫中心化機構。
PBFT(拜占庭容錯算法)
PBFT是一種狀態(tài)機副本復制算法,即服務作為狀態(tài)機進行建模,狀態(tài)機在分布式系統(tǒng)的不同節(jié)點進行副本復制。每個狀態(tài)機的副本都保存了服務的狀態(tài),同時也實現了服務的操作。將所有的副本組成的集合使用大寫字母R表示,使用0到|R|-1的整數表示每一個副本。
為了描述方便,假設|R|=3f+1,這里f是有可能失效的副本的最大個數。盡管可以存在多于3f+1個副本,但是額外的副本除了降低性能之外不能提高可靠性。
首先介紹view、replica 、primary、backups
所有的副本在一個被稱為視圖(View)的輪換過程(succession of configuration)中運作。在某個視圖中,一個副本作為主節(jié)點(primary),其他的副本作為備份(backups)。視圖是連續(xù)編號的整數。
主節(jié)點由公式p = v mod |R|
計算得到,這里v是視圖編號,p是副本編號,|R|是副本集合的個數。當主節(jié)點失效的時候就需要啟動視圖更換(view change)過程。
Viewstamped Replication算法和Paxos算法就是使用類似方法解決良性容錯的。
同所有的狀態(tài)機副本復制技術一樣,PBFT對每個副本節(jié)點提出了兩個限定條件:
(1)所有節(jié)點必須是確定性的。也就是說,在給定狀態(tài)和參數相同的情況下,操作執(zhí)行的結果必須相同;
(2)所有節(jié)點必須從相同的狀態(tài)開始執(zhí)行。在這兩個限定條件下,即使失效的副本節(jié)點存在,PBFT算法對所有非失效副本節(jié)點的請求執(zhí)行總順序達成一致,從而保證安全性。
接下去描述簡化版本的PBFT算法,忽略磁盤空間不足和消息重傳等細節(jié)內容。并且,本文假設消息驗證過程是通過數字簽名方法實現的,而不是更加高效的基于消息驗證編碼(MAC)的方法。
客戶端
客戶端c向主節(jié)點發(fā)送<REQUEST,o,t,c>請求執(zhí)行狀態(tài)機操作o,這里時間戳t用來保證客戶端請求只會執(zhí)行一次。客戶端c發(fā)出請求的時間戳是全序排列的,后續(xù)發(fā)出的請求比早先發(fā)出的請求擁有更高的時間戳。例如,請求發(fā)起時的本地時鐘值可以作為時間戳。
每個由副本節(jié)點發(fā)給客戶端的消息都包含了當前的視圖編號,使得客戶端能夠跟蹤視圖編號,從而進一步推算出當前主節(jié)點的編號。客戶端通過點對點消息向它自己認為的主節(jié)點發(fā)送請求,然后主節(jié)點自動將該請求向所有備份節(jié)點進行廣播。
副本發(fā)給客戶端的響應為<REPLY,v,t,c,i,r>,v是視圖編號,t是時間戳,i是副本的編號,r是請求執(zhí)行的結果。
客戶端等待f+1個從不同副本得到的同樣響應,同樣響應需要保證簽名正確,并且具有同樣的時間戳t和執(zhí)行結果r。這樣客戶端才能把r作為正確的執(zhí)行結果,因為失效的副本節(jié)點不超過f個,所以f+1個副本的一致響應必定能夠保證結果是正確有效的。
如果客戶端沒有在有限時間內收到回復,請求將向所有副本節(jié)點進行廣播。如果請求已經在副本節(jié)點處理過了,副本就向客戶端重發(fā)一遍執(zhí)行結果。如果請求沒有在副本節(jié)點處理過,該副本節(jié)點將把請求轉發(fā)給主節(jié)點。如果主節(jié)點沒有將該請求進行廣播,那么就有認為主節(jié)點失效,如果有足夠多的副本節(jié)點認為主節(jié)點失效,則會觸發(fā)一次視圖變更。
本文假設客戶端會等待上一個請求完成才會發(fā)起下一個請求,但是只要能夠保證請求順序,可以允許請求是異步的。
PBFT要系統(tǒng)共同維護一個狀態(tài),所有節(jié)點采取的行動一致。為此,需要運行三類基本協(xié)議,包括:一致性協(xié)議、檢查點協(xié)議和視圖更換協(xié)議。同時,一致性協(xié)議要求來自客戶端的請求在每一個服務節(jié)點上都按照一個確定的順序執(zhí)行,這個協(xié)議把節(jié)點分為兩類:主節(jié)點和從節(jié)點,主節(jié)點僅有一個并負責請求排序,從節(jié)點按照主節(jié)點排序處理請求。
1)主節(jié)點的“選舉”
PBFT的主節(jié)點“選舉”和Raft算法的選舉不一樣,只是通過一個模運算進行或者選擇當前存活的節(jié)點編號最小的節(jié)點成為新的主節(jié)點。
p = v mod |R
v 是view的編號,從0開始一直連續(xù)下去,這樣可以理解為從replica 0 到 replica |R-1 依次當primary節(jié)點,當每一次view change發(fā)生時。
p = v mod |R|,其中p為節(jié)點編號、v為視圖編號,|R|為節(jié)點數量。當主節(jié)點失效后就需要啟動視圖更換。
2)PBFT算法基本流程:
1)從全網節(jié)點選舉出一個主節(jié)點(Leader),新區(qū)塊由主節(jié)點負責生成。
(2)每個節(jié)點把客戶端發(fā)來的交易向全網廣播,主節(jié)點將從網絡收集到需放在新區(qū)塊內的多個交易排序后存入列表,并將該列表向全網廣播。
(3)每個節(jié)點接收到交易列表后,根據排序模擬執(zhí)行這些交易。所有交易執(zhí)行完后,基于交易結果計算新區(qū)塊的哈希摘要,并向全網廣播。
(4)如果一個節(jié)點收到的2f(f為可容忍的拜占庭節(jié)點數)個其它節(jié)點發(fā)來的摘要都和自己相等,就向全網廣播一條commit消息。
(5)如果一個節(jié)點收到2f+1條commit消息,即可提交新區(qū)塊及其交易到本地的區(qū)塊鏈和狀態(tài)數據庫。
3)算法核心三階段流程
三階段流程如下圖所示,核心三階段分別為pre-preare階段(預準備階段)、prepare階段(準備階段)和commit階段(提交階段),圖中的C代表客戶端,0、1、2、3代表節(jié)點的編號,打叉的3代表可能是一個故障節(jié)點或者問題節(jié)點,0為主節(jié)點。
首先,客戶端向主節(jié)點發(fā)起請求,主節(jié)點0收到客戶端請求,會向其它節(jié)點發(fā)送pre-prepare消息,其它節(jié)點就收到了pre-prepare消息,就開始了這個核心三階段共識過程了。
我們采用三階段協(xié)議來廣播請求給replicas:pre-prepare, prepare, commit。
(1)pre-prepare階段:
主節(jié)點收到客戶端請求,給請求編號,并發(fā)送pre-pre類型信息給其他從節(jié)點。
從1節(jié)點收到pre-pre類型信息,如果同意這個請求的編號,如果同意就進入prepare階段
(2)Prepare階段:
從1節(jié)點同意主節(jié)點請求的編號,將發(fā)送prepare類型消息給主節(jié)點和其他兩個從節(jié)點。如果不發(fā),表示不同意。
圖:從1節(jié)點發(fā)prepare信息給其他節(jié)點
從1節(jié)點如果收到另外兩個從節(jié)點都發(fā)出的同意主節(jié)點分配的編號的prepare類型的消息,則表示從1節(jié)點的狀態(tài)為prepared,該節(jié)點會擁有一個prepared認證證書。
為了防止viewchange導致主節(jié)點給請求分配的編號失效,引入commit階段。
(3)commit階段
從1節(jié)點進入prepared狀態(tài)后,將發(fā)送一條COMMIT類型信息給其它所有節(jié)點告訴他們它有一個prepared認證證書了。
圖:從1節(jié)點發(fā)commit類型信息給其他節(jié)點
如果從1節(jié)點收到2f+1條commit信息,證明從1節(jié)點已經進入commited狀態(tài)。
只通過這一個節(jié)點,我們就能認為客戶端的請求在需要的節(jié)點中都到達了prepared狀態(tài),每一個需要的節(jié)點都同意了主節(jié)點分配的編號。當一個請求在某個節(jié)點中到達commited狀態(tài)后,該請求就會被該節(jié)點執(zhí)行。
整個流程:
DBFT:小蟻區(qū)塊鏈(delegated BFT,授權拜占庭容錯機制)
用權益來選出記賬人,然后記賬人之間通過拜占庭容錯算法 達成共識。
優(yōu)點:專業(yè)化的記賬人可以容忍任何類型的錯誤記賬由多人協(xié)同完成,每一個區(qū)塊都有最終性,不會分叉算法的可靠性有 嚴格的數學證明缺點:當三分之一或以上記賬人停止工作后,系統(tǒng)將無法提供服務當三分之一或以上記賬人聯(lián)合作惡,且其他所有的記賬人恰好分割為兩個網絡孤島時,惡意記賬人可以使系統(tǒng)出現分叉,但是會留下密碼學證據