[ERC3224] トランザクション署名時にトランザクションの内容を確認する仕組みを理解しよう！

# はじめに

『DApps開発入門』という本や色々記事を書いているかるでねです。

https://amzn.asia/d/gxvJ0Pw

今回は、トランザクション署名時に、実行するトランザクションの内容を表示できる仕組みを提案している**ERC3224**についてまとめていきます！

:::note info
以下にまとめられているものを翻訳・要約・補足しながらまとめていきます。
:::

https://eips.ethereum.org/EIPS/eip-3224

他にも様々なEIPについてまとめています。

https://cardene.notion.site/ERC-EIP-2a03fa3ea33d43baa9ed82288f98d4a9?pvs=4

# 概要

**ERC3224**は、ユーザーがEthereumウォレット（例：MetaMask、Clef、ハードウェアウォレットなど）を通じて実行する操作について、よりわかりやすい説明を提供する仕組みを提案しています。
具体的には、機械が実行可能な操作に対して、人間が読める形で記述した説明文（トランザクションの内容説明）を、高レベルで機械が解釈可能なデータとして提供する方法を示しています。
これにより、ウォレットはユーザーに対して「**これからどのような操作が実行されるか**」を的確に説明できるようになります。

# 動機

現在のEthereumウォレットでは、ユーザーが署名やトランザクションを承認する際に、トランザクションの内容を把握することが難しいです。
**ERC20**トークンの送金など、ごく一部の操作については特別な対応がなされているものの、その他のトランザクションではバイナリデータ（人間には読めない形式）をそのまま表示されるだけのことが多く、ユーザーは中身を理解しないまま「**承認**」するしかない状況です。

**ERC3224**はこの問題を解消する提案をしています。
開発者がトランザクションの説明文を提供することで、ウォレットがその情報を元に「**ユーザーがこれから実行する操作の説明**」をより丁寧に表示できるようになります。
これは悪意のあるコントラクトを防ぐものではありませんが、誠実な開発者がユーザー体験を向上させるための実用的な仕組みとして設計されています。

また、これらの説明文はコントラクトのコードと一緒に監査されることを前提としており、外部の監査人やレビュワーが説明内容の正確性を確認できるようになっています。
これにより、ユーザーの信頼性向上にもつながると期待されています。

# 仕様

## 説明の生成

**ERC3224**では、ユーザーが実行しようとしているトランザクションに対して、その内容を人間が読める形式（説明文）と機械が処理できる形式（described data）の両方を、同時にコントラクト内で生成する仕組みを定義しています。

```js
function eipXXXDescribe(bytes describer_inputs) view returns (string description_string, bytes described_data);
```

この関数は読み取り専用の関数で、ログ出力（`logX`）やストレージの書き換え（`sstore`）が行われません。

また、関数呼び出し時のチェックでは、以下のトランザクションコンテキストが使用される必要があります。

* `ADDRESS`（`to`）
* `CALLER`（`from`）
* `VALUE`（ethの送金額、署名時は常に0）
* `GASPRICE`（ガス価格）

ブロック情報（`BLOCKHASH`, `NUMBER`, `TIMESTAMP`, `DIFFICULTY`）については、なるべく最新のブロックと一致させるべきです。
`COINBASE`（報酬受取アドレス）はゼロアドレスに設定します。

関数が`revert`した場合、説明付きトランザクションの署名は中止されます。

## 新しいJSON-RPCメソッドの導入

プライベートキーを管理するEthereumクライアントは、新しい説明付きインタフェースをサポートする以下のJSON-RPCメソッドを提供する必要があります。
これにより、ユーザーはトランザクションやメッセージの内容を確認した上で署名・送信できるようになります。

なお、ユーザーインターフェースを持たない環境では説明文（description string）は無視され、`described_data` のみが使用されます。

## メッセージへの署名（`eth_signDescribedMessage`）

```json
eth_signDescribedMessage(address, describer, describerInput)
```

このメソッドは、指定された `describer` コントラクトの `eipXXXDescribe` 関数を `describerInput` を使って呼び出し、その結果得られる `description_string` と `described_data` を生成し、署名します。

署名は **EIP191**に従って以下の形式で実行されます。

```
0x19 0x00 DESCRIBER_ADDRESS DESCRIBED_DATA
```

:::note info
**EIP191**については以下の記事を参考にしてください。
:::

https://qiita.com/cardene/items/ee4edd15ad6fe7f58f7e

戻り値には、説明文、署名対象データ、生成された署名が含まれます。

## トランザクションの送信（`eth_sendDescribedTransaction`）

```json
eth_sendDescribedTransaction(address, {
  to: "0x...",
  value: 1234,
  nonce: 42,
  gas: 42000,
  gasPrice: 9000000000,
  describerInput: "0x1234..."
})
```

このメソッドでは、指定された `to` アドレスのコントラクトに `describerInput` を渡して `eipXXXDescribe` を呼び出し、説明文とトランザクションデータを生成します。

ウォレットがUIを持つ場合は、手数料情報や `value` とともに、説明文をユーザーに表示します。
ユーザーが承認すれば、トランザクションが署名・送信されます。
戻り値には、説明文と送信された署名済みトランザクションが含まれます。

## トランザクションの署名のみ（`eth_signDescribedTransaction`）

```json
eth_signDescribedTransaction(address, {
  to: "0x...",
  value: 1234,
  nonce: 42,
  gas: 42000,
  gasPrice: 9000000000,
  describerInput: "0x1234..."
})
```

このメソッドも `eth_sendDescribedTransaction` と同様に `eipXXXDescribe` を呼び出しますが、トランザクションは送信されず署名済みのトランザクションデータのみを返します。
オフライン署名などで使用されます。

## 説明文（Description String）の形式

説明文は**MIMEタイプ + セミコロン（;）**から始める必要があります。
現在は `text/plain` のみが規定されており、例として以下のようになります。

```
text/plain;Transfer 5 tokens to 0xABC...
```

将来的には `text/markdown` のようなリッチな形式や多言語対応形式（`multipart/form-data` 風）も、他のEIPで拡張される可能性があります。
これにより、より直感的でインタラクティブなユーザー体験が実現されることが想定されています。

# 補足

## メタ的な説明手法の必要性

過去にも「**ユーザーにわかりやすい説明を提供する**」ための試みは多く存在しましたが、その多くはトランザクションやメッセージのエンコード済みデータそのものを解析*ようとするものでした。

しかし、トランザクションデータの中には、人間が理解するのに必要な情報が含まれていない場合があるため、それだけでは十分な説明を提供することができません。

例えば、ENSの `commit(bytes32)` のような関数では、コントラクトに渡されるのはすでにハッシュ化されたデータであり、その中には `name` や `address` などの元情報が含まれていません。
このようなブラインド化された情報に対しては、直接的なデータ解析による説明生成は不可能です。

**ERC3224**はこの限界を避けるために、以下のような間接的な説明方式を採用しています。

* 元データ
    * `name`, `address`, `secret`
* 説明
    * `"NAME を ADDRESS に対して commit（SECRETを使用）"`
* 実行データ
    * `commit(hash(name, address, secret))`

上記のように元データに基づいて人間にわかる説明を生成しつつ、同時にEVM実行に必要なデータ（ハッシュ化されたデータ）も計算するという仕組みです。

## 署名にコントラクトアドレスを含める

**ERC3224**では、異なるコントラクト間でデータが使い回されるリスクを避けるため、署名対象のデータにコントラクトアドレスを含める設計になっています。

* トランザクションにおいては `to` フィールドによって暗黙的にコントラクトアドレスが含まれます。
* メッセージにおいては、**EIP191**の仕様に従って、バージョン固有のデータとしてコントラクトアドレスが含まれます。

これにより、1つのコントラクト用に生成したデータや署名が、他のコントラクトに流用されることを防ぐことができます。
ゼロアドレス（`0x000...0`）は特別な用途のため予約されています。

## 他の方式との比較

**ERC3224**で採用された方法は、既存の手法に比べて以下のような利点があります。

### NatSpecなどの高機能言語との比較

NatSpec（Ethereum Natural Specification Format）は、トランザクション内容を自然言語で記述するためのフォーマットですが、以下のような問題があります。

* フル機能を使うにはランタイム環境が必要であり、ハードウェアウォレットなどリソース制限のある環境では実行が困難
* データを直接記述するため、ブラインド化されたトランザクションには対応できない

**ERC3224**は、EVMのみで説明生成を完結できるよう設計されており、より軽量で安全な実装が可能です。

### カスタム言語の提案との比較

Ethereumトランザクションの複雑性を考慮すると、新たな言語を設計する場合、多大な表現力が求められて再発明になる可能性があります。
EVM自体がすでに存在し、全ての処理に対応できるため、既存のEVMを活用する方が合理的です。

### フォーマット文字列との比較

過去に試みられた「**信頼性のある署名UIプロトコル**」などでは、フォーマット文字列を使って説明を生成しようとしましたが、これは正規言語にしか対応できないため、Ethereumのような複雑な構造をもつデータには表現力が不十分でした。

## 類似提案との関係（EIP712）

**ERC3224**は、署名付き構造化データを扱う**EIP712**と目的が重なる部分があります。
ただし、**EIP712**がデータ構造の明示的な記述を前提としているのに対し、この**ERC3224**では動的に生成されるデータと説明をEVM内で同時に扱う点で異なります。
より柔軟で拡張性のある設計となっており、複雑なトランザクションの扱いにも対応可能です。

:::note info
**EIP712**については以下の記事を参考にしてください。
:::

https://qiita.com/cardene/items/f54bc85cb66de5a54257

# セキュリティ

## テキストのエスケープ処理

ウォレットがコントラクトから提供されたテキスト（説明文）を表示する時には、必ず内容をサニタイズする必要があります。
この処理をしないと、ユーザーを騙したりセキュリティ上のリスクを引き起こす可能性があります。

注意すべきポイントは以下のとおりです。

* **HTMLタグの埋め込み**

 例えば、`<script>` タグを通じてコードが実行され、秘密鍵が外部に送信される可能性があります。

* **視覚的な偽装**

例として `"<span style='display:none'>not-</span>ricmoo.eth"` のような文字列は、見た目には `ricmoo.eth` と表示されてしまい本物と区別できなくなります。

* **特殊文字の混入**

クォート（"）、バックスラッシュ（\）、改行（`\n`）、タブ（`\t`）、その他の特殊な空白文字などが含まれると、表示やパースに支障が出る恐れがあります。

* **UTF-8の問題**

過去にはUTF-8の処理に関してレンダラがクラッシュしたり、任意コードが実行されるバグが報告されています。
一般的でないコードポイントは、代替文字（例：�）に置き換えることが推奨されます。

* **ホモグリフ攻撃**

見た目が似ているが異なる文字（例：ラテン文字の「o」とキリル文字の「о」）を使うことで、偽装が可能です。

* **右から左への制御文字**

表示の順序を操作してユーザーを混乱させる可能性があります。

* **その他の環境依存の問題**

表示する環境によって異なる脅威が存在します。

## 署名データの識別性

**ERC3224**を実装するアプリケーションは、署名するデータに曖昧さが生じないように注意する必要があります。

データの衝突（例えば、異なる意味を持つ2つのデータが同一の署名対象になるケース）を防ぐため、適切なエンコーディング（ABIエンコードなど）や構造化を行うことが求められます。

## 列挙攻撃への対策

署名が中断された場合と、ユーザーが署名を拒否した場合のレスポンスは区別されないようにすべきです。
そうでないと、攻撃者がレスポンス内容から署名の成否を推測できてしまいます。

さらに、レスポンスの時間に差があると（例：10ms以内に返答されると失敗だと分かる）、タイミング情報を使って列挙攻撃が可能になるため、応答時間にランダムな遅延を加えることが推奨されます。

:::note info
列挙攻撃とは、攻撃者がシステムからの応答の違いや時間差を利用して、内部の情報を少しずつ推測していく攻撃手法です。
:::

## 署名データの再利用（リプレイ）への対策

トランザクションには `nonce`（連番）が含まれているため、同じ内容を複数回使うことはできませんが、メッセージ署名には`nonce`がありません。

そのため、ログインなどの目的で署名を使う場合には、署名データに `block.timestamp` のような一時的な値を挿入してnonceの代わりとすることが重要です。
このタイムスタンプは通常、内部処理のために使われるものであり、説明文には含めなくても問題ありません。

メッセージ署名では再利用防止のために`nonce`を含めることが推奨されます。

# 引用

Richard Moore (@ricmoo), Nick Johnson (@arachnid), "ERC-3224: Described Data [DRAFT]," Ethereum Improvement Proposals, no. 3224, January 2021. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-3224.

# 最後に

今回は「トランザクション署名時に、実行するトランザクションの内容を表示できる仕組みを提案している**ERC3224**」についてまとめてきました！
いかがだったでしょうか？

質問などがある方は以下のTwitterのDMなどからお気軽に質問してください！

[Twitter @cardene777](https://twitter.com/cardene777)

他の媒体でも情報発信しているのでぜひ他も見ていってください！

https://chaldene.net/

https://zenn.dev/heku

https://cardene.substack.com/

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58

https://cardene.notion.site/ERC-EIP-2a03fa3ea33d43baa9ed82288f98d4a9?pvs=4