作者:羅奔奔,前Arbitrum 技術大使、極客web3 貢獻者
本文為Arbitrum 前技術大使及智慧合約自動化審計公司Goplus Security 前共同創辦人羅奔奔對Arbitrum One 的技術解讀。
因為中文圈子裡涉及Layer2 的文章或資料,缺乏對Arbitrum 乃至OP Rollup 的專業解讀,本文試圖透過科普Arbitrum 的運轉機理,填補這一領域的空缺。由於Arbitrum 本身的結構太複雜,全文在盡可能簡化的基礎上,還是超過了1 萬字篇幅,所以分成了上下兩篇,建議作為參考資料收藏轉發!
Rollup 排序器簡述
Rollup 擴容的原理可以歸納為兩點:
成本最佳化:將⼤部分運算與儲存任務移交至L1 鏈下也即L2 上。 L2 大多是運⾏在單一伺服器也即排序器(Sequencer/Operator)上的⼀條鏈。
排序器在觀感上接近一台中心化伺服器,在「區塊鏈不可能三⻆」中捨棄「去中心化」來換取TPS 與成本上的優勢。 ⽤戶可以讓L2 來取代以太坊處理交易指令,成本比在以太坊上交易低很多。
安全保障:L2 上的交易內容與交易後的狀態,會同步⾄以太坊L1,透過合約來校驗狀態轉換的有效性。同時,以太坊上會保留L2 的歷史記錄,排序器即便永久宕機,他⼈也可以透過以太坊上的記錄,還原出整個L2 的狀態。
從根本上來說,Rollup 的安全性是基於以太坊的。排序器如果不知道某個帳戶的私鑰,就無法以該帳戶的名義發起交易,或無法篡改該帳戶的資產餘額(即便這麼做了,也很快被識破)。
雖然排序器作為系統中樞帶有中⼼化色彩,但在成熟度比較高的Rollup 方案中,中心化排序器僅能實施交易審查等軟性作惡⾏為,或者惡意宕機,但在理想狀態的Rollup ⽅案中,有相應的⼿段進⾏遏制(例如強制提款或排序證明等抗審查機制)。
而防止Rollup 排序器作惡的狀態校驗⽅式,分為詐欺證明(Fraud Proof)和有效性證明(Validity Proof)兩類。使⽤詐欺證明的Rollup ⽅案稱為OP Rollup(Optimistic Rollup,OPR),⽽因為一些歷史包袱,使⽤有效性證明的Rollup 往往被稱為ZK Rollup(Zero-knowledge Proof Rollup,ZKR),而不是Validity Rollup。
Arbitrum One 是典型的OPR,它部署在L1 上的合約,並沒有主動驗證提交的數據,樂觀地認為這些數據沒有問題。如果提交的資料有錯誤,L2 的驗證者節點會主動發起挑戰。
因此OPR 也隱含一條信任假設:任意時刻⾄少有⼀個誠實的L2 驗證者節點。 ⽽ ZKR 的合約則透過密碼學計算,主動但低成本地驗證排序器提交的資料。
本文會深入介紹樂觀式Rollup 中的龍頭專案-Arbitrum One,涵蓋整個系統的方方面面,仔細閱讀完後你將對Arbitrum 和樂觀式Rollup/OPR 有深刻的理解。
Arbitrum 的核心元件與工作流程
核心合約:
Arbitrum 最重要的合約包括SequencerInbox, DelayedInbox, L1 Gateways, L2 Gateways, Outbox, RollupCore, Bridge 等。後續將詳細介紹。
排序器Sequencer:
接收用戶交易並進行排序,計算交易結果,並迅速(通常<1s)返還給用戶回執。使用者往往在幾秒鐘內就能看到自己的交易在L2 上鍊,體驗就如同Web2 平台。
同時,排序器也會在以太坊鏈下即時廣播最新產生的L2 Block,任何一個Layer2 節點都可以非同步的接收。但此時,這些L2 Block 不具備最終確定性,可以被排序器回滾掉。
每隔幾分鐘,排序器會將排序後的L2 交易資料進行壓縮,聚合成批次(Batch),提交至Layer1 上的收件匣合約SequencerInbox,以確保資料可用性和Rollup 協議的運作。一般而言,被提交至Layer1 上的L2 資料無法回滾,可以具備最終確定性。
從以上流程中我們可以概括:Layer2 有自己的節點網絡,但這些節點數量稀少,且一般沒有公鏈慣用的共識協議,所以安全性是很差的,必須要依附於以太坊來保證,數據發布的可靠性與狀態轉換的有效性。
Arbitrum Rollup 協議:
定義Rollup 鏈的區塊RBlock 的結構,鏈的延續方式,RBlock 的發布,以及挑戰模式流程等⼀系列的合約。注意,這⾥說的Rollup 鏈並不是大家理解的Layer2 帳本,而是Arbitrum One 為了施展詐欺證明機制,而獨立設定的一條抽像出來的「鏈狀資料結構」。
⼀個RBlock 可以包含多個L2 區塊的結果,⽽且資料也迥異,它的資料實體RBlock 儲存在RollupCore 的⼀系列合約中。如果⼀個RBlock 有問題,Validator 將⾯向該RBlock 的提交者對其進⾏挑戰。
驗證者Validator:
Arbitrum 的驗證者節點其實是Layer2 全節點的特殊子集,目前有白名單准入。
Validator 根據排序器提交至SequencerInbox 合約的交易批次batch,來創建新的RBlock(Rollup 區塊,也叫斷⾔ assertion),並監控當前Rollup 鏈的狀態,對排序器提交的錯誤資料進⾏挑戰。
主動型的Validator 需要事先在ETH 鏈上質押資產,有時我們也稱之為Staker。不進⾏質押的Layer2 節點雖然也可以監控Rollup 的運⾏動態,向⽤戶發送異常警報等,但⽆法在ETH 鏈上直接對排序器提交的錯誤資料進行⼲預。
挑戰:
基礎步驟可以概括為多輪互動式細分、單步證明。在細分環節,挑戰雙⽅先對有問題的交易資料進⾏多輪回合製細分,直⾄分解出有問題的那⼀步操作碼指令,並進⾏驗證。 「多輪細分- 單步證明」這個範式,被Arbitrum 開發者認為是詐欺證明中最節省gas 的實作⽅式。所有環節都在合約控制之下,沒有⼀⽅可以作弊。
挑戰期:
由於OP Rollup 的樂觀optimistic 本質,每個RBlock 提交上鍊後,合約並不主動檢查,預留給驗證者一段時間窗⼝期去證偽。此時間窗⼝即為挑戰期,在Arbitrum One 主⽹上為1 週。挑戰期結束後,該RBlock 才會被最終確認,區塊內對應的從L2 傳遞到L1 的訊息(例如透過官方橋執行的提款操作)才能被放行。
ArbOS, Geth, WAVM:
Arbitrum 採用的虛擬機器名為AVM,包含Geth 和ArbOS 兩部分。 Geth 是以太坊最常⽤的客戶端軟體,Arbitrum 對其進⾏了輕量化的修改。 ArbOS 負責所有L2 相關的特殊功能,如⽹絡資源管理、⽣成L2 區塊、與EVM 協同⼯作等。我們將兩者的組合視為⼀個Native AVM,也就是Arbitrum 所採用的虛擬機器。 WAVM 是把AVM 的程式碼編譯為Wasm 後的結果。 Arbitrum 挑戰流程中,最後的「單步驟證明」,驗證的就是WAVM 指令。
在此,我們可以將上述各組件之間的關係和⼯作流⽤下圖來表示:
L2 交易生命週期
一筆L2 交易的處理流程如下:
1.用戶向排序器發送交易指令。
2.排序器先處理處理交易進數字簽章等資料的驗證,剔除無效交易,並進行排序與運算。
3.排序器將交易回執傳送給⽤戶(通常都⾮常快),但這只是排序器在ETH 鏈下進行的「預處理」,處於Soft Finality 的狀態,並不可靠。但對於信任排序器的⽤戶(⼤部分⽤戶),可以樂觀的認為交易已經完成,不會被回溯。
4.排序器將預處理後的交易原始數據,⾼度壓縮後封裝為⼀個Batch(批次)。
5.每隔⼀段時間(受到資料量、ETH 擁堵程度等因素影響),排序器會向L1 上的Sequencer Inbox 合約發布交易Batch。此時可認為,交易已擁有最終性Hard Finality。
Sequencer Inbox 合約
合約會接收排序器提交的交易batch,保證資料可⽤性。深⼊地看,SequencerInbox 中的batch 數據完整記錄了Layer2 的交易輸入信息,即使排序器永久宕機,任何⼈都可以根據batch 的記錄還原Layer2 的當前狀態,接替故障/ 跑路的排序器。
⽤物理的⽅式理解,我們所看到的L2,只是SequencerInbox 中batch 的投影,光源則是STF。因為光源STF 不會輕易變化,所以影⼦的形狀只由充當物體的batch 來決定。
Sequencer Inbox 合約⼜稱為快箱,排序器專門向其提交已經被預處理的交易,且只有排序器可向其提交資料。對應快箱的是慢箱Delayer Inbox,其功能在後續流程中會有描述。
Validator 會一直監聽SequencerInbox 合約,每當排序器向該合約發布Batch 後,就會拋出一個鏈上事件,Validator 監聽到這個事件發生後,就會去下載batch 數據,在本地執⾏後,向ETH鏈上的Rollup 協議合約發布RBlock 。
Arbitrum 的bridge 合約內有個叫累加器accumulator 的參數,會針對新提交的L2 batch,以及慢Inbox 上新接收的交易數和信息,進行記錄。
SequencerInbox 合約有兩個主要函數:
add Sequencer L2Batch From Origin(),排序器每次都會呼叫函數向Sequencer Inox 合約提交Batch 資料。
force Inclusion(),函數任何人都可以調用,用於實現抗審查交易。這個函數的生效方式,會在後面談到Delayed Inbox 合約時詳細解釋。
上述兩個函數都會呼叫bridge.enqueueSequencerMessage(),來更新bridge 合約內的累加器參數accumulator。
Gas 定價
顯然,L2 的交易不可能免費,因為這樣會引來DoS 攻擊,另外則是排序器L2 本身的運⾏成本,以及在L1 上提交資料都會有開銷。 ⽤戶在Layer2 網路內發動交易時,gas 費的架構如下:
佔用Layer1 資源產生的資料發布成本,主要來自於排序器提交的batch(每個batch 有很多用戶的交易),成本最終由交易發起者們均攤。數據發布產生的手續費定價演算法是動態的,排序器會根據近期的損益狀況、batch ⼤⼩、當前以太坊gas 價格進⾏定價。
使用者因佔用Layer2 資源所產生的成本,設定了⼀個可以保證系統穩定運⾏的,每秒處理的gas 上限(⽬前Arbitrum One 是700 萬)。 L1 和L2 的gas 指導價格均由ArbOS 追蹤並調整,公式暫時不在此贅述。
雖然具體的gas 價格計算過程⽐較複雜,但⽤戶無需感知到這些細節,可以明顯感到Rollup 交易費⽤比ETH 主網便宜的多。
樂觀詐騙證明
回顧上文,L2 其實只是排序器在快箱中提交的交易輸入batch 的投影,也是:
Transaction Inputs -> STF -> State Outputs。輸入已經確定,STF 是不變的,則輸出結果也是確定的,而欺詐證明和Arbitrum Rollup 協議這套系統就是把輸出的狀態根,以RBlock (aka 斷言)的形式發佈到L1 上並對其進行樂觀式證明的一套系統。
在L1 上有排序器發布的輸⼊數據,也有驗證者發布的輸出狀態。我們再仔細考慮⼀下,是否有必要向鏈上發布Layer2 的狀態呢?
因為輸⼊已經完全決定了輸出,而輸入資料是公開可見的,再提交輸出結果- 狀態似乎是多餘的?但這種想法忽略了L1-L2 兩個系統之間實際上需要狀態結算,也即L2 向L1 ⽅向的提現⾏為,需要有對狀態的證明。
在搭建Rollup 的時候,⼀條最核⼼的思想就是把⼤部分運算和儲存放到L2 上來規避L1 ⾼昂的費⽤,這也就意味著,L1 並不知道L2 的狀態,它僅僅幫助L2排序器發布全體交易的輸入數據,但不負責計算L2 的狀態。
⽽提現⾏為,本質上是依照L2 給出的跨鏈訊息,從L1 的合約⾥解鎖相應資⾦,劃轉到⽤戶的L1 帳戶中或完成其他事情。
此時Layer1 的合約就會問:你在Layer2 上的狀態是怎樣的,怎麼證明你真的擁有這些聲明要跨走的資產。這時候用戶要給應該的Merkle Proof 等。
所以,如果我們建構⼀條沒有提現功能的Rollup,理論上不向L1 進⾏狀態同步是可以的,也不需要欺詐證明等狀態證明系統(雖然可能帶來其他問題)。但在現實應⽤中,這顯然是不可⾏的。
所謂的樂觀式證明中,合約不會去檢查提交到L1 的輸出狀態是否正確,樂觀地認為一切都是準確無誤的。樂觀證明系統會假設,在任意時刻都有⾄少⼀名誠實的Validator,如果出現錯誤的狀態,則透過詐欺證明進⾏挑戰。
這麼設計的好處是,不需要主動驗證每⼀個發佈到L1 上的RBlock,避免浪費gas。實際上對於OPR ⽽⾔,對每⼀個斷⾔進⾏驗證也是不現實的,因為每個Rblock 都包含著一或多個L2 區塊,要在L1 上去對每筆交易重新執⾏⼀遍,與直接在L1 上執行L2 交易無異,這就失去了Layer2 擴容的意義。
⽽ ZKR 不存在這個問題,因為ZK Proof 有簡潔性,只需要驗證⼀個很⼩的Proof,不需要真地去執⾏該Proof 背後所對應的許多條交易。所以ZKR 並不是樂觀式運⾏,每次發布狀態都會有Verfier 合約進⾏數學驗證。
詐欺證明雖然不能像零知識證明那樣具有⾼度的簡潔性,但Arbitrum 讓⽤了⼀種「多輪分割- 單步證明」的輪流式交互流程,最終需要證明的僅僅是單⼀的虛擬機操作碼,成本相對較⼩。
Rollup 協定
讓我們先來看看,發起挑戰和啟動證明的入口,也也就是Rollup 協定是如何運作的。
Rollup 協定的核心合約是RollupProxy.sol,在保證資料結構一致的情況下,使用了一個罕見的雙重代理結構,一個代理對應兩個實作RollupUserLogic.sol 和RollupAdminLogic.sol,在Scan 等工具目前還無法很好的解析。
另外還有ChallengeManager.sol 合約負責管理挑戰,OneStepProver 系列合約來判定詐欺證明。
在RollupProxy 中,記錄由不同Validator 提交的一系列RBlock(aka 斷言),也即下圖中的方塊:綠色- 已確認,藍色- 未確認,黃色- 已證偽。
RBlock 中包含了自上一個RBlock 以來,一個或多個L2 區塊執行後的最終狀態。這些RBlock 在形態上構成了一條形式上的Rollup Chain(注意L2 帳本本身相區別)。在樂觀情況下,這條Rollup Chain 應該是沒有分叉的,因為有分叉意味著有Validator 提交了彼此衝突的Rollup Block。
要提出或認同斷言,需要驗證者先為該斷言質押一定數量的ETH,成為Staker。這樣在發生挑戰/ 詐欺證明時,輸者的質押品將被罰沒,這是保障驗證者誠實行為的經濟學基礎。
圖中右下角的111 號藍色區塊最終會被證偽,因為其父區塊104 號區塊是錯誤的(黃色)。
此外,驗證者A 提出了106 號Rollup Block,而B 不同意,對其進行挑戰。
在B 發起挑戰後,ChallengeManager 合約負責驗證對挑戰步驟的細分過程:
1.細分是一個雙方輪流互動的過程,一方對某個Rollup Block 中包含的歷史資料進行分段,另一方指出是哪部分資料片段有問題。類似二分法(實際上是N/K)不斷漸進縮小範圍的一個過程。
2.之後,可以繼續定位至哪一筆交易及結果有問題,再進一步細分至該交易中有爭議的某條機器指令。
3.ChallengeManager 合約只檢查原始資料細分後,所產生的『資料片段』是否有效。
4.當挑戰者和被挑戰者定位到了將被挑戰的那條機器指令後,挑戰者調用oneStepProveExecution(),發送單步欺詐證明,證明這條機器指令的執行結果有問題。
單步證明
單步證明是整個Arbitrum 的詐欺證明的核心。我們來看看單步證明具體證明的是什麼內容。
這需要先理解WAVM,Wasm Arbitrum Virtual Machine,它是一個由ArbOS 模組和Geth(以太坊客戶端)核心模組共同編譯成的虛擬機器。由於L2 與L1 有許多截然不同的地方,原始的Geth 核心必須經過輕量修改,並且配合ArbOS 一起工作。
所以,L2 上的狀態轉換其實是ArbOS+Geth Core 的共同手筆。
Arbitrum 的節點客戶端(排序器、驗證者、全節點等),是將上述ArbOS+Geth Core 處理的程序,編譯為節點主機能直接處理的原生機器代碼(for x86/ARM/PC/Mac/etc .)。
如果把編譯後得到的目標語言改為Wasm,就得到了驗證者產生詐欺證明時所使用的WAVM,而驗證單步驟證明的合約上,模擬的也是WAVM 虛擬機的功能。
那為什麼在產生詐欺證明時,要編譯為Wasm 字節碼?主要還是因為,驗證單步驟詐欺證明的合約,要用以太坊智能合約模擬出能處理某套指令集的虛擬機VM,而WASM 易於在合約上實現模擬。
但WASM 比起Native 機器程式碼,運作速度略慢,所以只有在詐欺證明產生及驗證的時候,Arbitrum 的節點/ 合約才會用到WAVM。
在先前的多輪互動細分後,單步證明最終證明的是WAVM 指令集中的單步指令。
下面的程式碼中可以看到,OneStepProofEntry 首先要判定,待證明指令的操作碼屬於哪個類別,再呼叫對應的prover 如Mem,Math 等,將單步指令傳入該prover 合約。
最終結果afterHash 會回到ChallengeManager,如果該雜湊與Rollup Block 上記錄的,指令運算後的雜湊不一致,則挑戰成功。如果一致,表示Rollup Block 上記錄的這個指令運行結果沒問題,挑戰失敗。
在下一篇文章中,我們將解析Arbitrum 乃至於Layer2 與Layer1 之間處理跨鏈訊息/ 橋接功能的合約模組,並進一步闡明,一個真正意義的Layer2 應該怎麼實現抗審查。