作者:羅奔奔,前Arbitrum技術大使,極客web3貢獻者
本文是Arbitrum前技術大使 及 智能合約自動化審計公司Goplus Security前聯合創始人羅奔奔 對Arbitrum One的技術解讀。
因爲中文圈子裏涉及Layer2的文章或資料,缺乏對Arbitrum乃至OP Rollup的專業解讀,本文試圖通過科普Arbitrum的運轉機理,填補這一領域的空缺。由於Arbitrum本身的結構太復雜,全文在盡可能簡化的基礎上,還是超過了1萬字篇幅,所以分成了上下兩篇,建議作爲參考資料收藏轉發!
Rollup排序器簡述
Rollup擴容的原理可以概括爲兩點:
成本優化:將⼤部分運算與存儲任務移交至L1鏈下也即L2上。L2大多是運⾏在單台服務器也即排序器(Sequencer/Operator)上的⼀條鏈。
排序器在觀感上接近於一台中心化服務器,在“區塊鏈不可能三⻆”中舍棄“去中心化”來換取TPS與成本上的優勢。 ⽤戶可以讓L2來代替以太坊處理交易指令,成本比在以太坊上交易要低得多。
(圖源:BNB Chain)
安全保障:L2上的交易內容與交易後的狀態,會同步⾄以太坊L1,通過合約來校驗 狀態轉換的有效性。同時,以太坊上會保留L2的歷史記錄,排序器即便永久宕機,他⼈也可以通過以太坊上的記錄,還原出整個L2的狀態。
從根本上來說,Rollup的安全性是基於以太坊的。排序器如果不知道某個账戶的私鑰,就無法用該账戶的名義發起交易,或者無法篡改該账戶的資產余額(即便這么做了,也很快被識破)。
雖然排序器作爲系統中樞帶有中⼼化色彩,但在成熟度比較高的Rollup方案中,中心化排序器僅能實施交易審查等軟性作惡⾏爲,或者惡意宕機,但在理想狀態的Rollup⽅案中,有相應的⼿段進⾏遏制(比如強制提款或排序證明等抗審查機制)。
(路印協議在L1上的合約源碼中設置的,供用戶調用的強制提款函數)
而防止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運轉方式)
(ZK Rollup運轉方式)
本文會深度介紹樂觀式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不斷提交batch)
(Batch的具體信息,data字段對應着Batch數據,這部分數據尺寸很大,截圖沒顯示完)
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系列合約來判定欺詐證明。
(圖源:L2BEAT官網)
在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應該怎么實現抗審查。
鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播信息之目的,不構成任何投資建議,如有侵權行為,請第一時間聯絡我們修改或刪除,多謝。