istanbul演算法詳解
目錄文章以及資料(開源):github地址
- Terminology:
- Consensus
- Block hash, proposer seal, and committed seals
- Block locking mechanism
- Gossip network
Terminology:
- Validator:塊的驗證者
- Proposer:塊驗證者中被選擇用來出塊的
- Round: 共識的輪數。一輪中 Proposer 開始提出一個一個出塊建議,然後以提交區塊結束。
- Proposal:新的塊生成提議
- Sequence:提議的序號。當前序列號大於先前的;塊高就是此提議的序列號。
- Backlog:儲存未來的共識訊息
- Round state
- Consensus proof:用來證明塊已經通過共識處理的塊簽名
- Snapshot:上一個時期的驗證者投票狀態
Consensus
Proposer 必須在每個 round中連續不斷的為consensus 生成 block prorosal。
istanbul BFT 包括 3 個階段的共識:PRE-PREPARE,PREPARE,COMMIT。
容錯機制: N = 3F +1 ;N表示驗證節點,F表示錯誤節點。
在每輪之前將會以迴圈的方式選擇一個 validator 作為 proposer. 接著 proposer將會提出一個新的 block proposal 並且廣播通過 pre-prepare 訊息。一旦接受到 pre-prepare訊息 ,validators將會進入到 pre-prepared 階段並且廣播 prepare 訊息。這個步驟是為了確保 validators 執行在相同的 sequence 和相同的 round 中。當接收到2F+1 的Prepare訊息時,validators 進入到 prepared並且廣播 commit 訊息。此步驟是通知其它節點接受建議的塊並將塊插入鏈。 最後,validator等待2F + 1 COMMIT訊息進入COMMITTED狀態,然後將塊插入鏈。
注意:Istanbul中 的塊是最終的,沒有分叉,任何有效的塊必須位於主鏈的某個位置。
為了防止故障節點從主鏈生成完全不同的鏈,每個驗證器將2F + 1個接收到的COMMIT簽名附加到標頭中的extraData欄位,然後將其插入鏈中, 因此,塊是可自我驗證的,並且也可以支援輕客戶端。但是,動態extraData會導致塊雜湊計算出現問題。由於來自不同驗證器的相同塊可以具有不同的COMMIT簽名集,因此同一塊也可以具有不同的塊雜湊。 為了解決這個問題,我們通過排除COMMIT簽名部分來計算塊雜湊。 因此,我們仍然可以保持塊/塊雜湊一致性,並將共識證明放在塊頭中。
Consensus states
Istanbul BFT是一種狀態機複製演算法。 每個驗證器都維護一個狀態機副本,以達到塊一致性。
States:
NEW ROUND
: Proposer傳送新的 block proposal。 Validator等待PRE-PREPARE訊息。PRE-PREPARED
:驗證器已收到PRE-PREPARE訊息並廣播PREPARE訊息。 然後它等待2F + 1 個PREFARE或COMMIT訊息。PREPARED
: 驗證器已收到2F + 1個PREPARE訊息並廣播COMMIT訊息。 然後它等待2F + 1 COMMIT訊息。COMMITTED
:驗證器已收到2F + 1個COMMIT訊息,並能夠將建議的塊插入區塊鏈。FINAL COMMITTED
:新塊已成功插入區塊鏈,validator 已準備好進入下一輪。ROUND CHANGE
:驗證器正在等待同一個建議的輪數上的2F + 1個ROUND CHANGE訊息。
State transitions
NEW ROUND
->PRE-PREPARED
:- Proposer 從txpool 中收集交易
- Proposer生成塊提議並將其廣播給驗證者。 然後它進入PRE-PREPARED狀態。
- 每個validator在收到具有以下條件的PRE-PREPARE訊息後進入PRE-PREPARED:
- 塊提案來自有效的提案人。
- 塊頭有效
- 塊提議的sequence和round匹配validator的狀態
- Validator廣播
PREPARE
訊息給其他validators
PRE-PREPARED
->PREPARED
:- Validator接收2F + 1個有效的PREPARE訊息以進入PREPARED狀態。 有效訊息符合以下條件:
- 匹配 sequence 和 round
- 匹配block hash
- 訊息來自於已知 validators
- Validator接收2F + 1個有效的PREPARE訊息以進入PREPARED狀態。 有效訊息符合以下條件:
COMMITTED
->FINAL COMMITTED
:- validator 將 2F+1 個提交的簽名放到 extraData中並且嘗試將區塊上鍊
- 插入成功後,Validator進入FINAL COMMITTED狀態。
FINAL COMMITTED
->NEW ROUND
:- 驗證器選擇一個新的提議器並啟動一個新的round timer 。
Round change flow
- 3 個條件將會觸發 ROUND CHANGE
- Round change timer 過期
- 無效
PREPREPARE
訊息 - 塊插入失敗
- 當驗證器注意到上述條件之一適用時,它會廣播ROUND CHANGE訊息以及建議的 round number,並等待來自其他驗證器的ROUND CHANGE訊息。 建議的round number 根據以下條件選擇:
- 如果驗證器已從其peers接收到ROUND CHANGE訊息,則它將選擇具有F + 1個ROUND CHANGE訊息的最大 round number。
- 否則,它會選擇1 +當前的round number作為建議的輪數。
- 每當驗證器在同一個建議的輪數上收到F + 1個ROUND CHANGE訊息時,它就會將收到的訊息與它自己的一個進行比較。 如果接收的數量較大,驗證器將再次使用收到的號碼廣播ROUND CHANGE訊息。
- 在相同的建議round number上接收到2F + 1個ROUND CHANGE訊息後,驗證器退出round change loop,計算新的提議者,然後進入NEW ROUND狀態。
- 驗證器跳出round change loop的另一個條件是它通過對等同步接收驗證的塊。
Proposer selection
目前我們支援兩種策略:round robin 和 sticky proposer.。
- Round robin:在迴圈設定中,提議者將在每個塊和round change中進行更改
- Sticky proposer: 在 sticky proposer中, proposal只有在發生一輪變更時才會改變。
Validator list voting
我們使用與Clique類似的驗證器投票機制,並複製Clique EIP的大部分內容。 每個epoch交易都會重置驗證器投票,這意味著如果授權或取消授權投票仍在進行中,則投票過程將被終止。
對於所有交易塊:
- Proposer可以投一票來建議更改驗證人名單。
- 每個目標受益人的最新提案僅保留一個驗證人。
- 隨著鏈條的進展,投票將被實時統計(允許同時提議)。
- 達到多數共識的proposals ,VALIDATOR_LIMIT立即生效。
- 無效的提案不會因客戶端實現簡單而受到懲罰。
- 一項生效的提案需要放棄該提案的所有未決投票(贊成和反對),並以 clean state 開始
Future message and backlog
在非同步網路環境中,可以接收將來無法在當前狀態下處理的訊息。 例如,驗證器可以在NEW ROUND上接收COMMIT訊息。 我們將此類訊息稱為“未來訊息”。 當驗證程式收到將來的訊息時,它會將訊息放入其待辦事項中,並儘可能在稍後嘗試處理。
Optimization
為了加速共識過程,在接收PREFARE訊息的2F + 1之前接收到2F + 1 COMMIT訊息的驗證器將跳轉到COMMITTED狀態,這樣就不必等待進一步的PREPARE訊息。
Constants
我們定義以下常量:
EPOCH_LENGTH
:檢查點和重置待處理投票之後的塊數。- 建議30000使testnet保持類似於主要網路ethash時代。
REQUEST_TIMEOUT
: 在以毫秒為單位進行輪次更改之前,每個達成一致的超時。BLOCK_PERIOD
: 兩個連續塊之間的最小時間戳差異(秒)。PROPOSER_POLICY
:提議者選擇策略,預設為round robin.。ISTANBUL_DIGEST
:固定的 magic number, 0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365用於Istanbul塊識別的塊頭中的mixDigest。DEFAULT_DIFFICULTY
: 預設塊難度,設定為0x0000000000000001。EXTRA_VANITY
: 固定數量的額外資料字首位元組預留給提議者。- 建議保留當前額外資料容量和/或使用的32個位元組。
NONCE_AUTH
: magic nonce number 0xffffffffffffffff投票新增驗證器。NONCE_DROP
:magic nonce number 0x0000000000000000 投票移除驗證器UNCLE_HASH
:總是Keccak256(RLP([]))作為叔叔在PoW之外沒有意義。PREPREPARE_MSG_CODE
: 固定編號0. PREPREPARE訊息的訊息程式碼。COMMIT_MSG_CODE
: 固定編號1. COMMIT訊息的訊息程式碼。ROUND_CHANGE_MSG_CODE
:固定號碼2. ROUND CHANGE訊息的訊息程式碼。
我們還定義了以下每塊常量:
BLOCK_NUMBER
: 鏈中的塊高度,其中生成塊的高度為0。N
: 授權驗證人數。F
:允許的錯誤驗證器數量。VALIDATOR_INDEX
:當前授權驗證器的排序列表中的塊驗證器的索引。VALIDATOR_LIMIT
: 傳遞授權或取消授權提議的驗證者數量。- 必須是最低限額(N / 2)+ 1才能對鏈條達成多數共識。
Block header
我們沒有為伊斯坦布林BFT發明新的塊頭。 相反,我們跟隨Clique重新調整ethash標頭欄位,如下所示:
-
beneficiary
: 建議修改驗證器列表的地址。- 應該通常用零填充,僅在投票時修改。
- 儘管如此,允許使用任意值(即使是無意義的值,例如投票給非驗證者),以避免投票機制實施中的額外複雜性。
-
nonce
:關於受益人領域定義的帳戶的提議者提案。- 應該是NONCE_DROP建議取消授權受益人作為現有驗證人。
- 應該是NONCE_AUTH建議授權受益人作為新的驗證人。
- 必須填充零,NONCE_DROP或NONCE_AUTH
-
mixHash
: 固定 magic number 0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365 用於伊斯坦布林區塊識別 -
ommersHash
:必須是UNCLE_HASH,因為在PoW之外,叔叔沒有意義。 -
timestamp
:必須至少是父時間戳+ BLOCK_PERIOD -
difficulty
:必須填寫0x0000000000000001。 -
extraData
: 簽名者和RLP編碼的伊斯坦布林額外資料的組合欄位,其中伊斯坦布林額外資料包含驗證器列表,proposer seal和 commit seal。 伊斯坦布林的額外資料定義如下:type IstanbulExtra struct { Validators []common.Address //Validator addresses Seal []byte //Proposer seal 65 bytes CommittedSeal [][]byte //Committed seal, 65 * len(Validators) bytes }
因此extraData將採用EXTRA_VANITY |的形式 ISTANBUL_EXTRA其中| 表示用於分隔vanity和伊斯坦布林額外資料的固定索引(不是分隔符的實際字元)。
- 第一個EXTRA_VANITY位元組(固定)可以包含任意提議者vanity資料。
- ISTANBUL_EXTRA位元組是從RLP(IstanbulExtra)計算的RLP編碼的伊斯坦布林額外資料,其中RLP()是RLP編碼功能,而IstanbulExtra是伊斯坦布林額外資料。
Validators
: 驗證器列表,必須按升序排序。Seal
: 提議者的header seal 簽名。CommittedSeal
:提交的簽名列表作為共識證明
Block hash, proposer seal, and committed seals
由於以下原因,Istanbul塊雜湊計算與ethash塊雜湊計算不同:
- 提議者需要將提議者密封在extraData中以證明該塊由所選提議者簽名。
- 驗證者需要將2F + 1個已提交的密封作為extraData中的共識證明,以證明該塊已經達成共識。
計算仍然類似於ethash塊雜湊計算,但我們需要處理extraData。 我們按如下方式計算欄位:
Proposer seal calculation
在提議者密封計算時,committed的密封仍然是未知的,因此我們計算密封與那些未知的密封空。 計算如下:
Proposer seal
:SignECDSA(Keccak256(RLP(Header)), PrivateKey)
PrivateKey
: Proposer's的私鑰Header
: 和ethash 的header一樣,只不過extradata不一樣extraData
:vanity | RLP(IstanbulExtra)
, 在IstanbulExtra,
CommittedSealand
Seal` 是空陣列.
Block hash calculation
在計算塊雜湊時,我們需要排除已提交的密封,因為該資料在不同的驗證器之間是動態的。 因此,我們在計算雜湊時使CommittedSeal為空陣列。 計算如下:
Header
: 和ethash 的header一樣,只不過extradata不一樣extraData
:vanity | RLP(IstanbulExtra)
, 在IstanbulExtra,
CommittedSealand
Seal` 是空陣列.
Consensus proof
在將塊插入區塊鏈之前,每個驗證器需要從其他驗證器收集2F + 1個已提交的密封以構成共識證明。 一旦它收到足夠的提交密封,它將填充IstanbulExtra中的CommittedSeal,重新計算extraData,然後將塊插入區塊鏈。 請注意,由於已提交的密封可能因不同的來源而不同,因此我們會在計算塊雜湊時排除該部分,如上一節所述。
Committed seal calculation:
committed seal由每個簽名雜湊的驗證器以及其私鑰的COMMIT_MSG_CODE訊息程式碼計算。 計算如下:
Committed seal
:SignECDSA(Keccak256(CONCAT(Hash, COMMIT_MSG_CODE)), PrivateKey)
.CONCAT(Hash, COMMIT_MSG_CODE)
: 連線 block hash andCOMMIT_MSG_CODE
bytes.PrivateKey
: 簽署驗證者的私鑰。
Block locking mechanism
引入鎖定機制以解決安全問題。 通常,當提議者用塊B鎖定在某個高度H時,它只能為高度H提出B.另一方面,當驗證器被鎖定時,它只能在B上投票選擇高度H.
Lock
鎖定鎖(B,H)包含一個塊及其高度,這意味著它的所有驗證器當前被鎖定在某個塊B和高度H.在下面,我們還使用+表示多於和 - 表示小於。 例如,+ 2/3驗證器表示超過三分之二的驗證器,而-1/3驗證器表示不到三分之一的驗證器。
Lock and unlock
- Lock:驗證器在高度為“H”的塊“B”上接收到“2F + 1”“PREPARE”訊息時被鎖定。
- Unlock: 驗證器在高度“H”處解鎖,並在未能將塊“B”插入塊鏈時阻止“B”。
Protocol (+2/3
validators are locked with Lock(B,H)
)
-
PRE-PREPARE
:-
Proposer:
-
情況1,提議者被鎖定:在B上廣播PRE-PREPARE,並進入PREPARED狀態。
-
情況2,提議者未被鎖定:在塊B'上廣播PRE-PREPARE。
-
-
Validator:
- 情況1,在現有塊上接收PRE-PREPARE:忽略。
- 注意:它最終會導致輪次更改,並且提議者將通過同步獲得舊塊。
- 情況2,驗證器被鎖定:
- 案例2.1,在B上收到PRE-PREPARE:在B上廣播PREPARE
- 案例2.2,在B'上接收PRE-PREPARE:廣播ROUND CHANGE。
- 情況3,驗證器未鎖定:
- 情況3.1,在B上接收PRE-PREPARE:在B上廣播PREPARE
- 案例3.2,在B'上接收PRE-PREPARE:在B'上廣播PREPARE。
- 注意:由於+2/3被鎖定在B並且這將導致全面更改,因此此共識輪將最終進行全面更改。
- 情況1,在現有塊上接收PRE-PREPARE:忽略。
-
-
PREPARE
:- 案例1,驗證器被鎖定:
- 情況1.1,在B上接收PREPARE:在B上廣播COMMIT,並進入PREPARED狀態。
- 注意:這不應該發生,它應該跳過這一步並在PRE-PREPARE階段輸入PREPARED。
- 案例1.2,在B'上收到PREPARE:忽略。
- 注意:B'上不應該有+1/3 PREPARE,因為+2/3被鎖定在B.因此B'上的共識輪將導致輪次更改。 驗證器不能直接在此廣播ROUND CHANGE,因為此PREPARE訊息可能來自故障節點。
- 情況1.1,在B上接收PREPARE:在B上廣播COMMIT,並進入PREPARED狀態。
- 情況2,驗證器未鎖定:
- 情況2.1,在B上收到PREPARE:在B上等待2F + 1 PREPARE訊息
- 注意:在接收2F + 1 PREPARE訊息之前,它很可能會收到2F + 1 COMMIT訊息,因為有+2/3驗證器被鎖定在B.在這種情況下,它將直接跳轉到COMMITTED狀態。
- 情況2.2,在B'上收到PREPARE:在B'上等待2F + 1 PREPARE訊息。
- Note: This consensus will eventually get into round change since
+2/3
validators are locked onB
and which would lead to round change.
- Note: This consensus will eventually get into round change since
- 情況2.1,在B上收到PREPARE:在B上等待2F + 1 PREPARE訊息
- 案例1,驗證器被鎖定:
-
COMMIT
:- 驗證者必須被鎖定:
- 情況1,在B上收到COMMIT:等待2F + 1 COMMIT訊息。
- 案例2,B'收到COMMIT:不應該發生。
- 驗證者必須被鎖定:
Locking cases
- Round change:
- Case 1,
+2/3
are locked:- 如果提議者被鎖定,則建議B.
- 否則它會提出B',但這將導致另一輪變革。
- 結論:最終B將由誠實的驗證者承諾。
- Case 2,
+1/3 ~ 2/3
are locked:- 如果提議者被鎖定,則建議B.
- 否則它會提出B'。 但是,由於+1 / 3被鎖定在B,因此沒有驗證器可以在B'上接收2F + 1 PREPARE,這意味著沒有驗證器可以鎖定在B'。 此外,那些+1 / 3鎖定驗證器將不會響應B'並最終導致全面更改。
- 結論:最終B將由誠實的驗證者承諾。
- Case 3,
-1/3
are locked:- 如果提議被鎖定,則建議B.
- 否則它會提出B'。 如果+2/3在B'上達成共識,那些鎖定的-1/3將通過同步獲得B'並移動到下一個高度。 否則,將會有另一輪變更。
- 結論:它可以是B或其他塊B'最終提交。
- Case 1,
- 插入失敗導致的round change:
- 它將屬於上述一輪變更案例之一。
- 如果塊實際上是壞的(不能插入區塊鏈),最終+2/3驗證器將在H處解鎖塊B並嘗試建議新的塊B'。
- 如果塊是好的(可以插入區塊鏈),那麼它仍然是上述圓形更改案例之一。
- 它將屬於上述一輪變更案例之一。
- -1/3驗證器成功插入塊,但其他驗證器成功觸發迴圈更改,這意味著+1 / 3仍鎖定在鎖定(B,H)
- 案例1,提議者已插入B:提議者將在H'提出B',但+1 / 3被鎖定在B,因此B'將不會通過共識,這最終將導致輪次更改。 其他驗證器將對B執行共識或通過同步獲得B.
- 案例2,提議者未插入B:
- 案例2.1,提議者被鎖定:提議者提出B.
- 案例2.2,提議者未被鎖定:提議者將在H處提出B'。其餘與上述案例1相同。
- +1/3驗證器成功插入塊,-2 / 3試圖在H處觸發圓形更改
- 案例1,提議者已插入B:提議者將在H'提出B',但在+1/3通過同步獲得B之前不會通過共識。
- 案例2,提議者未插入B:
- 案例2.1,提議者被鎖定:提議者提出B.
- 案例2.2,提議者未被鎖定:提議者在H處提出B'。其餘與上述案例1相同。
- +2/3驗證器成功插入塊,-1 / 3試圖在H處觸發圓形更改
- 案例1,提議者已插入B:提議者將在H'提出B',這可能會導致成功的共識。 然後那些-1/3需要通過同步獲得B.
- 案例2,提議者未插入B:
- 案例2.1,提議者被鎖定:提議者提出B.
- 案例2.2,提議者沒有被鎖定:提議者在H處建議B'。因為+2/3已經在H處有B,所以這一輪將導致輪次改變。
Gossip network
傳統上,驗證者需要緊密連線才能達到穩定的共識結果,這意味著所有驗證者需要彼此直接連線; 但是,在實際的網路環境中,很難實現穩定和恆定的p2p連線。 為了解決這個問題,伊斯坦布林BFT實施了八卦網路來克服這種限制。 在八卦網路環境中,所有驗證器只需要弱連線,這意味著當它們直接連線或者它們之間連線有一個或多個驗證器時,任何兩個驗證器都會被連線。 共識訊息將在驗證器之間中繼。