原文標題:《前Arbitrum 技術大使解讀Arbitrum 的組件結構(上)》
原文作者:羅奔奔,前Arbitrum 技術大使,極客Web3 貢獻者

本文為Arbitrum 前技術大使及智能合約自動化審計公司Goplus Security 前共同創辦人羅奔奔對Arbitrum One 的技術解讀。
因為中文圈子裡涉及Layer2 的文章或資料,缺乏對Arbitrum 乃至OP Rollup 的專業解讀,本文試圖透過科普Arbitrum 的運轉機理,填補這一領域的空缺。由於 Arbitrum 本身的結構太複雜,全文在盡可能簡化的基礎上,還是超過了 1 萬字篇幅,所以分成了上下兩篇,建議作為參考資料收藏轉發!

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 最重要的合約包括SequencerInbox, DelayedInbox, L1 Gateways, L2 Gateways, Outbox, RollupCore, Bridge 等。後續將詳細介紹。
接收使用者交易並進行排序,計算交易結果,並迅速(通常<1s)返還給用戶回執。使用者往往在幾秒鐘內就能看到自己的交易在 L2 上鍊,體驗就如同 Web2 平台。
同時,排序器也會在以太坊鏈下即時廣播最新產生的 L2 Block,任何一個 Layer2 節點都可以非同步的接收。但此時,這些 L2 Block 不具備最終確定性,可以被排序器回滾掉。
每隔幾分鐘,排序器會將排序後的L2 交易資料進行壓縮,聚合成批次(Batch),提交至Layer1 上的收件匣合約SequencerInbox,以確保資料可用性和Rollup 協議的運作。一般而言,被提交至 Layer1 上的 L2 資料無法回滾,可以具備最終確定性。

從以上流程我們可以概括:Layer2 有自己的節點網絡,但這些節點數量稀少,且一般沒有公鏈慣用的共識協議,所以安全性是很差的,必須要依附於以太坊來保證,數據發布的可靠性與狀態轉換的有效性。
定義Rollup 鏈的區塊RBlock 的結構,鏈的延續方式,RBlock的發布,以及挑戰模式流程等⼀系列的合約。 注意,這裡說的 Rollup 鏈並不是大家理解的 Layer2 帳本,而是 Arbitrum One 為了施展詐欺證明機制,而獨立設定的一條抽像出來的「鏈狀資料結構」。
⼀個RBlock 可以包含多個L2 區塊的結果,⽽且資料也迥異,它的資料實體RBlock 儲存在RollupCore 的⼀系列合約中。如果⼀個 RBlock 有問題,Validator 將⾯向該 RBlock 的提交者對其進⾏挑戰。
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 的訊息(例如透過官方橋執行的提款操作)才能被放行。
Arbitrum 採用的虛擬機器名為AVM,包含Geth 和ArbOS 兩部分。 Geth 是以太坊最常用的客戶端軟體,Arbitrum 對其進行了輕量化的修改。 ArbOS 負責所有 L2 相關的特殊功能,如網路資源管理、產生 L2 區塊、與 EVM 協同⼯作等。我們將兩者的組合視為⼀個 Native AVM,也就是 Arbitrum 採用的虛擬機器。 WAVM 是把 AVM 的程式碼編譯成 Wasm 後的結果。 Arbitrum 挑戰流程中,最後的「單步證明」,驗證的就是 WAVM 指令。
在此,我們可以將上述各個元件之間的關係和工作流程用圖來表示:

1. 使用者傳送交易指令到排序器。
2. 排序器先處理處理交易進數位簽章等資料的驗證,剔除無效交易,並進行排序與運算。
3. 排序器將交易回執傳送給⽤戶(通常都⾮常快),但這只是排序器在ETH 鏈下進行的「預處理」,處於Soft Finality 的狀態,並不可靠。但對於信任排序器的⽤戶(大部分使用者),可以樂觀的認為交易已經完成,不會被回溯。
4. 排序器將預處理後的交易原始數據,⾼度壓縮後封裝為⼀個 Batch(批次)。
5. 每隔⼀段時間(受到資料量、ETH 壅塞程度等因素影響),排序器會向 L1 上的 Sequencer Inbox 合約發布交易 Batch。 此時可認為,交易已擁有最終性 Hard Finality。

合約會接收排序器提交的交易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 數據,這部分數據尺寸很大,截圖沒顯示完)
add Sequencer L2Batch From Origin(),排序器每次都會呼叫該函數向Sequencer Inox 合約提交Batch 資料。
force Inclusion(),函數任何人都可以調用,用於實現抗審查交易。這個函數的生效方式,會在後面談到 Delayed Inbox 合約時詳細解釋。
上述兩個函數都會呼叫 bridge.enqueueSequencerMessage(),來更新 bridge 合約內的累加器參數 accumulator。
顯然,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 協定的核心合約是RollupProxy.sol,在保證資料結構一致的情況下,使用了一個罕見的雙重代理結構,一個代理對應兩個實作RollupUserLogic.sol 和RollupAdminLogic.sol,在Scan 等工具中目前還無法很好的解析。
另外還有 ChallengeManager.sol 合約負責管理挑戰,OneStepProver 系列合約來判定詐欺證明。

(圖源:L2BEAT 官網) blockquote>
在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 應該怎麼實現抗審查。
原文連結
歡迎加入律動 BlockBeats 官方社群:
Telegram 訂閱群:https://t.me/theblockbeats
Telegram 交流群:https://t.me/BlockBeats_App
Twitter 官方帳號:https://twitter.com/BlockBeatsAsia