はじめに
『DApps開発入門』という本や色々記事を書いているかるでねです。
今回は、アカウントアブストラクション向けにEIP712を拡張する仕組みを提案しているERC7803についてまとめていきます!
以下にまとめられているものを翻訳・要約・補足しながらまとめていきます。
他にも様々なEIPについてまとめています。
概要
ERC7803は、EIP712の署名方式を改良してコントラクトアカウントに適した形にすること仕組みを提案しています。
EIP712については以下の記事を参考にしてください。
具体的には、以下の2つの改善点を提案しています。
1. 署名ドメインの導入によって、秘密鍵が複数のアカウントで共有される場合のリプレイ攻撃(過去の署名を悪用して不正に取引を行う攻撃)を防ぐ。
2. 認証方法の調整を可能にし、dAppsやウォレットが署名の検証方法を調整できるようにする。
この改善により、コントラクトアカウントの安全性を高めてより柔軟に運用できるようになります。
動機
署名ドメインとは
ERC1271やERC6492といった既存の規格では、コントラクトアカウントが署名を生成してdApps側でその署名を検証できるようになっています。
これにより、アカウントの所有者はブロックチェーンにトランザクションを送付することなく、第三者に対して特定の操作を許可できます。
ERC1271については以下の記事を参考にしてください。
ERC6492については以下の記事を参考にしてください。
しかし、コントラクトアカウントの中には、複数のアカウントを1つの秘密鍵で管理している場合があります
この場合、あるアカウントでの署名が別のアカウントでも有効になってしまう危険性があります。
これは「リプレイ攻撃」と呼ばれるもので、過去に発行された署名を流用することで、不正な取引が行われます。
EIP712では、署名を特定の「検証ドメイン」に紐づけることで、この問題を部分的に解決しようとしました。
例えば、「この署名は特定のコントラクトでしか有効にならない」といった制約を設ける仕組みです。
しかし、この方式をコントラクトアカウントにそのまま適用すると、以下のような問題が発生します。
-
複雑になりすぎる
- コントラクトアカウントは他のコントラクトアカウントを管理する場合があるため、検証ドメインの設定が難しくなる。
-
攻撃のリスクが増える
- コントラクトアカウントは複数の役割を持つことがあるため、不適切な設定が悪用される可能性がある。
そこで、ERC7803では新しく「署名ドメイン」を導入して署名が「どのアカウントによるものか」を明確にします。
こうすることで、リプレイ攻撃を防ぎつつ、コントラクトアカウントの柔軟性を維持することができます。
認証方法の課題と改善
ERC1271は、コントラクトアカウントの署名を検証するためのインターフェースとして機能しています。
この規格により、署名の検証ルールを理解していなくても、コントラクトアカウントの署名を認証できるようになっています。
ただし、ERC1271には「コントラクトがすでにデプロイされている必要がある」という制約がありました。
そこで、ERC6492が登場し、まだデプロイされていないコントラクトの署名も検証できるようになりました。
しかし、現在の状況では、プロトコルごとに異なる認証方法が採用されており、統一されていないため以下のような問題があります。
-
ERC1271の使われ方がバラバラ
- あるプロトコルでは、まずECRECOVER(署名を検証するための関数)を実行し、次に
isValidSignature
を呼び出すのに対し、別のプロトコルではその逆の順番で実行する。
- あるプロトコルでは、まずECRECOVER(署名を検証するための関数)を実行し、次に
-
EIP7702の影響
- EIP7702が導入された場合、署名の検証順序の違いによってプロトコル間で大きく異なる結果が生まれる可能性がある。
EIP7702については以下の記事を参考にしてください。
ERC7803では、dAppsがどの認証方法をサポートしているのかを示す仕組みを導入します。
つまり、どの方式で署名を検証するのか、またその順序を調整できるようにすることで、コントラクトアカウントとプロトコルの間の互換性を向上させます。
これにより、Account Abstractionに関する課題の解決につながると考えられています。
仕様
ERC7803では、JSON-RPC(eth_signTypedData
)やクライアントライブラリでの型付きデータの署名リクエストを拡張し、新しいオプションのプロパティとしてsigningDomains
とauthMethods
を追加しています。
これにより、コントラクトアカウントの署名がより安全になり、さまざまな認証方法への対応が柔軟になります。
signingDomains(署名ドメイン)
signingDomains
は、コントラクトアカウントがどのような階層構造になっているかを示す情報であり、リプレイ攻撃を防ぐために導入されました。
これは、コントラクトアカウントが多層構造になっている場合でも、署名の意図を明確にして不正利用を防ぐことができます。
このプロパティは配列であり、各要素は以下の2つのキーを持つオブジェクトです。
-
types
- EIP712で定義された「型情報」と同じ形式で表されます。
- 少なくとも「EIP712Domain」キーが含まれている必要があります。
-
domain
-
EIP712Domain
型に基づいたドメイン情報を持つオブジェクトです。
-
signingDomains
の配列は、右から左の順(先頭から末尾)に、どのアカウントを経由して署名されているかを示します。
これは、あるアカウントが別のアカウントを管理している場合に重要になります。
具体的な流れを見てみます。
- dAppが接続されているアカウントにEIP712署名をリクエストします。
- 最初は
signingDomains
が空です。
- 最初は
- 接続されたアカウントがマルチシグの場合、マルチシグアカウントは署名者に対してEIP712署名をリクエストします。
- このとき、マルチシグアカウントのドメイン情報を
signingDomains
に追加し、配列の長さは1になります。
- このとき、マルチシグアカウントのドメイン情報を
- 署名者の一人がコントラクトアカウントを使用している場合、アカウントはハードウェアウォレットに署名をリクエストします。
- このとき、SCAのドメイン情報を
signingDomains
に追加し、配列の長さは2になります。
- このとき、SCAのドメイン情報を
- 署名者は、ハードウェアウォレットで署名する時に、署名の内容を確認できます。
- このとき、dAppのドメイン情報、コントラクトアカウントのドメイン情報、マルチシグのドメイン情報が正しく表示されるため、署名の目的を明確に理解できます。
このように、signingDomains
を活用することで、署名の意図を明確にして不正利用を防ぐことができます。
署名データのエンコード方法
signingDomainsが存在する場合、署名するデータのエンコードは以下のような再帰的な手順で行われます。
- signingDomainSeparators(署名ドメインのハッシュ配列)が空でない場合、最初のハッシュ(first)をメッセージの前に追加し、残りのドメインについても同じ処理を繰り返します。
- signingDomainSeparatorsが空になったら、通常のEIP-712のエンコード方法でメッセージをエンコードします。
このエンコード方式により、署名がどのドメインに属しているのかを明確にすることができ、異なるドメインでのリプレイ攻撃を防ぐことができます。
authMethods
authMethods
は、コントラクトアカウントがどのような方法で署名を検証するのかを明示するプロパティです。
これにより、異なるプロトコル間での互換性を高め、認証の失敗を防ぐことができます。
このプロパティは配列であり、各要素は以下のキーを持つオブジェクトです。
id
認証方法を識別する文字列。
例えば、以下のような値が入ります。
-
ECDSA
- EOAによるECDSA署名。
-
ERC-{n}
- ERC規格に基づく署名方法。
-
n
はゼロ埋めなしの数字で、特定のERC規格に従います。 - 例えば、ERC1271なら「ERC1271」となります。
-
parameters
(オプション)
追加のパラメータを指定できる配列です。
具体的な認証方式に応じて異なる値が入ります。
authMethods
の配列の順序は、署名の検証がどの順番で行われるかを示します。
例えば、あるコントラクトアカウントが「最初にECDSA署名を検証し、その後ERC1271の署名を試す」といった設定が可能になります。
JSONスキーマ
{
type: 'object',
properties: {
types: {$ref: '#/$defs/EIP712Types'},
primaryType: {type: 'string'},
domain: {type: 'object'},
message: {type: 'object'},
signingDomains: {
type: 'array',
items: {$ref: '#/$defs/EIP712Types'}
}
authMethods: {
type: 'array',
items: {
type: 'object',
id: {type: 'string'},
parameters: {type: 'array'},
required: ['id'],
},
}
},
required: ['types', 'primaryType', 'domain', 'message'],
$defs: {
EIP712Types: {
type: 'object',
properties: {
EIP712Domain: {type: 'array'},
},
additionalProperties: {
type: 'array',
items: {
type: 'object',
properties: {
name: {type: 'string'},
type: {type: 'string'}
},
required: ['name', 'type']
}
},
required: ['EIP712Domain']
}
}
}
このJSON Schemaは、EIP712の署名リクエストに関するデータ構造を定義したものです。
コントラクトアカウントが関与する場合の署名検証を強化するため、signingDomains
とauthMethods
という2つのプロパティが追加されています。
これにより、署名の安全性を向上させるだけでなく、異なるウォレットやプロトコル間の互換性を確保することができます。
このスキーマは、JSONデータが正しく構造化されているかを検証するためのルールを定めており、署名リクエストがこの形式に従っていることを保証する役割を持ちます。
データ構造の詳細
このスキーマはオブジェクト型(type: 'object')として定義されており、以下のプロパティを持っています。
types
このプロパティはEIP712Types
を参照するオブジェクトです。
EIP712の型定義を含み、署名するデータの構造を記述します。
例えば、署名対象のデータが「名前」と「アドレス」を含む場合、その型情報をここに定義します。
primaryType
署名対象のデータの主な型を示す文字列です。
EIP712では、複数の型が定義されることがありますが、その中でも特に「主要な型」として扱うべきものを指定します。
例えば、コントラクトの取引であれば「Transaction」などが指定されることが一般的です。
domain
EIP712で定義される「ドメイン情報」です。
これは、署名の有効範囲を制限するために使用されます。
例えば、特定のコントラクトアプリケーションのみが署名を検証できるようにするために、アプリケーションの名前やバージョン、チェーンID(Ethereumのネットワーク識別子)などを指定できます。
message
署名する実際のデータを格納するオブジェクトです。
例えば、トークン送信のトランザクション情報やウォレットがユーザーに表示するメッセージの内容がここに入ります。
signingDomains
これはオプションのプロパティで、コントラクトアカウントの階層構造を定義する配列です。
各要素はEIP712Types
を参照するオブジェクトになっています。
この配列の役割は、署名の意図を明確にしてリプレイ攻撃を防ぐことです。
例えば、マルチシグや、コントラクトによる代理署名が絡む場合、それぞれのドメインをこの配列に追加することで「どのレイヤーのアカウントが署名を求めているのか」を明示することができます。
authMethods
こちらもオプションのプロパティで、署名の認証方法を定義する配列です。
各要素はオブジェクトで、以下のキーを持ちます。
-
id
- 認証方法を示す文字列です。
- 例えば、"ECDSA"や、"ERC-1271"(コントラクトアカウント向けの署名認証)が指定されます。
-
parameters
- 認証方法に応じた追加のパラメータを格納する配列です(オプション)。
-
required: ['id']
-
id
キーは必須であることを示しています。
-
このプロパティがあることで、ウォレットやdAppはどの認証方法を使って署名を検証すべきかを明確にすることができ、互換性の問題を防ぐことができます。
$defs
の役割
スキーマ内の $defs
には EIP712Types
というオブジェクトが定義されています。
これは、EIP712の型情報を表す共通の構造を定義するものです。
EIP712Types
このオブジェクトには、EIP712で必要とされる「型情報」の定義が含まれます。
-
EIP712Domain
- 必須の配列で、EIP712のドメイン情報を定義します。
-
additionalProperties
- 型情報を記述する配列です。
- 各要素は、型の名称(
name
)とその型(type
)を含むオブジェクトで、型情報の仕様に従う必要があります。
例えば、以下のようなデータがこのスキーマに適合する形になります。
{
"types": {
"EIP712Domain": [
{ "name": "name", "type": "string" },
{ "name": "version", "type": "string" },
{ "name": "chainId", "type": "uint256" },
{ "name": "verifyingContract", "type": "address" }
],
"Transaction": [
{ "name": "to", "type": "address" },
{ "name": "value", "type": "uint256" },
{ "name": "data", "type": "bytes" }
]
},
"primaryType": "Transaction",
"domain": {
"name": "MyDapp",
"version": "1",
"chainId": 1,
"verifyingContract": "0x1234567890abcdef1234567890abcdef12345678"
},
"message": {
"to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef",
"value": 1000,
"data": "0x"
},
"signingDomains": [],
"authMethods": [
{ "id": "ECDSA" },
{ "id": "ERC-1271" }
]
}
このような形式でデータを構造化することで、ウォレットやdAppはコントラクトアカウントの署名をより安全に扱うことができ、異なる認証方式にも対応しやすくなります。
引用
Francisco Giordano (@frangio), "ERC-7803: EIP-712 Extensions for Account Abstraction [DRAFT]," Ethereum Improvement Proposals, no. 7803, October 2024. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7803.
最後に
今回は「アカウントアブストラクション向けにEIP712を拡張する仕組みを提案しているERC7803」についてまとめてきました!
いかがだったでしょうか?
質問などがある方は以下のTwitterのDMなどからお気軽に質問してください!
他の媒体でも情報発信しているのでぜひ他も見ていってください!