跳至內容
0%

帳戶抽象化 第4部分:聚合簽章

發布於 2023年2月14日閱讀時間 2 分鐘

聚合簽章

我們目前的實作方式,是分別驗證 bundle 中的每一筆 user op。這樣理解驗證流程雖然很直觀,但可能造成浪費。因為驗證簽章需要相當多的密碼學運算,就 gas 而言可能相當昂貴。

如果能同時驗證多筆 op,只用一個簽章而不是多個,豈不是更好?

要做到這點,需要用到密碼學中的一個概念:聚合簽章(aggregate signatures)

支援聚合的簽章方案提供了一種方法:給定多筆以不同金鑰簽署的訊息,可以產生一個組合簽章,只要驗證這個組合簽章有效,就代表其中每一個組成簽章也都有效。

支援聚合的簽章方案中,常見的例子是 BLS

這項最佳化對於實作 rollup 特別有用,因為 rollup 的主要目標就是資料壓縮,而簽章聚合可以讓我們壓縮簽章的部分。

關於簽章聚合能節省多少空間,可參考 Vitalik 針對此主題發的推文

介紹 aggregator

一開始就能看出,並非 bundle 中所有 user op 的簽章都能聚合在一起。別忘了,wallet 可以使用任意邏輯來驗證它拿到的簽章,因此同一個 bundle 中可能存在多種不同的簽章方案。

由於不同方案的簽章通常無法互相聚合,我們的 bundle 最終會分成好幾組 op,每組使用一種特定的聚合方案,或完全不使用聚合。

由於鏈上需要呈現多種聚合方案,每種都有自己的邏輯,我們會用一個合約來代表每種聚合方案,稱之為 aggregator

一種聚合方案的定義,取決於它如何將多個簽章組合成一個,以及它如何驗證這個組合簽章,因此 aggregator 會以方法的形式,公開這兩個功能:

Aggregator 合約會將多個使用者的 ops 整合成一組,只需一個簽章。
Aggregator 合約會將多個使用者的 ops 整合成一組,只需一個簽章。

由於每個 wallet 都在定義自己的簽章方案,因此要不要相容某個 aggregator(如果有的話),由每個 wallet 自行決定。

如果一個 wallet 想要參與聚合,它會公開一個方法來選擇自己的 aggregator:

透過這個新的 getAggregator 方法,bundler 可以把使用相同 aggregator 的 op 分成一組,並使用該 aggregator 的 aggregateSignatures 方法,為它們計算出一個組合簽章。

一個分組可能長這樣:

💡 如果 bundler 對某個特定 aggregator 有鏈下的了解,它可以進行最佳化,直接寫死一份原生版本的簽章聚合演算法,而不是把 aggregateSignatures 當作 EVM 程式碼來執行。

接下來,我們需要更新 entry point 合約,讓它能使用這些新的 aggregator。

回想一下,entry point 有一個 handleOps 方法,接收一份 op 清單。

我們會給它一個新方法, handleAggregatedOps,做的事情一樣,但接收的是依 aggregator 分組後的 op:

新方法 handleAggregatedOps 的運作方式大致上和 handleOps 相同,唯一的差別在於驗證步驟。

handleOps 是透過呼叫每個 wallet 的 validateOp 方法來執行驗證,而 handleAggregatedOps 則改為針對每組的組合簽章,呼叫該組 aggregator 的 validateSignatures 方法。

示意圖:executor 透過 aggregator 將使用者 ops 分組,再送往 entry point 進行批次驗證
executor 會利用 aggregator 將 ops 分組,再送往 entry point,讓它們可以同時被驗證。

我們幾乎完成了!

但到這裡有一個大家已經很熟悉的問題。

bundler 想要在把一組 op 放進 bundle 之前,先模擬驗證流程,確認該 aggregator 會驗證通過這組 op,因為如果驗證失敗,bundler 就得自己付 gas。但一個邏輯任意的 aggregator,很容易在模擬時成功、卻在實際執行時失敗。

我們會用和 paymaster 與 factory 完全相同的方式來解決這個問題:限制 aggregator 能存取的儲存空間以及能使用的 opcode,並且除非它不存取儲存空間,否則要求它在 entry point 中質押 ETH。

聚合簽章的部分到此結束!

總結

到目前為止我們建立的,大致上就是 ERC-4337 的完整架構!雖然細節上有些差異,例如部分方法的名稱與參數,但已經沒有任何我會稱之為「架構上的差異」的東西了。如果我做得夠好,你現在應該能夠閱讀真正的 ERC-4337,並理解裡面在講什麼。

如果你讀到這裡,非常謝謝你讀完我的說明!希望這篇文章對你的幫助,就像寫作過程對我的幫助一樣大。

附錄:與 ERC-4337 的差異

雖然我們已經掌握了帳戶抽象化的整體架構,但 ERC-4337 背後那些聰明人,想出了一些和上面描述略有不同的做法。

我們來看看其中幾個!

1. 驗證的時間範圍

前面提到 wallet 的 validateOp 以及 paymaster 的 validatePaymasterOp 的回傳型別時,說得相當含糊。ERC-4337 找到了一個很好的方式來運用這一點。

wallet 很希望能做到的一件事,是只讓某個 user op 在一段特定時間內有效。否則,惡意的 bundler 可能會把這個 operation 壓著很久,然後在對自己有利的時間點,晚點才把它放進 bundle。

wallet 可能想在驗證時檢查 TIMESTAMP,確保時間不會太過未來,但它做不到,因為我們已經禁止在驗證期間使用 TIMESTAMP,以避免模擬結果失真。這代表 wallet 需要另一種方式,來表示這個 operation 在什麼時間範圍內有效。

因此,ERC-4337 讓 validateOp 有一個回傳值,讓 wallet 可以用來選擇一個時間範圍:

這個回傳值以兩個相連的 8 位元組整數,來表示該 operation 有效的時間範圍。

另外還有一點來自 ERC-4337:在驗證失敗的情況下,wallet 應該從 validateOp 回傳一個哨兵值(sentinel value),而不是直接 revert,這有助於 gas 估算,因為 eth_estimateGas 無法告訴你一筆 revert 的交易實際用了多少 gas。

2. wallet 與 factory 的任意呼叫資料

我們前面說過,wallet 的介面是:

在 ERC-4337 中,wallet 實際上並沒有一個叫做 executeOp 的方法。

取而代之的是,user operation 有一個 callData 欄位:

這會以呼叫資料的形式傳給 wallet。

對於一般的智慧合約來說,這份資料的前四個位元組會被解讀為函式選擇器(function selector),其餘部分則是函式參數。

這代表除了必要的 validateOp 方法之外,wallet 可以自行定義自己的介面,而 user operation 可以用來呼叫 wallet 上任意的方法。

同樣地,在 ERC-4337 中,factory 合約實際上也沒有 deployContract 方法。它們同樣接收任意的呼叫資料,這裡是來自 op 的 initCode 欄位。

3. paymaster 與 factory 的精簡資料

前面我們提到,user operation 包含用來指定 paymaster 的欄位,以及要傳給它的資料欄位:

在 ERC-4337 中,這兩者被合併成一個欄位,作為一種最佳化:該欄位的前 20 個位元組是 paymaster 位址,其餘部分則是資料:

factory 以及傳給它的資料也是同樣的情況:雖然我們用了 factoryfactoryData 這兩個欄位,但 ERC-4337 把它們合併成單一欄位 initCode

好,你做到了!

希望你對帳戶抽象化學到了很多東西。

你也可能發明帳戶抽象化

錯過這系列共四篇文章的開頭了嗎?回去從頭讀起吧!

  1. 帳戶抽象化 第一部分:保護我們的資產
  2. 帳戶抽象化 第二部分:用 Paymaster 贊助交易
  3. 帳戶抽象化 第三部分:Wallet 建立
Background gradient

打造區塊鏈魔法

Alchemy 結合最強大的 Web3 開發者產品與工具,並提供資源、社群與卓越的支援。