1. 以太坊中PoA產(chǎn)生的背景
如果你想用以太坊搭建一個聯(lián)盟/私有鏈, 并要求該鏈交易成本更低甚至沒有, 交易延時更低,并發(fā)更高, 還擁有完全的控制權(quán)(意味著被攻擊概率更低). 目前以太坊采用PoW或后續(xù)的casper能否滿足要求?
首先, pow存在51%攻擊問題, 惡意挖礦者超過全網(wǎng)算力的51%后基本上就能完全控制整個網(wǎng)絡(luò). 由于鏈無法被更改, 已上鏈的數(shù)據(jù)也無法更改, 但惡意挖礦者也可以做一些DoS攻擊阻止合法交易上鏈,考慮到具有相同創(chuàng)世塊的曠工都能加入你的網(wǎng)絡(luò), 潛在的安全隱患會長期存在.
其次, PoW大量的電力資源消耗也是需要作為后續(xù)成本考慮. PoS可以解決部分Pow問題, 比如節(jié)約電力,在一定程度上保護(hù)了51%的攻擊(惡意曠工會被懲罰), 但從控制權(quán)和安全考慮還有欠缺, 因?yàn)镻oS還是允許任何符合條件的曠工加入禀崖。
在已經(jīng)運(yùn)行的測試網(wǎng)絡(luò)Ropsten中, 由于pow設(shè)定的難度較低,惡意曠工濫用較低的PoW難度并將gaslimit擴(kuò)大到90億(正常是470萬)躬存,發(fā)送大量的交易癱瘓了整個網(wǎng)絡(luò)把敞。而在此之前除呵,攻擊者也嘗試了多次非常長的重組(reorgs),導(dǎo)致不同客戶端之間的分叉,甚至不同的版本。
這些攻擊的根本原因是PoW網(wǎng)絡(luò)的安全性依賴于背后的算力。而從零開始重新啟動一個新的testnet將不會解決任何問題牲平,因?yàn)楣粽呖梢砸淮斡忠淮蔚剡M(jìn)行相同的攻擊。 Parity團(tuán)隊(duì)決定采取緊急解決方案域滥,回滾大量的塊纵柿,并設(shè)置不允許gaslimit超過某一閾值的軟分叉規(guī)則。
雖然Parity的解決方案可能在短期內(nèi)有效, 但是這不是優(yōu)雅的:Ethereum本身應(yīng)該具有動態(tài)gaslimit限制; 也不可移植:其他客戶端需要自己實(shí)現(xiàn)新的軟分叉邏輯; 并與同步模式不兼容, 也不支持輕客戶端; 盡管并不完美启绰,但是Parity的解決方案仍然可行昂儒。 一個更長期的替代解決方案是使用PoA共識,相對簡單并容易實(shí)現(xiàn).
2. PoA的特點(diǎn)
- PoA是依靠預(yù)設(shè)好的授權(quán)節(jié)點(diǎn)(signers),負(fù)責(zé)產(chǎn)生block.
- 可以由已授權(quán)的signer選舉(投票超過50%)加入新的signer委可。
- 即使存在惡意signer,他最多只能攻擊連續(xù)塊(數(shù)量是
(SIGNER_COUNT / 2) + 1)
中的1個,期間可以由其他signer投票踢出該惡意signer渊跋。 - 可指定產(chǎn)生block的時間。
3. PoA需要解決的問題
- 如何控制挖礦頻率,即出塊時間
- 如何驗(yàn)證某個塊的有效性
- 如何動態(tài)調(diào)整授權(quán)簽名者(signers)列表,并全網(wǎng)動態(tài)同步
- 如何在signers之間分配挖礦的負(fù)載或者叫做挖礦的機(jī)會
對應(yīng)的解決辦法如下:
- 協(xié)議規(guī)定采用固定的block出塊時間, 區(qū)塊頭中的時間戳間隔為15s
- 先看看block同步的方法,從中來分析PoA中驗(yàn)證block的解決辦法
有兩種同步blockchain的方法
- 經(jīng)典方法是從創(chuàng)世塊開始挨個執(zhí)行所有交易着倾。 這是經(jīng)過驗(yàn)證的拾酝,但是在Ethereum的復(fù)雜網(wǎng)絡(luò)中,計(jì)算量非常大卡者。
- 另一個是僅下載區(qū)塊頭并驗(yàn)證其有效性蒿囤,之后可以從網(wǎng)絡(luò)下載任意的近期狀態(tài)對最近的區(qū)塊頭進(jìn)行檢查。
顯然第二種方法更好. 由于PoA方案的塊可以僅由可信任的簽名者來創(chuàng)建, 因此崇决,客戶端看到的每個塊(或塊頭)可以與可信任簽名者列表進(jìn)行匹配蟋软。 要驗(yàn)證該塊的有效性就必須得到該塊對應(yīng)的簽名者列表, 如果簽名者在該列表中帶包該塊有效. 這里的挑戰(zhàn)是如何維護(hù)并及時更改的授權(quán)簽名者列表镶摘? 存儲在智能合約中?不可行, 因?yàn)樵诳焖佥p量級同步期間無法訪問狀態(tài)。
因此, 授權(quán)簽名者列表必須完全包含在塊頭中 岳守。那么需要改變塊頭的結(jié)構(gòu), 引入新的字段來滿足投票機(jī)制嗎? 這也不可行:改變這樣的核心數(shù)據(jù)結(jié)構(gòu)將是開發(fā)者的噩夢。
所以授權(quán)簽名者名單必須完全符合當(dāng)前的數(shù)據(jù)模型, 不能改變區(qū)塊頭中的字段碌冶,而是 **復(fù)用當(dāng)前可用的字段: Extra字段. **
Extra 是可變長數(shù)組, 對它的修改是 非侵入
操作, 比如RLP,hash操作都支持可變長數(shù)據(jù). Extra中包含所有簽名者列表和當(dāng)前節(jié)點(diǎn)的簽名者對該區(qū)塊頭的簽名數(shù)據(jù)(可以恢復(fù)出來簽名者的地址).
- 更新一個動態(tài)的簽名者列表的方法是復(fù)用區(qū)塊頭中的 Coinbase和Nonce字段 湿痢,以創(chuàng)建投票方案:
- 常規(guī)的塊中這兩個字段置為0
- 如果簽名者希望對授權(quán)簽名者列表進(jìn)行更改,則將:
- Coinbase 設(shè)置為被投票的簽名者
- 將 Nonce 設(shè)置為 0 或 0xff ... f 投票,代表 添加或移除
- 任何同步的客戶端都可以在塊處理過程中“統(tǒng)計(jì)”投票扑庞,并通過投票結(jié)果來維護(hù)授權(quán)簽名者列表譬重。
為了避免一個無限的時間來統(tǒng)計(jì)投票,我們設(shè)置一個投票窗口, 為一個epoch,長度是30000個block罐氨。每個epoch的起始清空所有歷史的投票, 并作為簽名者列表的檢查點(diǎn). 這允許客戶端僅基于檢查點(diǎn)哈希進(jìn)行同步臀规,而不必重播在鏈路上完成的所有投票。
- 目前的方案是在所有signer之間輪詢出塊, 并通過算法保證同一個signer只能簽名
(SIGNER_COUNT / 2) + 1)
個block中第一個.
綜上, PoA的工作流程如下:
- 在創(chuàng)世塊中指定一組初始授權(quán)的signers, 所有地址 保存在創(chuàng)世塊Extra字段中
- 啟動挖礦后, 該組signers開始對生成的block進(jìn)行 簽名并廣播.
- 簽名結(jié)果 保存在區(qū)塊頭的Extra字段中
- Extra中更新當(dāng)前高度已授權(quán)的 所有signers的地址 ,因?yàn)橛行录尤牖蛱叱龅膕igner
- 每一高度都有一個signer處于IN-TURN狀態(tài), 其他signer處于OUT-OF-TURN狀態(tài), IN-TURN的signer簽名的block會 立即廣播 , OUT-OF-TURN的signer簽名的block會 延時 一點(diǎn)隨機(jī)時間后再廣播, 保證IN-TURN的簽名block有更高的優(yōu)先級上鏈
- 如果需要加入一個新的signer, signer通過API接口發(fā)起一個proposal, 該proposal通過復(fù)用區(qū)塊頭 Coinbase(新signer地址)和Nonce("0xffffffffffffffff") 字段廣播給其他節(jié)點(diǎn). 所有已授權(quán)的signers對該新的signer進(jìn)行"加入"投票, 如果贊成票超過signers總數(shù)的50%, 表示同意加入
- 如果需要踢出一個舊的signer, 所有已授權(quán)的signers對該舊的signer進(jìn)行"踢出"投票, 如果贊成票超過signers總數(shù)的50%, 表示同意踢出
signer對區(qū)塊頭進(jìn)行簽名
- Extra的長度至少65字節(jié)以上(簽名結(jié)果是65字節(jié),即R, S, V, V是0或1)
- 對blockHeader中所有字段除了Extra的 后65字節(jié) 外進(jìn)行 RLP編碼
- 對編碼后的數(shù)據(jù)進(jìn)行
Keccak256
hash - 簽名后的數(shù)據(jù)(65字節(jié))保存到Extra的 后65字節(jié) 中
授權(quán)策略
以下建議的策略將減少網(wǎng)絡(luò)流量和分叉
- 如果簽名者被允許簽署一個塊(在授權(quán)列表中栅隐,但最近沒有簽名)塔嬉。
- 計(jì)算下一個塊的最優(yōu)簽名時間(父塊時間+ BLOCK_PERIOD)。
- 如果簽名人是in-turn,立即進(jìn)行簽名和廣播block。
- 如果簽名者是out-of-turn菱阵,延遲
rand(SIGNER_COUNT * 500ms)
后再簽名并廣播
級聯(lián)投票
當(dāng)移除一個授權(quán)的簽名者時,可能會導(dǎo)致其他移除前的投票成立. 例: ABCD4個signer, AB加入E,此時不成立(沒有超過50%), 如果ABC移除D, 會自動導(dǎo)致加入E的投票成立(2/3的投票比例)
投票策略
因?yàn)閎lockchain可能會小范圍重組(small reorgs), 常規(guī)的投票機(jī)制(cast-and-forget, 投票和忘記)可能不是最佳的电禀,因?yàn)榘瑔蝹€投票的block可能不會在最終的鏈上,會因?yàn)橐延凶钚碌腷lock而被拋棄。
一個簡單但有效的辦法是對signers配置"提議(proposal)".例如 "add 0x...", "drop 0x...", 有多個并發(fā)的提議時, 簽名代碼"隨機(jī)"選擇一個提議注入到該簽名者簽名的block中,這樣多個并發(fā)的提議和重組(reorgs)都可以保存在鏈上.
該列表可能在一定數(shù)量的block/epoch 之后過期症歇,提案通過并不意味著它不會被重新調(diào)用,因此在提議通過時不應(yīng)立即丟棄。
- 加入和踢除新的signer的投票都是立即生效的,參與下一次投票計(jì)數(shù)
- 加入和踢除都需要 超過當(dāng)前signer總數(shù)的50% 的signer進(jìn)行投票
- 可以踢除自己(也需要超過50%投票)
- 可以并行投票(A,B交叉對C,D進(jìn)行投票), 只要最終投票數(shù)操作50%
- 進(jìn)入一個新的epoch, 所有之前的pending投票都作廢, 重新開始統(tǒng)計(jì)投票
投票場景舉例
- ABCD, AB先分別踢除CD, C踢除D, 結(jié)果是剩下ABC
- ABCD, AB先分別踢除CD, C踢除D, B又投給C留下的票, 結(jié)果是剩下ABC
- ABCD, AB先分別踢除CD, C踢除D, 即使C投給自己留下的票, 結(jié)果是剩下AB
- ABCDE, ABC先分別加入F(成功,ABCDEF), BCDE踢除F(成功,ABCDE), DE加入F(失敗,ABCDE), BCD踢除A(成功, BCDE), B加入F(此時BDE加入F,滿足超過50%投票), 結(jié)果是剩下BCDEF
4. PoA中的攻擊及防御
- 惡意簽名者(Malicious signer). 惡意用戶被添加到簽名者列表中鸯屿,或簽名者密鑰/機(jī)器遭到入侵. 解決方案是,N個授權(quán)簽名人的列表把敢,任一簽名者只能對每K個block簽名其中的1個寄摆。這樣盡量減少損害,其余的礦工可以投票踢出惡意用戶技竟。
- 審查簽名者(Censoring signer). 如果一個簽名者(或一組簽名者)試圖檢查block中其他signer的提議(特別是投票踢出他們), 為了解決這個問題冰肴,我們將簽名者的允許的挖礦頻率限制在1/(N/2)。如果他不想被踢出出去, 就必須控制超過50%的signers.
- "垃圾郵件"簽名者(Spamming signer). 這些signer在每個他們簽名的block中都注入一個新的投票提議.由于節(jié)點(diǎn)需要統(tǒng)計(jì)所有投票以創(chuàng)建授權(quán)簽名者列表, 久而久之之后會產(chǎn)生大量垃圾的無用的投票, 導(dǎo)致系統(tǒng)運(yùn)行變慢.通過epoch的機(jī)制,每次進(jìn)入新的epoch都會丟棄舊的投票
- 并發(fā)塊(Concurrent blocks). 如果授權(quán)簽名者的數(shù)量為N榔组,我們允許每個簽名者簽名是1/K熙尉,那么在任何時候,至少N-K個簽名者都可以成功簽名一個block搓扯。為了避免這些block競爭( 分叉 )检痰,每個簽名者生成一個新block時都會加一點(diǎn)隨機(jī)延時。這確保了很難發(fā)生分叉锨推。