ERC4337: Ethereumプロトコルの変更を伴わないAccount Abstraction(Vitalikの記事の日本語訳です。)
こちらは、下のVitalikのMediumの日本語訳です。
何度も読み直したいものの、英語では時間がかかると考え、日本語訳にしました。
まだ理解が不十分な箇所が多々あるため、違和感のある日本語訳の箇所がいくつかあります。
その部分は理解が進み次第、修正をおこなっていきます。
以下、本文です。
========================
アカウントの抽象化(Account Abstraction)は長い間、Ethereum開発者コミュニティの夢でした。
EVMコードはアプリケーションのロジックを実装するためだけに使用されるのではなく、個々のユーザーのウォレットの検証ロジック(nonces、署名...)を実装するためにも使用されるようになるでしょう。
これにより、重要な機能を提供できるウォレットの設計に創造性の扉を開くことができるようになるでしょう。
- マルチシグとソーシャルリカバリー
- より効率的でシンプルな署名アルゴリズム(Schnorr, BLSなど)
- ポスト量子安全署名アルゴリズム(Lamport, Winternitzなど)
- アップグレード可能性
スマートコントラクトウォレットでこれらのことをすべて行うことは可能ですが、イーサリアムのプロトコル自体が、ECDSAで保護された外部所有アカウント(EOA)から発信されるトランザクションですべてをパッケージ化することを求めているため、これは非常に困難なことなのです。
ユーザーの操作はすべてEOAからのトランザクションで包まれる必要があり、21000ガスのオーバーヘッドが追加されます。
ユーザーは、ガス代を支払うために別のEOAにETHを持ち、2つの口座で残高を管理するか、一般的に集中管理されているリレーシステムに依存する必要があるのです。
EIP2938は、EOAではなくコントラクトからトップレベルのイーサリアムトランザクションを開始できるように、いくつかのイーサリアムプロトコルの変更を導入することにより、これを解決するための1つの方法です。
コントラクト自体に、マイナーがチェックする検証や手数料支払いのロジックが含まれることになります。
しかし、これはプロトコル開発者がマージとスケーラビリティに重きを置いている時に、大幅なプロトコル変更を必要とします。私たちの新しい提案(ERC 4337)では、コンセンサス層のプロトコルを変更せずに同じメリットを得る方法を提供しています。
この提案がどのように機能するか
コンセンサス層自体のロジックを修正する代わりに、トランザクションのメンプールの機能をより上位のシステムで再現します。
ユーザーはUserOperation
オブジェクトを送信します。
このオブジェクトはユーザーの意図することを署名やその他のデータとともにパッケージ化し、検証を行います。
マイナー、またはFlashbotsなどのサービスを利用するバンドラーは、一連のUserOperation
オブジェクトを1つの「バンドルトランザクション」にまとめ、これをイーサリアムのブロックに含めることができます。
バンドラーはバンドルトランザクションの手数料をETHで支払い、個々のUserOperation
の実行の一部として支払われる手数料で補償されます。
バンドラーは、既存のトランザクションのメンプールにおけるマイナーの操作方法と同様の手数料優先順位付けロジックに基づいて、どのUserOperation
オブジェクトを含めるかを選択することになります。
UserOperation
はトランザクションのように見えます。それはABIエンコードされた構造体で、次のようなフィールドを含んでいます。
- 送信者: 操作を行うウォレット
- nonce と signature: ウォレットの検証関数に渡されるパラメータで、ウォレットが操作を検証できるようにします。
- initCode: ウォレットがまだ存在しない場合に、ウォレットを作成するための初期コードです。
- callData: 実際の実行ステップでウォレットを呼び出すためのデータです。
残りのフィールドは、ガスと手数料の管理に関係しています。フィールドの完全なリストは、ERC4337の仕様に記載されています。
ウォレットはスマートコントラクトであり、2つの機能を持つことが要求されます。
UserOperation
を入力値とするvalidateUserOp
。
この関数は、UserOperation
上の署名とnonceを検証し、検証に成功したらガス代を支払ってnonceをインクリメントし、検証に失敗したら例外をスローすることになっています。
op実行関数
calldataを、ウォレットがアクションを起こすための命令として解釈します。この関数が calldata をどのように解釈し、その結果何を行うかは完全に自由です。しかし、最も一般的な動作は、calldata を解析して、ウォレットが一つまたは複数の呼び出しを行うための命令とすることでしょう。
ウォレットのロジックを単純化するために、安全性を確保するために必要な複雑なスマートコントラクトのロジックの多くは、ウォレット自体ではなく、エントリポイントと呼ばれるグローバルコントラクトで実行されます。
validateUserOp
と実行関数は require(msg.sender == ENTRY_POINT)
でゲートされることが期待されており、信頼できるエントリポイントだけがウォレットに何らかのアクションを実行させたり、手数料を支払わせたりすることができるようになっています。
エントリーポイントは、calldata
を実行するUserOperation
でvalidateUserOp
が既に成功した後にのみウォレットに任意の呼び出しを行うので、攻撃からウォレットを守るにはこれで十分です。
エントリーポイントはまた、ウォレットがまだ存在しない場合、提供された initCode
を使用してウォレットを作成する責任があります。
handleOps
実行時のエントリーポイントの制御フロー
特に、validateUserOp
の実行は、他のコントラクトのストレージを読み書きできず、TIMESTAMP
などの環境オペコードを使用できず、他のコントラクトが自己破壊しないことが証明されていない限り、そのコントラクトを呼び出すことができません。
これは、バンドラーやUserOperation
のmempoolノードが、あるUserOperation
を含めるか、転送してよいかどうかを確認するために使用するvalidateUserOp
のシミュレーションの実行が、それが実際に未来のブロックに含まれる場合に、同じ効果を持つようにするために必要なものです。
UserOperation
の検証のシミュレーションに成功した場合、送信者
アカウントの内部状態が変化するまで、そのUserOperation
はインクルード可能であることが保証されます(同じ送信者の別のUserOperationや、送信者に呼び出された別のコントラクトが原因です。どちらの場合でも、1つのアカウントでこの条件をトリガーするには、7500以上のガスをオンチェーンに費やす必要があります。)
さらに、UserOperation
はvalidateUserOp
ステップのガスの上限を指定し、このガスの上限が非常に小さい場合(たとえば200000以下)以外は、mempoolノードとバンドラーはそれを拒否します。
これらの制限は、mempoolをDoS攻撃から安全に保つ既存のEthereumトランザクションの主要な特性を再現しています。バンドラーとmempoolノードは、今日のEthereumトランザクション処理ロジックと同様のロジックを使用して、UserOperation
を含めるか転送するかを決定することができます。
このデザインは、通常のEthereumトランザクションmempoolと比較して、どのような特性を追加、維持、犠牲にしているのでしょうか?
維持される特性
-
中央集権的なアクターは存在しない。:すべてピアツーピアのmempoolを通じて行われる。
-
DoS安全性(シミュレーションチェックを通過した
UserOperation
は、送信者
が別の状態変化を起こすまでインクルード可能であることが保証されており、攻撃者は送信者
ごとに7500以上のガスを支払う必要がある)。 -
ユーザーサイドのウォレットセットアップの複雑さがありません:ユーザーは、ウォレットコントラクトが「すでに公開」されているかどうかを気にする必要がありません。ウォレットは決定論的なCREATE2アドレスに存在し、ウォレットがまだ存在しない場合、最初の
UserOperation
は自動的にそれを作成します。 -
料金設定の簡素化を含むEIP1559の完全サポート(ユーザーは固定の料金のプレミアムと最大合計料金を設定することができ、迅速に含まれ、公正に請求されることを期待できます。)
-
料金による置き換えが可能:古いUserOperationよりもかなり高いプレミアムを持つ新しい
UserOperation
を送信して、オペレーションを置き換えたり、より早く含まれるようにすることができます。
新しいメリット
-
検証ロジックの柔軟性:
validateUserOp
関数に任意の署名・nonce検証ロジック(新しい署名方式、マルチシグ...)を追加できる。 -
実行レイヤーを量子安全にするのに十分な性能:この提案が普遍的に採用されれば、量子安全のために実行レイヤーにさらなる作業を行う必要はありません。ユーザーは個々に自分のウォレットを量子安全なものにアップグレードできます。マイナーはバンドルトランザクションごとに新しく作成された、つまりハッシュで保護されたEOAを使用し、ブロックに追加される前のトランザクションを公表しないため、ラッパー取引でさえも安全です。
-
ウォレットのアップグレード可能性:ウォレットの検証ロジックはステートフルであるため、ウォレットはその公開鍵を変更したり、(DELEGATECALLで公開されていれば)そのコードを完全にアップグレードしたりすることができます。
-
実行ロジックの柔軟性:ウォレットは実行ステップにカスタムロジックを追加できます。例えば、アトミックなマルチオペレーション(EIP 3074の主要目標)を作ることができます。
欠点
-
プロトコルの最善の努力にもかかわらず、検証ロジックが単一の ECDSA 検証という現状よりもいくぶん複雑になることが許容されるため、DoS 脆弱性がやや増加する
-
ガスのオーバーヘッド:通常のトランザクションよりもガスのオーバーヘッドがやや多い(ただし、マルチオペレーションのサポートによって補われる使用例もある)。
-
一度に1つのトランザクション:アカウントは複数のトランザクションをキューに入れ、mempoolに送ることはできない。しかし、アトミックなマルチオペレーションが可能なため、この機能はあまり必要ない。
ペイマスターとのスポンサーシップ
スポンサー付きトランザクションには、いくつかの重要なユースケースがある。最もよく引き合いに出される望ましいースケースは以下の通りである。
- アプリケーション開発者がユーザーに代わって料金を支払うことができるようにする。
- ユーザーがERC20トークンで料金を支払うことを許可し、コントラクトがERC20を収集しETHで支払う仲介役として機能する。
本提案では、組み込みのペイマスター機構により、この機能をサポートすることができます。
UserOperation
は、別のアドレスをpaymasterとして設定することができます。paymaster が設定されている場合(つまりゼロではない)、検証ステップの間にエントリポイントも paymaster を呼び出して、paymaster が UserOperation
に対して支払う意思があるかどうかを検証します。
支払う意思がある場合、手数料はウォレットではなく、エントリーポイント内にステークされたペイマスターのETHから(セキュリティのために引き出し遅延を伴って)引き落とされます。
実行ステップでは、通常通りUserOperation
内のcalldataでウォレットが呼び出されますが、その後paymasterがpostOp
で呼び出されます。
上記2つのユースケースのワークフロー例です。
-
paymasterは、
paymasterData
にスポンサーからの署名が含まれていることを確認し、スポンサーがUserOperation
に支払う意思があることを検証します。署名が有効であれば、paymasterは受け入れ、UserOperation
の料金はスポンサーのステークから支払われる。 -
Paymasterは、
送信者
のウォレットにUserOperation
の支払いに十分なERC20の残高があるかどうかを確認します。もしあれば、paymaster は ETH の手数料を受け取って支払い、postOp
で ERC20 トークンを報酬として請求します(UserOperation
が ERC20 の残高を消耗したためにpostOp
が失敗すると、実行は元に戻ってpostOp
が再び呼び出されるので、paymaster には常に支払いが行われることになります)。
今のところ、これはERC20がpaymaster自身によって管理されるラッパートークンである場合にのみ実行可能であることに注意してください。
特に、2番目のケースでは、時折のリバランスとパラメータの再設定を除いて、paymasterは純粋に受動的であることが可能であることに注意してください。これは、ペイマスターが常にオンラインで個々のトランザクションをアクティブにラップする必要があった既存のスポンサーの試みと比較して、抜本的な改善です。
この提案はどの程度進んでいるのでしょうか?
ERC4337はこちらでご覧になれます。実装はこちらで進行中です。開発者用の初期アルファ版が近々リリースされる予定です。
開発者は、アカウント抽象化ウォレットの実験をすぐに始めることができるはずです。
Discussion