
透過解耦共識機制實現更快速的最終性
本文提議將區塊生產與最終性流水線解耦,以便獨立優化兩者,解決目前以太坊共識協議中時隙速度與最終性確認時間之間的權衡問題。
感謝 EF 共識團隊的討論與建議
在目前的共識協議中,區塊生產與最終性(finality)的時間線是耦合的。一個龐大的委員會(由全體驗證者集的 1/32 組成)在每個時隙(slot)的關鍵路徑中進行見證。縮減此委員會的規模(例如縮減至驗證者集的 1/64)可以實現更快的時隙,但會減慢最終性,因為完成一個完整的投票輪次(一個 epoch)現在需要累積 64 個時隙的開銷,而非 32 個。在這個權衡空間的一個極端下,每個時隙只有單一驗證者投票,而完整的投票輪次大約每 100 萬個時隙才完成一次,即使時隙時間為 100 毫秒,這也需要超過一天的時間。即使對於不那麼極端的委員會規模(以及相應的 epoch 長度),執行「透過逐時隙累積實現最終性」所帶來的開銷也不容忽視。
另一方面,增加委員會規模會使最終性變快,代價是區塊生產變慢(時隙變長)。在權衡空間的另一個極端,全體驗證者集在每個時隙都進行投票,而時隙時間很大程度上取決於我們聚合投票的速度。
提議的共識升級,如 SSF 和 3SF 的變體 1, 2, 3,並未從根本上改變這種權衡。它們只是接受了這一點,並將自己定位在權衡的一端,即全體驗證者集在每個時隙都投票。為此,其假設是驗證者集將(透過合併)縮減到一個可行的程度,且不會對時隙時間造成過於嚴苛的限制。
另一條路徑是將區塊生產與最終性解耦:
-
區塊生產流水線可以依賴一個較小的隨機抽樣委員會,例如規模為 512(如果配合 秘密選舉 則更是如此)。
-
最終性流水線並行發生,處於區塊生產的關鍵路徑之外。
這種權衡可以被完全消除,且兩條流水線都可以獨立進行優化。
在這一方向上已有探索 4,但直到最近,拼圖中仍缺失一塊:沒有任何已知的基於投票(而非最長鏈)的可得鏈協議(decoupled 圖示中的頂部部分),在由快速變化的委員會運行時具備足夠好的屬性。特別是,此類可得協議 5, 6 在面對暫時的異步(asynchrony)時無法優雅地降級,而增加其異步韌性的技術 7, 8 在活躍參與者快速變化時會失效。這在很大程度上解釋了為什麼之前的努力(如 SSF 和 3SF)採取了讓全體驗證者集同時投票的方法。
在本文中,我們討論一種解決此問題的方法,首先構建一個既對委員會友好、在同步下具有最優安全性(在同步下容忍 1/2 的敵手以保證安全性和活性),且具備異步韌性的可得協議。接著,我們將此協議與最終性組件(finality gadget)一起,作為漲落協議(ebb-and-flow protocol)9 的兩個構建塊之一。特別地,我們考慮使用單輪最終性協議 10, 14, 15 作為最終性組件的基礎,以便能夠在小委員會的一輪投票和全體驗證者集的一輪投票內達成最終性。
Goldfish
Goldfish 5 是對 LMD-GHOST 的微小修改,增加了兩個關鍵特性:投票過期(vote expiry)和視圖合併(view-merge) 5, 12。投票過期使協議本質上是無記憶的:在時隙 t,分叉選擇中僅考慮來自時隙 t-1 的投票。這使得 Goldfish 非常簡單,同時實現了最優安全性——在沉睡模型(sleepy model)6(動態參與,即驗證者可以離線並返回)下,它在同步時可容忍高達 1/2 的敵對質押。
時隙結構很簡單:
-
(時間 0) - 提議者運行分叉選擇,包含其看到的所有來自前一時隙的投票,並提議一個區塊來擴展鏈頭。該區塊包含提議者看到的所有前一時隙投票。
-
(時間 \Delta) - 委員會對規範鏈(canonical chain)的鏈頭進行投票,鏈頭是透過運行包含前一時隙投票的分叉選擇來確定的,特別是透過將其本地凍結視圖(見下文)與提議者視圖(區塊中的內容)進行合併而獲得的投票。
-
(時間 2\Delta) - 每個人「凍結其視圖」,記錄截至此時已看到的見證消息。
凍結與合併的目標是實現以下屬性:
在同步情況下,來自誠實提議者的區塊會得到所有誠實委員會成員的投票。
這是因為誠實委員會成員在凍結截止時間前看到的任何投票,也將被下一個時隙的提議者看到,並因此包含在其區塊中。合併後的投票集將與提議者運行分叉選擇時使用的投票集相同,因此所有委員會成員都會在鏈頭上與提議者達成一致。
-
當誠實提議者在時隙 t 生產一個區塊時,所有構成委員會多數的誠實委員會成員都會看到它並為其投票(上述的視圖合併屬性)。
-
在下一個時隙 t+1 中,唯一有效的投票是來自時隙 t 委員會的投票(這是投票過期),其中多數支持該誠實區塊。因此,該區塊是規範的,且其某個後代會得到所有誠實驗證者的投票。透過歸納法,它在所有未來時隙中都保持規範。
下圖是一個事前重組(ex-ante reorg)嘗試的例子,它正是因為「視圖合併 + 投票過期」的結合而失敗。敵手在先前控制的時隙中累積的投票已過期,而最新時隙的誠實投票全部投給了誠實區塊(由於視圖合併),且佔據了所有分叉選擇權重的多數。
請注意,如果我們想要支持快速變化的委員會,投票過期在論證的第二部分是必要的。考慮 LMD-GHOST,其中驗證者的最新投票始終有效,無論其多舊。在委員會機制下,投票會隨時間累積,敵手可以跨多個時隙累積投票,並在稍後用於重組鏈(事前重組 13)。僅使用前一時隙的投票完全消除了這個問題,效果就像我們在每個時隙都讓全體驗證者集投票一樣(這也是 8 及相關漲落協議 1, 2, 3 解決該問題的方式)。
然而,這種無記憶性也使得 Goldfish 容易受到暫時異步的影響:如果網絡出現延遲且誠實投票遲到,分叉選擇將缺乏誠實貢獻,敵手可以隨意改寫歷史。具體來說,它可以將自己的投票權重放在任何它想要的分叉上,並讓節點重組到該分叉。
下圖就是一個例子。有一條長且穩定的鏈(上方那條),直到時隙 t+1,某些原因阻止了誠實投票(本應投給上方鏈)的發送或接收(被劃掉的綠色投票,可能因網絡問題缺失),此時敵手可以自由地將其投票集中在另一個分支(紅色區塊),導致長距離重組,因為這些是唯一有效的投票(來自先前時隙的灰色投票已過期)。
穩定組件 (Stabilization gadget)
我們很自然會質疑,如果 Goldfish(或任何可得鏈機制)的重點在於與最終性組件配合使用(後者明確是為了提供異步安全性),我們為什麼還要關心它的異步韌性?原因在於,我們希望協議在參與度不足以達成最終性時不會變得脆弱。換句話說,如果一個可得協議在理論上可以在參與度極低時取得進展,但該進展即使在極短的異步期間也會被完全抹除,那麼這樣的協議就沒有太大意義。
雖然最終性組件因其高參與度要求而並非答案,但我們在嘗試解決此問題時可以從中汲取靈感。特別是,我們可以採用一種所謂的穩定組件,這是一種增強可得協議的方法,使其對暫時的異步具有韌性,儘管它並非完全異步安全(因為這必然僅在高參與度下有效)。
這看起來可能有點循環論證,因為這種穩定組件本身本質上就是一個具備異步韌性的可得協議,而這正是我們最初試圖創建的東西。然而,困難僅在於我們希望我們的可得協議支持委員會。如果沒有這個要求,我們已經知道如何解決這個問題,例如使用 RLMD-GHOST 8、Majorum 2 或其他透過 7 技術增強的(不帶委員會的)可得協議。我們可以使用此類協議作為穩定組件,其延遲由全體驗證者集的一輪投票長度決定,就像最終性組件一樣。這樣一來,鏈中唯一容易受異步影響的部分就只有最頂端,大約只有幾個時隙。
混合分叉選擇
具體來說,我們可以像使用最終性組件一樣使用穩定組件,讓它為底層可得協議的分叉選擇確定一個起點。例如,結合 Majorum(類似於 RLMD-GHOST,但要求子樹必須獲得未過期權重之多數支持才能繼續)和 Goldfish,組合後的分叉選擇如下:
-
Majorum 前綴:找到最高的一個區塊,其子樹擁有來自未過期最新消息的多數權重。
-
Goldfish 後綴:從該處開始,運行帶有前一時隙投票的 GHOST。
有兩個注意事項:
-
對於漲落協議,我們必須小心可得協議與組件之間的交互。一種安全的交互模式是:僅當區塊已被底層可得協議*確認(confirmed)*時,才將其輸入組件(發送紫色投票)。這意味著組件在網絡同步下不會干擾可得協議,因為它只能輸出已經安全的區塊。
-
與最終性組件不同,穩定組件即使沒有多數支持也可能輸出內容。例如,LMD-GHOST 是一種積極的分叉選擇,它總是會持續到葉子節點,而不論其遇到的子節點是否有足夠支持。這抵消了「誠實投票者僅向穩定組件輸入已確認區塊」的好處,因為一個區塊即使沒有任何誠實投票支持(因此沒有任何確認保證),也可能被組件輸出。這就是為什麼前面的例子使用 Majorum,因為它只會輸出受相關權重(即未過期最新消息)多數支持的區塊,根據假設,這意味著至少有一些誠實支持。
請注意,我們之前並未討論 Goldfish 的確認規則。關於此內容的討論,包括如何增強 Goldfish 以獲得快速確認規則,請參閱附錄。
最終性組件 (Finality gadget)
我們還需要一個正式的最終性組件,以提供異步和經濟安全性。從分叉選擇的角度來看,這就像是將兩步分叉選擇變為三步分叉選擇,增加了從最新合理化(justified)檢查點開始的熟悉步驟(在使用 Casper FFG 以外的最終性組件時,邏輯大致如下):
-
FFG (合理化鏈):找到最新的合理化檢查點。
-
Majorum (穩定鏈):從該處開始,找到最高的一個區塊,其子樹擁有來自未過期最新消息的多數權重。
-
Goldfish (可得鏈):從該處開始,運行帶有前一時隙投票的 GHOST。
然而,這並不意味著我們現在要運行三個獨立的協議。在實踐中,我們只需要運行可得協議,然後在其之上運行一個單一組件,該組件在單輪投票中捆綁了最終性和穩定性。在下圖中,綠色投票同時用於 FFG 和 Majorum。
即使穩定組件和最終性組件的投票是捆綁的,兩者在不同條件下發揮作用。下圖精確展示了穩定組件幫助我們的場景:我們已經有一段時間無法合理化任何內容(可能是因為 < 2/3 的質押在線),但在線質押仍然能夠穩定除鏈尖以外的所有部分,防止在暫時異步期間發生長程重組嘗試。
附錄
讓我們深入探討可得鏈組件 Goldfish。
確認規則
k-深確認
在不作任何修改的情況下,Goldfish 支持簡單的 k-深確認規則,即我們確認規範鏈的 k-深前綴(透過切掉最新的 k-1 個區塊獲得)。只要我們選擇的 k 使得在每 k 個區塊中都有極高概率出現誠實提議者(假設誠實多數),這就是安全的。
快速確認
k-深確認規則的問題顯然在於其極高的延遲。理想情況下,我們希望能在單輪投票(來自委員會)後確認一個區塊。我們可以透過對 Goldfish 的時隙結構進行微調來實現這一點,具體是增加一個 Δ,並在投票階段和凍結階段之間(即 2Δ 處)增加一個確認階段。確認規則很簡單:如果你在 2Δ 時間看到來自委員會 3/4 規模的投票支持某個區塊,你就確認該區塊(如果委員會規模不固定,則為目標規模的 3/4 + epsilon。參見 Goldfish 委員會選擇 (VRF))。
讓我們看看這具體是如何運作的。假設一個見證者在 2Δ 時間看到 3/4 委員會的投票支持區塊 B,從而確認了它。這些投票將在 3Δ 時間到達所有其他驗證者,因此它們會出現在每個誠實驗證者的「凍結視圖」中。任何與 B 衝突的區塊最多只能獲得剩餘 1/4 的投票支持,加上任何雙重投票(equivocations)。然而,在計算分叉選擇時,雙重投票會被丟棄且不計入。因此,在 Δ 時間運行分叉選擇以為下一個時隙投票時,B 的支持度將至少為 3/4 減去雙重投票,而任何衝突區塊的支持度最多為 1/4。為了使 B 不在規範鏈中,至少需要 1/2 的委員會發送了雙重投票,但在誠實多數的假設下這是不可能的。
視圖合併 (View-merge)
讓我們明確視圖合併的運作方式,這也是指定如何處理雙重投票的機會:
-
節點僅追蹤最近一輪投票的投票,且僅記錄為每個驗證者收到的前兩個投票。在下方代碼中,為 validator_id 看到的第一個投票記錄在 votes[validator_id] 中,而可能的雙重投票(看到的第二個)記錄在 equivocations[validator_id] 中。
-
在凍結截止時間之後,只有提議者會繼續更新其投票記錄,而其他人僅透過區塊中包含的投票來更新。
-
最後,在運行分叉選擇時,雙重投票者將被忽略(權重歸零)。
def on_vote(self, vote: Vote, from_block: bool):
# 僅處理當前時隙的投票
if vote.slot != self.current_slot():
return
# 忽略已知雙重投票者的投票
if vote.validator_id in self.equivocations:
return
# 非提議者在視圖合併截止時間後忽略投票,除非投票來自區塊
time_in_slot = (self.network.time % SLOT_DURATION)
freeze_deadline = SLOT_DURATION * 2 // 3
if time_in_slot > freeze_deadline:
if not from_block and not self.is_proposer():
return
# 如果是第一次看到,記錄該投票
if vote.validator_id not in self.votes:
self.votes[vote.validator_id] = vote
return
# 如果已經看過一個,檢查是否為雙重投票
if self.equivocations[vote.validator_id] != vote:
self.equivocations[vote.validator_id] = vote
Goldfish 委員會選擇 (VRF)
我們使用的委員會越小,最大化從選擇方法中獲得的安全性就越重要。具體來說,我們可以:
-
以與質押成比例的概率選擇委員會成員,然後計算他們的投票次數,而不是按質押加權。這是我們在同步委員會和(即將推出的)PTC 中已經採用的做法,這對於在小委員會和多變餘額下保持高安全性是必要的(考慮一個 512 名驗證者的委員會,其中前 504 名有 32 ETH,其餘 8 名有 2048 ETH。後者本身就控制了 > 1/2 的委員會)。
-
透過只有委員會成員自己能計算的 VRF 秘密選擇成員。這是我們在聚合者選擇中所做的,儘管沒有與質押成比例。這樣,委員會成員只有在他們已經行動後才會被知曉,這可以防禦適應性敵手(賄賂、針對性 DoS 等)。
我們可以將這兩者結合如下:
def get_committee_seats_per_validator(slot_signature: bytes,
balance: Gwei,
total_balance: Gwei,
TARGET_COMMITTEE_SIZE: uint64) -> int:
balance_per_committee_seat = max(1, total_balance // TARGET_COMMITTEE_SIZE)
seats = balance // balance_per_committee_seat
random_value = bytes_to_uint64(hash(slot_signature)[:8]) % balance_per_committee_seat
remainder_balance = balance % balance_per_committee_seat
seats += 1 if random_value < remainder_balance else 0
return seats
擁有質押餘額的驗證者在委員會中獲得 balance // balance_per_committee_seat 個確定性席位,然後以 (balance % balance_per_committee_seat) / balance_per_committee_seat 的概率隨機分配一個額外席位,即概率與其確定性分配後的剩餘餘額成正比。
例如,假設 TARGET_COMMITTEE_SIZE = 512 且 total_balance = 2**25 ETH,因此 balance_per_committee_seat = 2**16。一個餘額為 2**16 + 2**15 ETH 的驗證者將獲得 1 個確定性席位,然後有 1/2 的概率獲得另一個席位。一個餘額為 1024 ETH 的驗證者將有 1/64 的概率獲得一個席位。
參考資料
-
LMD GHOST with ~256 validators and a fast-following finality gadget
-
RLMD-GHOST: Balancing Dynamic Availability With Asynchrony Resilience
-
Ebb-and-Flow Protocols: A Resolution of the Availability-Finality Dilemma
-
Hydrangea: Optimistic Two-Round Partial Synchrony with One-Third Fault Resilience
1 則貼文 - 1 位參與者 [閱讀完整主題](https://ethresear.ch/t/unblocking-faster-finality-with-decoupled-consensus/24527)