將 FOCIL 重新用作 L2 強制交易機制

本文為機器翻譯
展示原文

感謝Péter GaramvölgyiThomas ThieryFrancesco RisitanoJihoon Song的反饋和審閱。

本文基於或涉及處於不同納入階段的EIP,特別是: FOCIL (SFI)、可選執行證明(PFI)、塊級訪問列表(SFI)、 僅有效性部分無狀態(無 EIP)以及原生 Rollup (尚未提出)。因此,細節預計會隨時間而變化。雖然這項研究的主要動機是為原生 Rollup 尋找一種簡單的強制事務機制,但其發現普遍適用於所有 EVM L2,包括現有的 L2。

抽象的

我們提出了一種通過 FOCIL 實現的強制交易機制,該機制無需對狀態轉換函數或新的交易類型進行任何修改,即可繞過 EVM L2 的集中式序列器,這與現有的解決方案不同。

背景

FOCIL 通過添加一個新的交易列表(“包含列表”)來更新以太坊的 STF,The Block必須滿足該列表上的交易才能獲得認證。這些交易由 CL 端的“IL 委員會”選擇,該委員會由 16 個驗證者組成,並通過更新後的 Engine API 傳遞給 EL。

def state_transition ( ​chain: BlockChain, ​​block: Block, ​​inclusion_list_transactions: Tuple [LegacyTransaction | Bytes, ...]​ ) -> None :

IL 中的交易仍然可以出於以下三個原因被合法地從區塊中排除:

  • 內部檢查失敗:交易格式錯誤、鏈 ID 錯誤、gas 不足、簽名無效、參數超出範圍;

  • 狀態檢查失敗:隨機數不匹配,餘額不足;

  • 區塊相關檢查失敗:燃氣不足(相對於基本費用),區塊空間不足。

即使建造者可以通過“區塊填充”故意排除交易,該協議也能通過增加EIP-1559基本費用並對攻擊者施加指數級成本來提供抗審查性,並將交易保留在內存池中,該交易將被插入到下一個 IL 中。

目前所有現有的 EVM L2 區塊都通過引入新的交易類型並修改狀態轉換函數來實現強制交易。OPOP引入了“已存入交易”類型,該類型源自 L1 事件,會自動插入到相應 L2 區塊的頂部,在 L2 上無需簽名,在 L1 上支付 gas 費用,但在 L2 上不消耗 gas。op-geth 基於 geth 的所有更改可以在這裡找到。ArbitrumArbitrum也引入了一種新的交易類型(實際上是幾種),該類型無需簽名,其包含由鏈上強制交易隊列強制執行,但在 L2 上支付 gas 費用。基於 geth 的所有更改可以在這裡找到。最重要的是,在這兩種情況下,進入強制包含交易列表的有效交易永遠不會因為區塊已滿或基礎費用而被有效排除:系統始終會為其預留空間,並且要麼不考慮基礎費用,要麼會實施重試機制。

該機制

核心思路是,FOCIL 的 CL 和內存池邏輯可以完全被 L1 上的智能合約替代和複製:用戶向 L1 智能合約提交強制交易,而不是傳統的 L2 內存池(或許可以通過 L1 FOCIL!),該智能合約構建包含列表並將其作為輸入傳遞給 L2 STF 驗證器,類似於 Engine API 將其傳遞給 EL 的方式。假設 L2 STF 與 EIP-8025 引入的無狀態 STF 函數完全相同。由於 8025 尚未基於Hegotá構建,因此沒有考慮 FOCIL,我們可以自由地設想無狀態 IL 接口。

圖示“L2 FOCIL”如何取代 L1 FOCIL 檢查中涉及的 CL 和 EL 組件。
圖示“L2 FOCIL”如何替換L1 FOCIL檢查中涉及的CL和EL組件。2042 ×1574 240 KB

圖示“L2 FOCIL”如何取代 L1 FOCIL 檢查中涉及的 CL 和 EL 組件。

為了復現內存池的行為並保證抗審查性,我們需要確保最終進入 L2 IL 的交易不會被自動丟棄,因為與現有的強制交易機制不同,FOCIL 實際上並不保證包含在特定區塊中。此外,所有無效交易都應​​儘可能減少對 IL 的汙染,以防止證明者端的計算資源浪費以及對有效交易的拒絕服務攻擊。

我們假設運營商發佈的每個 L2 批次都對應一個 L2 區塊,否則運營商可以不斷生成空區塊來降低基礎費用,並低成本地進行區塊填充攻擊。目前大多數 Rollup 並非如此,但可以使用諸如Flashblocks之類的技術來模擬更快的區塊生成速度。

因此,我們設計了一個強制交易合約,如下所示:用戶將已簽名的交易提交到鏈上列表,該列表按 `maxFeePerGas` 值降序排列。提交時,所有內在(即無狀態)檢查都會執行。正如在“僅有效性部分無狀態 (VOPS)”研究論文和此處所述,執行有狀態檢查對於維護健康的內存池至關重要:即使在 L2 FOCIL 中,提交的交易會支付 L1 的 gas 費用,但將 `maxFeePerGas` 值很高但 nonce 無效或餘額不足的交易塞滿列表頭部成本很低,這會導致 IL 僅包含無效交易。因此,VOPS 建議無狀態節點仍然應該通過BAL來維護每個賬戶的餘額和 nonce。雖然 EVM L2 節點也能夠生成和發佈 BAL,但在智能合約中維護餘額和 nonce 是不可行的:對於 L1 節點,估計的存儲空間已經達到約 8.4GB,而對於 L2 節點,存儲空間可能會更高。因此,我們要求用戶提交賬戶證明,該證明需通過eth_getProof 函數針對最新的 L2 狀態獲取,方可被納入列表。由於 FOCIL 無法告知我們交易是否已被納入包含列表以及原因,因此即使經過這些檢查,最終進入包含列表的交易也無法自動刪除。可以使用兩種機制:

  1. 新增了一個無需許可的“修剪”函數,該函數根據針對新區塊的賬戶證明,證明 nonce 值已更改或餘額不足。需要注意的是,僅憑 nonce 檢查是不夠的,因為EIP-7702打破了賬戶餘額只能在 nonce 值增加時減少這一不變性。為了實現激勵相容性,可以要求強制交易提交者提交少量保證金,以便在交易失效時退還修剪者的款項。

  2. 運營商在結算期間提供針對交易根的默克爾證明。鑑於 IL 由 STF 驗證(例如通過零知識證明),我們可以檢查:如果某個交易未被包含在區塊中,且The Block中仍有剩餘空間,則該交易必定無效,可以將其丟棄。預計成本:IL 中每個交易約 27.5 萬 gas,32 筆交易的成本在 1000 萬 gas 以內,使用多重證明時成本可能更低。如果 IL 中的某個交易無效,但The Block中沒有剩餘空間,則該交易不會被丟棄,因為我們無法區分區塊已滿和無效的情況。1559 基本費用機制保證最終會生成一個具有足夠空間的區塊,前提是具有足夠的彈性。

L2 操作員在調用結算函數並傳入 L2 區塊時,會從列表頂部的交易中提取當前白名單 (IL),直至達到預設的 gas 預算或交易支付的費用不足以支付當前基礎費用為止。該白名單作為鏈上驗證器的輸入強制執行,其滿足情況被視為有效性規則。為防止競態條件和惡意攻擊,白名單可以僅包含至少早於某個Threshold的交易,以便證明器能夠預先知道它將被強制包含哪些交易。如果 L2 中心化序列器完全拒絕生成區塊,則可以在鏈上觸發超時,以移除白名單並恢復抗審查能力。

具體實現示例請參見此處。提交操作預計消耗約 130 萬 gas,而修剪操作預計消耗約 110 萬 gas,兩者均約合 0.001 ETH (按 1 gwei/gas 計算)。

雖然超出了本文的研究範圍,但強制交易合約自然可以根據具體的 L2 需求進行定製,以便在接受之前執行額外的檢查。

僅限賬戶節點

目前,獲取賬戶證明需要連接到完整節點,這對大多數用戶來說成本過高,尤其是對於二級用戶而言。VOPS提案結合 zkEVM,旨在通過僅使用證明驗證狀態並僅存儲通過BAL獲取的餘額和 nonce 來維護健康的內存池,從而將證明者和包含者的存儲負載從約 233GiB 降低到約 8.4GiB。由於二級用戶也會發布證明並能夠發佈 BAL,因此可以設想一種類似的節點供二級用戶使用,使他們能夠在審查的情況下更輕鬆地提交強制交易,而無需維護完整狀態。然而,由於 BAL 僅發佈存儲差異,因此必須跟蹤完整狀態才能重建提供賬戶證明所需的存儲根。為了方便參考, BAL 的定義如下:

BlockAccessList = List [AccountChanges]AccountChanges = [Address, # address List [SlotChanges], # storage_changes (slot -> [block_access_index -> new_value]) List [StorageKey], # storage_reads (read-only storage keys) List [BalanceChange], # balance_changes ([block_access_index -> post_balance]) List [NonceChange], # nonce_changes ([block_access_index -> new_nonce]) List [CodeChange] # code_changes ([block_access_index -> new_code]) ]

當 Account 對象在 trie 樹中被序列化為:

def encode_account ( ​raw_account_data: Account, ​​storage_root: Bytes​ ) -> Bytes: """Encode `Account` dataclass.Storage is not stored in the `Account` dataclass, so `Accounts` cannot beencoded without providing a storage root.""" return rlp.encode((raw_account_data.nonce,raw_account_data.balance,storage_root,raw_account_data.code_hash,))

如果修改 BAL,使其也能提供存儲根變更信息(其中只需要The Block末尾的最後一個變更),節點就能構建賬戶證明而無需維護完整的狀態。EIP -8268 (感謝Toni提供參考!)正是提出了這樣的變更。


來源
免責聲明:以上內容僅為作者觀點,不代表Followin的任何立場,不構成與Followin相關的任何投資建議。
喜歡
62
收藏
12
評論