# [ERC8107] AIエージェントの参加をENSで判定する仕組み

> ERC8107は、AIエージェントが別のエージェントへ仕事を任せる前に、任せてよい相手かを確かめるための規格です。ここでいう評価は、ENS名の所有者が「このENS名を、この用途の仕事の相手として受け入れる」と署名するものです。

- URL: https://cardene.dev/articles/3b0716e0af0587f56c5f
- 公開日: 2026-09-10
- 更新日: 2026-09-10
- トピック: Blockchain
- タグ: erc, ens, smartcontract, ai

## はじめに

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

https://amzn.asia/d/gxvJ0Pw

以下でも情報発信しているので、興味ある記事があればぜひ読んでみてください！

https://cardene.notion.site/EIP-2a03fa3ea33d43baa9ed82288f98d4a9

https://twitter.com/cardene777

https://zenn.dev/heku

https://cardene.substack.com/

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58

ERC8107は、AIエージェントが別のエージェントへ仕事を任せる前に、ENS名の所有者が署名した評価を使って参加可否を判定する仕組みです。
ENSは、Ethereum上で名前の所有者を確認できる名前サービスです。

公式仕様は以下です。

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

## 概要

ERC8107は、AIエージェントが別のエージェントへ仕事を任せる前に、任せてよい相手かを確かめるための規格です。
ここでいう評価は、ENS名の所有者が「**このENS名を、この用途の仕事の相手として受け入れる**」と署名するものです。

たとえば、仕事を振り分ける`gatekeeper.eth`は、候補の`bob.eth`を直接は知りません。
一方で、`gatekeeper.eth`は`alice.eth`を受け入れており、`alice.eth`も`bob.eth`を受け入れているとします。
この2つの署名付き評価を確認できれば、`gatekeeper.eth`は`alice.eth`を介して`bob.eth`へ仕事を任せられます。

こうした評価を保存し、提出されたENS名の列で参加条件を確認するコントラクトを、ERC8107ではTrust Registryと呼びます。
アプリケーションは`gatekeeper.eth`、`alice.eth`、`bob.eth`の順で名前を渡します。
Trust Registryは隣り合う2組の評価と参加条件を確認し、両方がそろう時だけ`bob.eth`を通します。

![ENS名の所有者が署名した評価をTrust Registryに保存し、作業を任せる相手の判定へ使う](/images/qiita-5ad246ed-afea-4f66-99c5-78a0785381e5.webp)

#### ENS名は誰のものかを確認する名前

ENS名は、Ethereum上で所有者を確認できる名前です。
ENS Registryは、`alice.eth`のような名前と現在の所有者のアドレスを対応付けています。
たとえば`alice.eth`の所有者を確認できるため、`alice.eth`が出した評価が誰の判断なのかを追えます。
ERC8107は、エージェントそのものへ評価を結び付けるのではなく、このENS名の所有者が出す署名を評価の根拠にします。

![ENS名からENS Registryで所有者を確認し、その所有者が評価へ署名する流れ](/images/qiita-441ff0bb-8291-450d-b50e-24092eac79b3.webp)

ENSの名前と所有者の対応をもう少し詳しく確認したい場合は、以下の記事を参考にしてください。

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

[\[ERC137\] アドレスに任意の名前を紐付けるENSの仕組みを理解しよう！](/articles/17e6e1a452006f08a197)

#### 誰が評価を登録するか

ENS名の所有者は、自分のENS名、相手のENS名、用途、期限を1つの署名対象にまとめます。
この「**誰が誰をどの用途でいつまで受け入れるか**」を表す署名の内容を、仕様では`TrustAttestation`と呼びます。
Trust Registryは、その評価を評価した側、評価された側、用途の組で保存します。

#### 誰が参加を判定するか

まとめ役は、候補までつながるENS名の列を用意してTrust Registryへ渡します。
この提出する名前の並びを、仕様では`TrustPath`と呼びます。
Trust Registryが確認するのは、渡された列の隣り合う名前どうしの評価だけです。
候補までの列を探す処理はアプリケーションやインデクサーが担い、コントラクトは用途、最低評価段階、期限、信頼の起点を少なくとも1つ通るかという参加条件に照らして通すか判定します。
この参加条件のまとまりを、仕様では`ValidationParams`と呼びます。

## 動機

中央の参加者一覧だけで任せる相手を選ぶ場合、依頼を振り分けるエージェントは候補を1人ずつ直接確認し続けます。
`gatekeeper.eth`が直接知らない`bob.eth`へ作業を任せるには、`alice.eth`の評価を判断に使える必要があります。
そこでERC8107は、ENS名の所有者が署名した評価を残し、依頼を振り分けるエージェントがその評価を順に確認できるようにします。

![中央の一覧による選別と、ENS名どうしの署名済み信頼を使う参加判定の比較](/images/qiita-c9ed5d53-7d2d-4cf6-8d1b-75d74727f361.webp)

ERC8001は、複数のエージェントへ作業を分担するための最小機能を定め、候補を受け入れる判断は外部モジュールへ委ねています。
つまり、ERC8001は作業の種類と参加者を扱いますが、誰を参加させるかの根拠までは決めません。
ERC8107は、そのモジュールとして参加ゲートを提供します。

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

[\[ERC8001\] 複数エージェントが安全に連携する仕組みを理解しよう！](/articles/d579c93bceca73cd08b7)

## 仕様

`gatekeeper.eth`が`alice.eth`の評価を根拠に、`bob.eth`へ仕事を任せる場面を使って、保存する評価と参加判定を対応付けます。
Trust Registryが保存する評価は、相手の能力を採点した点数ではありません。
あるENS名が、別のENS名を特定の用途の仕事へ受け入れるかを、署名して残した状態です。
`gatekeeper.eth`から`bob.eth`までの名前列を通すには、途中の各組に、参加条件を満たす評価が保存されている必要があります。

仕様では、この状態を4つの値で表します。

|値|保存されている状態|参加判定での扱い|
|---|---|---|
|`Unknown`|その組について、**受け入れる／受け入れない**という評価がまだ保存されていない|参加を通す根拠にできない|
|`None`|受け入れないという評価が保存されている|この評価を含む名前列は通さない|
|`Marginal`|ある程度受け入れるという評価が保存されている|参加ゲートの最低段階が`Marginal`以下なら使える|
|`Full`|評価した側が自分で確認したのと同等に受け入れる評価が保存されている|`Full`を要求する参加ゲートでも使える|

つまり、`Unknown`は「**まだ何も判断していない**」状態であり、`None`は「**受け入れないと判断済み**」の状態です。
参加ゲートは`Marginal`または`Full`を最低段階として選び、その段階以上の評価だけを通します。

![ENS所有者だけが評価へ署名し、リレイヤーは署名済みの評価を送信する](/images/qiita-e43e2f7b-9eac-49de-a0fe-628ff5a13896.webp)

#### 評価の内容と有効期間

コントラクトは文字列のENS名ではなく、ENS名から計算する固定長の識別子であるnamehashを使います。
`gatekeeper.eth`が`alice.eth`を受け入れる評価には、「**誰が**」、「**誰を**」、「**どの用途で**」、「**いつまで受け入れるか**」を入れます。
この署名対象のまとまりを、仕様では`TrustAttestation`と呼びます。
`scope`は評価を使える用途です。
たとえば、`bob.eth`を「**支払いの相手として受け入れる**」評価と「**投票の参加者として受け入れる**」評価は分けて保存できます。
`bytes32(0)`は、どの用途にも使える共通の評価を表します。
`nonce`は同じENS名が出す評価の更新順を示す番号です。
新しい評価へ署名する時は以前より大きい番号を入れるため、古い署名をあとから送り直して新しい状態を上書きできません。

### インターフェースの全体像

`ITrustRegistry`は、評価を置く、現在の評価を読む、候補となる名前列を確かめる、参加条件を設定する、という4つの操作を定めます。
この例では、まず`alice.eth`の所有者が`bob.eth`への評価へ署名します。
次に、その評価をTrust Registryへ保存します。
最後に`gatekeeper.eth`から始まる名前列を提出し、参加条件に照らして`bob.eth`を通すか判定します。
イベントは変更履歴を追うため、読取り関数は現在の設定を確かめるために使います。

```js
event TrustSet(bytes32 indexed trustorNode, bytes32 indexed trusteeNode, TrustLevel level, bytes32 indexed scope, uint64 expiry);
event TrustRevoked(bytes32 indexed trustorNode, bytes32 indexed trusteeNode, bytes32 indexed scope, bytes32 reasonCode);
event IdentityGateSet(bytes32 indexed coordinationType, bytes32 indexed gatekeeperNode, uint8 maxPathLength, TrustLevel minEdgeTrust);
event IdentityGateRemoved(bytes32 indexed coordinationType);

function setTrust(TrustAttestation calldata attestation, bytes calldata signature) external;
function setTrustBatch(TrustAttestation[] calldata attestations, bytes[] calldata signatures) external;
function revokeTrust(bytes32 trustorNode, bytes32 trusteeNode, bytes32 scope, bytes32 reasonCode) external;
function getTrust(bytes32 trustorNode, bytes32 trusteeNode, bytes32 scope) external view returns (TrustLevel level, uint64 expiry);
function getNonce(bytes32 trustorNode) external view returns (uint64);
function verifyPath(TrustPath calldata path, ValidationParams calldata params) external view returns (bool valid, bool anchorSatisfied);
function setIdentityGate(bytes32 coordinationType, bytes32 gatekeeperNode, ValidationParams calldata params) external;
function removeIdentityGate(bytes32 coordinationType) external;
function getIdentityGate(bytes32 coordinationType) external view returns (bytes32 gatekeeperNode, ValidationParams memory params, bool enabled);
function validateParticipantWithPath(bytes32 coordinationType, TrustPath calldata path) external view returns (bool isValid);
```

### `TrustAttestation`

|フィールド|役割|
|---|---|
|`trustorNode`|評価を表明するENS名のnamehash|
|`trusteeNode`|評価されるENS名のnamehash|
|`level`|`Unknown`、`None`、`Marginal`、`Full`の評価段階|
|`scope`|評価を使える用途。`bytes32(0)`は全用途|
|`expiry`|失効時刻。`0`は期限なし|
|`nonce`|署名を更新順に並べる、評価する側ごとの値|

ENS名の所有者だけがこの構造体へ署名できます。
つまり、リレイヤーが送信しても、`alice.eth`が誰を受け入れるかを決めるのは`alice.eth`の所有者です。
EIP712は、評価に入れるENS名、用途、期限、`nonce`をひとまとまりの構造化データとして署名する形式です。
署名する内容が変われば署名も無効になるため、リレイヤーは評価の相手や期限を書き換えられません。

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

[\[EIP712\] 型付き構造化データのハッシュ化と署名の仕組みを理解しよう！](/articles/f54bc85cb66de5a54257)

ENS名をコントラクトが所有する場合は、EIP1271でそのコントラクト自身が署名の有効性を返します。
そのため、通常のウォレットだけでなく、コントラクトウォレットが所有するENS名も同じ評価の主体にできます。
リレイヤーは有効な署名を送信できますが、別の評価へ書き換えられません。
署名のドメインにはチェーンIDとTrust Registryのアドレスを含め、別のチェーンやレジストリでの再利用を防ぎます。
EIP5267は、このEIP712ドメインの情報をコントラクトから読めるようにする規格です。
署名を作る側は、どのチェーンとTrust Registry向けの署名かを確認できます。

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

https://qiita.com/cardene/items/3f0d49e6796d6f35d3218b

### `ValidationParams`と`TrustPath`

`TrustPath`は、`gatekeeper.eth`から候補の`bob.eth`までをつなぐENS名の列です。
`TrustPath.nodes`に`[gatekeeper.eth, alice.eth, bob.eth]`を入れた場合、確認する評価は2つです。
`ValidationParams`は、その列を参加条件として使うための設定です。
「**何段までたどってよいか**」、「**各評価にどの段階を求めるか**」、「**どの用途だけ認めるか**」、「**期限を必須にするか**」、「**信頼の起点となるENS名を少なくとも1つ通るか**」を決めます。
最大長は1から10、信頼の起点として指定できるENS名は10個までです。
`Unknown`と`None`を最低評価段階には指定できません。

#### 評価を変更するAPI

`setTrust(TrustAttestation calldata attestation, bytes calldata signature)`は、ENS所有者のEIP712署名を検証して評価を設定します。
送信者はリレイヤーでも構いませんが、評価を決めるのは署名者です。

`setTrustBatch(TrustAttestation[] calldata attestations, bytes[] calldata signatures)`は、同じ`trustorNode`が作成した複数の記録をまとめて設定します。
`revokeTrust(bytes32 trustorNode, bytes32 trusteeNode, bytes32 scope, bytes32 reasonCode)`は、対象の評価を`None`へ変更します。
たとえば`alice.eth`が`bob.eth`をもう受け入れない時に使います。

|イベント|記録する変化|
|---|---|
|`TrustSet`|評価段階、用途、期限の設定または更新|
|`TrustRevoked`|明示的な取消と`reasonCode`|

#### 評価を読むAPI

`getTrust(bytes32 trustorNode, bytes32 trusteeNode, bytes32 scope)`は、指定した用途での現在の評価段階と期限を返します。
期限が`0`なら失効時刻を設けません。
`getNonce(bytes32 trustorNode)`は、以後の署名で使う単調増加値を返します。

#### 名前列を検証するAPI

![提出済みの名前列について用途、評価段階、期限、信頼の起点を順に検証する](/images/qiita-4e98639f-dd53-4af8-9471-30b26fdc9217.webp)

`verifyPath(TrustPath calldata path, ValidationParams calldata params)`は、探索済みの名前列を受け取り、隣り合う名前の評価を順に確認します。
コントラクト自身は候補を探しません。
インデクサーやアプリケーションが`[gatekeeper.eth, alice.eth, bob.eth]`のような列を見つけます。
コントラクトは提出された列が条件を満たすかだけを判定します。

確認する主な条件は、用途`scope`、隣り合う評価に必要な最低段階`minEdgeTrust`、期限、最大長`maxPathLength`、信頼の起点の候補`requiredAnchors`です。
返り値の`valid`は列全体が通るかを、`anchorSatisfied`は指定した起点の候補のうち少なくとも1つを中継しているかを示します。

#### ERC8001の参加ゲート

`setIdentityGate(bytes32 coordinationType, bytes32 gatekeeperNode, ValidationParams calldata params)`は、作業の種類ごとに、誰を起点にどの条件で候補を通すかを設定します。
`removeIdentityGate(bytes32 coordinationType)`はその設定を外します。
`getIdentityGate(bytes32 coordinationType)`は、ゲートキーパー、条件、有効化状態を返します。
`validateParticipantWithPath(bytes32 coordinationType, TrustPath calldata path)`は、設定済みの条件で候補を通すか判定します。
`gatekeeper.eth`が`bob.eth`を受け入れる最終判断にあたる関数です。

|イベント|記録する変化|
|---|---|
|`IdentityGateSet`|作業の種類、ゲートキーパー、最大のつながり数、最低評価段階|
|`IdentityGateRemoved`|参加ゲートの削除|

### `ITrustRegistry`

#### `TrustSet`

```js
event TrustSet(
      bytes32 indexed trustorNode,
      bytes32 indexed trusteeNode,
      TrustLevel level,
      bytes32 indexed scope,
      uint64 expiry
   );
```

あるENS名が別のENS名への評価を設定または更新した時に発行するイベントです。

- **フィールド**

| フィールド | 詳細 |
| --- | --- |
| `trustorNode` | 評価を設定したENS名のnamehashです。 |
| `trusteeNode` | 評価されるENS名のnamehashです。 |
| `level` | 設定後の評価段階です。 |
| `scope` | この評価を使う用途です。 |
| `expiry` | 評価の有効期限です。`0`は無期限を表します。 |

#### `TrustRevoked`

```js
event TrustRevoked(
      bytes32 indexed trustorNode,
      bytes32 indexed trusteeNode,
      bytes32 indexed scope,
      bytes32 reasonCode
   );
```

設定済みの評価を明示的に取り消す時に発行するイベントです。

- **フィールド**

| フィールド | 詳細 |
| --- | --- |
| `trustorNode` | 取消した側のENS名のnamehashです。 |
| `trusteeNode` | 取消対象のENS名のnamehashです。 |
| `scope` | 取消した用途です。 |
| `reasonCode` | 取消理由を識別する値です。 |

#### `IdentityGateSet`

```js
event IdentityGateSet(
      bytes32 indexed coordinationType,
      bytes32 indexed gatekeeperNode,
      uint8 maxPathLength,
      TrustLevel minEdgeTrust
   );
```

参加ゲートを設定する時に発行するイベントです。

- **フィールド**

| フィールド | 詳細 |
| --- | --- |
| `coordinationType` | ERC8001上の作業の種類です。 |
| `gatekeeperNode` | 参加者を判定する起点ENS名です。 |
| `maxPathLength` | 許容する評価のつながりの最大数です。 |
| `minEdgeTrust` | 各評価に必要な最低段階です。 |

#### `IdentityGateRemoved`

```js
event IdentityGateRemoved(bytes32 indexed coordinationType);
```

参加ゲートを削除する時に発行するイベントです。

- **フィールド**

| フィールド | 詳細 |
| --- | --- |
| `coordinationType` | 削除されたERC8001上の作業の種類です。 |

#### 実装が返すエラー

実装は、無効な評価や参加条件を受け取ると、以下のカスタムエラーで処理を取り消します。
呼び出し側はエラー名から、署名、更新順、期限、ENS名、名前列の条件のどこを直すか判断できます。

| エラー | 起きる条件 | 呼び出し側が確認すること |
| --- | --- | --- |
| `SelfTrustProhibited` | 自分自身への評価を送る | `trustorNode`と`trusteeNode`が異なるか |
| `NonceTooLow` | nonceが現在値以下 | `getNonce`を読み、より大きい値で再署名する |
| `AttestationExpired` | 署名済み記録の期限が過ぎた | 新しい期限で再署名する |
| `InvalidSignature` | ENS所有者の署名を検証できない | EIP712ドメインと署名権限を確認する |
| `NotAuthorized` | 取消や設定の権限がない | ENS所有者または、所有者が操作を任せた`operator`か確認する |
| `ENSNameNotFound` | 指定したENS名が存在しない | namehashとENS登録状態を確認する |
| `TrustNotFound` | 取消対象の評価がない | 評価した側、相手、用途の組を確認する |
| `GateNotFound` | 対象の参加ゲートがない | `coordinationType`とゲート設定を確認する |
| `InvalidValidationParams` | 名前列の条件が許容範囲外 | 長さ、最低評価段階、必須ENS名の数を確認する |
| `BatchTrustorMismatch` | バッチ内の評価した側が一致しない | すべて同じ`trustorNode`へそろえる |
| `BatchNonceNotIncreasing` | バッチ内のnonce順が増加していない | nonceを昇順で作り直す |

#### `setTrust`

```js
function setTrust(
      TrustAttestation calldata attestation,
      bytes calldata signature
   ) external;
```

ENS所有者が署名した評価を設定する関数です。

1. **署名者を確認する**

    `attestation.trustorNode`のENS所有者、またはそのENS名を所有するコントラクトがEIP1271で認める署名者を確認します。

2. **更新順を確認する**

    `nonce`が評価した側の現在値より新しいことを確認します。

3. **評価を保存する**

    `trustorNode`、`trusteeNode`、`scope`の組へ評価段階と期限を保存し、`TrustSet`を発行します。

- **引数**

| 引数 | 詳細 |
| --- | --- |
| `attestation` | 評価するENS名、相手、評価段階、用途、期限、nonceを含む署名対象です。 |
| `signature` | ENS所有者がEIP712ドメインで作成した署名です。 |

- **実行条件**

- ENS名に署名権限を持つ所有者の署名が必要です。
- `nonce`は過去の記録より大きい値を使います。
- 送信者は署名者本人でなくても構いません。

#### `setTrustBatch`

```js
function setTrustBatch(
      TrustAttestation[] calldata attestations,
      bytes[] calldata signatures
   ) external;
```

同じENS名が署名した複数の評価をまとめて設定する関数です。

1. **配列を対応付ける**

    各`attestations`に対応する`signatures`を確認します。

2. **署名と評価する側を確認する**

    すべての記録が同じ`trustorNode`から作られ、有効な署名を持つことを確認します。

3. **各記録を保存する**

    各評価を設定し、それぞれの更新をイベントで追えるようにします。

- **引数**

| 引数 | 詳細 |
| --- | --- |
| `attestations` | まとめて設定する評価の配列です。 |
| `signatures` | 各評価に対応するEIP712署名です。 |

- **実行条件**

- 配列の要素数が対応している必要があります。
- 全記録の`trustorNode`が同じで、有効な署名と新しいnonceが必要です。

#### `revokeTrust`

```js
function revokeTrust(
      bytes32 trustorNode,
      bytes32 trusteeNode,
      bytes32 scope,
      bytes32 reasonCode
   ) external;
```

用途ごとの評価を`None`へ変更する関数です。

1. **取消権限を確認する**

    ENS所有者、または所有者が操作を任せた`operator`であることを確認します。

2. **評価を無効化する**

    対象の用途について評価段階を`None`へ変更します。

3. **取消理由を記録する**

    `TrustRevoked`へ`reasonCode`を記録します。

- **引数**

| 引数 | 詳細 |
| --- | --- |
| `trustorNode` | 取消する側のENS namehashです。 |
| `trusteeNode` | 評価を取り消されるENS namehashです。 |
| `scope` | 取消を適用する用途です。 |
| `reasonCode` | 取消理由を識別する値です。 |

- **実行条件**

- 呼び出し元はENS所有者か、所有者が操作を任せた`operator`である必要があります。

#### `getTrust`

```js
function getTrust(
      bytes32 trustorNode,
      bytes32 trusteeNode,
      bytes32 scope
   ) external view returns (TrustLevel level, uint64 expiry);
```

2つのENS名の間にある、用途ごとの現在の評価段階と期限を返す関数です。

1. **記録のキーを組み立てる**

    `trustorNode`、`trusteeNode`、`scope`の組で記録を特定します。

2. **評価を返す**

    `TrustLevel`と有効期限を返します。

- **引数**

| 引数 | 詳細 |
| --- | --- |
| `trustorNode` | 評価する側のENS namehashです。 |
| `trusteeNode` | 評価される側のENS namehashです。 |
| `scope` | 読み出す用途です。`bytes32(0)`は共通用途です。 |

- **戻り値**

| 戻り値 | 詳細 |
| --- | --- |
| `level` | 現在の評価段階です。 |
| `expiry` | Unix時刻の期限です。`0`は無期限です。 |

- **実行条件**

- 誰でも参照できます。

#### `getNonce`

```js
function getNonce(bytes32 trustorNode) external view returns (uint64);
```

署名の更新順を確認する現在のnonceを返す関数です。

1. **ENS名を指定する**

    `trustorNode`をキーにして、その評価する側に紐づくnonceを読み出します。

2. **現在のnonceを返す**

    新しい署名を作る側は、この値を基準に更新順を決めます。

- **引数**

| 引数 | 詳細 |
| --- | --- |
| `trustorNode` | nonceを読むENS namehashです。 |

- **戻り値**

| 戻り値 | 詳細 |
| --- | --- |
| `uint64` | 現在のnonceです。 |

- **実行条件**

- 誰でも参照できます。

#### `verifyPath`

```js
function verifyPath(
      TrustPath calldata path,
      ValidationParams calldata params
   ) external view returns (bool valid, bool anchorSatisfied);
```

オフチェーンで見つけたENS名の列を、コントラクト上の評価に照らして検証する関数です。

1. **名前列の形を確認する**

    ENS名の数、評価のつながり数、先頭と末尾の対応を確認します。

2. **隣り合う評価を確認する**

    用途、期限、最低評価段階を満たすかを順に確認します。

3. **必須のENS名を通るか判定する**

    名前列に必要なENS名が含まれるかを判定して結果を返します。

- **引数**

| 引数 | 詳細 |
| --- | --- |
| `path` | 提出するENS名の列と、隣り合う評価の用途です。 |
| `params` | 列の長さ、最低評価段階、少なくとも1つ通る信頼の起点を定める条件です。 |

- **戻り値**

| 戻り値 | 詳細 |
| --- | --- |
| `valid` | 名前列が検証条件を満たすかです。 |
| `anchorSatisfied` | 必須のENS名を通るかです。 |

- **実行条件**

- 誰でも参照できます。

#### `setIdentityGate`

```js
function setIdentityGate(
      bytes32 coordinationType,
      bytes32 gatekeeperNode,
      ValidationParams calldata params
   ) external;
```

ERC8001で扱う作業の種類ごとに、参加判定の起点と名前列の条件を設定する関数です。

1. **作業の種類を選ぶ**

    `coordinationType`で設定対象を特定します。

2. **ゲート条件を保存する**

    `gatekeeperNode`と`params`を保存し、`IdentityGateSet`を発行します。

- **引数**

| 引数 | 詳細 |
| --- | --- |
| `coordinationType` | ERC8001上の作業の種類です。 |
| `gatekeeperNode` | 名前列の起点となるENS namehashです。 |
| `params` | 列の長さ、最低評価段階、少なくとも1つ通る信頼の起点の条件です。 |

- **実行条件**

- 設定権限を持つ実装上の管理者が呼び出します。

#### `removeIdentityGate`

```js
function removeIdentityGate(bytes32 coordinationType) external;
```

作業の種類から参加ゲートを削除する関数です。

1. **作業の種類を指定する**

    `coordinationType`で削除する設定を特定します。

2. **ゲートを無効化する**

    設定を外し、`IdentityGateRemoved`を発行します。

- **引数**

| 引数 | 詳細 |
| --- | --- |
| `coordinationType` | ゲートを外すERC8001上の作業の種類です。 |

- **実行条件**

- 設定権限を持つ実装上の管理者が呼び出します。

#### `getIdentityGate`

```js
function getIdentityGate(
      bytes32 coordinationType
   ) external view returns (
      bytes32 gatekeeperNode,
      ValidationParams memory params,
      bool enabled
   );
```

作業の種類に設定された参加ゲートの起点、検証条件、有効状態を返す関数です。

1. **作業の種類を指定する**

    `coordinationType`で読み出す設定を特定します。

2. **設定状態を返す**

    ゲートキーパー、検証条件、有効かどうかをまとめて返します。

- **引数**

| 引数 | 詳細 |
| --- | --- |
| `coordinationType` | 読み出すERC8001上の作業の種類です。 |

- **戻り値**

| 戻り値 | 詳細 |
| --- | --- |
| `gatekeeperNode` | 参加判定の起点ENS名です。 |
| `params` | 設定済みの名前列の検証条件です。 |
| `enabled` | ゲートが有効かどうかです。 |

- **実行条件**

- 誰でも参照できます。

#### `validateParticipantWithPath`

```js
function validateParticipantWithPath(
      bytes32 coordinationType,
      TrustPath calldata path
   ) external view returns (bool isValid);
```

提出されたENS名の列を、作業の種類に設定された参加ゲートで判定する関数です。

1. **ゲート設定を読む**

    `coordinationType`のゲートが有効か、起点がどのENS名かを確認します。

2. **名前列の両端を確認する**

    ENS名の列がゲートキーパーから始まり、候補へ到達していることを確認します。

3. **名前列を検証して判定する**

    設定済みの`ValidationParams`で検証し、参加可否を返します。

- **引数**

| 引数 | 詳細 |
| --- | --- |
| `coordinationType` | 判定に使うERC8001上の作業の種類です。 |
| `path` | ゲートキーパーから候補までの事前計算済みのENS名の列です。 |

- **戻り値**

| 戻り値 | 詳細 |
| --- | --- |
| `isValid` | 参加者がゲートを通過できるかです。 |

- **実行条件**

- 誰でも参照できます。

## 補足

ERC8107は、評価のつながり全体をオンチェーンで探しません。
名前列を探すのはインデクサーやクライアントで、コントラクトが確かめるのは提出された列だけです。
これにより、候補の探し方はアプリケーションごとに選びながら、`bob.eth`を通した根拠はコントラクト上で再現できます。

評価は`scope`ごとに分けられます。
支払いと投票のように、同じENS名に対しても用途ごとに別の評価を保存できます。

## 互換性

#### 既存規格との関係

ERC8107は、ENS名の所有者を確認して署名を検証し、その結果を使う参加ゲートを追加します。
ENSの名前や所有者を更新する規格ではなく、既存の名前を評価の主体として使います。

|規格|ERC8107で使うもの|ERC8107が追加・変更しないもの|
|---|---|---|
|ENS|ENS名のnamehashと現在の所有者|ENS名の登録、譲渡、レコードの更新|
|EIP712・EIP1271|評価へ署名する形式と、コントラクト所有ENS名の署名確認|ウォレットの署名方式そのもの|
|ERC8001|作業の種類ごとに参加可否を返すidentity gate|ERC8001の作業フローや参加者のデータ|

ERC8001側は、作業の種類に`setIdentityGate`で条件を設定した時だけERC8107の判定を使います。
参加ゲートを設定していない作業は、ERC8107の名前列を提出せずに参加できます。

## 参考実装

#### 評価を保存する処理

仕様が示す`TrustRegistry.sol`は、評価を保存する処理と、提出されたENS名の列を検証する処理を1つのコントラクトへまとめています。
評価を保存する入口は`setTrust`です。
`attestation.trustorNode`のENS名に所有者がいるかを確認し、その所有者によるEIP712署名か、所有者がコントラクトならEIP1271で有効な署名かを確認します。

署名が有効でも、同じENS名が以前に出した評価より`nonce`が小さい場合や、`expiry`が過ぎている場合は保存しません。
確認を通った評価だけを、評価したENS名、評価されたENS名、用途の組で保存します。
その直後に保存した`nonce`を更新し、`TrustSet`イベントを発行します。
この順序により、リレイヤーが送信しても、評価の内容と更新順を決めるのはENS名の所有者です。

#### 提出された名前列を検証する処理

参加判定では、コントラクトが候補を検索しません。
アプリケーションが`[gatekeeper.eth, alice.eth, bob.eth]`のような名前列を渡し、`verifyPath`が隣り合う2つずつを確認します。
この関数は、指定した用途の評価を先に読み、見つからない時だけ全用途の評価を読みます。

```js
(TrustLevel level, uint64 expiry) = getTrust(
   path.nodes[i],
   path.nodes[i + 1],
   params.scope
);

if (level == TrustLevel.Unknown && params.scope != bytes32(0)) {
   (level, expiry) = getTrust(
      path.nodes[i],
      path.nodes[i + 1],
      bytes32(0)
   );
}
```

読み出した評価が`minEdgeTrust`より低い場合、`None`の場合、期限が切れている場合は、その場で名前列を拒否します。
さらに、途中のENS名に`requiredAnchors`で指定した名前が含まれるかを確認します。
最初と最後の名前は中継点ではないため、anchorの判定に使いません。

`validateParticipantWithPath`は、この検証をERC8001の参加ゲートへつなぐ関数です。
対象の作業に参加ゲートが設定されていなければ`true`を返します。
設定されている場合は、列の先頭が`gatekeeperNode`と一致することを確認し、`verifyPath`が返す有効性とanchor条件の両方が`true`の時だけ候補を通します。

https://eips.ethereum.org/EIPS/eip-8107#reference-implementation

## セキュリティ

#### 起点のない評価の循環

攻撃者が多数のENS名を作り、それらの間で評価を設定しても、それだけではアプリケーションが決めた起点へ到達しません。
参加条件では、信頼の起点として少なくとも1つ通るENS名、`maxPathLength`、`minEdgeTrust`、期限を明示します。

#### ENS名の譲渡と期限

![多数のENS名を作る攻撃、信頼の起点、短い経路、譲渡後の監視を一続きで示す](/images/qiita-40fae9cb-9f02-49e3-95b4-41501a08b571.webp)

多数のENS名の間で評価を設定しても、アプリケーションが決めた起点へ条件を満たす名前列を作れなければ参加できません。
`maxPathLength`を短くし、`minEdgeTrust`を`Full`へ上げ、期限を設けることで、弱い評価の連鎖を参加条件へ使いにくくします。

ENS名が譲渡されても、そのENS名が**評価される側**として保存されていた評価は残ります。
また、譲渡前の所有者が**評価する側**として署名した評価も、期限までは有効です。
そのためアプリケーションは、譲渡を新しい評価と扱わず、期限を使い、ENSの`Transfer`を監視して、譲渡後に名前列を再検証します。
署名には`nonce`が含まれるため、古い署名を繰り返し送って状態を戻すことも防ぎます。

## 最後に

ERC8107は、ENS名の所有者が署名した評価をTrust Registryへ保存し、あらかじめ見つけたENS名の列で参加可否を検証する提案です。
評価を決める署名者と、評価を送信するリレイヤーを分けるため、誰の判断かを残したまま記録できます。
アプリケーションは、信頼の起点として少なくとも1つ通るENS名、評価段階、用途、期限、名前列の長さを選び、その作業に必要な参加条件を組み立てます。

他でも色々記事を書いているのでぜひよろしければ読んでいってください！

https://amzn.asia/d/gxvJ0Pw

https://cardene.notion.site/EIP-2a03fa3ea33d43baa9ed82288f98d4a9

https://zenn.dev/heku

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58

https://twitter.com/cardene777

https://cardene.substack.com/
