[ERC5732] commit-revealスキームをサポートするcommitインターフェースの仕組みを理解しよう！

# はじめに

初めまして。
**CryptoGames**というブロックチェーンゲーム企業でエンジニアをしている **cardene（かるでね）** です！
スマートコントラクトを書いたり、フロントエンド・バックエンド・インフラと幅広く触れています。

https://cryptogames.co.jp/

代表的なゲームは**クリプトスペルズ**というブロックチェーンゲームです。

https://cryptospells.jp/

今回は、commit-revealスキームをサポートするシンプルなcommitインターフェースの仕組みを提案している規格である**ERC5732**についてまとめていきます！

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

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

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

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

# 概要

このコントラクトのインターフェースは、ブロックチェーン上で特定の情報を隠しながら、後でそれを明らかにするための方法を提供します。
これは「**コミット-リベール方式**」と呼ばれ、特に投票や資産の移転などのシーンで使われます。

:::note info
**コミット-リベール方式**

クラスで秘密の投票をすることになりました。
皆が自分の投票を他の人に知られたくないと思っています。
ここでコミット-リベール方式が役立ちます。

### コミットの段階
まず、各生徒は自分の投票内容を小さな紙に書きます。
でも、ただ書くだけではなく、紙を折って封筒に入れます。
これで、自分の投票内容を秘密に保ちながら、先生に渡すことができます。
ブロックチェーンで言うところの「**コミット**」は、この封筒に投票内容を入れて秘密にすることに相当します。

### リベールの段階
投票が全員分終わったら、先生はその封筒を開けて、中の投票内容を数えます。
この時点で初めて、各生徒が何に投票したかが明らかになります。
ブロックチェーンにおける「**リベール**」は、この封筒を開けて内容を公開することに当たります。

この方式の利点は、投票が進行している間、誰も他人の投票内容を知ることができないため、公平な投票が保証されることです。
また、誰かが不正をしようとしても、内容が隠されているので、どの投票を変えれば自分に有利になるか分かりません。

ブロックチェーンでは、これと同じ原理を使って、投票や他の重要な情報を安全に扱います。
この方法により、情報が公開されるまで秘密が保たれ、公正で透明なプロセスが実現されるのです。
:::

このインターフェースには`commit`というメソッドがありますが、`reveal`（リベール、つまり情報を明らかにするメソッド）はありません。
なぜなら、`reveal`の方法はさまざまで、それをインターフェースに組み込むと柔軟性が失われるからです。
そのため、`commit`メソッドを使って情報を隠し、後で異なる方法（例えば投票や移転など）で情報を明らかにすることが可能になります。

:::note info
このインターフェースは、ブロックチェーン上で情報を一時的に「**封印**」する役割を持ちます。
この封印を解く方法は多様で、異なるシナリオや要件に合わせて変更できます。
これにより、秘密を保ちつつ、必要なときに情報を公開することが可能になるのです。
:::

# 動機

「**コミット-リベール**」方式は、ブロックチェーン上での投票や他のアプリケーションにおけるプライバシー保護のために使用されます。
この方式では、まずユーザーが重要な情報（例えば投票内容）を暗号化して、それをブロックチェーン上のコントラクトに送信します。
これが「**コミット**」と呼ばれる行為です。
この段階では、送信された情報は他人には見えない状態です。

その後、定められた時期になったら、この暗号化された情報を解読して公開します。
これが「**リベール**」、つまり情報公開の行為です。

この方法の利点は、特にフロントランニング、バックランニング、サンドイッチアタックといった攻撃を防ぐことです。
これらの攻撃は、ブロックチェーン上の取引の順序を悪用して不正な利益を得るものです。
しかし、コミット-リベール方式を使うと、攻撃者はコミットされた情報が何であるかを知ることができないため、こうした攻撃を行うのが難しくなります。

:::note info
この方式はブロックチェーン上の取引をより安全にし、ユーザーの情報を守りながら、攻撃者による不正行為を防ぐのに役立ちます。
:::

:::note info
**フロントランニング**
ブロックチェーンの世界で、ある人が大きな取引をしようとしているのを別の人が知ってしまい、その人が先に同じ取引をすることです。
この方法で、先に取引をした人は、後で取引価格が変動することを利用して利益を得ます。

**バックランニング**
これは、ある人が大きな取引をした直後に、別の人が同じような取引をすることです。
この行為により、最初の取引の影響を利用して、後に取引をする人が利益を得ます。

**サンドイッチアタック**
これは、ある人の取引を、別の人が前後から挟むように取引をすることです。
つまり、最初の取引の直前に取引を行い、その取引が終わった直後にもう一度取引を行います。
これにより、中に挟まれた取引は不利な価格で行われることがあり、挟んだ人が利益を得ます。

これらの行為は、ブロックチェーンの取引で見られる不正な行動の例で、公平な取引を妨げるものとされています。
:::

# 仕様

```javascript
pragma solidity >=0.7.0 <0.9.0;

// The EIP-165 identifier of this interface is 0xf14fcbc8
interface IERC_COMMIT_CORE {
    function commit(bytes32 _commitment) payable external;
}

pragma solidity >=0.7.0 <0.9.0;

// The EIP-165 identifier of this interface is 0x67b2ec2c
interface IERC_COMMIT_GENERAL {
    event Commit(
        uint256 indexed _timePoint,
        address indexed _from,
        bytes32 indexed _commitment,
        bytes _extraData);
    function commitFrom(
        address _from,
        bytes32 _commitment,
        bytes calldata _extraData)
    payable external returns(uint256 timePoint);
}
```

この仕様では、ブロックチェーンのコントラクトで使用される２つのインターフェース、`IERC_COMMIT_CORE`と`IERC_COMMIT_GENERAL`について説明されています。

- `IERC_COMMIT_CORE`インターフェース
    - このインターフェースには`commit`という関数があります。
    - これは、ブロックチェーン上で何か秘密のデータを隠す（コミットする）ために使われます。
    - この関数は外部からアクセス可能で、支払いが伴うことがあります。

- `IERC_COMMIT_GENERAL`インターフェース
    - こちらには`Commit`イベントと`commitFrom`関数が含まれています。
    - `commitFrom`関数を使うと、別のアドレスからデータをコミットすることができ、追加情報も含めることが可能です。
    - この関数はコミットが成功したことを`Commit`イベントで知らせます。

適合するコントラクトは、必ず`IERC_COMMIT_CORE`インターフェースを実装しなければなりません。
また、`IERC_COMMIT_GENERAL`インターフェースの実装も推奨されています。
`IERC_COMMIT_GENERAL`を実装する場合、`commit`関数は`commitFrom`の簡単なバージョンとして機能します。

`commitFrom`関数の戻り値`timePoint`は、コミットメントの順序を示すために、ブロックのタイムスタンプや番号などを使うことが推奨されています。

`extraData`は将来の機能拡張のために予約されていて、`_from`がトランザクション署名者と異なる場合は、署名を検証することが推奨されています。
これは、EOAであればECDSA署名の検証、スマートコントラクトアカウントであれば**EIP1271**を使用した署名の検証を意味します。

:::note info
**ERC1271**については以下を参考にしてください。
:::

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

リベール（秘密を公開する行為）のためには、`secret_salt`という追加フィールドを供給する方法が必要ですが、その詳細は意図的に明記されていません。

また、適合するコントラクトは**EIP165**の実装が推奨されています。
これらのインターフェースと機能は、ブロックチェーン上でのコミットとリベールをより安全かつ効率的に行うために設計されています。

:::note info
**ERC165**については以下を参考にしてください。
:::

https://qiita.com/cardene/items/541184977a5cba51b759

# 補足

この提案では、ブロックチェーンのコントラクトで使われるコミット-リベール方式をサポートするための設計選択について話しています。

### コミットインターフェースの汎用性
**ERC20**や**ERC721**などの様々な基準に、コミット機能を追加することができますが、より簡単で広く使えるコミットインターフェースを作ることにしました。
これにより、どんなERC基準も、基本的な方法を変えずにコミット-リベールをサポートできるようになります。

:::note info
**ERC20**については以下を参考にしてください。
:::

https://chaldene.net/erc20

:::note info
**ERC721**については以下を参考にしてください。
:::

https://chaldene.net/erc721

### コミットメソッドの標準化
コミット機能は標準化されますが、リベール（情報を公開する方法）は標準化せず、カスタマイズできるようにしました。
これにより、様々な既存の基準と組み合わせてコミットを使うことができます。

### シンプルなコアインターフェース
コアインターフェースには、単純な`commit`メソッドが含まれています。
これは以前からあるシステム（例えばENS）とも互換性があります。

### `commitFrom`メソッドの追加
オフチェーン（ブロックチェーン外）で作られたコミットを、他のアカウントが代わりに提出できるようにするために、`commitFrom`メソッドを追加しました。

:::note info
このコントラクトの設計は、既存の**ERC**基準とも上手く合い、さまざまなブロックチェーンアプリケーションでコミット-リベール方式を使いやすくするためのものです。
これにより、様々なアプリケーションで安全かつ効率的にこの方式を活用することができます。
:::

# 後方互換性

このEIP（イーサリアム改善提案）は、既存の**ERC**基準との互換性に関するものです。

### 既存の**ERC**との互換性
このEIPは、`extraData`という追加データを持つ、今までの**ERC**基準のメソッド（関数）と互換性があります。
新しいEIPを作る時に、「`salt`」というフィールドを加えると、このEIPのサポートがしやすくなりますが、これは必須ではありません。

### `IERC_COMMIT_CORE`の互換性
`IERC_COMMIT_CORE`というインターフェースは、ENS（イーサリアム名前サービス）や他の既存技術と互換性があります。

:::note info
このEIPは新しい機能を加えつつも、既にある**ERC**基準やENSなどのシステムと問題なく連携できるように設計されています。
つまり、新しいブロックチェーンの機能を追加しても、既存のアプリケーションやサービスに影響を与えずに済むということです。
:::

# 参考実装

## ENSでコミットする

イーサリアム名前サービス（ENS）で、ドメイン名を登録する際に、特定のコントラクト（`ETHRegistrarController`）内で`commit`という関数が使われています。
これは、他の人が登録者の意向を先読みして横取りする（フロントランニング）のを防ぐためです。

現在のENSの登録方法では、`commit`関数によって秘密の情報（`commitment`）が送られ、一定期間が過ぎていないことを確認した後に、その情報と現在の時刻が紐付けられて保存されます。

```javascript
function commit(bytes32 commitment) public {
    require(commitments[commitment] + maxCommitmentAge < now);
    commitments[commitment] = now;
}
```

提案されているこのEIPにより、ENSの`commit`関数は次のように変更されます。

```javascript
function commit(bytes32 commitment, bytes calldata data) public {
    require(commitments[commitment] + maxCommitmentAge < now);
    commitments[commitment] = now;
    emit Commit(...);
}
```

この変更により、追加のデータ（`data`）を取り入れ、`Commit`イベントを発行することができるようになります。
これによって、ENSのドメイン登録プロセスがさらに透明かつ効率的になることが期待されています。

# セキュリティ考慮事項

### デモ用の実装について 
提供されているコントラクトのコードは、実際の運用環境ではなく、デモンストレーション（実演）用です。
本番環境での使用には向いていません。

### リベールのための秘密保持
リベール（情報を公開する行為）するためのトランザクションやパラメータ、特に`secret_salt`（秘密の塩）は、公開するまで秘密にしておく必要があります。

### `secret_salt`の安全性
`secret_salt`は十分な長さがあり、安全に生成されたランダムな値からなる必要があります。
一度使用した`secret_salt`は再利用してはいけません。
クライアントアプリケーションは、ユーザーが再利用しようとした場合に警告するべきです。

### コントラクトの安全な実装
コントラクトでは、再生攻撃やリエントリ攻撃のリスクを減らすために、特定の送信者のコミットメントをすぐに削除することが推奨されます。
また、リベールトランザクションの順序に関する制約を設けることもできます。

### チェーン間のリプレイ攻撃対策
異なるチェーンIDを持つブロックチェーンやフォークされたチェーンでのリプレイ攻撃のリスクに対処するため、コミットメントを生成する際にはチェーンIDを含めるべきです。
リプレイ攻撃のリスクが高いアプリケーションでは、**EIP712**のような安全なソリューションを採用することを検討すべきです。

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

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

### フロントランニング攻撃対策のための時間差
フロントランニング攻撃を避けるためには、適切な時間差を設定することが推奨されます。
さらに、同じブロック内のトランザクションで`_timePoint`が同じにならないように、厳格な順序を確保するための追加措置が必要です。

これらのポイントは、コントラクトの安全性を確保し、効果的なコミット-リベール方式を実装するために重要です。

# 引用

Zainan Victor Zhou (@xinbenlv), Matt Stam (@mattstam), "ERC-5732: Commit Interface," Ethereum Improvement Proposals, no. 5732, September 2022. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-5732.

# 最後に

今回は「commit-revealスキームをサポートするシンプルなcommitインターフェースの仕組みを提案している規格である**ERC5732**」についてまとめてきました！
いかがだったでしょうか？

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

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

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

https://chaldene.net/

https://zenn.dev/heku

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58


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