以太坊 layer2: optimism 源碼學習 (一)

作者:林冠宏 / 指尖下的幽靈。轉載者,請: 務必標明出處。

掘金://juejin.im/user/1785262612681997

博客://www.cnblogs.com/linguanh/

GitHub : //github.com/af913337456/

出版的書籍:


前序:

對於區塊鏈每個階段的風口產品,我都會挑選其中某個研究其源碼,當作學習並增加經驗。早前看的是 zkSync,難度巨大而能力有限,遂轉於 Optimism

本次是系列文章,這是第一篇。

這裡就不說什麼是 Layer2 了,可以去網絡搜索其它文章來閱讀。在 Layer2 的擴容方案中目前有 3 種,Rollups 是其中一種,稱為打包。打包又分兩大派別:

這兩派的風格各異,特點突出。Optimistic rollups智能合約的支持可以說是完全兼容,而 ZK rollups目前對智能合約支持不友好,但是在交易提款上鏈這塊非常快、更安全卻計算量巨大,技術難度複雜。關於它們的對比,我後續再寫篇文章詳細說說


目錄:

  • Op 簡介
  • Op 的程序組件
  • 如何使用 Op
  • 源碼分析 — 充值與提現
    • L1 合約層源碼
    • L2 層源碼
    • 提現

1. 正題,Op 簡介

Layer2 是公鏈下的第二層。比如安卓系統下的底層軟件。

Optimism,下面簡稱 Op,是基於 Optimistic rollups 方案所實現的 Layer2 應用,下面簡稱 L2。作用是幫助 以太坊 擴容以及加速交易。

特點如下:

  1. 提款到 Layer1 的周期,約一周。下面簡稱 L1;
  2. 速度,L2 內交易,極快,手續費很低;
  3. 通用性,EVM 兼容,基於以太坊的修改
  4. 技術複雜度,簡單;
  5. 鏈下成本,低。鏈上成本,高;
  6. 交易安全保證,基於默克爾樹–樹根哈希的欺詐證明
  7. 源碼://github.com/ethereum-optimism/optimism

2. Op 的程序組件

雖說 Op 是個 L2 應用,但它的組成程序組件非常多。我這裡列出會和我本文內容相關的組件:

  1. 合約,這些會被部署在 L1 公鏈上面,由 L2 組件或用戶來調用,包含不限於有:
    • L2 側鏈,基於 geth 源碼改造的鏈,運行在 Op 生態里;
    • L1StandardBridge.sol 垮鏈橋合約,用來處理充值 Token 到 Op 地址或從 Op 地址提現到 L1 地址 所用;
    • CanonicalTransactionChain.sol 規範處理 L2 --> L1 的交易,下面簡稱 CTC
    • L1CrossDomainMessenger.sol 跨鏈信使合約,裏面主要編寫進行了各種要觸發跨鏈事件的函數。
  2. DataTransportLayer,定時掃描 L1 區塊,從中獲取到 TransactionEnqueued 事件,並存儲到 LevelDB 數據庫;
  3. Sequencer
    • 接受用戶發來 L2 的交易;
    • 定期從數據庫中獲取 DataTransportLayer 存儲的 TransactionEnqueued 事件數據,並把交易在 L2 鏈中執行,使之正常被打包到 L2 區塊中;
  4. Batch-Submitter,定期從 L2 區塊中將交易數據以打包的形式組裝到交易:
    • 打包批量交易 txBatch 提交到 L1 的 CTC 合約;
    • 打包批量狀態 stateBatch 提交到 L1 的StateCommitmentChain.sol
    • 之後這些交易進入等待挑戰窗口,挑戰方式就是欺詐證明
  5. Relayer,定時從 L2 區塊中過濾交易中的 SentMessage 事件數據:
    • 判斷當前交易是否過了挑戰時間;
    • 為此交易生成證明,調用 L1 上的 L1CrossDomainMessenger.relayMessage 函數,使之完成合約檢查然後在內部調用目標合約,最終在 L1 完成 L2 交易的最終目的;

它們的組合通訊流程圖如下:

注:

目前所有的 Op 組建,都由官方運行着,欺詐證明還在完善。用戶必須要相信官方不會做惡。

3. 如何使用 Op

使用 Op 網絡分兩種情況:

  1. 直接使用原生 Token 進行交易,即以太坊,那麼:
    1. 需要先在 L1 訪問 L1StandardBridge.sol 進行充值到 Op;
    2. 充值結束後,到賬了,可以進行 Op 網絡內的交易活動;
    3. 提現到 L1 的地址;
    4. 流程到這裡閉環
  2. 其他協議,比如 ERC-20:
    1. 先去 Op 網絡部署對應的合約;
    2. 在 L1 的 L1StandardBridge.sol 充值 Token 到 L2 地址;
    3. 到賬後,便可自由交易。提現動作和 ETH 的一樣。

4. 源碼分析 — 充值與提現

充值部分 — L1 合約層源碼

按照流程,用戶需要調用 L1 上的 L1StandardBridge.sol 智能合約充值,如下圖所示:

注意,payable 就是 solidity 語法中標明可以接收 ETH 的語法糖。此時充值 ETH 到了 Op 的 L1StandardBridge 跨鏈橋合約中。

上圖,函數走完了,都沒有痕迹告訴我們如何在為 L2 的地址充值了 ETH,我們只需要留意其中的 IL2ERC20Bridge.finalizeDeposit.selector 這一行。這裡設置了一個 L2 到時需要調用的函數。

上圖,結合我們前面的結論 DataTransportLayer,會定時掃描 L1 區塊,從中獲取到 TransactionEnqueued 事件,並存儲到 LevelDB 數據庫。

所以在 _sendXDomainMessag 內部,最終會觸發 emit TransactionEnqueued 事件。L1 層面的閉環結束

ERC-20 的充值也是一樣的,只是函數不一樣。如下圖:

// TransactionEnqueued 事件在 enqueue 內發生
function _sendXDomainMessage(
    address _canonicalTransactionChain,
    bytes memory _message,
    uint256 _gasLimit
) internal {
    // slither-disable-next-line reentrancy-events
    ICanonicalTransactionChain(_canonicalTransactionChain).enqueue(
        Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER,
        _gasLimit,
        _message
    );
}
充值部分 — L2 層源碼

在這裡我們主要看 Sequencer 的是如何執行 TransactionEnqueued 中的交易到 L2 的。

Sequencer 的啟動中,首先是它會進入一個定時從數據庫中獲取交易的函數,如下圖所示:

接下來的調用鏈是: s.sequnece --> syncQueueToTip --> syncQueue --> syncQueneTransaction

如下圖所示,在 syncQueneTransaction 中,調用 GetEnqueue 從遠程接口即 DataTransportLayer 服務中獲取目標交易數據。最後執行 applyTransaction 執行交易

回到前面合約層的 IL2ERC20Bridge.finalizeDeposit.selector 這一句,最後交易的執行會走到 L2 合約層的 L2ERC20Bridge 合約的 finalizeDeposit 函數,如下圖所示,最終 mint 操作完成充值流程,至此閉環。

提現

內容比較多,提現留給下一篇。持續關注