はじめに
初めまして。
CryptoGamesというブロックチェーンゲーム企業でエンジニアをしている cardene(かるでね) です!
スマートコントラクトを書いたり、フロントエンド・バックエンド・インフラと幅広く触れています。
代表的なゲームはクリプトスペルズというブロックチェーンゲームです。
今回は、モジュール式のスマートコントラクトアカウントとアカウントプラグインのインターフェースを提案しているERC6900についてまとめていきます!
以下にまとめられているものを翻訳・要約・補足しながらまとめていきます。
ERC6900は現在Draft段階です。
そのため今後内容が変更される可能性があるのでご注意ください。
概要
この提案は、スマートコントラクトアカウントと、そのアカウント内で使える「プラグイン」と呼ばれるコードのインターフェースを標準化しようとするものです。
スマートコントラクトは、プログラムのようなもので、アカウント内で特定のロジックや動作を実行します。
しかし、この提案では、アカウント内で使えるロジックを分けて、それぞれの部分を別々のコードで管理できるようにします。
これにより、アカウントの機能を必要に応じて柔軟に組み合わせることができるようになります。
この提案は、ERC4337という規格に従っています。
ERC4337については以下を参照してください。
また、機能の更新や情報の取得のためのインターフェースを定義する際に、ERC2535という規格からも影響を受けています。
ERC2535については以下を参照してください。
これにより、他のスマートコントラクトやツールとの互換性を高めることができます。
具体的には、この提案はアカウントの機能を3つのカテゴリに分けます。
そして、それぞれのカテゴリに対する機能を、外部のコントラクト内に実装します。
最後に、アカウントからの実行が期待通り実行されるように定義します。
アカウントからの実行が期待通り実行されるように定義
「アカウントからの実行が期待通り実行されるように定義」することは、スマートコントラクトの動作や機能が期待どおりに実行されるために重要です。
トリガーの検出
スマートコントラクトが実行されるトリガーを検出します。
これは、外部からのトランザクションや特定の条件が満たされたときなどに実行されることを指します。
アクションの開始
トリガーが検出されると、スマートコントラクト内で定義されたアクションが開始されます。
例えば特定の関数やメソッドの呼び出し、データの更新、新しいトランザクションの発行などが含まれます。
アクションの実行
定義されたアクションが実行されます。
これには、スマートコントラクト内のコードが実行されることや、外部のコントラクトとのやり取りが含まれます。
結果の処理
アクションの実行が完了すると、その結果を適切に処理します。
これは、トランザクションの成否の確認、データの更新、イベントの発行などです。
このアプローチによって、より複雑なスマートコントラクトを作成する際に、機能をより効果的に管理・組み合わせることができるようになります。
動機
ERC4337の提案は、特定のスマートコントラクトアカウント内で実行と検証のためのロジックを抽象化することを目指しています。
ERC4337の問題点
RERC4337には以下のような問題点があります。
カスタム性の欠如
新しくコントラクトアカウントを作成ために、開発者は「既存の実装をフォークして追加機能を追加する」か「基本実装から開始して、新しい機能セットとともに標準コンポーネントを再構築する」必要があります。
また、上記両方において開発者はアカウント全体を監査して再デプロイする必要があります。
これは、開発者のエクスペリエンスが低下し、コントラクトアカウントがアプリ層でのプラットフォームになる可能性が制限され、ベンダーロックインやユーザーの断片化などの問題が生じます。
ベンダーロックイン
EOA使用すると、ユーザーは自分のアカウントを他のEOAベースのウォレットに簡単に移行できます(シードフレーズなどを使用することで)。
これにより、ユーザーはウォレットのベンダーを選択できるようになり、ウォレットの機能も選択できるようになります。
一方、コントラクトアカウントはウォレットベンダー間で簡単に譲渡できず、機能の譲渡性を保証する相互運用性の標準実装もありません。
その結果、ユーザーが希望する機能を異なるウォレットベンダーが備えている場合、ユーザーは複数のアカウントを使用する必要があり、ユーザーエクスペリエンスが低下し、ユーザーの断片化につながります。
ユーザーの断片化
具体例を用います。
AliceはベンダーAによって作成されたコントラクトアカウントを持っています。
ベンターBによってサポートされるWeb3ゲームをプレイすることを仮定し、併せてゲームの開発者がバッチ処理やマルチコールなどを利用して、ゲームを強化していると仮定します。
AliceはベンダーAをサポートしているUXが比較的低いゲームをやるか、ベンダーBのゲームがサポートしている新しいアカウントを作成するかの選択をする必要があります。
これは、彼女の資産が断片化され、彼女 (ウォレット管理のオーバーヘッドの増加) とゲーム開発者 (ユーザーに関するオンチェーン データへのアクセスの減少) にとって悪影響を及ぼします。
セキュリティ
新しい機能を追加するために、アカウントをフォークして再構築すると、重複した作業の発生とDappとウォレット間の統合の複雑度が増すという問題が生じます。
また、セキュリティを脆弱性も増します。
新しいアカウントの機能は、検証と実行のステップに関連するロジックをカスタマイズすることで追加できます。
例えば、セッションキー、サブスクリプション、支出制限、ロールベースのアクセス制御などがそうです。
現在、これらの機能の一部は特定のスマートコントラクトアカウント内でネイティブに実装されており、他の一部はプラグインシステムを通じて実装されています。
プラグインシステムの例としては、Safe
モジュールやZeroDev
プラグインが挙げられます。
しかしながら、複数のアカウントを管理することは、ユーザーエクスペリエンスを損ないます。
アカウントが複数の機能やセキュリティ設定のために分散し、プラグイン開発者はどのプラットフォームをサポートするかを選択する必要があります。
これにより、プラットフォームへの依存や重複した開発作業が生じることがあります。
この提案では、プラグイン開発者とウォレット開発者との協力を促進する標準を提供します。
この標準により、標準に準拠した全てのプラグインをサポートできるモジュラーなスマートコントラクトアカウントが定義されます。
これにより、ユーザーはデータを移植しやすくなり、プラグイン開発者は特定のアカウント実装を選ぶ必要がなくなります。
モジュラーなスマートコントラクトアカウント
イメージとしては、スマートアカウントを構成可能なレゴピースに分割し、それらを組み合わせていくようなもの。
これによりアカウントをカスタムするという問題が解決され、開発者は全く新しいアカウントを作成するのではなく。新しいアカウントの機能に集中できるようになります。
引用: https://blog.rhinestone.wtf/part-1-modular-account-abstraction-for-everyone-else-84567422bc46
MSCAとは、Modular Smart Contract Accountsの略です。
モジュールで実現できる機能
モジュールで実現できる機能としては以下のようなものがあります。
- 様々な署名スキームの使用。
- トークン転送時に特定のアクションをトリガーする。
- 1日あたりの支出制限。
- 所有者の同意を必要とせずに、追加で資金を送れる許可をする。
- セッションキーによる定期的なトランザクション。
- スケジュールされたトランザクションや自動トランザクション。
- ソーシャルリカバリー。
この提案は、ERC2535のダイヤモンドパターンを参考にし、関数セレクターに基づく実行ルーティングを採用し、同様に組み合わせ可能なアカウントを提案しています。
ダイヤモンドパターン
1つのダイヤモンドの中に複数の異なる面が含まれており、それぞれの面が独自の特性を持っています。
同様に、スマートコントラクトアカウントも、関数セレクターという仕組みを使って、異なる機能を持つ複数の部分で構成されることができます。
関数セレクターは、外部からスマートコントラクトに送信されるトランザクションや呼び出しに含まれる関数の識別子です。
我々の提案は、これを活用して、どの機能がどの部分で実行されるかを選択する方法を提案しています。
つまり、特定の関数が呼び出されたとき、それに対応する部分のコードが実行される仕組みを作り出すことが目標です。
このアイデアにより、アカウント内で異なる機能を組み合わせることが可能となり、より柔軟なアカウントの設計が可能となります。
アカウントの持つ機能が関数セレクターによって適切にルーティングされることで、ユーザーエクスペリエンスが向上し、プラグインや機能の組み込みがスムーズに行えるようになります。
ただし、この標準ではマルチファセットプロキシパターンは必要ありません。
マルチファセットプロキシパターン
複数の異なる機能を持つコントラクトを1つのアカウントで組み合わせるための手法です。
しかし、この提案ではこのパターンは必要ありません。
ダイヤモンドパターンは、マルチファセットプロキシパターンに比べて、よりシンプルな設計が可能となります。
各機能部分は独立しており、関数セレクターによって適切な部分が選択されて実行されるため、複雑なプロキシ構造を必要としません。
これにより、スマートコントラクトの管理やセキュリティの向上が容易になります。
これらのプラグインには、実行ロジック、検証スキーム、フックが含まれます。
検証スキームは、スマートコントラクトアカウントが代わりに実行されるアクションを許可する条件を定義し、フックは事前および事後の制御を可能にします。
プラグインは、スマートコントラクトアカウントに組み込まれる拡張機能です。
これらのプラグインには、3つの主要な要素が含まれます。
-
実行ロジック:
プラグインは、特定の機能や機能群を実行するためのロジックを含んでいます。
例えば、支払い処理、データ更新、トランザクションの発行などの実行ロジックがプラグインに含まれます。
これにより、スマートコントラクトアカウントに新しい機能を追加したり、既存の機能を拡張したりすることができます。 -
検証スキーム:
検証スキームは、プラグインが実行される際に、どのような条件下でその実行を許可するかを定義します。
特定のアクションが実行される前に、スマートコントラクトアカウントは検証スキームに基づいて、そのアクションの妥当性を確認します。
例えば、あるトランザクションが特定のアカウントの残高を超えないことを確認するための検証スキームが考えられます。 -
フック:
フックは、プラグインの実行前や実行後に追加の処理を挿入するための仕組みです。
事前フックは、実行前に特定の操作や確認を行うために使用され、事後フックは、実行後の後処理や結果の制御に使用されます。
これにより、プラグインがアクションの前後に特定の操作を行うことができます。
総括すると、プラグインはスマートコントラクトアカウントの機能を拡張するためのモジュールであり、実行ロジック、検証スキーム、フックが含まれます。
これにより、スマートコントラクトアカウントはより柔軟でカスタマイズ可能な動作を実現し、新しい機能の統合やセキュリティの強化が容易になります。
この提案に従うアカウントは、モジュラーでアップグレード可能な実行と検証のロジックをサポートします。
スマートコントラクトアカウントのためのこの標準は、セキュアなプラグイン開発を容易にし、高い相互運用性を実現することを目指しています。
目標
- スマートコントラクトアカウントの検証、実行、フック関数の書き方に関する標準を提供する。
- 準拠アカウントがプラグインを追加、更新、削除、検査するための標準を提供する。
仕様
条件
アカウント(またはスマートコントラクトアカウント、SCA) は、トランザクションを送信し、デジタルアセットを保持するためのスマートコントラクトです。
ERC4337のIAccount インターフェースを実装しています。
これにより、アカウントはトランザクションを受け付けて処理し、デジタルアセットの管理を行うことができます。
モジュラーアカウント(またはモジュラースマートコントラクトアカウント、MSCA) は、モジュラーな機能をサポートするアカウントです。
モジュラーな機能は、アカウントの動作をカスタマイズするための部品です。
以下にその詳細を説明します。
1. Validation(検証)関数
呼び出し元の正当性とアカウントへの権限を検証する機能です。
アクションを実行する前に、そのアクションを実行できる権限を持つかどうかを確認します。
Validation(検証)関数
具体的には、アクションが実行される前に、そのアクションを実行するための適切な権限や条件を持つかどうかを確認します。
例えば、あるアカウントが特定のアクション(例: 資産の送金、情報の変更など)を行おうとした場合、そのアクションを行うための必要な条件や承認が満たされているかどうかを検証関数がチェックします。
簡単な例として、あるアカウントが資産の送金を行おうとする場合、その送金が正当であるかについて、送金の条件や送金を行う権限を持っているかどうかを確認し、正当な場合にのみ送金が実行されるようにします。
これにより、不正なアクションや権限のない操作が実行されることを防ぐことができます。
検証関数は、アカウントのセキュリティや正常な運用を確保する重要な役割を果たしています。
2. Execution(実行)関数
アカウントで許可されるカスタムなロジックを実行する機能です。
これにより、アカウントの機能を拡張したり、特定のカスタム操作を行ったりできます。
Execution(実行)関数
アカウントは、トランザクションや操作を受け入れて処理するためのスマートコントラクトです。
しかし、基本的な操作だけでなく、特定のニーズや要件に合わせてカスタムな処理を行いたい場合があります。
そのような場合に実行関数を使用します。
例えば、特定のイベントが発生した際に自動的に特定の処理を行いたい場合、その処理を実行関数内に記述しておくことができます。
また、アカウントの振る舞いを変更したり、新しい機能を追加したりするためにも実行関数を使用します。
具体例としては、ボタンが押された時にトークンAをトークンBに交換し、Xプロトコろうに預けるなどが挙げられます。
実行関数は、アカウントが持つ基本的な機能に加えて、ユーザーがカスタムな動作や操作を追加できる柔軟性を提供します。
これにより、アカウントの利用範囲を広げたり、特定の目的に合わせた動作を実現したりすることができます。
3. Hooks(フック)
実行関数の前後で特定のアクションを実行する機能です。
フックを使用することで、実行前後に追加の操作を組み込むことができます。
Hooks
例えば、ある特定のアクションが実行される前に、そのアクションに関する特定の条件をチェックしたい場合、事前のフックを使用してその条件を確認することができます。
また、アクションの実行後に特定の処理を行いたい場合は、事後のフックを使用してその処理を実行することができます。
フックはアカウントの機能を拡張し、特定の操作にカスタムな処理を追加するための仕組みです。
例えば、特定のイベントが発生した際に通知を送信したり、操作のログを記録したりするためにフックを使用できます。
フックはアカウントの振る舞いをカスタマイズするための強力なツールであり、特定の要件やニーズに合わせて動作を追加したり調整したりする際に役立ちます。
これにより、アカウントの柔軟性と機能性を向上させることができます。
検証関数には以下の2つのタイプがあります。
User Operation Validator(ユーザーオペレーションバリデータ)関数
ERC4337のユーザーオペレーション(アクション)の妥当性を確認するための関数です。
特定のアクションが妥当かどうかを確認します。
User Operation Validator(ユーザーオペレーションバリデータ)関数
特定のユーザーオペレーションがアカウント内で適切に実行されるかどうかを確認する役割を果たします。
ユーザーオペレーションには特定の条件や要件があり、それに合致しているかどうかをバリデータ関数がチェックします。
例えば、あるアカウントが資産の送金操作を行う際に、送金が許可された条件や制約が満たされているかどうかをユーザーオペレーションバリデータ関数が確認します。
もし条件を満たしていない場合、送金操作は拒否されるかエラーが発生します。
このように、ユーザーオペレーションバリデータ関数はアカウント内の特定のアクションが妥当であるかどうかを確認し、アカウントの正常な運用やセキュリティを確保するのに役立ちます。
これにより、誤った操作や不正なアクションを防ぐことができます。
Runtime Validator(ランタイムバリデータ)関数
ユーザーオペレーション経由で呼び出されない場合、実行関数の前に実行されます。
例えば、所有者による実行などの特定の条件をチェックします。
Runtime Validator(ランタイムバリデータ)関数
例えば、アカウント内の特定の実行関数が、所有者による実行であるかどうかをチェックする必要があるとします。
この場合、ランタイムバリデータ関数は実行関数が呼び出される前に、実行を行う呼び出し元がアカウントの所有者であるかどうかを確認します。
また、特定の操作が特定のユーザーによってのみ実行されるべきである場合も、ランタイムバリデータ関数を使用してそのチェックを行うことができます。
これにより、アカウント内の実行関数の正当性を確保することができます。
ランタイムバリデータ関数は、実行関数が実行される前に実行されるため、実行関数の安全性や正確性を保証するのに役立ちます。
特定の条件や要件が満たされていない場合、実行関数は実行されないかエラーが発生します。
-
実行関数は、モジュラーアカウントの主要な実行ステップを定義する関数です。
アカウントの動作の中核をなす部分であり、カスタムな操作の具体的な実装を含みます。 -
標準実行関数は、モジュラーアカウントに組み込まれる2つの特定の実行関数です。
これらはアカウント自体に実装され、プラグインではなくアカウントが直接提供します。 -
フックは、実行関数の前後で追加の操作やチェックを実行するための仕組みです。
事前の制御や事後の処理を行うために使用され、アカウントの動作をカスタマイズできます。 -
プラグインは、モジュラーアカウントに追加の機能を提供するためのスマートコントラクトです。
これらの機能には、実行関数、検証関数、フックが含まれます。
プラグインを使用することで、アカウントの機能を柔軟に拡張できます。
プラグイン関数の署名
ユーザーオペレーションバリデータ関数と事前ユーザーオペレーション検証フック
-
コード
function(UserOperation calldata, bytes32) external returns (uint256)
-
説明
- この関数は、アカウントから送信されるユーザーオペレーションとそのハッシュを受け取ります。
- その後、関数は
authorizer
、validUnti
l、validAfter
の検証データをパックした戻り値を返さなければなりません。 - このパッキングの順序は、最初の
6
バイトにvalidAfter
、次の6
バイトにvalidUntil
、最後の20
バイトにauthorizer
を配置します。 - ただし、事前ユーザーオペレーション検証フックは、
0
または1
以外のauthorizer
値を返してはいけません。
ランタイムバリデータ関数と事前ランタイム検証フック
-
コード
function(address, uint256, bytes calldata) external
-
説明
- この関数は、アカウントから送信される呼び出し元のアドレス、呼び出しの値、および送信された
calldata
を受け取ります。 - この関数は、特定の条件をチェックし、実行関数の前に実行されます
- もし関数内で
revert
が必要な場合、関数はrevert
しなければなりません。
- この関数は、アカウントから送信される呼び出し元のアドレス、呼び出しの値、および送信された
事前実行フック
-
コード
function(address, uint256, bytes calldata) external returns (bytes memory)
-
説明
- この関数は、アカウントから送信される呼び出し元のアドレス、呼び出しの値、および送信された
calldata
を受け取ります。 - この関数は実行関数の前に実行され、実行前の操作を設定できます。
- 戻り値には、事後実行フックに渡すためのコンテキストが含まれている必要があります。
- ただし、空のバイト配列を返すことも可能です。
- この関数は、アカウントから送信される呼び出し元のアドレス、呼び出しの値、および送信された
事後実行フック
-
コード
function(bytes calldata) external
-
説明
- この関数は、関連する事前実行フックによって返されるコンテキストを受け取ります。
- この関数は実行関数の後に実行され、実行後の操作を行うために使用されます。
- もし関数内で
revert
が必要な場合、関数はrevert
しなければなりません。
実行関数
-
説明
- 実行関数は、任意の関数シグネチャを持つことができます。
- これにより、アカウントの機能を拡張したり、特定のカスタム操作を実行したりすることが可能です。
これらの関数シグネチャは、プラグイン内で定義される関数の形式を指定し、アカウントのロジックを効果的に制御するために使用されます。
概要
モジュラーアカウントは、2つの種類の呼び出しを処理します。
1つはERC4337を介したエントリーポイントからの呼び出しであり、もう1つは外部所有アカウント(EOA)や他のスマートコントラクトからの直接呼び出しです。
この標準は、両方の使用ケースをサポートします。
スマートコントラクトアカウントへの呼び出しは、以下の図に示す通り、5つのステップに分解できます。
最初の2つのステップ(1と2)は呼び出し元が呼び出しを許可されているかどうかを検証します。
3つ目のステップ(事前実行フック、Step 3)は、実行前のチェックや更新を行うために使用されます。
また、4つ目のステップ(実行、Step 4)は、定義されたタスクや操作を実行します。
5つ目のステップ(事後実行フック、Step 5)は、実行後に追加のアクションや検証を行うために使用されます。
各ステップはモジュラーであり、異なる実行関数ごとに異なる実装をサポートし、さらにフックを通じて複数のステップを組み合わせることができます。
これにより、柔軟性があり、拡張可能なプログラム可能なアカウントが実現されます
インターフェース
モジュラーアカウントは、ERC4337のIAccount
インターフェースを実装する必要があります。
このインターフェースは、モジュラーアカウントが必要な機能を提供できるようにするためのルールとガイドラインを提供します。
この設計により、モジュラーアカウントは異なる呼び出し元からの要求に対応できるだけでなく、その機能を簡単に拡張できる柔軟な仕組みが提供されています。
通常タイプ
以下のタイプは、次のインターフェース間で共通です。
インプリメンターは、タイプのエイリアス代わりに基本的な型を使用することができます。
これは、複数のインターフェースで同じタイプのデータや構造を使い回すことを示しています。
たとえば、複数のインターフェースで同じ種類のデータを扱う場合、それを共通のデータ型として定義し、各インターフェースで利用することができます。
この共通のデータ型は、コードの再利用性を高め、煩雑さを減少させるのに役立ちます。
また、インプリメンター(実装者)は、基本的な型を使用しても構いません。
タイプのエイリアス(別名)を使用する代わりに、具体的なデータ型を直接指定することで、コードの明確さやシンプルさが向上します。
このガイドラインは、異なるインターフェースを組み合わせて利用する際に、データ型やタイプの統一性を保ちつつ、効率的なプログラミングをサポートする目的で提供されています。
type FunctionReference is bytes24;
type HookGroupId is uint32;
struct Execution {
address target;
uint256 value;
bytes data;
}
FunctionReferenceのタイプの変数は、特定の条件に従う必要があります。
この変数の中には2つの要素が含まれています。
-
最初の20バイト
これはアドレスを表します。
アドレスは、スマートコントラクトや外部所有アカウントなどの識別子です。
このアドレス部分が、関数が存在する場所を指します。 -
残りの4バイト
これは関数セレクターと呼ばれるもので、関数の識別子です。
関数セレクターは、関数の名前や引数の型などから生成される一意のハッシュです。
これによって、正確にどの関数を呼び出すかが識別されます。
この変数の構造によって、1つの変数内で関数の場所(アドレス)と関数の識別子(関数セレクター)がまとめて表現されるため、プログラムがよりシンプルで効率的になります。
関数へのアクセスや呼び出しに関する情報が1つの変数にまとまっているため、コードの理解や保守が容易になります。
IPluginUpdate.sol
モジュラーアカウントは、このインターフェースを実装することができます。
これにより、プラグインの変更をサポートすることができます。
これは、モジュラーアカウントに新しい機能や修正を追加するための方法です。
アカウントがこのインターフェースを実装すると、後からプラグインの機能や動作を変更することができます。
具体的には、新しいプラグインを追加したり、既存のプラグインを変更したりする際に使用されます。
このインターフェースの実装によって、モジュラーアカウントは柔軟性を持ち、将来的な変更やアップデートに対応できるようになります。
これによって、スマートコントラクトの機能を拡張し、新しい要件に合わせて調整することが可能となります。
interface IPluginUpdate {
enum PluginAction {
ADD,
REPLACE,
REMOVE
}
enum ValidatorType {
USER_OP_VALIDATOR,
RUNTIME_VALIDATOR
}
enum HookType {
PRE_EXEC_HOOK,
POST_EXEC_HOOK,
PRE_USER_OP_VALIDATION_HOOK,
PRE_RUNTIME_VALIDATION_HOOK
}
struct ExecutionUpdate {
PluginAction action;
address pluginAddress;
bytes4[] executionSelectors;
ValidatorUpdate[] validatorUpdates;
}
struct ValidatorUpdate {
PluginAction action;
ValidatorType validatorType;
FunctionReference functionReference;
}
struct HookGroupUpdate {
PluginAction action;
HookGroupId hookGroupId;
bytes4[] executionSelectors;
}
struct HookUpdate {
PluginAction action;
HookGroupId hookGroupId;
HookType hookType;
FunctionReference functionReference;
}
event ExecutionPluginUpdate(ExecutionUpdate[] executionUpdates);
function updatePlugins(
ExecutionUpdate[] calldata executionUpdates,
HookUpdate[] calldata hookUpdates,
HookGroupUpdate[] calldata hookGroupUpdates,
Execution[] calldata initializationCalls
) external;
}
PluginAction
概要
この列挙型は、プラグインに対するアクションを表します。
追加(ADD
)、置換(REPLACE
)、削除(REMOVE
)の3つのアクションが定義されています。
詳細
プラグインの追加、既存プラグインの置換、プラグインの削除といったアクションを表現するために使用されます。
ValidatorType
概要
この列挙型は、バリデータのタイプを表します。
ユーザーオペレーションバリデータ(USER_OP_VALIDATOR
)とランタイムバリデータ(RUNTIME_VALIDATOR
)の2つのタイプが定義されています。
詳細
ユーザーオペレーションバリデータやランタイムバリデータなど、異なるバリデーションタイプを区別するために使用されます。
HookType
概要
この列挙型は、フックのタイプを表します。
事前実行フック(PRE_EXEC_HOOK
)、事後実行フック(POST_EXEC_HOOK
)、事前ユーザーオペレーション検証フック(PRE_USER_OP_VALIDATION_HOOK
)、事前ランタイム検証フック(PRE_RUNTIME_VALIDATION_HOOK
)の4つのタイプが定義されています。
詳細
異なるフックの種類を区別し、それぞれのフックの役割を表現するために使用されます。
ExecutionUpdate
概要
この構造体は、実行関数のアップデート情報を表します。
詳細
実行関数に対する追加、置換、削除などのアップデート情報が含まれます。
プラグインアクション、プラグインのアドレス、実行関数セレクター、バリデータのアップデート情報などが含まれます。
ValidatorUpdate
概要
この構造体は、バリデータのアップデート情報を表します。
詳細
バリデータの追加、置換、削除などのアップデート情報が含まれます。プラグインアクション、バリデータのタイプ、関数リファレンスなどが含まれます。
HookGroupUpdate
概要
この構造体は、フックグループのアップデート情報を表します。
詳細
フックグループに対する追加、置換、削除などのアップデート情報が含まれます。
プラグインアクション、フックグループの識別子、実行関数セレクターなどが含まれます。
HookUpdate
概要
この構造体は、フックのアップデート情報を表します。
詳細
フックに対する追加、置換、削除などのアップデート情報が含まれます。
プラグインアクション、フックグループの識別子、フックのタイプ、関数リファレンスなどが含まれます。
ExecutionPluginUpdate
概要
実行関数のアップデートを通知するイベント。
詳細
実行関数のアップデートが行われた際に、このイベントがトリガーされます。
実行関数に対するアップデート情報が配列として引数に含まれます。
updatePlugins
概要
プラグインの更新を実行する関数。
詳細
実行関数やフック、初期化コールのアップデートを行うために使用されます。
新しい実行関数やフックの追加、置換、削除などの変更を適用することができます。
引数
関数の引数には、実行関数のアップデート情報、フックのアップデート情報、フックグループのアップデート情報、初期化コール情報が含まれます。
戻り値
なし。
IPluginLoupe.sol
プラグイン検査インターフェースは、モジュラーアカウントがチェーン上でプラグイン構成の可視性をサポートするために実装することができるインターフェースです。
これは、モジュラーアカウントが搭載しているプラグインの設定や構成情報をチェーン上で確認できるようにするための手段です。
プラグインがどのように構成されているか、どの関数がどのプラグインに紐づいているかなどの情報を、チェーン上で透明に閲覧できるようにすることができます。
このインターフェースを実装することで、ユーザーや他のスマートコントラクトがモジュラーアカウントのプラグイン構成に関する情報を簡単に取得し、適切な操作を行うことができるようになります。
これにより、チェーン上での可視性と透明性が向上し、スマートコントラクトの運用が効率的に行えるようになります。
interface IPluginLoupe {
struct ExecutionPluginConfig {
address executionPluginAddress;
HookGroupId[] hookGroupIds;
HookGroup[] hookGroups;
FunctionReference userOpValidator;
FunctionReference runtimeValidator;
}
struct HookGroup {
FunctionReference preUserOpValidation;
FunctionReference preRuntimeValidation;
FunctionReference preExec;
FunctionReference postExec;
}
function getExecutionFunctionConfig(bytes4 executionSelector)
external
view
returns (ExecutionPluginConfig memory);
function getStandardExecutionValidators()
external
view
returns (FunctionReference[] memory userOpValidators, FunctionReference[] memory runtimeValidators);
}
ExecutionPluginConfig
概要
実行プラグインの設定情報を格納する構造体。
実行プラグインのアドレスや関連するフックグループ、バリデータ関数の情報が含まれます。
詳細:
-
executionPluginAddress
- 実行プラグインのアドレスが格納されます。
-
hookGroupIds
- 関連するフックグループのIDが格納された配列。
-
hookGroups
- 関連するフックグループの詳細情報が格納された配列。
-
userOpValidator
- ユーザーオペレーションバリデータ関数の参照が格納されます。
-
runtimeValidator
- ランタイムバリデータ関数の参照が格納されます。
HookGroup
概要
フックグループの設定情報を格納する構造体。
事前実行フックや事後実行フックなどの関数の参照が含まれます。
詳細
-
preUserOpValidation
- 事前ユーザーオペレーション検証フックの関数参照が格納されます。
-
preRuntimeValidation
- 事前ランタイム検証フックの関数参照が格納されます。
-
preExec
- 事前実行フックの関数参照が格納されます。
-
postExec
- 事後実行フックの関数参照が格納されます。
getExecutionFunctionConfig
概要
指定された実行セレクターに対する実行プラグインの設定情報を取得する関数。
引数
-
executionSelector
- 実行セレクター(関数の識別子)。
戻り値
-
ExecutionPluginConfig
- 指定された実行セレクターに対する実行プラグインの設定情報が返されます。
getStandardExecutionValidators
概要
標準的な実行のバリデータ関数の情報を取得する関数。
戻り値
-
userOpValidators
- ユーザーオペレーションバリデータ関数の配列。
-
runtimeValidators
- ランタイムバリデータ関数の配列。
IStandardExecutor.sol
このインターフェースは、モジュラーアカウントが開放的な実行をサポートするために実装する必要があるものです。
つまり、異なる実行関数をサポートし、アカウントの機能を拡張するための仕組みを提供します。
このインターフェースを実装することで、モジュラーアカウントは様々な実行関数を呼び出すことができ、アカウントの機能や動作をカスタマイズできるようになります。
標準実行インターフェースは、モジュラーアカウントがオープンエンドな実行機能を提供し、柔軟性のあるプログラミングやアカウントの振る舞いの変更を容易にするための重要な要素です。
interface IStandardExecutor {
function execute(address target, uint256 value, bytes calldata data, FunctionReference validator)
external
payable;
function executeBatch(Execution[] calldata executions, FunctionReference validator) external payable;
}
execute
概要
指定されたターゲットアドレスに対してトランザクションを送信し、カスタムなロジックを実行する関数。
必要な場合にはバリデータ関数を指定して実行前のチェックを行うことができます。
詳細
この関数は、指定されたターゲットアドレスに向けてトランザクションを送信します。
トランザクションの送信時には、任意の価値(イーサ)やデータも指定することができます。
また、実行前にバリデータ関数を指定することで、実行前のチェックを行うこともできます。
この関数を使用することで、カスタムなロジックを実行することができます。
引数
-
target
- トランザクションを送信する対象のアドレス(スマートコントラクトやEOA)。
-
value
- 送信するイーサの量(価値)。
-
data
- トランザクションに含めるデータ。
-
validator
- 実行前に呼び出されるバリデータ関数のアドレス。
戻り値:
なし。
executeBatch
概要
複数の実行をまとめて一括で実行する関数。
各実行に対してバリデータ関数を指定することができます。
詳細:
この関数は、実行をまとめて一括で実行するためのものです。
executions
という配列で複数の実行情報を受け取り、それぞれの実行に対して必要な情報を使って実行を行います。
各実行に対してバリデータ関数を指定することで、実行前のチェックを行うこともできます。
引数:
-
executions
- 複数の実行情報を持つ
Execution
型の配列。
- 複数の実行情報を持つ
-
validator
- 実行前に呼び出されるバリデータ関数のアドレス。
戻り値:
なし。
期待される振る舞い
updatePlugins関数の呼び出し
-
updatePlugins
関数は、実行アップデート、フックアップデート、フックグループアップデートの情報を受け取り、それらを順番に実行するための関数です。 - オプションで初期化呼び出しの情報も受け取ることができます。
アップデート操作の順次実行
- 受け取った実行アップデート、フックアップデート、フックグループアップデートなどの情報は、順番に実行されます。
- 各アップデート操作は、アカウント内のプラグインの構成を変更する操作です。
初期化呼び出しの実行
- 初期化呼び出しの配列に含まれる各要素に対して、指定されたアドレスとデータを使用して呼び出しを行います。
- これによって、アカウントの初期化やアップデート後の追加処理などを行うことができます。
初期化呼び出しの実行
- 初期化呼び出しの配列に含まれる各要素に対して、指定されたアドレスとデータを使用して呼び出しを行います。
- これによって、アカウントの初期化やアップデート後の追加処理などを行うことができます。
⚠️ 注意
- プラグインの更新は非常に強力な機能です。
-
updatePlugins
関数のセキュリティがアカウント全体のセキュリティに影響を与えるため、適切なセキュリティ対策とアクセス制御が必要です。 - モジュラーアカウントの実装者は、
updatePlugins
関数を適切なセキュリティ考慮とアクセス制御を持つように実装することが重要です。
validateUserOp関数の呼び出し
-
validateUserOp
関数は、エントリーポイントによってモジュラーアカウント上で呼び出される際に使用されます。 - この関数は、ユーザーオペレーションの妥当性を検証するために使用されます。
ユーザーオペレーション検証の手順
- ユーザーオペレーションの
callData
内の最初の4
バイトにあるセレクタ(関数識別子)に対応するユーザーオペレーションバリデータを特定します。 - セレクタに対応するバリデータが定義されていない場合、または
callData
の長さが4
未満の場合、実行はrevert
します。 - もしセレクタに関連するフックグループがあれば、そのフックを順番に実行します。
- いずれかが
revert
した場合、外部呼び出しもrevert
します。
- いずれかが
- フックが
0
または1
以外のauthorizer
値を返した場合、実行はrevert
します。 -
authorizer
値が1
を返し、無効な署名を示す場合、外部呼び出しの戻り値としても1
を返す必要があります。 -
validUntil
またはvalidBefore
値を指定して時間制約の検証が行われた場合、結果の検証データは提供されたすべての時間制約の交差部分となります。
標準実行関数の場合
- もし呼び出し先が標準実行関数である場合、モジュラーアカウントは提供されたバリデータが以前に標準実行関数と関連付けられているかを検証します。
- 関連付けが行われていれば、指定されたユーザーオペレーションバリデータが実行されます。
- ユーザーオペレーションとそのハッシュをパラメータとしてバリデータ関数を呼び出し、その結果の検証データが返されます。
- 必要に応じて、
pre user operation validation
フックの戻り値によってバリデーションデータが更新され、最終的にvalidateUserOp
関数から返されます。
実行関数の呼び出し
モジュラーアカウント(MSCA)において、ネイティブに定義された関数以外が呼び出された場合、対応するセレクタ(関数識別子)に関連するプラグイン設定をupdatePlugins
を通じて見つける必要があります。
対応するプラグインが見つからない場合、アカウントはrevert
(処理を中断)しなければなりません。
それ以外の場合、以下の手順を実行する必要があります。
また、モジュラーアカウントがIPluginUpdateとIStandardExecutor
の関数をネイティブに実装している場合、同じ以下の手順をそれらの関数に対しても実行する必要があります。
その他のネイティブに実装された関数は、これらの手順を実行することがあります。
実行手順は以下になります。
-
もし呼び出し元がエントリーポイントでない場合、関連付けられた
runtime validator
(ランタイムバリデータ)関数を探します。
存在しない場合、実行はrevert
(処理を中断)しなければなりません。
モジュラーアカウントは、すべてのpre runtime validation
フックを実行し、その後、runtime validator
ランタイムバリデータ関数を呼び出します。
これらの関数は、呼び出し元、値、および実行関数のcalldata
をパラメータとして受け取らなければなりません。
いずれかの関数がリバートする場合、実行もリバートします。 -
実行関数に関連するフックグループに
pre execution hook
が定義されている場合、それらのフックを呼び出し、呼び出し元、値、および実行関数のcalldata
をパラメータとして渡します。
これらのフックのうちどれかがデータを返す場合、そのデータはpost execution hook
への呼び出しまで保持されます。
この操作は、call opcode
(コール命令)を用いて行われます。いずれかのフックがrevrt
する場合、実行もrevrt
します。 -
実行関数を実行します。
-
関連する
post execution hook
が定義されている場合、それらの関数を実行します。
同じフックグループ内のpre execution hook
がアカウントにデータを返した場合、そのデータをpost execution hook
のパラメータとして渡す必要があります。
この操作は、call opcode
(コール命令)を用いて行われます。
いずれかのフックがrevrt
する場合、実行もrevrt
します。
⚠️ 注意
もし実行関数にpre runtime validation
フック、pre execution hook
、post execution hook
の定義がない場合、未定義の関数はスキップされます。
実行関数自体は実行され、アカウントの状態が変更される可能性があります。
これによって、モジュラーアカウントが外部からの関数呼び出しを正しく処理し、関連するバリデータやフックを適切に実行することができます。
ただし、これらの手順の正確な実装とセキュリティ対策が重要であることに注意してください。
プラグインの更新操作
updatePlugins
がPluginAction.ADD
として呼び出された場合
以下の操作が行われます。
-
各実行セレクタは、指定された
pluginAddress
に対応するコントラクトを実装コントラクトとして追加されます。
これにより、新たな実行関数が定義され、その実装は指定されたコントラクトになります。 -
各バリデータ関数リファレンスは、指定された
validatorType
に対応する親実行関数に関連付けられます。
これにより、実行関数の呼び出し前にバリデーションが行われ、セキュリティが強化されます。 -
各フックの更新は、指定された
hookTypeをhookGroupId
で指定されたグループに関連付けます。
ただし、すでに同じフックタイプがそのグループに定義されている場合、更新は取り消されます。 -
各フックグループの更新は、指定された
hookGroupUpdates
を対応する実行セレクタに追加します。
これにより、特定の実行関数に対して事前または事後の処理がフックとして追加されます。
実行関数のセレクタは、追加時に一意である必要があります。
これにより、同じ実行セレクタが複数回追加されることはありません。
updatePlugins
がPluginAction.REPLACE
として呼び出された場合
- 各実行セレクタまたは関数リファレンスは、以前の定義を上書きする必要があります。
- また、この操作で指定されていない既存の関連関数は変更してはいけません。
updatePlugins
がPluginAction.REMOVE
として呼び出された場合
- 実行関数の定義および関連する関数の定義は削除される必要があります。
updatePlugins
を呼び出す際には、指定されたバリデータとフックが事前に実行されます。
これにより、アップデート操作の前に必要な確認や処理が行われます。
特に、アップデート前に定義されたpost execution hook
は、アップデート後に実行されます。
これにより、アップデート前の状態から新しい設定に適切に移行できます。
これらの手順により、モジュラーアカウントはプラグインの追加、更新、削除を安全に行い、適切な機能性を保つことができます。
アカウントのセキュリティと操作の一貫性を保つためには、上記の操作を慎重に実装することが重要です。
補足
ERC4337に準拠したアカウントは、バリデーションと実行を1つのvalidateUserOp
メソッドでまとめるIAccount
インターフェースを実装する必要があります。
この提案の主な設計思想の一つは、これらの機能を分離し、アカウントの機能を拡張することです。
同時に、アカウント抽象化の利点を保ちます。
ERC2535の関数ルーティングパターンは、複数の機能を持つアカウントを実現するための論理的な出発点です。
また、他の主な設計思想として、複数の実装コントラクト間で実行呼び出しを一般化することもあります。
しかし、厳密なダイヤモンドパターンは、validateUserOp
のコンテキストで特定の実行関数に対するバリデーションスキームをカスタマイズすることができず、delegatecall
が必要です。
この提案では、ERC4337を基にしており、ERC2535からの着想を取り入れた複数のインターフェースが含まれています。
まず、スマートコントラクト開発者による柔軟なバリデーション、実行、フックのロジックの組み合わせを可能にするモジュラープラグインのセットを標準化しています。
また、IPluginUpdate
やIPluginLoupe
などのインターフェースを提案し、実行関数、バリデーション関数、フックを更新およびクエリするためのメソッドを提供しています。
このようなアプローチにより、スマートコントラクトアカウントの機能を拡張し、柔軟性と効率性を両立させる設計が行われています。
ERC6900の機能
カスタマイズ可能な取引手数料
- 開発者は特定の条件で取引手数料をカスタマイズできます。
- この柔軟性により、よりスムーズな操作が保証され流ことでDAppsを操作するユーザーのコストが削減される可能性があります。
署名検証によるセキュリティの強化
- トランザクションのセキュリティを強化するために署名検証を実装しています。
- ユーザーとDApp間のやり取りの信頼性が高まり、不正アクセスのリスクが大幅に軽減されるようになります。
シームレスな相互運用性
- 他の標準とのシームレスな相互運用性を促進し、多様なブロックチェーンプラットフォームとのスムーズな統合を促進します。
後方互換性
後方互換性の問題は見つかっていません。
参考実装
まだ具体的な参考実装は提供されていない状態です(TBD)。
セキュリティ考慮事項
新しい機能や変更がセキュリティにどのような影響を及ぼす可能性があるかについて議論が必要です。
セキュリティの観点から十分な検討と評価が行われるべきです。
引用
Adam Egyed (@adamegyed), Fangting Liu (@trinity-0111), "ERC-6900: Modular Smart Contract Accounts and Plugins [DRAFT]," Ethereum Improvement Proposals, no. 6900, April 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-6900.
参考
最後に
今回は「モジュール式のスマートコントラクトアカウントとアカウントプラグインのインターフェースを提案しているERC6900」についてまとめてきました!
いかがだったでしょうか?
質問などがある方は以下のTwitterのDMなどからお気軽に質問してください!