PBFT
摘要:
PBFT是Practical Byzantine Fault Tolerance的縮寫,即:實用拜占庭容錯算法。該算法是Miguel Castro(卡斯特羅)和Barbara Liskov(利斯科夫)在1999年提出來的,解決了原始拜占庭容錯算法效率不高的問題,算法的時間復雜度是O(n^2),使得在實際系統應用中可以解決拜占庭容錯問題。該論文發表在1999年的操作系統設計與實現國際會議上(OSDI99)。其中Barbara Liskov就是提出了著名的里氏替換原則(LSP)的人,2008年圖靈獎得主。以下拜占庭容錯問題簡稱BFT。
BFT是區塊鏈共識算法中,需要解決的一個核心問題,以比特幣和以太訪為代表的POW,EOS為代表的DPOS,以及今后以太訪逐漸替換的共識算法POS,這些都是公鏈算法,解決的是共識節點眾多情況下的BFT。而PBFT是在聯盟鏈共識節點較少的情況下BFT的一種解決方案。
網上已經有很多關于PBFT算法的描述,但是寫的都不是很明白,本文以一種更為清晰易懂的方法,徹底講明白PBFT算法原理。下一篇文章將會結合fabric-0.6.0-preview 中的代碼,講解超級賬本項目是如何實現PBFT算法的。
本文假設讀者已經理解什么是BFT問題。
PBFT算法流程:
PBFT算法前提,采用密碼學算法保證節點之間的消息傳送是不可篡改的。
PBFT容忍無效或者惡意節點數:f,為了保障整個系統可以正常運轉,需要有2f+1個正常節點,系統的總節點數為:|R| = 3f + 1。也就是說,PBFT算法可以容忍小于1/3個無效或者惡意節點,該部分的原理證明請參考PBFT論文,下文有鏈接地址。
PBFT是一種狀態機副本復制算法,所有的副本在一個視圖(view)輪換的過程中操作,主節點通過視圖編號以及節點數集合來確定,即:主節點 p = v mod |R|。v:視圖編號,|R|節點個數,p:主節點編號。
PBFT算法主體實現流程圖如下:
以下詳細說明,每個主體流程內容:
1. REQUEST:
客戶端c向主節點p發送<REQUEST, o, t, c>請求。o: 請求的具體操作,t: 請求時客戶端追加的時間戳,c:客戶端標識。REQUEST: 包含消息內容m,以及消息摘要d(m)。客戶端對請求進行簽名。
2. PRE-PREPARE:
主節點收到客戶端的請求,需要進行以下交驗:
a. 客戶端請求消息簽名是否正確。
非法請求丟棄。正確請求,分配一個編號n,編號n主要用于對客戶端的請求進行排序。然后廣播一條<<PRE-PREPARE, v, n, d>, m>消息給其他副本節點。v:視圖編號,d客戶端消息摘要,m消息內容。<PRE-PREPARE, v, n, d>進行主節點簽名。n是要在某一個范圍區間內的[h, H],具體原因參見垃圾回收章節。
3. PREPARE:
副本節點i收到主節點的PRE-PREPARE消息,需要進行以下交驗:
a. 主節點PRE-PREPARE消息簽名是否正確。
b. 當前副本節點是否已經收到了一條在同一v下并且編號也是n,但是簽名不同的PRE-PREPARE信息。
c. d與m的摘要是否一致。
d. n是否在區間[h, H]內。
非法請求丟棄。正確請求,副本節點i向其他節點包括主節點發送一條<PREPARE, v, n, d, i>消息, v, n, d, m與上述PRE-PREPARE消息內容相同,i是當前副本節點編號。<PREPARE, v, n, d, i>進行副本節點i的簽名。記錄PRE-PREPARE和PREPARE消息到log中,用于View Change過程中恢復未完成的請求操作。
4. COMMIT:
主節點和副本節點收到PREPARE消息,需要進行以下交驗:
a. 副本節點PREPARE消息簽名是否正確。
b. 當前副本節點是否已經收到了同一視圖v下的n。
c. n是否在區間[h, H]內。
d. d是否和當前已收到PRE-PPREPARE中的d相同
非法請求丟棄。如果副本節點i收到了2f+1個驗證通過的PREPARE消息,則向其他節點包括主節點發送一條<COMMIT, v, n, d, i>消息,v, n, d, i與上述PREPARE消息內容相同。<COMMIT, v, n, d, i>進行副本節點i的簽名。記錄COMMIT消息到日志中,用于View Change過程中恢復未完成的請求操作。記錄其他副本節點發送的PREPARE消息到log中。
5. REPLY:
主節點和副本節點收到COMMIT消息,需要進行以下交驗:
a. 副本節點COMMIT消息簽名是否正確。
b. 當前副本節點是否已經收到了同一視圖v下的n。
c. d與m的摘要是否一致。
d. n是否在區間[h, H]內。
非法請求丟棄。如果副本節點i收到了2f+1個驗證通過的COMMIT消息,說明當前網絡中的大部分節點已經達成共識,運行客戶端的請求操作o,并返回<REPLY, v, t, c, i, r>給客戶端,r:是請求操作結果,客戶端如果收到f+1個相同的REPLY消息,說明客戶端發起的請求已經達成全網共識,否則客戶端需要判斷是否重新發送請求給主節點。記錄其他副本節點發送的COMMIT消息到log中。
垃圾回收:
在上述算法流程中,為了確保在View Change的過程中,能夠恢復先前的請求,每一個副本節點都記錄一些消息到本地的log中,當執行請求后副本節點需要把之前該請求的記錄消息清除掉。最簡單的做法是在Reply消息后,再執行一次當前狀態的共識同步,這樣做的成本比較高,因此可以在執行完多條請求K(例如:100條)后執行一次狀態同步。這個狀態同步消息就是CheckPoint消息。
副本節點i發送<CheckPoint, n, d, i>給其他節點,n是當前節點所保留的最后一個視圖請求編號,d是對當前狀態的一個摘要,該CheckPoint消息記錄到log中。如果副本節點i收到了2f+1個驗證過的CheckPoint消息,則清除先前日志中的消息,并以n作為當前一個stable checkpoint。
這是理想情況,實際上當副本節點i向其他節點發出CheckPoint消息后,其他節點還沒有完成K條請求,所以不會立即對i的請求作出響應,它還會按照自己的節奏,向前行進,但此時發出的CheckPoint并未形成stable,為了防止i的處理請求過快,設置一個上文提到的高低水位區間[h, H]來解決這個問題。低水位h等于上一個stable checkpoint的編號,高水位H = h + L,其中L是我們指定的數值,等于checkpoint周期處理請求數K的整數倍,可以設置為L = 2K。當副本節點i處理請求超過高水位H時,此時就會停止腳步,等待stable checkpoint發生變化,再繼續前進。
View Change:
如果主節點作惡,它可能會給不同的請求編上相同的序號,或者不去分配序號,或者讓相鄰的序號不連續。備份節點應當有職責來主動檢查這些序號的合法性。如果主節點掉線或者作惡不廣播客戶端的請求,客戶端設置超時機制,超時的話,向所有副本節點廣播請求消息。副本節點檢測出主節點作惡或者下線,發起View Change協議。
副本節點向其他節點廣播<VIEW-CHANGE, v+1, n, C, P, i>消息。n是最新的stable checkpoint的編號,C是2f+1驗證過的CheckPoint消息集合,P是當前副本節點未完成的請求的PRE-PREPARE和PREPARE消息集合。
當主節點p = v + 1 mod |R|收到2f個有效的VIEW-CHANGE消息后,向其他節點廣播<NEW-VIEW, v+1, V, O>消息。V是有效的VIEW-CHANGE消息集合。O是主節點重新發起的未經完成的PRE-PREPARE消息集合。PRE-PREPARE消息集合的選取規則:
1. 選取V中最小的stable checkpoint編號min-s,選取V中prepare消息的最大編號max-s。
2. 在min-s和max-s之間,如果存在P消息集合,則創建<<PRE-PREPARE, v+1, n, d>, m>消息。否則創建一個空的PRE-PREPARE消息,即:<<PRE-PREPARE, v+1, n, d(null)>, m(null)>, m(null)空消息,d(null)空消息摘要。
副本節點收到主節點的NEW-VIEW消息,驗證有效性,有效的話,進入v+1狀態,并且開始O中的PRE-PREPARE消息處理流程。
總結:
PBFT算法由于每個副本節點都需要和其他節點進行P2P的共識同步,因此隨著節點的增多,性能會下降的很快,但是在較少節點的情況下可以有不錯的性能,并且分叉的幾率很低。PBFT主要用于聯盟鏈,但是如果能夠結合類似DPOS這樣的節點代表選舉規則的話也可以應用于公聯,并且可以在一個不可信的網絡里解決拜占庭容錯問題,TPS應該是遠大于POW的。
參考:
拜占庭容錯(Byzantine Fault Tolerance) WIKI: BFT - Wikipedia
PBFT論文地址:http://pmg.csail.mit.edu/papers/osdi99.pdf
作者:ether029
鏈接:https://www.jianshu.com/p/78e2b3d3af62
總結
- 上一篇: [剑指offer]面试题第[6]题[JA
- 下一篇: POJ-3061 尺取