Rollup Bridge 介紹(八):Arbitrum 原生橋

Cyan Ho
imToken
Published in
17 min readJun 28, 2022

--

本篇是 Rollup Bridge 介绍系列的第八篇,介紹 Arbitrum 的原生橋。

Photo by Jan Bolz on Unsplash

Arbitrum 原生橋是基於 Arbitrum 底層的訊息機制搭建而成,專為 Arbitrum 設計與服務的跨鏈橋,由 Arbitrum 官方所維護,是目前市面上與 Arbitrum 相容性最好、安全性最高的跨鏈橋。

在接下來的段落裡,筆者將先介紹 Arbitrum 底層的訊息機制,訊息機制是連結 Ethereum(L1) 與 Arbitrum(L2)的核心,不只確保了 L2 上交易內容與順序的唯一性,還能讓 L1 與 L2 之間能夠互相通訊。

* 訊息與交易兩個詞在 Arbitrum 文件或是程式碼中常常會交替使用,本文以訊息來統稱介紹。
* 本文中提到 L1 都是指 Ethereum,L2 都是指 Arbitrum。

在了解 Arbitrum 訊息機制的運作原理之後,接著會介紹 Arbitrum 在跨鏈訊息中獨有的 Retryable Ticket 機制,並在文末以「ERC20 token 透過 Arbiturm 原生橋在 L1 與 L2 之間進行跨鏈轉移 」為範例,讓讀者能更了解原生橋的實用場景。

對於 Rollup 和 Arbitrum 運作原理還不熟悉的讀者,非常推薦事先閱讀這篇 Arbitrum 開發者文件對 Rollup 的介紹:Arbitrum Rollup Basics

訊息機制

Arbitrum 在 L1 上有許多處理訊息的合約,包含 InboxOutboxBridge 以及 SequencerInbox,這些合約都可以在 Arbitrum GitHub repository 中看到。

本文參考的合約實作和連結,皆以 Arbitrum GitHub repository 中的 2d002950 commit 為基準。

其中 SequencerInbox 是決定訊息內容與順序的唯一參考源(Single Source of Truth),也就是說,SequencerInbox 紀錄的訊息(包含 L1 和 L2 之間的跨鏈訊息、和 L2 上原生的交易),從頭到尾依序執行一遍,可以得到當下 L2 鏈上的狀態。目前只有官方維運的 Sequencer 節點有權力直接對 SequencerInbox 寫入訊息,以確保訊息排序的一致性。

相對於 SequencerInbox 只允許 Sequencer 節點寫入,Inbox 提供了開放的入口,讓一般使用者與第三方節點也能夠寫入訊息至 Arbitrum,為 Arbitrum 注入更多的應用空間。透過 Inbox 寫入的訊息會先被保存在 Bridge 裡,最後會定期被 Sequencer 收入到 SequencerInbox,或是可以藉由 Force Inclusion 機制(後面會介紹)強制收入到 SequencerInbox。

Outbox 負責紀錄、驗證並處理 L2 至 L1 的跨鏈訊息(例如從 L2 提領 token 至 L1),讓 L2 發起的跨鏈訊息能夠正確地在 L1 執行兌現。

讓我們先用一個架構圖來表達他們之間的關連:

Arbitrum Message Flow

此架構圖以使用者的視角出發,描繪了許多不同情境下的訊息流程,在接下來的小節中會逐一的拆解介紹。

Sequencer Flow

Arbitrum Message Flow through Sequencer

透過官方提供的 Offchain Labs RPC 節點(即 Sequencer 節點),使用者可以將 L2 原生交易和 L2 -> L1 跨鏈訊息委派給 Sequencer 處理。Sequencer 收到使用者的訊息後,會先行執行訊息內容,即時更新 Sequencer 本地節點上的 L2 狀態,此時訊息雖然還未上鏈,但使用者已經可以從 Sequencer 身上獲得最新狀態。為了節省將訊息寫入 L1 SequencerInbox 的成本,Sequencer 會在時間和容量的限制允許之下,盡可能收集多筆訊息,將它們打包成一個 batch,以 batch 的方式一次將多筆訊息寫入 SequencerInbox。

在此提供一筆 Sequencer 寫入 batch 的 L1 交易參考,該交易中呼叫的合約方法實作可以參考 SequencerInbox 合約的原始碼

Sequencer 在處理完本地節點收集的訊息後,會接著從 L1 Bridge 提取部分尚未處理的訊息(即其他人透過 Inbox 寫入的訊息),繼續執行更新 Sequencer 本地節點上的 L2 狀態。等到這些訊息都處理完後,Sequencer 會將本地的訊息 batch 寫入 SequencerInbox,並以參數的方式告知 SequencerInbox 這次 batch 額外包含了多少筆來自 Bridge 裡的訊息(合約原始碼參考),SequencerInbox 會依照參數資訊,主動向 Bridge 收錄對應數量的訊息,以保持 Sequencer 與 SequencerInbox 的歷史同步。

Inbox Flow

Arbitrum Message Flow through Inbox

除了透過官方的 Sequencer 節點,使用者(或第三方節點)可以選擇自行透過 L1 Inbox 發送訊息,包括 L2 原生交易、L2 -> L1 和 L1 -> L2 的跨鏈訊息,特別注意的是,L1 -> L2 跨鏈訊息只能藉由 Inbox 發送,無法透過 Sequencer。Inbox 接收到使用者的訊息後,會對資料做基本的格式包裝,包裝後的訊息會接著被轉發到 Bridge 合約保存起來,並等待 Sequencer 定期將尚未處理的訊息同步至 SequencerInbox。

在正常情形下,使用者透過 Inbox 發送的訊息,背後必須仰賴 Sequencer 遵守規則,才能被收錄至 SequencerInbox。若 Sequencer 刻意略過 Inbox 發送的訊息,進行審查攻擊(Censorship Attack),使用者的訊息將不會被處理和執行。

因此,除了單方面依賴 Sequencer,Arbitrum 在 SequencerInbox 上設計了 Force Inclusion 機制,當某些條件達成時,例如 Sequencer 已經有足夠長的時間未處理 Bridge 裡的訊息,此時任何人都可以直接要求 SequencerInbox 收錄 Bridge 裡的訊息,來確保當 Sequencer 進行交易審查時,整個系統還是能公平地運作。

Outbox Flow

Arbitrum Message Flow through Outbox

Outbox 主要負責紀錄、執行 L2 ->L1 的跨鏈訊息,例如從 L2 提領 token 回 L1,使用者在進行 Outbox 流程時,必須先在 L2 網絡中發起 L2 -> L1 跨鏈訊息(雖然上圖並沒有特別標示出 SequencerInbox,但 L2 -> L1 跨鏈訊息如同前面小節介紹的流程,一樣會先被收錄到 SequencerInbox),接著就是等待包含這筆訊息的 Rollup 狀態的挑戰期結束,確定大家對 L2 狀態無異議後,這筆 L2 -> L1 跨鏈訊息最後會被寫進 L1 Outbox 裡,等待使用者在 L1 執行兌現。

在此提供一筆在挑戰期結束後,將 L2 至 L1 跨鏈訊息寫入 Outbox 的 L1 交易參考,成功寫入時 Outbox 會發出 OutboxEntryCreated 的事件。由於寫入跨鏈訊息至 Outbox 是確認挑戰期結束時中間的一個環節,單從這筆交易比較難看出合約互動的過程,想了解更深入的技術讀者可以從這段 Rollup 合約的原始碼向下挖掘。

當 Rollup 挑戰期結束、L2 -> L1 跨鏈訊息成功寫入 Outbox 後,使用者必須在 L1 主動請求 Outbox 執行交易,並提供這筆 L2 -> L1 跨鏈訊息存在 L2 的證據(可透過 Arbitrum 節點進行查詢),Outbox 驗證通過後,就會在 L1 執行兌現訊息的內容。

在此提供一筆從 Outbox 兌現跨鏈訊息的 L1 交易參考。

以上是 Arbitrum 底層訊息機制的概括介紹,接下來會繼續介紹 Arbitrum 獨有的 L1 -> L2 跨鏈訊息機制:Retryable Ticket。

Retryable Ticket — L1 -> L2 跨鏈訊息機制

在實際介紹跨鏈場景前,我們必須先了解一下 L1 -> L2 跨鏈訊息可能發生的問題,以及 Arbitrum 獨有的 Retryable Ticket 機制如何優雅地解決這個困難。

L1 -> L2 跨鏈訊息傳遞的過程中參雜了許多不確定的因素(例如 L2 gas limit 估算誤差、L2 gas price 波動等等),有可能 L1 -> L2 跨鏈訊息已經在 L1 成功發送,同時發生了與訊息相關的副作用(side effect),例如轉移 token 給 Arbitrum 原生橋,最後訊息卻在 L2 執行失敗,這個狀況會破壞 L1 -> L2 跨鏈訊息的原子性,導致 L1 和 L2 對狀態有不同的認知。

Retryable Ticket 就是為了克服這個問題而設計出來的機制,我們先來了解一下一筆 L1 -> L2 跨鏈訊息的成本結構如下:

L1 -> L2 message fee = 
L1 gas fee (L1 gas * L1 gas price) +
L2 gas fee (L2 gas * L2 gas price)

使用者在 L1 發起一筆 L1 -> L2 跨鏈訊息時,除了需要預估當下發送 L1 交易所需的 gas 費用之外,還需要事先預估好未來訊息在 L2 執行時所需要的 gas 費用,並在 L1 發起的跨鏈訊息裡,以 call value 的方式,事先帶上足夠的 ETH,以支付跨鏈訊息未來在 L2 上執行所需的 gas 費用。

而唯有等到 L1 -> L2 跨鏈訊息實際在 L2 執行時,才能確切地知道執行所需的 gas 用量,與當下網路的 gas price 需求。因此,使用者在 L1 發送 L1 -> L2 跨鏈訊息時預估的未來 L2 執行 gas 費用,與實際狀況會有一定程度上的誤差,當訊息在 L1 call value 夾帶的 ETH 不足以支付 L2 gas 費用時,訊息在 L2 上就會執行失敗,使用者可能因此遭受損失。

舉個例子來說,使用者在 L1 上發起一筆 L1 -> L2 跨鏈轉移 token 的訊息,並以當下 L2 節點數據,預估好未來在 L2 執行的 gas 費用(以 L1 call value 方式夾帶),順利地在 L1 將跨鏈訊息和 token 交由 Arbitrum 原生橋處理。但不幸的是,訊息在 L2 執行時花費了比預期還多的 gas,使得使用者在 L1 發送訊息夾帶的 ETH 不足以支付 L2 gas 費用,造成 L2 轉移 token 給使用者的動作失敗。在這個例子中,使用者不只沒有在 L2 收到相應數量的 token,而且還沒有 L2 鏈上的證據來取回 L1 上交由 Arbitrum 原生橋保管的 token,這些 token 將永遠沉澱在 Arbitrum 原生橋裡。

目前 Optimism L1 -> L2 跨鏈訊息也有相同的風險存在。

Arbitrum 獨有的 Retryable Ticket 機制讓 L1 -> L2 跨鏈訊息在 L2 上擁有成功執行前重試的能力,儘管無法百分之百控制所有不確定的因素,但可以為系統加入時間的維度,來消彌這些短期的波動。以上述的 gas 費用波動為例,當 L1 -> L2 跨鏈訊息在 L2 上因爲 gas 費用高於預期而執行失敗,這筆跨鏈訊息將會被暫時保存在 L2 上的 retry buffer 裡,使用者可以等待 L2 網絡順暢後,再從 retry buffer 取出訊息進行重試(ArbRetryableTx.redeem)。

但 Retryable Ticket 並不是沒有成本,Arbitrum 為了保存這些提供重試的訊息,需要消耗額外的儲存資源,因此在建立 Retryable Ticket 時使用者需要額外支付保存資料的費用(submission cost),費用將與訊息的資料大小成正比。而每一筆 Retryable Ticket 都有其保存期限,以七天為單位,如果在保存期限到期前,想要延長 Retryable Ticket 的期限,只需要再支付一次保存費用,就可以再額外延長七天的時間。若 Retryable Ticket 最終不幸過期,訊息將會從 retry buffer 裡永久移除,也就無法再進行重試。

Arbitrum 開發者文件中有對 Retryable Ticket 做更細節的介紹,有興趣了解更多的讀者可以參考看看。

Arbitrum 原生橋在執行 L1 -> L2 跨鏈訊息時,都會使用 Retryable Ticket 來保障使用者的訊息最終能在 L2 上完成兌現。

下一章節將開始介紹 Arbitrum 原生橋的跨鏈場景,我們將會看到訊息機制以及 Retryable Ticket 如何在實際的應用中發揮功用。

原生橋跨鏈場景 — 以 ERC20 token 跨鏈轉移為例

本章節以 ERC20 token(以下簡稱 token)透過 Arbitrum 原生橋在 L1 與 L2 之間進行跨鏈轉移為例子,了解 Arbitrum 訊息機制以及 Retryable Ticket 如何在實際場景中發揮作用。

Arbitrum 原生橋對於跨鏈轉移 token 的機制為「Escrow on L1, mint/burn on L2」,意即使用者在 L1 的 token 會全權交由 Arbitrum 原生橋託管,並在 L2 上鑄造(mint)或銷毀(burn)相同數量的 token,來達成出入金的動作。接下來會分別深入探討 L1 -> L2 入金(deposit)以及 L2 -> L1 出金(withdraw)的流程細節。

Deposit (L1 -> L2)

在 L1 轉移 token 至 L2 的情境中,由於 L2 是以鑄造的方式,憑空在 L2 產生對應數量的 token。因此,L2 token 鑄造的權限管控是一個非常重要的安全議題。Arbitrum 基於先前介紹的訊息機制,在 L1 和 L2 各自搭建了互相對應的 Token Gateway 架構,使 L1/L2 Token Gateway 成為 Arbitrum 原生橋轉移 token 的唯一出入口,L2 token 只需要賦予 L2 Token Gateway 鑄造的權力,而 L2 Token Gateway 只接受來自 L1 Token Gateway 的跨鏈鑄造請求,來保證 L1 -> L2 token 轉移的安全性。

L1/L2 Token Gateway 不只會紀錄與其對應的 Token Gateway 地址,也會紀錄 L1 token 與 L2 token 之間的地址映射關係(由 Arbitrum 官方設定),減少了 L1 與 L2 token 之間誤轉的風險。

整體流程如下圖:

L1 -> L2 Deposit Flow

使用者首先需要透過 L1 Gateway Router(outboundTransfer)發起將 token 從 L1 轉移至 L2 的請求,L1 Gateway Router 會為使用者找到有能力處理該 token 轉帳的 Gateway,以 ERC20 token 來說一般會是由 L1 ERC20 Gateway 來負責。L1 ERC20 Gateway 接到請求後,會將使用者要跨鏈轉移的 token 數量託管到自己身上;接著,L1 ERC20 Gateway 會透過 Inbox 建立 Retryable Ticket(createRetryableTicket)的跨鏈訊息,訊息接收方為 L2 ERC20 Gateway,內容為跨鏈轉移 token 的請求(finalizeInboundTransfer),Inbox 會協助標記這筆跨鏈訊息來自 L1 ERC20 Gateway;最後,這筆跨鏈訊息將由 Inbox 轉發至 Bridge 保存。

等待一段時間後,Arbitrum 會協助將這筆跨鏈訊息發送到 L2 ERC20 Gateway,L2 ERC20 Gateway 會檢查這筆轉移 token 的訊息是否來自 L1 ERC20 Gateway,若不是,則拒絕處理。確認跨鏈訊息來自 L1 ERC20 Gateway 後,L2 ERC20 Gateway 會在 L2 鑄造出對應的 token 數量給 L2 使用者,完成 L1 -> L2 deposit token 的流程。

Withdraw (L2 -> L1)

在 L2 轉移 token 至 L1 的情境中,L2 是以銷毀對應數量的方式,減少 L2 上的 token 總量。因此,L2 token 銷毀的權限管控也是一個非常重要的安全議題。基於上一小節所介紹的 Token Gateway 架構,L2 token 只需要賦予 L2 Token Gateway 銷毀的權力,而 L1 Token Gateway 只接受來自 L2 Token Gateway 的跨鏈解除 token 託管的請求,來保證 L2 -> L1 token 轉移的安全性。

整體流程如下圖:

L2 -> L1 Withdraw Flow

使用者首先需要透過 L2 Gateway Router(outboundTransfer)發起將 token 從 L2 轉移至 L1 的請求,L2 Gateway Router 會為使用者找到有能力處理該 token 轉帳的 Gateway,以 ERC20 token 來說一般會是由 L2 ERC20 Gateway 來負責。L2 ERC20 Gateway 接到請求後,會將 L2 上要移轉的 token 進行銷毀,並對 ArbSys 發出一筆跨鏈訊息,訊息接收方為 L1 ERC20 Gateway,內容為跨鏈移轉 token 的請求(finalizeInboundTransfer),等待挑戰期結束後,這筆跨鏈訊息會被寫入 L1 上的 Outbox 以供兌現。

ArbSys 是 Arbitrum 上預先部署好的合約,地址固定為 0x64,是 L2 與 L1 溝通的橋樑。

當訊息被成功寫入 Outbox 後,使用者需要主動要求 Outbox 執行訊息(executeTransaction),並附上訊息存在的證明,Outbox 檢查無誤後,就會幫助使用者在 L1 執行訊息的內容。L1 ERC20 Gateway 接收到來自 Outbox 的轉帳訊息,會檢查這筆轉移 token 的訊息是否來自 L2 ERC20 Gateway,若不是,則拒絕處理。確認跨鏈訊息來自 L2 ERC20 Gateway 後,L1 ERC20 Gateway 會將使用者在 deposit 時交由它託管的 token,轉回給使用者,完成 L2 -> L1 withdraw token 的流程。

結語

本文概括地介紹了 Arbitrum 原生橋的運作原理與實際場景,其中還有很多細節無法一一詳述,想更深入了解的讀者,非常推薦閱讀 Arbitrum 開發者文件,文件中從設計緣由、運作原理、到落地實作都有非常清楚的介紹,對 Arbitrum 與 Rollup 會有更深刻的了解。

References

[1] Arbitrum 開發者文件:https://developer.offchainlabs.com/docs/developer_quickstart
[2] Arbitrum GitHub Repository:https://github.com/OffchainLabs/arbitrum

了解更多

風險提示:本文內容均不構成任何形式的投資意見或建議。imToken 對本文所提及的第三方服務和產品不做任何保證和承諾,亦不承擔任何責任。數位資產投資有風險,請審慎評估該等投資風險,諮詢相關專業人士後自行做出決定。

--

--