1. 程式人生 > >以太坊PoA共識引擎演算法介紹(1)

以太坊PoA共識引擎演算法介紹(1)

1. 以太坊中PoA產生的背景

如果你想用以太坊搭建一個聯盟/私有鏈, 並要求該鏈交易成本更低甚至沒有, 交易延時更低,併發更高, 還擁有完全的控制權(意味著被攻擊概率更低). 目前以太坊採用PoW或後續的casper能否滿足要求?

  • 首先, pow存在51%攻擊問題, 惡意挖礦者超過全網算力的51%後基本上就能完全控制整個網路. 由於鏈無法被更改, 已上鍊的資料也無法更改, 但惡意挖礦者也可以做一些DoS攻擊阻止合法交易上鍊,考慮到具有相同創世塊的曠工都能加入你的網路, 潛在的安全隱患會長期存在.

  • 其次, PoW大量的電力資源消耗也是需要作為後續成本考慮. PoS可以解決部分Pow問題, 比如節約電力,在一定程度上保護了51%的攻擊(惡意曠工會被懲罰), 但從控制權和安全考慮還有欠缺, 因為PoS還是允許任何符合條件的曠工加入。

在已經執行的測試網路Ropsten中, 由於pow設定的難度較低,惡意曠工濫用較低的PoW難度並將gaslimit擴大到90億(正常是470萬),傳送大量的交易癱瘓了整個網路。而在此之前,攻擊者也嘗試了多次非常長的重組(reorgs),導致不同客戶端之間的分叉,甚至不同的版本。

這些攻擊的根本原因是PoW網路的安全性依賴於背後的算力。而從零開始重新啟動一個新的testnet將不會解決任何問題,因為攻擊者可以一次又一次地進行相同的攻擊。 Parity團隊決定採取緊急解決方案,回滾大量的塊,並設定不允許gaslimit超過某一閾值的軟分叉規則。

雖然Parity的解決方案可能在短期內有效, 但是這不是優雅的:Ethereum本身應該具有動態gaslimit限制; 也不可移植:其他客戶端需要自己實現新的軟分叉邏輯; 並與同步模式不相容, 也不支援輕客戶端; 儘管並不完美,但是Parity的解決方案仍然可行。 一個更長期的替代解決方案是使用PoA共識,相對簡單並容易實現.

2. PoA的特點

  • PoA是依靠預設好的授權節點(signers),負責產生block.
  • 可以由已授權的signer選舉(投票超過50%)加入新的signer。
  • 即使存在惡意signer,他最多隻能攻擊連續塊(數量是 (SIGNER_COUNT / 2) + 1) 中的1個,期間可以由其他signer投票踢出該惡意signer。
  • 可指定產生block的時間。

3. PoA需要解決的問題

  1. 如何控制挖礦頻率,即出塊時間
  2. 如何驗證某個塊的有效性
  3. 如何動態調整授權簽名者(signers)列表,並全網動態同步
  4. 如何在signers之間分配挖礦的負載或者叫做挖礦的機會

對應的解決辦法如下:

  1. 協議規定採用固定的block出塊時間, 區塊頭中的時間戳間隔為15s
  2. 先看看block同步的方法,從中來分析PoA中驗證block的解決辦法

有兩種同步blockchain的方法

  1. 經典方法是從創世塊開始挨個執行所有交易。 這是經過驗證的,但是在Ethereum的複雜網路中,計算量非常大。
  2. 另一個是僅下載區塊頭並驗證其有效性,之後可以從網路下載任意的近期狀態對最近的區塊頭進行檢查。

顯然第二種方法更好. 由於PoA方案的塊可以僅由可信任的簽名者來建立, 因此,客戶端看到的每個塊(或塊頭)可以與可信任簽名者列表進行匹配。 要驗證該塊的有效性就必須得到該塊對應的簽名者列表, 如果簽名者在該列表中帶包該塊有效. 這裡的挑戰是如何維護並及時更改的授權簽名者列表? 儲存在智慧合約中?不可行, 因為在快速輕量級同步期間無法訪問狀態。

因此, 授權簽名者列表必須完全包含在塊頭中 。那麼需要改變塊頭的結構, 引入新的欄位來滿足投票機制嗎? 這也不可行:改變這樣的核心資料結構將是開發者的噩夢。

所以授權簽名者名單必須完全符合當前的資料模型, 不能改變區塊頭中的欄位,而是 **複用當前可用的欄位: Extra欄位. **

Extra 是可變長陣列, 對它的修改是 非侵入 操作, 比如RLP,hash操作都支援可變長資料. Extra中包含所有簽名者列表和當前節點的簽名者對該區塊頭的簽名資料(可以恢復出來簽名者的地址).

  1. 更新一個動態的簽名者列表的方法是複用區塊頭中的 Coinbase和Nonce欄位 ,以建立投票方案:
  • 常規的塊中這兩個欄位置為0
  • 如果簽名者希望對授權簽名者列表進行更改,則將:
    • Coinbase 設定為被投票的簽名者
    • Nonce 設定為 00xff ... f 投票,代表 新增或移除
    • 任何同步的客戶端都可以在塊處理過程中“統計”投票,並通過投票結果來維護授權簽名者列表。

為了避免一個無限的時間來統計投票,我們設定一個投票視窗, 為一個epoch,長度是30000個block。每個epoch的起始清空所有歷史的投票, 並作為簽名者列表的檢查點. 這允許客戶端僅基於檢查點雜湊進行同步,而不必重播在鏈路上完成的所有投票。

  1. 目前的方案是在所有signer之間輪詢出塊, 並通過演算法保證同一個signer只能簽名 (SIGNER_COUNT / 2) + 1) 個block中第一個.

綜上, PoA的工作流程如下:

  1. 在創世塊中指定一組初始授權的signers, 所有地址 儲存在創世塊Extra欄位中
  2. 啟動挖礦後, 該組signers開始對生成的block進行 簽名並廣播.
  3. 簽名結果 儲存在區塊頭的Extra欄位中
  4. Extra中更新當前高度已授權的 所有signers的地址 ,因為有新加入或踢出的signer
  5. 每一高度都有一個signer處於IN-TURN狀態, 其他signer處於OUT-OF-TURN狀態, IN-TURN的signer簽名的block會 立即廣播 , OUT-OF-TURN的signer簽名的block會 延時 一點隨機時間後再廣播, 保證IN-TURN的簽名block有更高的優先順序上鍊
  6. 如果需要加入一個新的signer, signer通過API介面發起一個proposal, 該proposal通過複用區塊頭 Coinbase(新signer地址)和Nonce("0xffffffffffffffff") 欄位廣播給其他節點. 所有已授權的signers對該新的signer進行"加入"投票, 如果贊成票超過signers總數的50%, 表示同意加入
  7. 如果需要踢出一箇舊的signer, 所有已授權的signers對該舊的signer進行"踢出"投票, 如果贊成票超過signers總數的50%, 表示同意踢出

signer對區塊頭進行簽名

  1. Extra的長度至少65位元組以上(簽名結果是65位元組,即R, S, V, V是0或1)
  2. 對blockHeader中所有欄位除了Extra的 後65位元組 外進行 RLP編碼
  3. 對編碼後的資料進行 Keccak256 hash
  4. 簽名後的資料(65位元組)儲存到Extra的 後65位元組

授權策略

以下建議的策略將減少網路流量和分叉

  • 如果簽名者被允許簽署一個塊(在授權列表中,但最近沒有簽名)。
    • 計算下一個塊的最優簽名時間(父塊時間+ BLOCK_PERIOD)。
    • 如果簽名人是in-turn,立即進行簽名和廣播block。
    • 如果簽名者是out-of-turn,延遲 rand(SIGNER_COUNT * 500ms) 後再簽名並廣播

級聯投票

當移除一個授權的簽名者時,可能會導致其他移除前的投票成立. 例: ABCD4個signer, AB加入E,此時不成立(沒有超過50%), 如果ABC移除D, 會自動導致加入E的投票成立(2/3的投票比例)

投票策略

因為blockchain可能會小範圍重組(small reorgs), 常規的投票機制(cast-and-forget, 投票和忘記)可能不是最佳的,因為包含單個投票的block可能不會在最終的鏈上,會因為已有最新的block而被拋棄。

一個簡單但有效的辦法是對signers配置"提議(proposal)".例如 "add 0x...", "drop 0x...", 有多個併發的提議時, 簽名程式碼"隨機"選擇一個提議注入到該簽名者簽名的block中,這樣多個併發的提議和重組(reorgs)都可以儲存在鏈上.

該列表可能在一定數量的block/epoch 之後過期,提案通過並不意味著它不會被重新呼叫,因此在提議通過時不應立即丟棄。

  • 加入和踢除新的signer的投票都是立即生效的,參與下一次投票計數
  • 加入和踢除都需要 超過當前signer總數的50% 的signer進行投票
  • 可以踢除自己(也需要超過50%投票)
  • 可以並行投票(A,B交叉對C,D進行投票), 只要最終投票數操作50%
  • 進入一個新的epoch, 所有之前的pending投票都作廢, 重新開始統計投票

投票場景舉例

  • ABCD, AB先分別踢除CD, C踢除D, 結果是剩下ABC
  • ABCD, AB先分別踢除CD, C踢除D, B又投給C留下的票, 結果是剩下ABC
  • ABCD, AB先分別踢除CD, C踢除D, 即使C投給自己留下的票, 結果是剩下AB
  • ABCDE, ABC先分別加入F(成功,ABCDEF), BCDE踢除F(成功,ABCDE), DE加入F(失敗,ABCDE), BCD踢除A(成功, BCDE), B加入F(此時BDE加入F,滿足超過50%投票), 結果是剩下BCDEF

4. PoA中的攻擊及防禦

  • 惡意簽名者(Malicious signer). 惡意使用者被新增到簽名者列表中,或簽名者金鑰/機器遭到入侵. 解決方案是,N個授權簽名人的列表,任一簽名者只能對每K個block簽名其中的1個。這樣儘量減少損害,其餘的礦工可以投票踢出惡意使用者。
  • 審查簽名者(Censoring signer). 如果一個簽名者(或一組簽名者)試圖檢查block中其他signer的提議(特別是投票踢出他們), 為了解決這個問題,我們將簽名者的允許的挖礦頻率限制在1/(N/2)。如果他不想被踢出出去, 就必須控制超過50%的signers.
  • "垃圾郵件"簽名者(Spamming signer). 這些signer在每個他們簽名的block中都注入一個新的投票提議.由於節點需要統計所有投票以建立授權簽名者列表, 久而久之之後會產生大量垃圾的無用的投票, 導致系統執行變慢.通過epoch的機制,每次進入新的epoch都會丟棄舊的投票
  • 併發塊(Concurrent blocks). 如果授權簽名者的數量為N,我們允許每個簽名者簽名是1/K,那麼在任何時候,至少N-K個簽名者都可以成功簽名一個block。為了避免這些block競爭( 分叉 ),每個簽名者生成一個新block時都會加一點隨機延時。這確保了很難發生分叉。

作者:shi_qinfeng 連結:https://www.jianshu.com/p/9025a523ab0f 來源:簡書