《迅雷鏈精品課》第十三課:PBFT 演算法

迅雷鏈開放平臺發表於2020-12-25

上一節課我們學習了PoW(Proof-of-Work,工作量證明)共識演算法,瞭解其來源和優缺點,這節課我們將學習PBFT演算法,瞭解PBFT演算法如何通過三階段協議保證了系統能夠在包含作惡節點的情況下達成共識。

第十三課 PoW共識演算法

1. 背景

通常情況下,分散式系統由很多節點組成,系統的可靠性要求系統必須具備應付異常節點傳送過來的不一致訊息的能力。分散式的這個場景最早由 Lamport,Shostak 和 Pease 於1982年形象地描述為拜占庭將軍問題,即多個拜占庭將軍各自率領了小分隊駐紮在敵方城市之外,他們需要對是否進攻敵軍達成統一的作戰計劃,但是這些將軍之間存在反叛者,他們可能會篡改、延遲或者丟棄其他將軍的命令,迷惑其他可信將軍。拜占庭將軍問題就是要找到一個演算法來保證可信將軍之間能夠達成一致的作戰計劃。

Lamport他們證明了對於拜占庭將軍問題,系統中如果存在f個不可信節點,那麼只有總節點數不少於3f+1才存在一個演算法解決該問題。下面我們來簡單地證明這個問題解的不可能性,假設系統中總共有3個節點,Commander是提議者,記為C,Lieutenant 1 和Lieutenant 2 是驗證節點,記為 LT1 和 LT2。這三個節點中存在一個作惡節點,下面我們證明在存在一個作惡節點的情況下,無論是提議者還是驗證節點,系統均無法達成統一的共識。

如圖1-1所示,假設 C 是作惡節點,另外兩個節點是可信節點。C 向節點LT1和LT2傳送了相互矛盾的命令— 進攻和撤退,那麼LT1在接收到C傳送過來的進攻命令和LT2轉發過來的命令撤退,那麼它就無法確定是要進攻還是撤退。

在這裡插入圖片描述
圖1-1 提議者是作惡節點

如圖1-2所示,假設 LT2 是作惡節點,節點 C 和 LT1 是可信節點,C要求LT1和LT2進攻,但是LT2在轉發C的命令給LT1的時候作假,告訴LT1它接收到C發過來的命令是撤退,那麼LT1在就無法判斷到底是要進攻還是撤退。
在這裡插入圖片描述
圖1-2 Lieutenant 2 是作惡節點

從上述論證中我們可以直觀地覺察到3個節點的情況下,即使只有一個作惡節點,也無法達成共識。但如果系統中多一個可信節點LT3,則LT1可以收到可信節點Commander發出的和LT3轉發的進攻命令,即使LT2節點發出撤退命令,LT1也可以正確選擇跟大多數節點一致的進攻命令,這樣系統能達成共識。我們可以將這個結論進行推廣,即對於存在f個作惡節點的分散式系統,當節點總數不超過3f的時候,是不存在一個可以達成共識的解的。

學者們陸續提出瞭解決該問題的拜占庭容錯演算法BFT,不是隻能用於同步系統,就是效能太差,難以在生產環境中廣泛運用。為了解決BFT的實際運用問題,MIT計算機實驗室的Castro和Liskov於1999年在OSDI會議上提出了能夠在非同步網路環境中工作的PBFT演算法,在借鑑分散式系統的狀態機複製和quorum的基礎上設計了三階段協議來解決一致性問題,同時引入了優化項,演算法的複雜度由原來的指數級降低為多項式級別。下面我們會講解PBFT演算法的模型、演算法流程、優化,其中演算法流程即三階段協議是PBFT演算法的核心演算法流程,是我們的重點講解物件。

2. PBFT演算法模型

PBFT演算法本質上是一個狀態機複製演算法,能夠用於實現帶有狀態和特定操作的任意確定性狀態複製服務,它的目的是讓所有的可信節點執行相同的序列。此外,PBFT演算法使用安全的雜湊函式對訊息做摘要、使用公私鑰對訊息進行簽名和驗籤、同時增加了訊息驗證碼,保證了訊息的完整性和不可篡改性。在作惡節點總數不超過系統節點總數的1/3的情況下,PBFT演算法能夠保證系統的安全性和活性。PBFT演算法主要包含三類基本協議:

  1. 三階段協議:解決如何達成共識

  2. 檢查點協議:類似於作業系統的還原點,主要用於垃圾回收

2.1 基本概念

首先我們瞭解一下PBFT演算法的基本概念:

  1. 主節點primary:負責對請求進行排序,發起新的請求

  2. 副節點backup:負責驗證請求是否有效

  3. 客戶端client:負責提出請求,要求節點執行某個操作,通常跟主節點合二為一

  4. 序列號sequence number:請求的編號

  5. 檢視view:一個主節點和多個備份節點形成一個檢視

  6. 檢查點checkpoint:如果某個序列號對應的請求收到了超過的節點的確認,則稱為一個檢查點。

根據前面的討論,我們知道PBFT演算法是用於解決拜占庭將軍問題的,所以系統要能夠容忍f個作惡節點,則系統的總節點數不少3f+1個。假定系統中包含1個主節點和2f+1個副節點。

3. 三階段協議

三階段協議是PBFT演算法的核心流程,用於解決系統的一致性問題,保證所有可信節點在給定狀態和引數組的條件下,按照相同的順序執行完請求後能夠取得相同的狀態。三階段分別為pre-prepare、prepare和commit階段,具體的執行流程圖如圖3-1所示,下面是正常的流程:

  1. 客戶端c在時間為t傳送請求給主節點,要求執行操作o;

  2. Pre-prepare階段:主節點0對請求分配序列號,完成排序後將請求廣播給所有的副節點;

  3. prepare階段:副節點i驗證在接受到主節點的請求後,需要驗證請求和序列號在當前的檢視中是否有效,將投票結果發給其他節點;

  4. commit階段:每個節點在收到超過2f個副節點的投票資訊之後,如果請求、序列號和檢視均匹配,則生成一個提交資訊傳送給其他節點,表明接收了對應的請求;

  5. 每個節點在收到超過f+1個節點的提交資訊後,將請求的發起時間t、請求的執行結果r和當前的檢視號v傳送給客戶端 c;

  6. 客戶端在接受執行結果r之前,需要阻塞等待f+1個節點的響應,要求各個副本節點i對於客戶端c的請求的響應中的簽名必須有效,同時這些響應中的各個結果r以及請求的時間戳t需要相同。

如果客戶端超時沒有收到響應,客戶端會廣播請求給系統中的所有節點。節點接收到重複的請求的時候,如果已經處理過該請求的話,那麼會將響應再次傳送給客戶端。同時,副本會記錄下自己最近一次傳送給客戶端的響應訊息。此外,如果節點不是主節點的話,需要把請求轉發給主節點。如果主節點不將請求廣播給副節點,當有足夠的副節點懷疑主節點是作惡節點的時候, 亦會引發檢視變更,將主節點替換掉。

在這裡插入圖片描述
圖3-1 PBFT演算法流程圖

3.1 pre-prepare階段

主節點在收到客戶端的請求後,會基於當前的檢視v對請求分配編號n,將檢視號v、請求序列號n、請求摘要d、簽名封裝成pre-prepare訊息,記錄到本地的訊息日誌中,然後將pre-prepare訊息連同客戶端原始請求m傳送給其他的副節點,同時追加到自己的訊息日誌中。注意,pre-prepare訊息中,是不包含客戶端原本的請求訊息m的,而只是包含了該請求的摘要而已。pre-prepare階段主要是用來對請求進行絕對排序,將請求傳輸和請求排序進行解耦,同時還可以證明在檢視變更協議中,主節點在視點為v的時候把請求編號為n。

在這裡插入圖片描述
圖3-2 pre-prepare 階段

當副節點接受到主節點的pre-prepare訊息的時候,在滿足如下的條件的情況下會接受該訊息。
在這裡插入圖片描述

3.2 prepare階段

每個副節點在上一個階段驗證主節點的pre-prepare訊息有效之後,會進入到 prepare 階段,生成一個prepare訊息記錄到本地,表明自己已經接受了主節點的提議,同意在檢視v中把序列號n分配給了客戶端請求,保證自己在這個檢視中不會再將序列號分配給其他的客戶端請求,然後把prepare訊息傳送給其他節點。

在這裡插入圖片描述

圖3-3 準備階段

對於各個節點傳送過來的prepare訊息,包括主節點在內的所有節點接受該訊息的條件是:

在這裡插入圖片描述

演算法的 pre-prepare 和 prepare 階段保證了系統中可信節點在檢視v中對於請求m的絕對排序取得了共識。

3.3 commit 階段

當各個節點做好準備之後,即本地訊息日誌中記錄了摘要為d的請求m、記錄了主節點對請求m進行排序的pre-prepare訊息,同時接收到了超過2f個其他節點發過來的有效的prepare訊息,各個節點會進入下一個階段,即commit階段。各個節點會生成一個檢視為v、請求序列號為n、請求摘要為D(m)的commit訊息追加到本地訊息日誌檔案中,同時廣播給系統內的其他節點。

在這裡插入圖片描述
圖3-4 提交階段

對於各個節點傳送過來的commit訊息,接受該訊息的條件是

提交階段保證了可信節點就本地提交的客戶端請求的序列號取得了共識,即便這些客戶端請求在每個節點是在不同的檢視號中提交的,保證了所有的可信節點按照給定的順序執行了所有的客戶端請求,從而實現了安全性。當節點收到超過f+1個不同節點傳送過來的commit訊息之後,會執行客戶端請求m中所要求的服務操作,同時將執行結果傳送給客戶端,這個保證了在可信節點中提交的任意一個客戶端請求,最終都會在另外f+1個節點中被提交到本地的。

4. 演算法優化

4.1 檢查點協議

PBFT通過三階段協議來對請求達成共識,但是各個階段產生的訊息如果不進行垃圾回收的話,系統的儲存資源將會不堪重負。為此,PBFT演算法設計了檢查點協議來丟棄本地的訊息日誌檔案中的舊訊息。垃圾回收的設計需要考慮何時該刪除訊息,同時保證某個訊息在可信節點中都被刪除之後,某個節點在缺失這些訊息的情況下在同步到最新的狀態後能夠證明這個狀態是正確的。

根據前面的三階段協議,我們可以知道客戶端收到某個請求的執行結果的時候,表明該請求已經被至少f+1個節點提交過了,這個時候就可以刪除該訊息了。對於第二個問題,檢查點協議是通過提供額外的證據,checkpoint 訊息來證明這個狀態是正確的。但是如果每次執行完一個客戶端請求之後都生成上述要求的證據,那麼這個操作將是非常昂貴的。實際上,PBFT的檢查點協議是週期性地生成這些證明,比如當請求的序列號整除週期T的時候。

在這裡插入圖片描述
圖4-1 檢查點協議

如圖4-1 所示,檢查點協議的的工作流程如下所示:
在這裡插入圖片描述

檢查點協議除了用於垃圾回收外,還用於更新請求序列號的有效範圍,序列號的最低值h設為最近一次檢查點裡面的請求序列號,序列號的最高值設為H=h+k,其中需要設定得大一點,比checkpoint的週期要大,不然節點收到序列號較大的請求之後需要阻塞等待到下一個檢查點才能處理。比如說,如果檢查週期是100個請求,那麼k可以設定成200。

4.2 檢視變更協議

前面我們提到過,一些拜占庭演算法不能解決主節點出故障的問題。PBFT演算法是通過檢視變更協議來允許系統在主節點出故障的情況下仍能夠正常運轉,從而保證了系統的活性。檢視變更協議實際上是通過超時機制觸發的,通過超時機制,我們可以避免主節點不工作的情況下,副節點無限期地等待客戶端請求被執行。假定系統當前狀態如圖4-2所示,檢視號為v。

在這裡插入圖片描述
圖4-2 檢視變更 — 初始檢視v

下面我們來具體闡述一下檢視變更協議的具體工作流程。

4.2.1 維持計時器

副節點會針對檢視v維持一個計時器。當在收到主節點傳送過來的一個有效的請求而且沒有執行的時候,如果是針對當前檢視的計時器還沒有啟動的話,那麼節點會啟動一個新的計時器。如果節點還在執行其他請求的話,那麼節點會重置該請求的計時器。如果節點不再等待執行該請求的時候,會停止檢視v的計時器。

4.2.2 請求檢視變更

如圖4-3所示,當副節點的檢視v的計時器如果超時了,就會生成一個view-change 訊息,記錄到本地日誌檔案中,同時廣播給其他節點,要求替換掉主節點,變更到下一個檢視v+1。注意,在檢視變更期間,除了 checkpoint、view-change 和 newview 訊息之外,備份節點i是不會接受其他訊息的。

在這裡插入圖片描述
圖4-3 檢視變更 — 計時器超時,發起檢視變更
在這裡插入圖片描述

各個節點收到view-change 訊息後,會檢驗該訊息是否有效,如果有效的話,會追加到本地的日誌檔案中。

4.2.3 切換到新檢視

如圖4-4所示,當新檢視v+1對應的新的主節點接收到2f個節點的傳送過來的有效的view-change訊息之後,會向其他節點傳送一個帶上自己簽名的new-view訊息,提議系統內所有節點切換到下一個檢視v+1,同時接受自己成為新的主節點。這個 new-view 訊息的另外一個作用是找到所有節點的共同的穩定檢查點,同時針對那些攜帶了尚未提交執行的請求的 prepare 訊息重新生成相應的 pre-prepare 訊息,這個主要是在切換到新檢視之後,新的主節點需要重新對這些未處理的請求分配序列號,然後對這些請求再次執行一遍三階段協議。

在這裡插入圖片描述
在這裡插入圖片描述

副節點在收到要求將視點變更為v+1的new-vew訊息之後,在確認訊息有效之後,會接受該new-view訊息,記錄到本地訊息檔案中,同時將視點更換到新的視點v+1。此外,副節點還會new-view中攜帶的由新的主節點重新生成的pre-prepare訊息都追加記錄到本地的訊息日誌中,並按照檢查點協議進行垃圾回收,刪除比較老的訊息。然後,對於new-vew訊息中集合攜帶的所有由新主節點生成的新的pre-prepare訊息,備份節點都會生成相應的prepare訊息,記錄到本地日誌檔案中,轉發給其他節點,即對這些未處理的請求在新的檢視中重新執行一遍三階段協議,保證檢視切換過程中未處理的請求能夠重新被處理。

5. PBFT演算法應用

PBFT演算法通過三階段協議保證了系統能夠在包含作惡節點的情況下達成共識,將演算法複雜度由指數級別降低成了多項式級別,大大地降低了網路通訊的開銷,為拜占庭演算法的在生產環境中的實際運用提供了可能。此外,PBFT演算法通過檢查點協議進行垃圾回收的同時保證了系統能夠感知全域性狀態的一致性,而檢視變更協議解決了主節點失效的問題,保證了系統的活性。實際上,相比於比特幣等區塊鏈系統使用的概率性的PoW演算法,PBFT演算法的確定性特性保證了系統具有應付作惡節點的能力同時不會分叉,非常適合於聯盟鏈和私鏈的搭建,比如聯盟鏈 fabric 中最初就提供了PBFT作為共識演算法。雖然 PBFT 演算法效能比原有的拜占庭演算法提高了很多,但是PBFT演算法的效能會隨著節點個數的增加而急劇下降,所以通常會結合DPoS等演算法來對節點的許可權進行控制,比如 tendermint 共識演算法,即區塊鏈上的PBFT演算法,就是使用DPoS來控制驗證節點的數量的。

相關文章