# [ERC137] アドレスに任意の名前を紐付けるENSの仕組みを理解しよう！

> このERC137では、Ethereum Name Service（ENS）の詳細を説明していきます。

- URL: https://cardene.dev/articles/17e6e1a452006f08a197
- 公開日: 2023-07-28
- 更新日: 2023-07-28
- トピック: Blockchain
- タグ: ethereum, solidity, smartcontract, ブロックチェーン

## はじめに

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

https://cryptogames.co.jp/

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

https://cryptospells.jp/

今回は、アドレスを人間が覚えやすい名前と紐付ける、ENSと呼ばれるサービスのプロトコルである**ERC137**についてまとめていきます！

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

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

## 概要
この**ERC137**では、Ethereum Name Service（ENS）の詳細を説明していきます。
**ERC137**は、サービスやリソースのアドレスを人間が読み取れる名前に変換するプロトコルとABI（アプリケーションバイナリインタフェース）定義を提案しています。
これにより、ユーザーや開発者はコントラクトなどのアドレスを覚えやすく、人間が理解しやすくなり、アドレスが変更された場合にも必要に応じて紐づいている名前を更新できます。

ドメイン名の目的は、ネットワークリソースを指定するための安定した、人間が読みやすい識別子を提供することです。

:::message
ここでいうネットワークリソースやリソースとはアドレスのことです。
:::

これにより、ユーザーは「`vitalik.wallet`」や「`www.mysite.swarm`」といった覚えやすい文字列を入力し、適切なリソースにアクセスできます。

:::message
**Swarm**とは、Ethereumプラットフォーム上で動作する分散型ストレージおよびコンテンツ配信プロトコルです。
:::

https://www.ethswarm.org/

名前とリソースの対応関係は時間とともに変更される場合があります。
例えば、ユーザーがウォレットを変更したり、ウェブサイトがホストを変更したり、あるいはSwarmドキュメントが新しいバージョンに更新される場合がありますが、その際にドメイン名を変更する必要はありません。
さらに、1つのドメインが1つのリソースのみを指定する必要もありません。
異なるレコードタイプによって、同じドメインが異なるリソースを参照できます
たとえば、ブラウザは「**A（アドレス）**」レコードを取得すると「`mysite.swarm`」をサーバーのIPアドレスとして解決し、メールクライアントは同じアドレスをメールサーバーとして解決することができます。

:::message
ごちゃごちゃ難しいことを言っていますが、2023年7月現在において簡単に説明すると以下のようになります。
「**コントラクトアドレスやウォレットアドレスはそのままだと読みにくいから、`vitalik.eth`など人間が読みやすくて理解しやすい文字列と紐付けたら良いんじゃない？**」
:::

![ブログERC & EIP.png](/images/qiita-c839245d-b607-5e53-ea33-93ec02cbab89.webp)

## 動機

### 課題点と提案
Ethereumにおける名前解決の既存の仕様と実装は基本的な機能を提供していますが、以下のような欠点から長期的な使用に制約が発生します。

1. すべての名前に対して単一の「**中央集権的**」リゾルバを持つ単一のグローバル名前空間。
2. 委任とサブネーム/サブドメインのサポートが制限されるか、サポートされていない。
3. 1つのレコードタイプのみで、ドメインに複数のレコードを関連付けるサポートがない。
4. 単一のグローバル実装により、複数の異なる名前割り当てシステムのサポートがない。
5. 責任の混同：名前の解決、登録、whois情報。

:::message
だいぶWeb2のことも交えて難しい説明になっていますが、簡単にまとめると以下のような制約があります。
1. 1つの組織がドメイン名を管理しているため、名前解決の信頼性やセキュリティの依存が発生する。
2. サブドメインを他のユーザーやサービスに委任できない。
3. ドメイン名とアドレスの紐付けが1対1しかできず、複数紐づけることができない。
4. ドメイン名の割り当てや管理は中央の組織が行うため、ユーザーやサービスが好きなように名前の管理ができない。
5. 名前解決、ドメインの登録、Whoisなどのドメイン所有者情報の管理が同じ組織によってされているため、役割の混同やセキュリティ上の問題が発生する。
:::

**ERC137**が可能にするユースケースとして以下が挙げられます。

:::message
以下の部分はわかりやすく説明するために書き換えています。
元の文章が気になる方は以下を確認してください。
:::

https://eips.ethereum.org/EIPS/eip-137#motivation

#### ドメイン名の階層構造
ドメイン名の階層構造を作成できるようになります。
これにより、異なるサブドメインに対してアドレスやサービスを関連づけられるようになります。

:::message
例：`mysite.tld`というドメインの下に、`live.mysite.tld`や`forum.mysite.tld`のようなサブドメインを作成。
:::

#### 1つのドメイン名で複数のサービスをサポート
1つのドメインに対して複数のアドレスを紐づけることができるようになります。
これにより、1つのドメイン名で異なるサービスやプロトコルを提供できます。

:::message
例：`mysite.tld`というドメイン名にDAppをホストする**Swarm**のアドレス、**Whisper**アドレス、およびメールサーバーのアドレスを紐づける。
:::

#### DNSレコードタイプのサポート
様々なFNDレコードタイプをサポートできるようになります。
これにより、今までDNSで使用されてきたドメイン名をブロックチェーン上でホスティングできます。
ETHアドレスやIPFSハッシュなどをレコードに関連付けることで、従来のドメイン名に対応するブロックチェーン上のリソースを解決できるようになります。

#### DNSゲートウェイによるレガシークライアントのサポート
ENSのドメインはブロックチェーン上に存在しますが、従来のDNSを使用しているクライアントも存在します。
DNSゲートウェイは、ENSドメインを従来のDNSサービスに公開する役割を果たします。
これにより、既存のレガシークライアントがENSドメインを解決し、ブロックチェーン上のリソースにアクセスできるようになります。
DNSゲートウェイを介することで、従来のインターネットとブロックチェーンの接続をスムーズに行うことができます。


### 動機の補足

#### ユースケース
最初の2つのユースケース（**ドメイン名の階層構造**、**1つのドメイン名で複数のサービスをサポート**）は、従来のDNSを使用した現代のインターネット上で広く見られるものです。
ENSはこれらの機能をEthereumプラットフォーム上に再現し、拡張することを目指しています。
そして、これらの機能はEthereumプラットフォームの発展と成長に伴い、依然として重要な特徴として存在し続けると考えられています。

#### プロトコルのドキュメント化
**ERC137**では、提案されたシステムの実装を具体的に指定しているわけではありません。
その代わりに、異なるリゾルバ（名前解決を行うコンポーネント）の実装が一貫した名前解決を実現できるようにプロトコルを提案しています。
後半では、リゾルバのコントラクトとライブラリのサンプル実装が提供されていますが、これらはあくまで具体例であり、実際の実装はこれらのサンプルを参考にしながら独自に行うことができます。

#### ドメインの登録と更新
**ERC137**では、ドメインがどのように登録または更新されるべきか、またどのシステムが特定のドメインの所有者を見つける方法を具体的に指定していません。
ドメインの登録は登録業者の責任であり、トップレベルドメインによって異なる統治方法が採用されることが予想されています。

#### ドメインレコードの更新
ドメインレコードの更新は名前解決とは別に処理できます。
たとえば、**Swarm**などの一部のシステムは、ドメインの更新に対して明確に定義されたインターフェースが必要となるかもしれないです。
そのような場合、この提案ではその標準化の開発が予測されています。

## 仕様

### 概要

ENSシステムは以下の3つの部分で構成されています。
- ENSレジストリ
- リゾルバ
- レジストラ

#### ENSレジストリ
ENSレジストリは、任意の登録されたドメイン名に対してそれに対応するリゾルバ（名前解決を行うコンポーネント）を提供する単一のスマートコントラクトです。
また、ドメイン名の所有者はリゾルバのアドレスを設定したり、サブドメインを作成したりすることができます。
サブドメインは親ドメインとは別の所有者に設定することも可能です。

#### リゾルバ
リゾルバは、ドメイン名に関連するリソースの検索を行います。
たとえば、コントラクトアドレス、コンテンツハッシュ、またはIPアドレスなどを返します。
リゾルバの仕様はここで定義されており、他のEIPで拡張される場合もあります。
リゾルバは異なるタイプのレコードの解決をサポートするためのメソッドを実装することが指定されています。

#### レジストラ
レジストラは、ユーザーに対してドメイン名を割り当てを行います。
ENSのレジストリに登録されているノード（ドメイン）の所有者はレジストラです。
レジストラはスマートコントラクトまたは外部所有アカウント（EOA）の形態で実装されることがありますが、少なくともルートおよびトップレベルのレジストラはスマートコントラクトとして実装されることが予想されています。

#### 補足
ENSで名前解決を行う場合は、2つのステップがあります。
1. まず、解決したい名前をハッシュ化してENSレジストリにリクエストし、レコードが存在する場合、レジストリは対応するリゾルバのアドレスを返します。
2. 次に、対応するリゾルバを呼び出し、要求されているリソースに適したメソッドを使用して解決を行い結果を返します。

ENSは、このような仕組みによって柔軟な名前解決とドメイン管理を実現しており、異なるリソースに対して人間が読み取りやすい名前を関連付けることができます。

例えば、「`beercoin.eth`」に関連するトークンコントラクトのアドレスを見つけたいとします。

まずは、リゾルバを取得します。

```javascript
var node = namehash("beercoin.eth");
var resolver = ens.resolver(node);
```

次に、リゾルバにコントラクトのアドレスを尋ねます。

```javascript
var address = resolver.addr(node);
```

`namehash`というハッシュ値に変換する処理では、名前にのみ依存するため一度計算した`namehash`は再利用できます。
事前に計算してコントラクトに挿入することで、文字列操作の必要性をなくし、生の名前の構成要素の数に関係なく、高速でENSレコードのO(1)検索を可能にする。

### 名前の構文
ENS名は以下の構文に従わなければなりません。

```javascript
<domain> ::= <label> | <domain> "." <label>
<label> ::= any valid string label per [UTS46](https://unicode.org/reports/tr46/)
```

ENSの名前はドットで区切られたラベルの連続から構成されます。
各ラベルは、**UTS46**で説明されている**valid normalised label**である必要があります。

:::message
**UTS46**とは、国際化ドメイン名（IDN）の標準化に関する規格です。
非ASCII文字（例：日本語、中国語などの文字）を含むドメイン名を表現するために使用されます。
:::

:::message
**valid normalised label**とは、UTS46によって定義されたIDNのドメイン名のラベル（部分）の形式を示しています。
これにより、ドメイン名が特定の条件を満たすことが保証され、IDNにおける一貫性と正確性が確保されます。
:::

また、Javascriptの実装では、名前を正規化してチェックするためのライブラリが利用可能です。

名前の大文字と小文字は許可されていますが、**UTS46**の正規化プロセスはラベルをハッシュ化する前に、ラベルのケースを統一します。
したがって、大文字と小文字が異なってもスペルが同じであれば同じ`namehash`を生成します。

ラベルとドメインの長さは任意ですが、従来のDNSとの互換性を考慮して、ラベルは64文字以下、完全なENS名は255文字以下に制限することが推奨されています。
同じ理由で、ラベルがハイフンで始まったり終わったり、または数字で始まったりすることは推奨されていません。

### namehashアルゴリズム
`namehash`アルゴリズムは、ENSで使用される前に、名前をハッシュ化するために使用されます。
このアルゴリズムは、名前の構成要素を再帰的にハッシュ化し、任意の有効な入力ドメインに対して一意で固定長の文字列（**ノード**）を生成します。

`namehash`アルゴリズムの疑似コードは以下になります。

```python
def namehash(name):
  if name == '':
    return '\0' * 32
  else:
    label, _, remainder = name.partition('.')
    return sha3(namehash(remainder) + sha3(label))
```

上記の処理を詳しく説明していきます。

1. まず、ドメイン名（名前）はドットで区切られたラベルに分割されます。
    - 例えば、「`mysite.swarm`」は「`mysite`」と「`swarm`」の2つのラベルに分かれます。
2. 次に、各ラベルが個別にハッシュ化されます。
    - ハッシュ化は、ラベルを一意の固定長の文字列に変換する処理です。
    - 例えば、「`mysite`」のハッシュ値は**A**、「`swarm`」のハッシュ値は**B**と仮定します。
3. その後、処理は最後のラベルから始まります。
    - 最後のラベルである「`swarm`」のハッシュ値**B**が取得されます。
4. 次に、前のハッシュ値（ここでは**B**）と現在のラベルのハッシュ値（ここでは**A**）を連結（結合）します。
    - これにより、新しいデータ（**B + A**）が得られます。
5. この新しいデータ（**B + A**）は、再びハッシュ化されます。
    - 新しいハッシュ値（仮に**C**）が得られます。
6. この処理を、ラベルがなくなるまで繰り返します。
    - つまり、次に「`mysite`」のハッシュ値**A**と前のハッシュ値（ここでは**C**）を連結し、再びハッシュ化します。
    - 新しいハッシュ値（**D**）が得られます。
7. 最終的に、最初のラベルである「`mysite`」のハッシュ値**A**と前のハッシュ値（**D**）を連結します。

```python
node = '\0' * 32
node = sha3(node + sha3('swarm'))
node = sha3(node + sha3('mysite'))
```

8. また、最初のラベルのハッシュ値の前に32個の「`0`」バイトを連結します。

```python
namehash('') = 0x0000000000000000000000000000000000000000000000000000000000000000
namehash('eth') = 0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae
namehash('foo.eth') = 0xde9b09fd7c5f901e23a3f19fecc54828e9c848539801e86591bd9801b019f84f
```

9. 最後に、これらのデータ（32個の「`0`」バイト + **A** + **D**）をハッシュ化します。
    - この最終的なハッシュ値が、名前「`mysite.swarm`」の`namehash`となります。

このようにして、名前「`mysite.swarm`」は再帰的なハッシュ化処理を経て一意のハッシュ値に変換されます。
これにより、ドメイン名を短い一意の文字列に変換して保持することができ、高速な名前解決を実現することができるようになります。

### レジストリの仕様

以下の関数を使用することで、ENSのレジストリコントラクトを操作してドメイン名の所有権の移動やリゾルバの設定などが可能となります。
また、トランザクションの成功や変更がイベントログに記録されるため、操作の履歴を確認できます。

```javascript
function owner(bytes32 node) constant returns (address);
```

指定されたノード（ドメイン名）の所有者（登録者）を返す関数。

```javascript
function resolver(bytes32 node) constant returns (address);
```

指定されたノードのリゾルバを返す関数。
リゾルバは、名前解決に使用されるコントラクトやサービスのアドレスを指します。

```javascript
function ttl(bytes32 node) constant returns (uint64);
```

指定されたノードのTime-to-Live（TTL）を返す関数。

:::message
TTLは、ノードの情報がキャッシュされる最大期間を示します。
:::

```javascript
function setOwner(bytes32 node, address owner);
```

ノードの所有権を別の登録者に移動させる関数。
この関数は、現在のノードの所有者によってのみ呼び出すことができます。
成功した場合、`Transfer(bytes32 indexed, address)`というイベントが発行されます。

```javascript
function setSubnodeOwner(bytes32 node, bytes32 label, address owner);
```

新しいノード（サブノード）を作成し、その所有者を設定関数。
既にサブノードが存在する場合は所有者を更新します。
この関数は、現在のノードの所有者によってのみ呼び出すことができます。
成功した場合、`NewOwner(bytes32 indexed, bytes32 indexed, address)`というイベントが発行されます。

```javascript
function setResolver(bytes32 node, address resolver);
```

ノードのリゾルバアドレスを設定する関数。
ノードの所有者によってのみ呼び出すことができます。
成功した場合、`NewResolver(bytes32 indexed, address)`というイベントが発行されます。

```javascript
function setTTL(bytes32 node, uint64 ttl);
```

ノードのTTLを設定します。
TTLは、レジストリ内の「**所有者**」や「**リゾルバ**」レコード、および関連するリゾルバが返す情報に適用されます。

### リゾルバの仕様

リゾルバ（Resolver）は、指定されたレコードタイプのサブセットを実装することができます。
ただし、特定のレコードタイプに関連する複数の関数を提供する必要がある場合、リゾルバはそれらすべてを実装するか、すべて実装しないかのどちらかでなければなりません。
また、リゾルバは例外を投げるフォールバック関数を実装する必要があります。

リゾルバには、次の1つの必須の関数があります：

```javascript
function supportsInterface(bytes4 interfaceID) constant returns (bool)
```
`supportsInterface`関数は、**ERC165**で提案されており、指定された4バイトの識別子に対応するインターフェースをリゾルバが実装している場合は`true`を返します。

:::message
**ERC165**については以下を参照してください。
:::

[\[ERC165\] コントラクトが特定のインターフェースをサポートしているか確認する仕組みを理解しよう！](/articles/541184977a5cba51b759)

インターフェース識別子は、そのインターフェースが提供する関数の関数シグネチャハッシュのXORで構成されます。
単一の関数のインターフェースの場合は、その関数のシグネチャハッシュと等しいです。
リゾルバが`supportsInterface()`に対して`true`を返す場合、そのインターフェースで指定された関数を実装する必要があります。

`supportsInterface`は、常に`0x01ffc9a7`に対して`true`を返さなければなりません。
これは`supportsInterface`自体のインターフェースIDです。

現在、標準化されているリゾルバのインターフェースは以下の表で指定されています：

| インターフェース名 | インターフェースハッシュ | 仕様 |
|------------------|-----------------------|-----|
| addr             | 0x3b3b57de            | コントラクトアドレス |
| name             | 0x691f3431            | #181 |
| ABI              | 0x2203ab56            | #205 |
| pubkey           | 0xc8690233            | #619 |

また、EIPはこのレジストリに追加する新しいインターフェースを定義することができます。

リゾルバはこれらのインターフェースのいずれかまたは複数を実装することで、異なる種類のリゾルバを作成することが可能です。
これにより、異なるレコードタイプに対応したリゾルバが提供され、柔軟な名前解決が可能になります。

### コントラクトアドレスのインターフェース

コントラクトアドレスインターフェースをサポートするために、リゾルバは以下の関数を実装する必要があります。

```javascript
function addr(bytes32 node) constant returns (address);
```

リゾルバが`addr`ルックアップをサポートしている場合、要求されたノード（ドメイン名）に`addr`レコードが存在しない場合、リゾルバはゼロアドレスを返さなければなりません。

:::message
`addr`レコードは、ENSにおいてドメイン名（ノード）と関連するコントラクトのアドレスを紐付けるレコードです。
`addr`レコードを持つドメイン名は、ドメイン名に紐づけられたコントラクトのアドレスを確認するために使用されます。
:::

アドレスレコードを解決するクライアントは、リゾルバがゼロの値を返すかどうかをチェックし、これを名前にリゾルバが指定されていない場合と同じように扱わなければなりません。
つまり、そのアドレスに対して資金を送付したり、やりとりすることを拒否する必要があります。
これを怠ると、ユーザーが誤って0アドレスに資金を送信してしまう可能性があります。

アドレスが変更された場合、リゾルバは次のイベントを発行する必要があります。

```
event AddrChanged(bytes32 indexed node, address a);
```

このイベントは、アドレスが変更されたことを監視するために使用されます。
リゾルバが`addr`レコードを更新した場合、このイベントがログに記録され、他のアプリケーションやスマートコントラクトが必要なアクションを実行できるようになります。

## レジストリの実装
以下のコントラクトは、ENSのレジストリのコードです。

```javascript
contract ENS {
    struct Record {
        address owner;
        address resolver;
        uint64 ttl;
    }

    mapping(bytes32=>Record) records;

    event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);
    event Transfer(bytes32 indexed node, address owner);
    event NewResolver(bytes32 indexed node, address resolver);

    modifier only_owner(bytes32 node) {
        if(records[node].owner != msg.sender) throw;
        _
    }

    function ENS(address owner) {
        records[0].owner = owner;
    }

    function owner(bytes32 node) constant returns (address) {
        return records[node].owner;
    }

    function resolver(bytes32 node) constant returns (address) {
        return records[node].resolver;
    }

    function ttl(bytes32 node) constant returns (uint64) {
        return records[node].ttl;
    }

    function setOwner(bytes32 node, address owner) only_owner(node) {
        Transfer(node, owner);
        records[node].owner = owner;
    }

    function setSubnodeOwner(bytes32 node, bytes32 label, address owner) only_owner(node) {
        var subnode = sha3(node, label);
        NewOwner(node, label, owner);
        records[subnode].owner = owner;
    }

    function setResolver(bytes32 node, address resolver) only_owner(node) {
        NewResolver(node, resolver);
        records[node].resolver = resolver;
    }

    function setTTL(bytes32 node, uint64 ttl) only_owner(node) {
        NewTTL(node, ttl);
        records[node].ttl = ttl;
    }
}
```

コントラクトの処理を簡単に説明します。

### Record

ドメイン名（ノード）に関連する情報を保持する構造体。
情報として所有者（`owner`）、リゾルバ（`resolver`）、およびTTL（キャッシュ期間）が含まれます。

### records
ドメイン名（ノード）とその情報（`Record`）をマッピングする配列。
ドメイン名に対応する情報を取得するために使用されます。

### 補足

このコントラクトはENSのレジストリを実装しており、ドメイン名に関連する情報を管理しています。
ユーザーはこのコントラクトを介してドメイン名の所有者やリゾルバを設定・変更できます。
また、イベントを通じてドメイン名に関連する変更が他のアプリケーションやコントラクトに通知されます。
これにより、ENSの機能が柔軟かつ安全に動作することが確保されています。

## リゾルバの実装

###  Built-in resolver

このレゾルバは、コントラクトアドレスリソースプロファイルを実装し、コントラクト自体が名前の解決を行います。
このコントラクトはシンプルな用途に適しており、ENSレジストリに直接入れ込めます。
ただし、未知の関数呼び出しに対してエラーを投げるため、一部のコントラクトの正常な動作に影響を与える可能性があります。

```javascript
contract DoSomethingUseful {
    // Other code

    function addr(bytes32 node) constant returns (address) {
        return this;
    }

    function supportsInterface(bytes4 interfaceID) constant returns (bool) {
        return interfaceID == 0x3b3b57de || interfaceID == 0x01ffc9a7;
    }

    function() {
        throw;
    }
}
```
#### addr
指定されたノードに関連するコントラクトのアドレスを返す関数。

#### supportsInterface
指定されたインターフェースIDがサポートされているかを返す関数。


###  Standalone resolver

このレゾルバは、コントラクトアドレスプロファイルを実装し、所有者のみがレコードを更新できる基本的なレゾルバです。
このレゾルバは、ENSのレジストリにデプロイした後、レジストリを更新してこのレゾルバを指定することで使用できます。

```javascript
contract Resolver {
    event AddrChanged(bytes32 indexed node, address a);

    address owner;
    mapping(bytes32=>address) addresses;

    modifier only_owner() {
        if(msg.sender != owner) throw;
        _
    }

    function Resolver() {
        owner = msg.sender;
    }

    function addr(bytes32 node) constant returns(address) {
        return addresses[node];    
    }

    function setAddr(bytes32 node, address addr) only_owner {
        addresses[node] = addr;
        AddrChanged(node, addr);
    }

    function supportsInterface(bytes4 interfaceID) constant returns (bool) {
        return interfaceID == 0x3b3b57de || interfaceID == 0x01ffc9a7;
    }

    function() {
        throw;
    }
}
```

#### addr
指定されたノードに関連するコントラクトのアドレスを返す関数。

#### setAddr
コントラクトのアドレスを設定できる関数。
所有者のみが呼び出すことができる。


###  Public resolver

このレゾルバもコントラクトアドレスプロファイルを実装し、所有者のみがレコードを更新できる関数。
レジストリを使用して誰がエントリーの更新を許可されるかを決定します。
このレゾルバもENSのレジストリにデプロイし、使用することができます。

```javascript
contract PublicResolver {
    event AddrChanged(bytes32 indexed node, address a);
    event ContentChanged(bytes32 indexed node, bytes32 hash);

    ENS ens;
    mapping(bytes32=>address) addresses;

    modifier only_owner(bytes32 node) {
        if(ens.owner(node) != msg.sender) throw;
        _
    }

    function PublicResolver(address ensAddr) {
        ens = ENS(ensAddr);
    }

    function addr(bytes32 node) constant returns (address ret) {
        ret = addresses[node];
    }

    function setAddr(bytes32 node, address addr) only_owner(node) {
        addresses[node] = addr;
        AddrChanged(node, addr);
    }

    function supportsInterface(bytes4 interfaceID) constant returns (bool) {
        return interfaceID == 0x3b3b57de || interfaceID == 0x01ffc9a7;
    }

    function() {
        throw;
    }
}
```

#### addr
指定されたノードに関連するコントラクトのアドレスを返す関数。

#### setAddr
コントラクトのアドレスを設定できる関数。
所有者のみが呼び出すことができる。

## レジストラの実装

このレジストラでは、ユーザーが最初に名前を要求した場合、無料で名前を登録することができます。
コントラクトは特定のルートノード（`rootNode`）のサブノードを登録するために使用されます。

```javascript
contract FIFSRegistrar {
    ENS ens;
    bytes32 rootNode;

    function FIFSRegistrar(address ensAddr, bytes32 node) {
        ens = ENS(ensAddr);
        rootNode = node;
    }

    function register(bytes32 subnode, address owner) {
        var node = sha3(rootNode, subnode);
        var currentOwner = ens.owner(node);
        if(currentOwner != 0 && currentOwner != msg.sender)
            throw;

        ens.setSubnodeOwner(rootNode, subnode, owner);
    }
}
```

コントラクトをデプロイする際に、ENSのアドレス（`ensAddr`）とルートノード（`node`）を指定して初期化します。
`register`関数を呼び出すことで、新しい名前を登録できます。
登録する名前は、ルートノードと指定されたサブノードの組み合わせ（`sha3(rootNode, subnode)`）によって一意に識別されます。
既に名前が他のユーザーによって所有されている場合、または自分以外の所有者が既に存在する場合は、名前を登録できません（他のユーザーが所有する名前を奪うことはできません）。
最初に特定の名前を要求したユーザーは、その名前を無料で登録できます。
他のユーザーが同じ名前を登録したい場合は、コントラクトによって拒否されます。
FIFSレジストラは、最初に来た者が名前を無料で登録できるという公平な仕組みを提供します。
これは独自のドメイン名を登録するためのシンプルな方法として使用できます。


## 最後に

今回は「アドレスを人間が覚えやすい名前と紐付ける、ENSと呼ばれるサービスの元となる提案である**ERC137**」についてまとめてきました！
いかがだったでしょうか？

実装については今後追記していきます。

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

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

## 採用強化中！

CryptoGamesでは一緒に働く仲間を大募集中です。

https://www.wantedly.com/companies/cryptogames

この記事で書いた自分の経験からもわかるように、裁量権を持って働くことができて一気に成長できる環境です。
「ブロックチェーンやWeb3、NFTに興味がある」、「スマートコントラクトの開発に携わりたい」など、少しでも興味を持っている方はまずはお話ししましょう！
