[ERC6538] 匿名取引を可能にするステルスアドレスを管理するレジストリの仕組みを理解しよう！

# はじめに

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

https://cryptogames.co.jp/

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

https://cryptospells.jp/

今回は、アドレスをステルスメタアドレスに紐づけ、管理するレジストリの仕組みを提案している規格である**ERC6538**についてまとめていきます！

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

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

:::note warn
**6538**は現在（2024年1月7日）では「**Stagnant**」段階です。
:::

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

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

# 概要

この規格は、エンティティが自分の身元や取引を秘密に保ちながら活動するための特別なアドレス、つまりステルスメタアドレスを管理する方法を標準化しています。
これは**ERC5564**というイーサリアムの拡張を通じて行われます。

ステルスメタアドレスは、個人や組織が他人に気づかれずに取引を行うための隠れたアドレスです。
これにより、ユーザーは自分のアイデンティティを隠しながら、安全に取引やコントラクトの操作ができるようになります。

**ERC5564**は、イーサリアムの基本的なコントラクトの規格に新しい機能や役割（ロール）を追加することで、ステルスメタアドレスのより効率的かつ安全な管理を可能にします。
これにより、ユーザーは自分のプライバシーを保ちつつ、イーサリアム上でより自由に活動できるようになります。

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

https://qiita.com/cardene/items/115df838afdb3f582731

# 動機

ステルスアドレスの標準化によって、イーサリアムでの取引の時に相手に自分の正体を明かさずに資産を受け取れるようになります。
これはまるで、誰にも知られずに手紙を受け取るようなものです。
各ユーザーは、自分専用の隠れた住所（ステルスメタアドレス）を中央のスマートコントラクトに保存します。
これにより、個人やコントラクトが、様々な種類の隠れた住所を使って、隠れた状態で他者とやり取り（ステルスな相互作用）を行うことが可能になります。
このシステムは、取引のプライバシーを大きく向上させ、より安全なイーサリアム環境を実現します。

# 仕様

このコントラクトは、エンティティが自分だけの秘密のアドレス（ステルスメタアドレス）を安全に保存できる場所、つまり**ERC5564Registry**を作ります。
この秘密のアドレスは、アドレスやENS名のような識別子でエンティティを見つけることができます。
重要なのは、このコントラクトは各チェーンに1つだけ存在し、他のどこにも複製されないということです。

```javascript
pragma solidity ^0.8.17;

/// @notice Registry to map an address or other identifier to its stealth meta-address.
contract ERC5564Registry {
  /// @dev Emitted when a registrant updates their stealth meta-address.
  event StealthMetaAddressSet(
    bytes indexed registrant, uint256 indexed scheme, bytes stealthMetaAddress
  );

  /// @notice Maps a registrant's identifier to the scheme to the stealth meta-address.
  /// @dev Registrant may be a 160 bit address or other recipient identifier, such as an ENS name.
  /// @dev Scheme is an integer identifier for the stealth address scheme.
  /// @dev MUST return zero if a registrant has not registered keys for the given inputs.
  mapping(bytes => mapping(uint256 => bytes)) public stealthMetaAddressOf;

  /// @notice Sets the caller's stealth meta-address for the given stealth address scheme.
  /// @param scheme An integer identifier for the stealth address scheme.
  /// @param stealthMetaAddress The stealth meta-address to register.
  function registerKeys(uint256 scheme, bytes memory stealthMetaAddress) external {
    stealthMetaAddressOf[abi.encode(msg.sender)][scheme] = stealthMetaAddress;
  }

  /// @notice Sets the `registrant`s stealth meta-address for the given scheme.
  /// @param registrant Recipient identifier, such as an ENS name.
  /// @param scheme An integer identifier for the stealth address scheme.
  /// @param signature A signature from the `registrant` authorizing the registration.
  /// @param stealthMetaAddress The stealth meta-address to register.
  /// @dev MUST support both EOA signatures and EIP-1271 signatures.
  /// @dev MUST revert if the signature is invalid.
  function registerKeysOnBehalf(
    address registrant,
    uint256 scheme,
    bytes memory signature,
    bytes memory stealthMetaAddress
  ) external {
    // TODO If registrant has no code, spit signature into r, s, and v and call `ecrecover`.
    // TODO If registrant has code, call `isValidSignature` on the registrant.
  }

  /// @notice Sets the `registrant`s stealth meta-address for the given scheme.
  /// @param registrant Recipient identifier, such as an ENS name.
  /// @param scheme An integer identifier for the stealth address scheme.
  /// @param signature A signature from the `registrant` authorizing the registration.
  /// @param stealthMetaAddress The stealth meta-address to register.
  /// @dev MUST support both EOA signatures and EIP-1271 signatures.
  /// @dev MUST revert if the signature is invalid.
  function registerKeysOnBehalf(
    bytes memory registrant,
    uint256 scheme,
    bytes memory signature,
    bytes memory stealthMetaAddress
  ) external {
    // TODO How to best generically support any registrant identifier / name
    // system without e.g. hardcoding support just for ENS?
  }
}
```

---

### StealthMetaAddressSet

```javascript
event StealthMetaAddressSet(
  bytes indexed registrant, uint256 indexed scheme, bytes stealthMetaAddress
);
```

**概要**
登録者が自分のステルスメタアドレスを更新したときに発行されるイベント。

**詳細**
このイベントは、登録者が新しいステルスアドレスを設定したり、既存のものを更新したりしたときにブロックチェーンに記録されます。
これにより、アドレスの更新が透明かつ追跡可能になります。

**パラメータ**
- `registrant`
    - 登録者の識別子（例：イーサリアムアドレスやENS名）。
- `scheme`
    - ステルスアドレススキームを識別する整数。
- `stealthMetaAddress`
    - 登録者が設定したステルスメタアドレス。

---

### stealthMetaAddressOf

```javascript
mapping(bytes => mapping(uint256 => bytes)) public stealthMetaAddressOf;
```

**概要**
登録者の識別子からステルスアドレススキームへ、そしてステルスメタアドレスへのマッピングを保持するマッピング。

**詳細**
このマッピングは、登録者がどのステルスアドレススキームを使用しているか、およびそのスキームのどのアドレスを持っているかを追跡します。
登録者が特定の入力に対してキーを登録していない場合、ゼロを返す必要があります。

**パラメータ**
- `bytes`
    - 登録者の識別子（`160`ビットアドレスやその他の識別子）を表すバイト列です。
- `uint256`
    - ステルスアドレススキームを識別する整数。
- `bytes`
    - ステルスメタアドレスを表すバイト列。

---

### registerKeys

```javascript
function registerKeys(uint256 scheme, bytes memory stealthMetaAddress) external {
  stealthMetaAddressOf[abi.encode(msg.sender)][scheme] = stealthMetaAddress;
}
```

**概要**

呼び出し元のユーザーが特定のステルスアドレススキームに対して自分のステルスメタアドレスを設定する関数。

**詳細**

この関数は、ユーザーが自分自身のステルスメタアドレスを登録するために使用されます。
登録することで、他のコントラクトやユーザーがこのアドレスを参照できるようになり、プライバシーを保ちながら相互作用が可能になります。

**引数**

- `scheme`
    - ステルスアドレススキームを識別する整数。
- `stealthMetaAddress`
    - 登録するステルスメタアドレス。

---

### registerKeysOnBehalf

```javascript
function registerKeysOnBehalf(
  address registrant,
  uint256 scheme,
  bytes memory signature,
  bytes memory stealthMetaAddress
) external {
  // TODO If registrant has no code, spit signature into r, s, and v and call `ecrecover`.
  // TODO If registrant has code, call `isValidSignature` on the registrant.
}
```

**概要**

指定されたスキームに対して、別のユーザー（`registrant`）のステルスメタアドレスを設定する関数。

**詳細**

この関数は、登録者に代わってステルスメタアドレスを登録するために使用されます。
これにより、ユーザーが自分自身でなくても、信頼できる代理人によってプライバシー設定が可能になります。
関数は、EOA署名と**ERC1271**署名の両方をサポートし、無効な署名の場合には処理を中断します。

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

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

**引数**

- `registrant`
    - 登録するユーザーの識別子（例：アドレスやENS名）。
- `scheme`
    - ステルスアドレススキームを識別する整数。
- `signature`
    - 登録を承認する`registrant`からの署名。
- `stealthMetaAddress`
    - 登録するステルスメタアドレス。

---

### registerKeysOnBehalf

```javascript
function registerKeysOnBehalf(
  bytes memory registrant,
  uint256 scheme,
  bytes memory signature,
  bytes memory stealthMetaAddress
) external {
  // TODO How to best generically support any registrant identifier / name
  // system without e.g. hardcoding support just for ENS?
}
```

**概要**

指定されたスキームに対して、任意の形式の識別子を持つ別のユーザー（`registrant`）のステルスメタアドレスを設定する関数。

**詳細**

この関数は、さまざまな形式の識別子を持つユーザーに対して、代理でステルスメタアドレスを登録するために使用されます。
これにより、ENS名だけでなく、他の形式の識別子もサポートする柔軟性が確保されます。
関数は、EOA署名と**ERC1271**署名の両方をサポートし、無効な署名の場合には処理を中断します。

**引数**

- `registrant`
    - 登録するユーザーの識別子。
    - この識別子はバイト列として表されます。
- `scheme`
    - ステルスアドレススキームを識別する整数。
- `signature`
    - 登録を承認する`registrant`からの署名。
- `stealthMetaAddress`
    - 登録するステルスメタアドレス。

---

コントラクト内では、ステルスアドレススキームを区別するために小さな数字（`1`バイトの整数）を使います。
これにより、さまざまなタイプのステルスアドレスを識別できます。
登録者が自分の秘密のアドレスを更新するたびに、`StealthMetaAddressSet`というイベントが発行されて記録されます。

さらに、登録者がどのスキームのどの秘密アドレスを使っているのかを追跡するための`stealthMetaAddressOf`というMapping配列があります。
登録者は自分の秘密のアドレスを`registerKeys`関数で登録でき、他人が登録者の代わりに登録するために`registerKeysOnBehalf`関数もあります。
これらの関数は安全に署名を確認し、何か間違っていたら処理を止めます。

最後に、このコントラクトは特別な方法（Nickの方法として知られている）でデプロイされます。
これは、**ERC1820**や**ERC2470**などで使われる手法をベースにした詳細なプロセスです。

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

https://qiita.com/cardene/items/81744cc4b3f81da70949

:::note info
**ERC1820**と**ERC2470**はイーサリアムのコントラクトの標準で、特定の方法でコントラクトをデプロイするプロセスを定義しています。

**ERC1820 - Universal Registry**
**ERC1820**は、インターフェースを実装しているコントラクトを発見するためのユニバーサルなレジストリです。
これは、コントラクトがどのような機能を持っているかを他のコントラクトが知るための一元的な場所を提供します。
例えば、あるトークンが**ERC20**と**ERC777**の機能を持っている場合、**ERC1820**を使ってその情報を公開し、他のコントラクトがそのトークンと適切にやり取りできるようにします。

**ERC2470 - Singleton Factory**
**ERC2470**は、Nick's Method（またはKeyless deployment）として知られるデプロイメント手法を提供します。
この方法は、スマートコントラクトをデプロイする時に外部のアドレス（EOA）からの署名やトランザクションを必要とせず、特定のバイトコードを使用してコントラクトを一意のアドレスにデプロイすることを可能にします。
これは、デプロイメントがより予測可能で、何度も繰り返す必要がないことを保証します。

これらの標準は、スマートコントラクトのインターフェースとデプロイメント方法を標準化し、イーサリアム上での相互運用性と安定性を向上させることを目的としています。
**ERC5564Registry**で述べられているような、ステルスメタアドレスを登録するためのコントラクトも、これらの標準に従ってデプロイや登録が行われることで、より安全で信頼性の高いものとなります。
:::

:::note info
このコントラクトはイーサリアムチェーン上でエンティティの秘密のアドレスを安全に管理し、プライバシーを保護するための一元的なシステムを提供します。
:::

# 補足

中央のスマートコントラクトを使ってステルスメタアドレスを登録することで、イーサリアムのプライバシーと使いやすさが向上します。

### 相互運用性
他のコントラクトがステルスメタアドレスを簡単に取得できます。
これは、ENSやGnosis Safeのようなアプリがステルスアドレスを使いやすくするため、イーサリアムのアプリがよりスムーズに連携できるようになります。

### 依存性の削減
ユーザーは外部の情報源に頼ることなく、自分のステルスメタアドレスを簡単に取得できます。
これにより、より直接的かつ迅速にプライバシーを保ちながら取引できるようになります。

### 標準化された参加方法
このコントラクトにアドレスを登録することで、ユーザーは「**私はプライバシーを重視する取引に参加する準備ができています**」と簡単に伝えることができます。

### アクセスの共有
シングルトンコントラクトとしてデプロイされるため、多くのプロジェクトが同じステルスメタアドレスのセットにアクセスでき、より統一された方法でプライバシーを扱えるようになります。

:::note info
このコントラクトはイーサリアム上でのプライバシーを強化しつつ、多くのアプリやサービスがステルスアドレスを簡単かつ効率的に使えるようにするための「**中央システム**」のような役割を果たします。
:::

# 互換性

このEIPには完全な互換性があります。

# 引用

Matt Solomon (@mds1), Toni Wahrstätter (@nerolation), Ben DiFrancesco (@apbendi), Vitalik Buterin (@vbuterin), "ERC-6538: Stealth Meta-Address Registry [DRAFT]," Ethereum Improvement Proposals, no. 6538, January 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-6538.

# 最後に

今回は「アドレスをステルスメタアドレスに紐づけ、管理するレジストリの仕組みを提案している規格である**ERC6538**」についてまとめてきました！
いかがだったでしょうか？

質問などがある方は以下の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