PBFT算法流程

本文爲萬向區塊鏈技術中心研究組撰寫,介紹了PBFT算法的正常流程。算法


1. 系統模型

本部分介紹PBFT算法運行的系統模型。安全


1.1 網絡

PBFT工做在異步的分佈式系統中,系統中各個節點彼此經過網絡鏈接。 系統運行時,消息的傳遞容許出現下列情形:
不能正確發送
延遲
重複
亂序網絡


1.2 Byzantine failure model

系統容許錯誤節點也就是拜占庭節點表現出任意行爲,可是須要附加一個限定條件: 節點失效彼此應相互獨立,從而大部分或所有節點不會同時失效。

在有惡意攻擊存在的狀況下,能夠採起相似於下列措施來保證這個限制的成立:
各節點運行的服務程序和操做系統的版本儘量多樣化
各節點的管理員賬號和密碼不一樣異步


1.3 消息加密屬性
1.3.1 使用加密技術的目的

防止身份欺騙、重播攻擊
監測錯誤消息分佈式


1.3.2 具體使用的加密技術

公鑰簽名:用於驗證消息發送者身份,PBFT中,實際上只用於view-change和new-view消息,以及出現錯誤的狀況。其餘消息都採用下面將會提到的MAC(消息認證碼)進行認證。這是算法設計中提出的一種優化措施,用於提高算法性能。
MAC:即消息認證碼,用於算法正常操做流程中的消息認證
消息摘要:用於檢測錯誤消息性能


1.4 敵手特性

算法限定敵手(adversary)能夠:
串通拜占庭節點
延遲通訊或正常節點
同時,敵手不能夠:
無限延遲正常節點的通訊
僞造正常節點簽名
從消息摘要反推原始消息
讓不一樣消息產生相同摘要區塊鏈


2. 服務屬性

本部分介紹運行PBFT算法的系統的服務屬性。優化


2.1 關於副本複製服務

PBFT算法可用於實現肯定性的副本複製服務(Replicated service)。 副本複製服務擁有狀態(state)和操做(operation)。
客戶端(client)向服務發起請求,以執行操做,並等待響應。
服務由 n個節點組成。操做能夠執行任何計算,只要這些計算始終產生肯定性的結果。
節點和客戶端若是遵循算法的預約步驟執行操做,則被稱爲正常節點或客戶端。加密


2.2 關於Safety和Liveness

只要系統中失效節點的個數不超過容錯數 ,系統就能提供safety和liveness。spa


2.2.1 Safety

Safety的提供,是系統能保證客戶端請求的線性一致性(linearizability),即請求按順序一次一條地被執行。
PBFT相對於以前的算法如Rampart等的一個顯著的不一樣在於,其Safety不依賴於同步假設。
算法不需限定客戶端必定是正常的,容許其發送不合法的請求,緣由是各正常節點能夠一致性地監測客戶端請求的各類操做。而且算法能夠經過權限控制的方式對客戶端進行限制。


2.2.2 Liveness

因爲算法不依賴於同步提供 Safety,所以必須經過同步假設來提供 Liveness。
這裏的同步假設是,客戶端的請求最終總能在有限的時間內被系統接收到。
客戶端可能會經過屢次重傳的方式,發送請求到服務,確保其被服務接收到。
PBFT所依賴的同步假設實際上是比較弱的假設,緣由是在真實的系統中,網絡錯誤最終總能夠修復。


2.3 關於算法彈性

PBFT的算法彈性(resiliency)是最優的:假定系統中失效節點最大個數爲f,則系統最少只須要 3f+1 個節點就能夠保證Safety和Liveness。


簡單證實:
考慮到最不理想的狀況,系統有最大數量的失效節點,即f個。(總節點數爲n) 客戶端此時能夠接收到的回覆個數最壞狀況是 n-f,由於失效節點可能都不會回覆。 可是,因爲網絡等緣由,客戶端接收到的 n-f 個請求中,實際上有可能包含有失效節點的回覆(有多是錯誤的),而另一些正常節點的回覆還未及時收到。 這其中,最壞的狀況是,n-f個結果中,有f個是失效節點發送的。按照PBFT算法的定義,客戶端須要收到 f+1 個相同的回覆,才被看成是正確的結果。所以 n-f 個結果中,出去f個失效節點的結果,即 n-f-f = n-2f 至少要是f+1, 即 n-2f = f+1,也就是說 n=3f+1是最少須要的節點數量。


2.4 關於信息保密性

通常狀況下,爲確保服務的高效性,不能提供容錯的信息保密性。
可能可使用secret sharing scheme來得到操做參數和部分對操做來講透明的狀態的保密性。


3. 算法主流程

本部分介紹運行PBFT算法的主流程,即正常操做流程。


3.1 主流程簡介

3.1.1 相關定義

算法是狀態機副本複製技術的一種形式:服務被建模爲狀態機,其狀態在分佈式系統中的不一樣副本節點上被複制。每一個狀態機副本節點保存維護着服務狀態,並實現服務的各類操做。

假設全部副本節點個數爲n,算法中,每一個節點依次編號爲 0, 1, ..., n-1

方便起見,假設系統中的副本節點總數爲 3f+1。能夠有更多數量的節點,可是這不會使算法的彈性更優,只會使系統的性能下降。

系統在稱爲視圖(view)的配置下工做。視圖以整數編號,從0開始。在一個具體的視圖 v 中,經過 p =v mod n,決定出主節點(primary),而其他節點成爲副本節點(backup)。當主節點失效時,進行視圖變動(view change)。視圖的編號是連續遞增的。


3.1.2 算法主流程簡要描述

算法主流程可簡要描述以下:
1. 客戶端經過向主節點發送請求,以調用服務的操做;
2. 主節點向其餘全部副本節點廣播該請求;
3. 各節點執行客戶請求,同時將回復發送到客戶端;
4. 客戶端收到 f+1 個來自不一樣節點的相同的回覆後,此回覆即爲本次請求的結果。

由於算法基於狀態機副本複製技術,因此節點需知足兩個條件:
必須是肯定性的,即對於給定的狀態,以及給定的參數集合,調用相同的操做後,將始終獲得相同的結
果。
各節點擁有相同的初始狀態。


在知足上述兩個條件的狀況下,算法能夠保證系統的Safety屬性:即便存在失效的節點,各正常副本節點仍能夠就不一樣的請求的執行順序達成整體的一致。


3.2 算法主流程

接下來詳細描述算法主流程。爲方便起見,這裏省略討論如下細節:
節點因空間不足致使錯誤,以及如何恢復;
相似網絡的緣由等致使的客戶端消息的重傳。

另外,假設消息使用公鑰簽名進行認證,而不是更高效的 MAC 的方式。算法流程的啓動從客戶端發送請求開始。

 

3.2.1 客戶端操做

客戶端操做流程示意圖以下:



客戶端向其認爲的Primary節點發送請求:<REQUEST, o, t, c >。其中,o是請求的操做,t是時間戳,c表明客戶端信息。這裏省略了消息的簽名,包括下文提到的全部消息都應該有發送方的簽名,爲了討論方便,做了省略。


相關的幾點說明:
請求中的時間戳用於保證請求只被執行一次的語義:全部的時間戳都嚴格排序,即後發送的請求比先發送的請求擁有更高的時間戳。
每一個副本節點向客戶端發送回覆時,都會包含當前的視圖編號。客戶端能夠經過該視圖編號來肯定當前的主節點,而且只向其認爲的主節點發送請求。


每一個副本節點執行完請求後,各自單獨地向客戶端發送回覆,其格式爲: <REPLY, v, t, c, i, r > 。v是當前的視圖編號,t是請求發送時對應的時間戳,i是副本節點的編號,r是請求的操做的執行結果。


客戶端等待來自 f+1 個不一樣副本節點的相同回覆,即t和r要相同。若是客戶端等到了 f+1 個相同回覆,r即爲請求的結果。 之因此該結果是有效的,是由於錯誤節點的個數最多爲f個,所以必然至少有一個正常節點回復了正確結果,此結果就是 r。


若是客戶端沒有及時收到回覆,則會將請求廣播給全部副本節點。副本節點收到請求後,若是發現已經執行過該請求,就只是將結果從新發送至客戶端;不然,它會把請求轉發到主節點。若是主節點沒有把請求廣播給其餘節點,則最終會被足夠多的副本節點認定爲錯誤節點,從而觸發視圖變動。


在接下的流程討論中,假定客戶端等待一個請求完成後,才發送下一個請求。可是,算法容許客戶端異步地發送請求,而且能夠保證不一樣請求按順序執行。


3.2.2 三階段協議

主節點收到客戶端請求後,將啓動三階段協議,也就是算法接下來的流程。


這三階段是pre-prepare,prepare和commit。前兩階段,即 pre-prepare 和 prepare 用於保證當前視圖中請求
被排好序,然後兩階段 prepare 和 commit 保證請求在視圖變動後,仍舊是排好序的。


3.2.2.1 pre-prepare階段

pre-prepare 階段流程示意圖以下:

pre-prepare階段中,主節點組裝預準備消息,同時把客戶端請求附加在其後:<<PRE-PREPARE, v, n, d>, m>,其中,v 指示當前消息當前在哪一個視圖中被編號和發送,m 是客戶端的請求消息,n是主節點給 m 分配的一個序號(sequence number), d 是 m 的摘要。

 

這裏須要注意的是,請求 m 並無放在預準備消息中,這樣作可使預準備消息變得更簡短。這樣作有兩個好處:
一、下降通訊的負載:在視圖變動時,因爲各節點收到的預準備消息會被用來證實一個特定的請求確實在特定的視圖中被賦予了一個序號,較簡短的預準備消息將使數據傳輸量更少。
二、有助於傳輸優化:算法運行中,一方面須要向各節點發送客戶端請求,另外一方面須要傳輸協議消息以實現對客戶請求的排序。經過對這二者解耦,能夠實現對較小的協議消息的傳輸以及對應於較大的請求的大消息的傳輸分別進行優化。

每一個副本節點接收到預準備消息後,會進行以下校驗,若是條件都知足的話,就接受該消息;不然就什麼也不作:
客戶端請求和預準備消息的簽名都正確;d 和 m的消息摘要一致;
當前節點的當前視圖是v;
當前節點不曾接受另一條預準備消息,其包含的視圖編號和消息序號都和本條消息相同,但對應的是不一樣的客戶端請求;
預準備消息中的序號 n 位於低水線 h 和高水線 H 之間。這是爲了防止有可能出錯的主節點隨意地選擇序號來耗盡序號空間,例如故意選擇一個很是大的序號。


若是副本節點接受預準備消息,接下來就進入prepare 階段,以下節所示。


3.2.2.2 prepare階段

prepare 階段流程示意圖以下:

在 prepare 階段,節點會組裝並廣播準備消息給其餘全部副本節點,同時把預準備和準備消息寫入到本地消息日誌中。

準備消息格式以下: <PREPARE, v, n, d, i>。其中,i 爲節點編號,其他參數和預準備消息中的含義相同。

對於副本節點(包括主節點)來講,當其收到其餘節點發送過來的準備消息時,會對這些消息進行校驗,若是這些消息知足下列條件:
簽名正確
其視圖編號和節點的當前視圖編號相同
消息中的序號在 h 和 H 之間


則節點會接受準備消息,並寫入消息日誌中。


對於一個副本節點 i 來講,若是其消息日誌中包含以下消息:
客戶端請求 m
在視圖 v 中將 m 分配序號 n 的預準備消息
2f個由不一樣的副本節點發送的、和預準備消息相匹配的準備消息;這裏匹配的含義是,有相同的視圖編號、請求序號,以及消息摘要。


咱們就稱prepared(m, v, n, i)爲true。


算法的預準備和準備階段用於保證全部的正常副本節點就同一視圖中的全部請求的順序達成一致。具體來講,這兩階段能確保如下不變式:
若是prepared(m, v, n, i)爲true,則對任意一個正常的副本節點j(包含i)來講,prepared(m', v, n, j)確定爲false,這裏m'是不一樣於m的一個請求。


簡單證實以下:
由於prepared(m, v, n, i)爲true,而錯誤節點最多爲f個,因此至少有f個正常節點發送了準備消息,再加上主節點,這樣至少有f+1個節點已經就m在視圖v中被編號爲n達成了一致。所以,若是prepared(m', v, n, j)爲true,意味着上述f+1個節點中至少有一個節點發送了兩個相互矛盾的預準備或準備消息,也就是說,這些消息擁有相同的視圖編號和序號,可是對應着不一樣的請求消息。但這是不可能的,由於該節點是正常節點,所以prepared(m', v, n, j)必定爲false。


對於副本節點i來講,prepared(m, v, n, i)變爲true,則其將進入commit階段,以下節所示。


3.2.2.3 commit階段
commit 階段流程示意圖以下:

節點進入 commit 階段時,副本節點i將向其餘全部副本節點廣播確認消息:
<COMMIT, v, n, D(m), i>,
對於副本節點來講,當其收到其餘節點發來的確認消息的時候,會判斷其是否知足下列條件:
簽名正確;
消息中的視圖編號等於當前節點的視圖編號;
消息中的請求序號在h和H之間。


若是以上條件均知足,則節點則會接受確認消息並寫入本地的日誌消息中。


對於副本節點i來講,若是:
prepared(m, v, n, i)爲true
而且已經接受了 2f+1 個來自不一樣節點的、和 m 對應的預準備消息相匹配的確認消息(可能包含它本身的)


咱們稱 committed-local(m, v, n, i) 爲 true。這裏確認消息和預準備消息匹配的含義是,它們有相同的視圖編號、消息序號,以及消息摘要。


另外,若是至少存在 f+1 個節點,對於其中每個節點i來講,若是 prepared(m, v, n, i) 爲true,咱們則稱committed(m, v, n) 爲 true。


commit 階段能保證如下不變式:
若是對某個副本節點來講,committed-local(m, v, n, i) 爲 true,則 committed(m, v, n) s也爲true。


上述不變式和視圖變動協議一塊兒可以保證:
全部正常節點可以就全部本地確認的請求的序號達成一致,即便這些請求是在不一樣的視圖中確認的。對應的證實將在另一篇文檔中給出。


另外,該不變式也能保證:任何一個請求若是在一個副本節點被確認,那麼它最終也會被至少 f+1 個副本節點確認。


對於任何一個副本節點i來講,若是:
committed-local(m, v, n, i) 爲 true
i 的狀態反映了全部序號小於 n 的請求順序執行的結果


此時,它就能夠執行 m 所請求的操做。這就保證了全部的正常節點,按相同的順序執行請求,從而保證算法的安全性。


在執行了請求的操做後,每一個節點單獨地給客戶端發送回覆。對於一樣內容的請求,節點會忽略那些時間戳更早的,以保證請求只被執行一次。


此外,算法並不要求消息按順序投遞,所以,節點能夠亂序確認請求。這樣作沒有問題,由於算法只在一個請求對應的預準備、準備和確認消息都收集徹底時纔會執行該請求。


如下是 f=1,即失效節點數爲1個,總共節點數爲 4個時,PBFT 算法的運行示意圖:

 

關注我,後續將更新PBFT的補充算法流程。

相關文章
相關標籤/搜索