PBFT 拜占庭容錯演算法

2022-07-12 13:42:10 字數 3691 閱讀 6362

根據**《practical byzantine fault tolerance and proactive recovery》整理

practical byzantine fault tolerance and proactive recoverym.castro and b.liskov. 2002.

本演算法是基於狀態機複製模型的,服務被抽象稱為乙個狀態機,分布式系統的各個節點複製相同的狀態,當存在少數節點故障時,狀態機依然可以正常運轉。

在狀態機複製模型中,分布式系統的每個節點都複製完全相同的狀態,並支援相同的操作。對於一組相同順序的輸入操作,每個節點都產生相同順序的輸出結果。

bft演算法保證了正常節點在相同的輸入下產生相同的輸出狀態。分布式節點都有乙個相同的起始狀態,而且每個節點多餘相同的輸入會產生相同的輸入結果。所以,只要保證在分布式系統執行時輸入操作的順序都是一致的,那麼各個節點的狀態會在執行時始終保持一致。也就是說,對於狀態機模型的分布式系統,對輸入請求操作的排序是共識演算法的關鍵。

本演算法採用「primary-backup」和"quorum replication"技術結合,來實現任務排序。雖然paxos和viewstamped replication也採用了相同的排序機制,但是本演算法可以實現拜占庭容錯。

primary-backup

在「primary-backup」機制中,各節點都是view的繼承表示。在乙個view中,其中乙個節點作為主節點(primary)存在,其他節點作為backup。主節點決定操作請求的順序。主節點給乙個請求操作分配乙個序列號,並將該分配廣播到所有其他節點(backup)。但是主節點也是可能出現錯誤的,這時需要其他節點來檢查序列號,並通過超時檢測來確認主節點是否停止工作。一旦檢測到當前主節點失敗,就會觸發view轉換,選擇乙個新的主節點。

演算法保證任務序列號是連續的,不能跳躍。當存在view轉換時,可以分配序列號給一些空操作,以保證新的主節點內序列號的連續性。

quorum replication

為了保證系統的容錯性能,演算法依賴於quorum機制(大多數原則),quorum機制有以下兩個特性:

quorum的特性使得它可以作為協議資訊的乙個可靠儲存。節點向quorum寫入資訊,同時等待quorum中節點的驗證訊息。收到的驗證訊息可以證明,資訊已經被寫入成功了。

對於乙個節點集合r,我們用整數表示每乙個節點。簡化問題,我們假設|r| = 3f+1,其中f是允許的錯誤節點個數的最大值。view試圖用整數v表示,v也是從小到大的整數。主節點可以用p = v%|r|表示。一般情況下,quorum是包含至少2f+1個節點的集合。

client傳送請求

client請求乙個操作\(o\)時,會向節點廣播乙個訊息\(\alpha_c\)。其中,\(t\)表示請求發生的時間。對於\(c\)的時間戳用來進行全域性排序,需要保證後面的請求有更大的時間戳值。

節點收到請求訊息並將其新增到自己的log中,請求的執行及排序在後面的章節描述。節點執行完操作後直接向client傳送請求的回覆,其回應訊息為\(\mu_\) , \(v\)表示當前view的值,\(t\)是對應請求的時間戳,i表示本節點id,r表示執行的結果。

當client收到了\(f+1\)個帶有相同的\(t\)和\(r\)訊息時,就接收處理的結果\(r\)。由於最多存在\(f\)個節點發生錯誤,所以\(f+1\)個節點一致時,就表示該結果的正確的。

如果client沒有收到足夠的回應,那麼他會重發請求。節點收到請求訊息時,如果該訊息應景被執行過了,那麼就會直接回覆該請求,傳送乙個reply訊息。節點需要儲存上一次reply的訊息,以便後面重發使用。如果主節點沒有分配乙個有效的序列號,那麼就會被足夠多的節點認為是乙個錯誤的節點,並觸發一次檢視轉換。

雖然我們假定client是完成當前請求之後,才會觸發下一次請求,但是這也很容易擴充套件到非同步的執行請求。

也可以將協議擴充套件到多個client的情形,給每個client乙個秘鑰,節點中儲存各個client的秘鑰。雖然這個方法簡單可行,但是當大量client存在時,還是會出現問題。我們做了如下優化,節點值共享存活的client秘鑰,而且限制活動的client數量。

請求排序

client發出請求之後的過程,用三個步驟來實現,分別是pre-prepare、prepare、commit。

節點的狀態包含當前service狀態、訊息log(記錄節點收到和傳送的訊息)、節點當前所處的view(可用整數v表示)。

1. pre-prepare

當主節點p收到乙個來自client的請求 \(m_ = _\)時,p會給這個訊息\(m\)分配乙個序列號\(n\)。之後,p會向其他節點廣播乙個pre-prepare訊息,並將該訊息記錄到訊息log中。而主節點之外的節點收到訊息就直接存在訊息log中了。pre-prepare的格式可以是\(_\),其中v表示當前所處的view,d(m)表示訊息的雜湊值。

與pre-prepcare階段一樣,其他幾個階段傳送的訊息中都會包含\(n\)、\(v\)值。節點只會接受與該節點當前所處的view一致的v值的訊息。n會用乙個區間去判斷,即節點會接收符合某個區間的n值的訊息。

2. prepare

當訊息m通過驗證時,back-up節點會接收乙個pre-prepare訊息。如果該節點接收乙個pre-prepare訊息,並且該節點的訊息log中有對應的\(m\)訊息,那麼該節點進入prepare階段,這時節點會廣播prepare訊息\(_\), 其中i表示該節點。該節點將收到的pre-prepare訊息和發出的prepare訊息都加入到訊息log中。當乙個節點收到pre-prepare訊息和2f個符合n, v, m的prepare訊息時,該節點就被prepare驗證通過,說明該節點prepare了該請求。

協議保證了不會出現有著相同view和序列號n的不同訊息通過prepare驗證。這保證了在同乙個view中的所有訊息,都有確定的全域性順序。

假設存在兩個不同訊息$m$、$m'$通過prepare驗證,而他們有著相同的v、n值。根據quorum原則,乙個quorum中至少有乙個節點是正確的。這個節點會傳送pre-prepare或者prepare訊息,給在同乙個view中的m和m'分配相同的序列號。而根據之前的規定,節點分配序列號應該是累加的,對於不同的訊息不應該是相同的序列值。
3. commit

對於不同view下帶有相同序列號的訊息來說,commit階段解決了全域性排序的問題。每個節點都廣播\(_\)的commit訊息,表明該節點已經經過prepare驗證,並將該訊息加入訊息log中。每個節點都會保持接收commit訊息,直到它收到了匹配v和n的2f+1條commit訊息,我們叫這個過程為commit驗證,驗證通過稱之為請求被commit了。這個過程之後,節點就同時通過了prepare驗證和commit驗證。

當請求被commit之後,協議保證請求已經被大多數節點所prepare,也就是說存在乙個quorum知道乙個quorum的節點接受了乙個在v檢視下的n序列值。這時發生檢視轉換時,新的主節點可以通過從乙個quorum中讀取prepare驗證訊息,來給新的檢視中選擇新的序列值n。新的序列值n應該是上乙個檢視中n的下乙個整數。這就可以實現在不同檢視下的全域性排序了。

當請求被commit之後,節點就會執行訊息m對應的操作,節點會根據序列號值從小到大執行操作。所有沒有發生錯誤的節點都會以相同的順序執行操作。執行完請求操作之後,節點向client傳送乙個回應資訊。為了保證訊息只被執行一次,當節點收到比上一次回應的請求訊息中的時間戳小的訊息時,會忽略。

在這個過程中,沒有依賴順序的訊息傳送,因此節點commit請求的過程可以是亂序的。

PBFT(拜占庭容錯)簡述

區塊鏈中最重要的便是共識演算法,位元幣使用的是pow proof of work,工作量證明 以太幣使用的是pos proof of stake,股權證明 使得算力變的不怎麼重要了,而今pos的變體dpos delegated proof of stake,股份授權證明 進一步削減算力的浪費,同時也...

PBFT 拜占庭共識演算法

pbft演算法是根據拜占庭問題演變而來的拜占庭共識演算法。在拜占庭問題被提出後一直有各種共識演算法來解決拜占庭問題,但是無論從執行流程的複雜度還是演算法效率來說,pbft是目前公認效率最好的演算法。該演算法是miguel castro 卡斯楚 和barbara liskov 利斯科夫 在1999年提...

解釋拜占庭容錯

拜占庭將軍問題很多人可能聽過,但不知道是什麼意思,本文從非專業的角度來講講,拜占庭將軍問題到底是說什麼的。拜占庭將軍問題 byzantine generals problem 首先由leslie lamport與另外兩人在1982年提出,很簡單的故事模型,卻困擾了計算機科學家們數十年。故事大概是這麼...