---
title: "帳戶抽象化 第4部分：聚合簽章"
description: "探索 ERC-4337 在聚合簽章上的突破。使用單一密碼學簽章簡化多筆 user op 的驗證，提升交易效率。"
---

## 聚合簽章

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

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

要做到這點，需要用到密碼學中的一個概念：**聚合簽章（aggregate signatures）**。

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

支援聚合的簽章方案中，常見的例子是 [BLS](https://en.wikipedia.org/wiki/BLS_digital_signature)。

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

關於簽章聚合能節省多少空間，可參考 [Vitalik 針對此主題發的推文](https://twitter.com/VitalikButerin/status/1554983955182809088)。

### 介紹 aggregator

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

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

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

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

<ImageBlock
  src="https://media.alchemy.com/1704793497-aggregator-contract-combining-user-ops-to-single-signature.jpeg"
  alt="Aggregator 合約會將多個使用者的 ops 整合成一組，只需一個簽章。"
  width={960}
  height={540}
  caption="Aggregator 合約會將多個使用者的 ops 整合成一組，只需一個簽章。"
/>

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

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

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

**一個分組可能長這樣：**

<CalloutBlock>

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

</CalloutBlock>

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

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

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

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

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

<ImageBlock
  src="https://media.alchemy.com/1703863779-account-abstraction-key-concepts.jpeg"
  alt="示意圖：executor 透過 aggregator 將使用者 ops 分組，再送往 entry point 進行批次驗證"
  width={960}
  height={540}
  caption="executor 會利用 aggregator 將 ops 分組，再送往 entry point，讓它們可以同時被驗證。"
/>

我們幾乎完成了！

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

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

我們會用和 paymaster 與 factory 完全相同的方式來解決這個問題：限制 aggregator 能存取的[儲存空間](https://www.alchemy.com/docs/smart-contract-storage-layout)以及能使用的 opcode，並且除非它不存取儲存空間，否則要求它在 entry point 中質押 ETH。

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

### 總結

到目前為止我們建立的，大致上就是 [ERC-4337 的完整架構](https://eips.ethereum.org/EIPS/eip-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](https://www.alchemy.com/docs/chains/ethereum/ethereum-api-endpoints/eth-estimate-gas) 無法告訴你一筆 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 以及傳給它的資料也是同樣的情況：雖然我們用了 `factory` 和 `factoryData` 這兩個欄位，但 ERC-4337 把它們合併成單一欄位 `initCode`。

好，你做到了！

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

### 你也可能發明帳戶抽象化

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

1. [帳戶抽象化 第一部分：保護我們的資產](https://www.alchemy.com/overviews/what-is-account-abstraction)
1. [帳戶抽象化 第二部分：用 Paymaster 贊助交易](https://www.alchemy.com/overviews/what-is-account-abstraction-paymasters)
1. [帳戶抽象化 第三部分：Wallet 建立](https://www.alchemy.com/overviews/what-is-account-abstraction-wallet-creation)
