跳至內容
0%

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

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

聚合簽章

我們目前的實作方式是分別驗證 bundle 中的每個 user op。這是思考驗證問題的一種直觀方式,但可能會造成浪費。驗證簽章在 gas 花費上可能偏高,因為這需要相當大量的加密演算運算。

如果我們能用一個簽章就同時驗證多個 op,而不是分別驗證每一個,豈不是更好?

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

一個支援聚合的簽章方案,能讓我們把多個由不同金鑰簽署的訊息,合併成一個簽章,使得驗證這個合併後的簽章即可證明所有原本的簽章都是有效的。

一個常見的支援聚合的簽章方案例子是 BLS。

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

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

介紹聚合器(aggregator)

我們馬上就能發現,並非 bundle 中所有的 user op 簽章都能聚合在一起。別忘了錢包可以使用任意邏輯來驗證給定的簽章,因此同一個 bundle 中可能會出現各種不同的簽章方案。

由於我們大概無法把不同方案的簽章聚合在一起,我們的 bundle 最終會由多組 op 組成,每一組使用各自獨立的聚合方案,或是完全不使用聚合方案。

由於鏈上需要有各種聚合方案各自的邏輯表示,我們會讓每個聚合方案都由一個合約來代表,我們稱之為聚合器(aggregator)。

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

Aggregator 合約會將多個使用者的 ops 合併成一組,並以單一簽章表示。
Aggregator 合約會將多個使用者的 ops 合併成一組,並以單一簽章表示。

由於每個錢包都自行定義自己的簽章方案,因此要相容哪一個聚合器(如果有的話),是由各個錢包自行決定的。

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

透過這個新的 getAggregator 方法,bundler 就能把使用相同聚合器的 op 分在一組,並使用該聚合器的 aggregateSignatures 方法為它們計算出一個合併簽章。

一組可能長得像這樣:

💡 如果 bundler 對某個特定聚合器有鏈下的了解,它可以透過硬編碼一個原生版本的簽章聚合演算法來最佳化,而不必以 EVM code 的形式執行 aggregateSignatures。

接下來,我們需要更新入口點合約(entry point contract),讓它能使用這些新的聚合器。

回想一下,入口點有一個 handleOps 方法,接收一份 op 的清單。

我們會為它新增一個方法, handleAggregatedOps,功能相同,但接收的是依聚合器分組後的 op:

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

handleOps 是透過呼叫每個錢包的 validateOp 方法來進行驗證,而 handleAggregatedOps 則會改為對每組合併後的簽章,使用該組所屬的聚合器呼叫其 validateSignatures 方法。

圖表:executor 會先透過 aggregator 將 user ops 分組,再送到 entry point 進行批次驗證
executor 會使用 aggregator 將 ops 分組,再送到 entry point,以便一次驗證。

我們幾乎完成了!

但這裡有個問題,相信大家現在已經很熟悉了。

bundler 想要模擬驗證過程,確認聚合器會驗證通過某一組 op,之後才把這些 op 放進 bundle 裡,因為如果驗證失敗,bundler 就得自己承擔 gas 費用。但一個邏輯任意的聚合器,很可能在模擬階段成功通過,卻在實際執行時失敗。

我們會用和 paymaster、factory 完全相同的方式來解決這個問題:限制聚合器能存取哪些 storage、能使用哪些 opcode,並且要求它在入口點中質押 ETH,除非它不存取 storage。

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

總結

我們在這裡建立的內容,基本上就是 ERC-4337 的完整架構!雖然在一些細節上有所不同,例如某些方法的名稱和參數,但已經沒有任何我認為屬於架構層面的差異了。如果我做得夠好,你現在應該已經能夠閱讀真正的 ERC-4337,並理解其中的內容。

如果你讀到了這裡,非常感謝你閱讀我的說明!希望這篇文章對你的幫助,就像撰寫它對我的幫助一樣大。

附錄:與 ERC-4337 的差異

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

讓我們來看看其中幾個!

1. 驗證的時間範圍

在前面,我對錢包的 validateOp 和 paymaster 的 validatePaymasterOp 的回傳型別講得相當含糊。ERC-4337 找到了一個很好的方式來運用這一點。

錢包很希望能做到的一件事,是只讓某個 user op 在特定一段時間內有效。否則,一個心懷不軌的 bundler 可能會把這個操作壓著很長一段時間,然後在對自己有利的時機,才把它放進 bundle 裡。

錢包可能會想在驗證期間檢查 TIMESTAMP,確保它不會離現在太遙遠,藉此來防範這種情況,但它做不到,因為我們已經禁止在驗證期間使用 TIMESTAMP,以避免模擬結果不準確。這代表錢包需要另一種方式,來表示這個操作在什麼時間範圍內有效。

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

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

ERC-4337 的另一個要點是:錢包在驗證失敗時,應該從 validateOp 回傳一個哨兵值(sentinel value),而不是 revert,這有助於 gas 估算,因為 eth_estimateGas 無法告訴你一筆會 revert 的交易實際用掉了多少 gas。

2. 錢包與 factory 的任意呼叫資料

我們之前說過,我們錢包的介面是:

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

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

這個欄位會以呼叫資料(call data)的形式傳遞給錢包。

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

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

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

3. paymaster 與 factory 的精簡資料

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

在 ERC-4337 中,作為一種優化,這些欄位被合併成單一欄位,其中前 20 個位元組是 paymaster 的地址,其餘部分則是資料:

factory 以及傳給它的資料也是同樣的情況:我們原本使用 factory 和 factoryData 這兩個欄位,而 ERC-4337 將它們合併成單一欄位 initCode。

好了,你做到了!

希望你從中學到了不少關於帳戶抽象的知識。

你也能發明帳戶抽象

錯過這個四篇系列的開頭了嗎?回去從頭開始閱讀吧!

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

打造區塊鏈魔法

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