1. 程式人生 > >以太坊的POS共識機制

以太坊的POS共識機制

Original post by Vitalik Buterin, on December 28th, 2015

特別感謝Vlad Zamfir,他提出了按塊達成共識這個想法,並且說服我認同它和Casper的其它一些核心想法的價值;以及Vlad Zamfir和Greg Meredith,為他們在這個協議上的持續努力。

在這個系列的上一篇中,我們討論了Serenity的兩大旗艦特性之一:深度抽象,它將給平臺帶來極大的靈活性,使以太坊從“比特幣加圖靈完備”向“通用去中心化的計算”邁進一大步。現在,讓我們把注意力轉向另一個主要特性,也是當初設立Serenity里程碑的原因:Casper權益證明演算法。

投注共識

Casper向公開經濟學共識這個領域引入了一個根本上全新的理念作為自己的基礎:投注共識。投注共識的核心思想很簡單:為驗證人(validator)提供與協議對賭哪個塊會被最終確定的機會。在這裡對某個區塊X的投注就是一筆交易,在所有區塊X被處理了的世界中都會帶給驗證人Y個幣的獎勵(獎勵是憑空“印”出來的,因而是“與協議”對賭),而在所有區塊X沒有被處理的世界中會對驗證人收取Z個幣的罰款(罰金被銷燬)。

(譯註:一個世界(world)在這裡應理解為區塊鏈未來的一種可能狀態。)

只有在相信區塊X在人們關心的那個世界中被處理的可能性足夠大時,這才是值得去做的交易,驗證人才會願意投注。然而,接下來就是經濟上遞迴的有趣部分:人們關心的那個世界,也就是使用者的客戶端在使用者想要知道他們的賬戶餘額或是合約的狀態時所展現的那個狀態,本身就是根據人們對哪個區塊投注最多推匯出來的

。因此,每一個驗證人都具有根據他們所預期的其他人的投注情況進行投注的動機,驅使這個過程走向收斂。

一個有幫助的類比是工作量證明共識 - 它本身看起來非常獨特,實際上可以成為投注共識的一個特別子模型。理由如下:當你基於一個塊挖礦時,你是在花費每秒E的電力成本換取每秒p的出塊概率,並且在所有包含你的出塊的分叉中獲得R個幣,在其它分叉中分文不得。


因此,每一秒鐘,在你挖礦的鏈上你可以獲得p*R-E的期望收益,在其它鏈上遭受E的損失;因此你的挖礦選擇可以理解為下注賭你所在的鏈有E:p*R-E的相對概率(odds)勝出。比如,假設p等於百萬分之一,R是25個幣約等於10000美元,而E是0.007美元,則你在勝出鏈上每秒鐘的期望收益是0.000001 * 10000 - 0.007 = 0.003

,你在失敗鏈上的損失是0.007的電力成本,因此你是在賭自己挖礦的鏈有7:3的相對概率(或者說70%的概率)勝出。注意工作量證明滿足上面所說的經濟上遞迴的要求:使用者的客戶端通過處理擁有最大工作量的那條鏈來計算其賬戶餘額。

投注共識可以看作是包含了以特定方式看待的工作量證明的一個框架,也適合為其他多種型別的共識協議提供能促進收斂的經濟博弈。例如傳統的拜占庭容錯共識協議中,通常在對某個結果進行最後"commit"之前還有"pre-votes"和"pre-commits"的概念;在投注共識的模型下,我們可以把每一階段都變成投注,這樣後面階段的參與者就有更大把握相信前面階段的參與者“真的是這個意思”。

投注共識還可以用於激勵鏈外人類共識(out-of-band human consensus)的正確行為,如果為了克服類似51%攻擊的極端情況有需要的話。假設有人購買了一條PoS鏈上超過一半的幣,並進行攻擊,那麼社群只需要協商出一個讓客戶端忽略攻擊者分叉的補丁,就能自動讓攻擊者和他的小夥伴們失去他們所有的幣。一個極有野心的目標是讓線上節點可以自動的產生這種分叉決定 - 如果能成功實現,傳統容錯研究中的一個被低估但卻重要的結論 - 在強同步假設下,即使幾乎所有節點都在嘗試攻擊系統,剩下的節點依然可以達成共識 - 也可以被納入投注共識的框架中。

在投注共識的情境中,不同的共識協議只在一件事情上有區別:誰,可以以什麼賠率,投多少注?工作量證明只提供了一種賭局:投注勝出鏈有E:p*R-E的相對概率包含你自己出的塊。在廣義的投注共識中,依據一種被稱為評分規則(scoring rule)的機制我們本質上可以提供無限多種賭局:在1:1上壓極小的一注,在1.000001:1上也壓極小注,在1.000002:1上也壓極小注,如此繼續。

參與者依然可以選擇在每一個概率等級上的這些極小邊際投注的確切大小,但大體上這個技術讓我們能打探出驗證人認為某個塊會被確認的相當精確的概率讀數。如果驗證人認為一個塊有90%的概率會被確認,那麼他們就會接受所有相對概率低於9:1的賭局,拒絕相對概率高於9:1的賭局,而協議就能基於這一點準確得出這個塊會被確認的概率是90%的“看法”。事實上,顯示原理(revelation principle)告訴我們可以要求驗證人直接給出他們對某個塊被確認概率的“看法”的簽名訊息,讓協議代表驗證人計算投注。


多虧了神奇的微積分,我們可以得到在每一個概率等級上計算總獎勵和總懲罰的簡單函式,計算結果在數學上等價於根據驗證人信心在所有概率等級上形成的投注的無限集合的總和。一個簡單的例子是s(p) = p(1-p)f(p) = (p/(1-p))^2/2,這裡s計算如果你投注的事件發生能獲得的獎勵,f計算如果沒有發生你受到的懲罰。

投注共識的廣義形式有一個重要優點。在工作量證明中,給定區塊背後的“經濟權重”僅僅隨著時間線性增加:如果一個塊有6個確認,那麼要撤銷它只需要花費礦工大約6倍於出塊獎勵(在均衡態下)的成本,而如果一個塊有600個確認,那麼撤銷它的成本就是出塊獎勵的600倍。在廣義的投注共識中,驗證人在一個塊上投入的經濟權重可以指數級增加:如果其他大多數驗證人願意以10:1下注,你可能會想冒險以20:1下注;而一旦幾乎所有人都增加到20:1,你可能會跳到40:1或者更高。因此,一個塊很可能在幾分鐘之內,取決於驗證人有多少勇氣(以及協議提供的激勵大小),就達到一種“準最終確定”的狀態,這種狀態下驗證人的所有保證金都成為了支援這個塊的投注。

在50000英尺的高度看:區塊鏈是一個關於自身的預測市場。

區塊,鏈,共識與拔河

Casper的另一個獨特之處在於它的共識是按塊達成的(by-block)而不是像工作量證明那樣按鏈達成的(by-chain):共識過程在某個高度上對區塊狀態的決策是獨立於其它所有高度的。這個機制確實會導致一定程度的低效 - 特別是,一次投注必須表達驗證人對於每一個高度上區塊的意見,而不能僅是鏈的頭部區塊 - 但是在這個模型下為投注共識實現投注策略會十分簡單,而且它還有個優點是對高速區塊鏈友好:理論上,這個模型中的出塊時間甚至可以比網路傳播時間還要塊,因為區塊可以相互獨立的被製造出來,雖然有個明顯的附帶條件,即區塊的最終確定依然要一段時間。

在按鏈達成的共識中,我們可以把共識過程看作是正無窮和負無窮在每個分叉點進行的拔河遊戲,每次分叉的“狀態值”的含義是了右手邊最長鏈的區塊數減去左邊最長鏈的區塊數:


為了找出“正確的鏈”我們只需從起源塊(genesis block)開始前進,在每一個分叉處,如果狀態值是負數則往左邊移動,反之向右邊移動。這裡的經濟激勵很明顯:一旦狀態值變正,則說明有很強的經濟壓力迫使它走向正無窮,儘管過程很緩慢。如果狀態值變負,則有很強的經濟壓力迫使它走向負無窮。

順便說一句,在這個框架下,GHOST評分規則的核心思想變成了一種自然的範化:與其只通過最長鏈的長度來計算狀態值,不如計算分叉單邊所有塊的數量:


在按塊達成的共識中,同樣有這個拔河遊戲,不過此時“狀態值”僅僅是通過協議的某些操作可以任意增加或者減少的數字。在每一個高度上,如果狀態值為正,客戶端就處理這個塊,如果為負則不處理。注意雖然工作量證明當前是按鏈達成共識的,但並非必須如此:我們很容易可以想象一個協議,其中一個包含有效工作量證明的區塊不引用父區塊,而是給它自身歷史上的所有區塊投一張+1或者-1票。+1票只有在所投的區塊被處理時才獲得獎勵,而-1票只在所投的區塊沒有被處理時才得到獎勵:


不過,在工作量證明中這樣的設計無法很好的工作,原因很簡單:如果你需要對每一個歷史高度進行投票,那麼需要進行的投票數量會隨著時間的平方增長,很快就能讓系統宕機。而在投注共識中,由於拔河遊戲可以以指數級速度收斂到最終確定,投票的開銷要容易承受的多。

這個機制的一個反直覺結果是,一個塊可以在其後的塊都最終確定之後依然處於未確認的狀態。這看起來像是一個很大的效率問題,因為如果其上有10個塊的一個區塊狀態一直反覆變化的話,每一次變動都會導致重新計算這10個塊的狀態轉移,但其實在按鏈達成的共識中在鏈與鏈之間會發生完全相同的事情,而按塊的共識實際上可以提供更多的資訊給使用者:如果他們的交易在第20101個塊被確認和最終確定,而且他們知道不管第20100個塊的內容是什麼,這筆交易都有一個確定的結果,那麼他們關心的這個結果就是已經最終確定的,即使結果前的部分歷史還不是。按鏈達成的共識演算法永遠也不會有這個性質。

那麼Casper到底是如何工作的?

在所有基於安全準備金的權益證明協議中,都有一群有擔保的驗證人,這個資訊也記錄在系統狀態中。如果要投注或者進行某一種協議中的關鍵操作,你必須先加入這個群體,這樣才能確保你的惡意行為會被處罰。加入和退出有擔保的驗證人都是特殊的交易型別,協議的關鍵操作例如投注同樣也是一種交易型別。投注可以作為獨立的物件在網路上被傳輸,也可以被打包進區塊之中。

基於Serenity的抽象精神,所有的邏輯都通過一個Casper合約來實現,它提供投注,加入,取款和獲取共識資訊等一系列功能,因此通過簡單的呼叫Casper合約我們就能提交投注或者進行其他操作。Casper合約的內部狀態看起來是這個樣子:


這個合約會記錄當前的驗證人集合,對於每位驗證人記錄6項主要資料:

  • 驗證人保證金的返還地址
  • 當前驗證人保證金的數量(注意驗證人的投注會使這個值增加或減少)
  • 驗證人的驗證程式碼(validation code)
  • 最近一次投注的序號
  • 最近一次投注的hash
  • 驗證人的意見

“驗證程式碼”概念是Serenity的另一個抽象特性。其他的權益證明協議會要求驗證人使用某一種特定的簽名驗證演算法,而Serenity的Casper實現允許驗證人定製一段程式碼,這段程式碼可以接受一個hash和一個簽名做引數,返回0或者1,在投注被接受之前,程式碼就可以用簽名來驗證投注的hash正確無誤。預設的驗證程式碼是一個橢圓曲線簽名驗證演算法,你可以試驗其它的演算法:多重簽名,threshold signature(有發展成去中心化資金池的潛力!),Lamport signature(譯註:可抵禦量子計算機)等等。

每一次投注都必須包含一個比上一次投注大1的序號,而且每次投注必須包含上次投注的hash。因此,你可以把某位驗證人的一系列投注看作是某種“私有鏈”;這樣理解的話,驗證人的意見實際上是這條鏈的狀態。驗證人意見是描述如下問題的一張表格:

  • 在每一個高度,驗證人認為哪個是最佳的狀態樹根節點
  • 在每一個高度,驗證人認為哪個是最佳的區塊hash(如果還沒有區塊hash產生可以給0)
  • 該hash對應的區塊有多大概率被最終確定

一次投注是看起來如下的一個物件:


這裡的關鍵資訊是:

  • 投注的序號
  • 上次投注的hash
  • 簽名
  • 意見更新組成的列表

Casper合約中處理投注的函式有三個部分。首先,它會驗證投注的序號,上次投注的hash和投注簽名。然後它會用投注中的新資訊來更新驗證人的意見表。一次投注通常只會有少數概率,區塊hash和狀態樹根節點更新,因此意見表的大部分不會變化。最後,它會對意見表應用評分規則:如果你的意見是相信某個塊有99%的機會被最終確定,並且,如果在該特定合約執行的特定世界中,這個塊被最終確定了,你將會得到99分,否則你會失去4900分。

需要注意的是,由於Casper合約的這個函式是作為狀態轉移函式的一部分被執行的,執行過程完全清楚之前的每一個區塊和狀態樹根節點是什麼,至少在它自己所在的世界中是這樣。即使從外部世界來看,對第20125個塊進行提議和投票的驗證人不知道第20123個塊是否會被最終確定,但是當驗證人處理到那個塊的時候他們就知道了 - 或者,他們可能兩個世界都處理,然後在決定要跟隨哪一個。為了防止驗證人在不同的世界中提供不同的投注,我們還有一個簡單嚴格的條款:如果你有兩次投注序號一樣,或者說你提交了一個無法讓Casper合約處理的投注,你將失去所有保證金。

從驗證人資金池取款需要兩個步驟。首先,你需要提交一個最大高度為-1的投注;它會自動完結投注鏈,並且啟動一個為期四個月的倒計時(在testnet上是20個塊/100秒),這之後投注人才能通過呼叫另一個方法,withdraw,來收回他的資金。任何人都可以觸發取款,資金會被髮送回之前傳送join交易的那個地址。

區塊提議

每個區塊都包含 (i) 一個代表區塊高度的數字, (ii) 提議人的地址,(iii) 交易樹根節點hash和 (iv) 提議人簽名。一個有效區塊的提議人地址必須是協議安排在這個高度出塊的驗證人的地址,而簽名則必須能通過該驗證人的驗證程式碼驗證。高度為N的區塊的提交時間由公式T = G + N*5確定,其中G是起源塊的時間戳。因此,一般來說每5秒中會出現一個新塊。

一個NXT風格的隨機數發生器被用來決定在每個高度應該由誰來出塊,不可避免的,缺失的出塊人也會作為熵的一個來源。採取這個方案背後的原因是雖然這個熵是可操縱的,操縱的代價非常高:你必須放棄建立一個塊能收取的交易費用收益。如果確實有必要,我們也可以用類似RANDAO的協議取代NXT風格的隨機數發生器,將這個代價進一步加大數個級別。

驗證人策略

那麼在Casper協議下作為驗證人該如何行動呢?驗證人有兩類主要活動:出塊和投注。出塊是一個獨立於其它所有事件而發生的過程:驗證人收集交易,當輪到他們的出塊時間時,他們就製造一個區塊,簽名,然後傳送到網路上。投注的過程更為複雜一些。目前Casper預設的驗證人策略被設計為模仿傳統的拜占庭容錯共識:觀察其他的驗證人如何投注,取33%處的值,向0或者1進一步移動。

為了實現這個策略,每一位驗證人都要收集其他驗證人的投注,並且儘可能保持該資料處於最新狀態,用於跟蹤每一位驗證人的當前意見。如果某個高度上還沒有或者只有很少的其他驗證人發表了意見,那麼我們用大致如下的演算法來處理:

  • 如果這個高度的塊還沒有出現,且當前時間離這個塊應該出現的時間過去不久,則預計概率為0.5
  • 如果這個高度的塊還沒有出現,且離這個塊應該出現的時間過去了很長時間,則預計概率為0.3
  • 如果這個高度的塊已經出現,且按時出現,則預計概率為0.7
  • 如果這個高度的塊已經出現,但是出現時間過早或者過晚,則預計概率為0.3

這個過程還會增加一些隨機性來防止“卡住”的場景,但是基本原則是一樣的。

如果對某個高度其他驗證人已經發布了許多意見,那麼我們使用如下策略:

  • 設三分之二驗證人的預計高於概率LM為預期的中位數(即有一半驗證人的估值高於M);三分之二驗證人的預計低於概率H
  • e(x)是一個讓x更“極端”的函式,例如讓數值遠離0.5走向1。一個簡單的例子是這個分段函式:e(x) = 0.5 + x/2 if x > 0.5 else x/2.
  • 如果 L > 0.8, 則預計概率為e(L)
  • 如果 H < 0.2, 則預計概率為e(H)
  • 其他情況,預計概率為e(M), 但是結果不能超出[0.15, 0.85]這個區間,因此少於67%的驗證人無法強迫其他的驗證人大幅調整其預計。


在這個策略中,驗證人可以自由的通過改變e的形狀來選擇他們自己的風險厭惡程度。選一個在x > 0.8e(x) = 0.99999的函式也可以(而且很可能產生和Tendermint一樣的行為),但是它有更高的風險,如果佔有了擔保資金一大部分的驗證人是惡意的,他們只需要很低的成本,就能設計讓使用該e函式的驗證人損失全部保證金(攻擊策略為先預計概率為0.9,引誘其他驗證人預期0.99999,然後突然改為預計0.1,迫使系統預期收斂到0)。另一方面,一個收斂很慢的函式會導致系統在沒有遭受攻擊的情況下更低效,因為最終確定會更慢,且驗證人對每個高度的投注需要持續更久。

現在,作為客戶端要如何確定當前狀態呢?過程基本如下:一開始先下載所有的區塊和投注,然後用上面的演算法來形成自己的意見,但是不公佈意見。它只要簡單的按順序在每個高度進行觀察,如果一個塊的概率高於0.5就處理它,否則就跳過它。在處理所有的區塊之後得到的狀態就可以顯示為區塊鏈的“當前狀態”。客戶端還可以給出對於“最終確定”的主觀看法:當高度k之前的每個塊,意見要麼高於99.999%或者低於0.001%,那麼客戶端就可以認為前k個塊已經最終確定。

進一步的研究

Casper和一般化的投注共識還需要大量研究。特別包括以下幾個方面:

  • 給出能表明即使存在一些拜占庭驗證人系統也能在經濟上激勵收斂的證據
  • 找出最佳的驗證人策略
  • 確保把投注打包進區塊的機制沒有漏洞
  • 提高效率。目前的概念原型(POC1)能模擬大約16個驗證人同時執行,理想情況下這個數字應該越高越好(注意系統在生產網路能處理的驗證人數量大約是概念原型效能的平方,因為概念原型把所有節點都執行在一臺機器上)。

該系列的下一篇文章會介紹Serenity可伸縮性方面的工作,估計會和Serenity的第二個概念原型(POC2)同時釋出。