# [ENSIP1] ENSの登録と更新の仕組みを理解しよう！

> Ethereum Name Service（ENS）は、人間が理解しやすい短い名前を、コントラクトやデータといったリソースに結びつけるための仕組みを定義した提案です。

- URL: https://cardene.dev/articles/9bfb5c7b037fc33dc2ff
- 公開日: 2025-08-20
- 更新日: 2025-08-20
- トピック: Blockchain
- タグ: ethereum, solidity, smartcontract, ブロックチェーン

## はじめに

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

https://amzn.asia/d/gxvJ0Pw

今回は、ENSの取得と登録者だけが登録内容を更新できる仕組みを提案している**ENSIP1**についてまとめていきます！

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

https://docs.ens.domains/ensip/1

他にも様々なEIP・BIP・SLIP・CAIP・ENSIPについてまとめています。

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

## 概要

### 概要

Ethereum Name Service（ENS）は、人間が理解しやすい短い名前を、コントラクトやデータといったリソースに結びつけるための仕組みを定義した提案です。
例えば「`vitalik.wallet`」や「`www.mysite.swarm`」といった文字列を入力すると、紐づくウォレットアドレスやウェブサイトが表示されます。
これにより、複雑で長いアドレスを直接扱う必要がなくなり、使いやすさが向上します。
さらに、この名前とリソースの対応関係は変更可能で、ユーザーがウォレットを変えたりサイトがサーバーを移転しても、同じ名前を使い続けることができます。
また、1つの名前に複数種類のレコード（記録）を紐づけられるため、同じドメインがウェブサイト、メール、分散アプリケーション（DApp）など異なるサービスを指し示すことも可能です。

### 動機

既存のEthereum上の名前解決の仕組みは、基本的な機能は提供しているものの以下のような課題を抱えています。

* 単一のグローバル名前空間と中央集権的なリゾルバしか存在しない
* サブドメインや委任のサポートがほとんどない
* レコードの種類が限られており、複数の情報を1つの名前に結びつけられない
* 名前解決、登録、所有者情報管理などの役割が混在している

これらの制約を解消することで、次のようなユースケースが可能になります。
例えば、`live.mysite.eth` や `forum.mysite.eth` のようにサブドメインを利用できること、1つの名前にSwarmでホストされたDAppやメールサーバーなど複数のサービスを関連付けられること、DNSのレコードを扱えるようになり従来型のウェブサイトやメールのアドレスもブロックチェーンから解決できるようになるなどです。
こうした仕組みが整うことで、ENSの名前を既存のインターネット環境に橋渡しする「**DNSゲートウェイ**」も構築でき、従来のクライアントからもスムーズにブロックチェーンサービスへアクセス可能になります。

## 仕様

Ethereum Name Service（ENS）は、3つの主要な要素で構成されています。

1つ目は「**ENSレジストリ**」で、これは単一のコントラクトとして存在し、登録された名前とその名前に対応するリゾルバを結びつけます。
さらに、名前の所有者はリゾルバのアドレスを設定したり、サブドメインを作成することができます。
サブドメインは親ドメインと異なる所有者を持つことも可能です。

2つ目は「**リゾルバ**」で、名前に対して実際のリソースを返す役割を担います。
例えば、特定のコントラクトアドレスやコンテンツハッシュ、IPアドレスを返すことができます。
リゾルバは複数のレコードタイプを扱えるように設計されており、ENSIP（ENS改善提案）によって拡張されます。

3つ目は「**レジストラ**」で、利用者にドメイン名を割り当てる役割を持ちます。
ENSを更新できるのはレジストラだけであり、ENSレジストリ上のノードの所有者はそのレジストラとなります。
レジストラはスマートコントラクトとして実装されることも、外部アカウントとして実装されることもありますが、特にルートやトップレベルドメインのレジストラはコントラクトとして実装されることが想定されています。

### 名前解決の仕組み

ENSにおける名前解決は2段階のプロセスです。
まず、解決したい名前を「`namehash`」というアルゴリズムでハッシュ化し、その結果を使ってENSレジストリを呼び出します。
レジストリは対応するリゾルバのアドレスを返します。
その後、返されたリゾルバを使って求めるリソース（例えばアドレス）を問い合わせます。

例として「`beercoin.eth`」という名前に関連付けられたトークンコントラクトのアドレスを取得する場合は次のようになります。

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

このように、namehashは文字列処理を省略できるため、事前に計算してコントラクトに埋め込むことができ、どんな名前でも一定時間で高速に検索可能です。

### 名前の構文

ENSの名前は「**ラベル**」と呼ばれる要素を「`.`（ドット）」で区切って構成されます。
各ラベルは国際標準であるUTS46のルールに従って正規化されます。
これにより、大文字小文字の違いは無視され、同じスペルであれば同じ名前として扱われます。

ラベルや名前の長さに制限はありませんが、従来のDNSとの互換性のため、1つのラベルは最大64文字、全体の名前は最大255文字に収めることが推奨されています。
また、ハイフンで始まったり終わったりするラベル、数字で始まるラベルは避けることが推奨されます。

### namehashアルゴリズム

ENSで名前を使う際には、必ず「`namehash`」アルゴリズムを通してハッシュ化されます。
これは階層的な名前をラベルごとに分割し、再帰的にハッシュを計算していく手法です。
どんな入力でも固定長の一意なハッシュ値（ノード）が得られます。

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

例えば「`mysite.swarm`」を処理する場合は以下のように計算されます。

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

これにより、どのような名前でも再現性のある結果が得られます。

### namehashのテストベクトル

仕様に準拠した実装であることを確認するため、次のテストベクトルが提示されています。

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

このように、`namehash` を用いることで、ENSは階層的な名前をブロックチェーン上で効率的かつ一貫して扱えるようになっています。

### ENS Registry

ENS Registryは、すべてのENSドメインに関する基本情報を管理する中心的なコントラクトです。
名前（ノード）ごとに所有者、リゾルバ、TTLといった情報を保持しており、ドメイン管理の基盤となっています。

主な関数は以下のとおりです。

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

指定されたノードの所有者（レジストラのアドレス）を返します。

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

ノードに対応するリゾルバのアドレスを返します。
リゾルバは、実際のリソースを解決する役割を持ちます。

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

TTL（Time To Live）を返します。
TTLはノードの情報がキャッシュできる最大期間を示します。

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

ノードの所有者を別のアドレスに移転します。
この操作は現在の所有者だけが実行可能で、成功すると `Transfer` イベントが発行されます。

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

サブドメインを作成し、その所有者を設定します。
既に存在する場合は所有者を更新します。
成功すると `NewOwner` イベントが発行されます。

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

ノードに対応するリゾルバを設定します。
成功すると `NewResolver` イベントが発行されます。

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

ノードに対するTTLを設定します。
このTTLは所有者・リゾルバの情報や、リゾルバが返すデータ全体に適用されます。

### Resolver

リゾルバは、ドメイン名に対応する実際のリソース（アドレス、テキスト情報、コンテンツハッシュなど）を返す役割を担います。
リゾルバは必要に応じて複数の機能を実装できますが、仕様で定義された関数群をすべて実装する必要があります。
また、リゾルバには必ず「**存在しない関数呼び出しを拒否するフォールバック関数**」が必要です。

#### supportsInterface関数

すべてのリゾルバは次の関数を実装する必要があります。

```javascript
function supportsInterface(bytes4 interfaceID) constant returns (bool)
```

この関数は、与えられたインターフェースIDをサポートしているかどうかを返します。
インターフェースIDは、そのインターフェースを構成する関数シグネチャのハッシュをXORしたものです。
単一関数のインターフェースの場合、その関数シグネチャのハッシュそのものがインターフェースIDになります。

必ず `0x01ffc9a7` に対して `true` を返す必要があります。
これは `supportsInterface` 自身のインターフェースIDです。

現在標準化されているインターフェースには以下があります。

| インターフェース名 | インターフェースハッシュ | 定義仕様 |
| --------------------- | ------------ | ------------- |
| `addr` | `0x3b3b57de`   | コントラクトアドレスの解決 |
| `name`                  | `0x691f3431`   | **ENSIP3**       |
| `ABI`                   | `0x2203ab56`   | **ENSIP4**       |
| `text`                  | `0x59d1d43c`   | **ENSIP5**       |
| `contenthash`           | `0xbc1c58d1`   | **ENSIP7**       |
| `interfaceImplementer`  | `0xb8f2bbb4`   | **ENSIP8**       |
| `addr(bytes32,uint256)` | `0xf1cb7e06`   | **ENSIP9**       |

新しいインターフェースはENSIPによって追加される可能性があります。

### Contract Addressインターフェース

最も基本的なインターフェースが「**アドレス解決**」です。
これに対応するリゾルバは以下の関数を実装する必要があります。

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

もし対象ノードに `addr` レコードが存在しない場合、リゾルバはゼロアドレスを返さなければなりません。
クライアントはこの戻り値を必ず確認し、ゼロアドレスだった場合には資産を失う危険性があるため資金を送付やコントラクトのやりとりを止める必要があります。

アドレスが変更された場合は、以下のイベントが発行されます。

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

これにより、外部のシステムはアドレスの更新を検知できるようになります。

## Registry参考実装

```js
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`

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

ENSの各ノードに関連する情報を格納する構造体。
この構造体は、ノードの所有者、リゾルバ、TTL（有効期間）を保持します。
ENSにおける各ドメイン名は、この構造体を通して必要なメタデータを管理します。

**パラメータ**

* `owner`
    * ノードの所有者アドレス。
* `resolver`
    * ノードに紐づくリゾルバのアドレス。
* `ttl`
    * ノード情報のキャッシュ有効期間（秒単位）。

#### `records`

```javascript
mapping(bytes32=>Record) records;
```

各ノードのハッシュ値と、それに対応する `Record` 構造体を関連付けるマッピング。
ノードをキーにして、そのノードに関連する所有者・リゾルバ・TTLを記録した `Record` を取得できます。
ENSのコアデータベースとして機能します。

**パラメータ**

* `bytes32`
    * ノードのハッシュ値。
* `Record`
    * ノードに関連する情報。

#### `NewOwner`

```javascript
event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);
```

新しいサブノードの所有者が設定された時に発行されるイベント。
サブドメインの作成や更新時に発行され、どのノードのどのラベルに新しい所有者が割り当てられたかを通知します。

**パラメータ**

* `node`
    * 親ノードのハッシュ値。
* `label`
    * サブノードを識別するラベルのハッシュ。
* `owner`
    * 新しい所有者のアドレス。

#### `Transfer`

```javascript
event Transfer(bytes32 indexed node, address owner);
```

ノードの所有権が別のアドレスへ移転された時に発行されるイベント。
ノードの所有者が変更された際に、更新を通知するために発行されます。

**パラメータ**

* `node`
    * 所有権が移転されたノードのハッシュ値。
* `owner`
    * 新しい所有者のアドレス。

#### `NewResolver`

```javascript
event NewResolver(bytes32 indexed node, address resolver);
```

ノードのリゾルバが新しく設定された時に発行されるイベント。
ノードがどのリゾルバを参照するかが変更されたことを通知します。

**パラメータ**

* `node`
    * リゾルバが変更されたノードのハッシュ値。
* `resolver`
    * 新しいリゾルバのアドレス。

#### `only_owner`

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

指定されたノードの所有者のみが関数を実行できるように制御する修飾子。
関数呼び出し時に、呼び出し元アドレスが対象ノードの所有者でなければ処理を中断します。
ノードの所有権を持つアドレスだけが、そのノードに関する設定を変更できるようにします。

**パラメータ**

* `node`
    * アクセス制御の対象となるノードのハッシュ値。

### 関数

#### `ENS`

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

ENSコントラクトの初期化関数。
デプロイ時に呼び出され、ルートノード（node=0）の所有者を指定されたアドレスに設定します。

**引数**

* `owner`
    * ルートノードの所有者アドレス。

#### `owner`

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

ノードの所有者を取得する関数。
指定されたノードの所有者アドレスを返します。

**引数**

* `node`
    * 所有者を取得したいノードのハッシュ値。

**戻り値**

* `address`
    * ノードの所有者アドレス。

#### `resolver`

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

ノードのリゾルバを取得する関数。
指定されたノードに関連付けられたリゾルバのアドレスを返します。

**引数**

* `node`
    * リゾルバを取得したいノードのハッシュ値。

**戻り値**

* `address`
    * ノードに設定されたリゾルバのアドレス。

#### `ttl`

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

ノードのTTLを取得する関数。
指定されたノードのTTL（キャッシュ有効期限）を返します。

**引数**

* `node`
    * TTLを取得したいノードのハッシュ値。

**戻り値**

* `uint64`
    * ノードのTTL値。

#### `setOwner`

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

ノードの所有者を変更する関数。
対象ノードの所有権を新しいアドレスに移転します。
処理が成功すると `Transfer` イベントが発行されます。

**引数**

* `node`
    * 対象となるノードのハッシュ値。
* `owner`
    * 新しい所有者のアドレス。

#### `setSubnodeOwner`

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

サブノードの所有者を設定または更新する関数。
指定した親ノードとラベルからサブノードを生成し、その所有者を設定します。
既に存在する場合は所有者を更新します。
成功すると `NewOwner` イベントが発行されます。

**引数**

* `node`
    * 親ノードのハッシュ値。
* `label`
    * サブノードを特定するラベルのハッシュ。
* `owner`
    * 新しい所有者のアドレス。

#### `setResolver`

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

ノードに対応するリゾルバを設定する関数。
指定されたノードに新しいリゾルバアドレスを割り当てます。
成功すると `NewResolver` イベントが発行されます。

**引数**

* `node`
    * 対象となるノードのハッシュ値。
* `resolver`
    * 新しいリゾルバのアドレス。

#### `setTTL`

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

ノードのTTLを設定する関数。
指定されたノードに新しいTTLを設定します。
これにより、情報がキャッシュされる最大期間を変更できます。

**引数**

* `node`
    * 対象となるノードのハッシュ値。
* `ttl`
    * 新しいTTL値。

## サンプルResolver実装

### Built-in Resolver

このリゾルバは最も単純な形で、自身が直接リゾルバとして機能します。
指定されたノードに対して自分自身のアドレスを返すため、外部に別のリゾルバコントラクトを用意する必要がありません。
ただし、定義されていない関数の呼び出し時に `throw` を実行する仕様が、他の用途での利用を妨げる可能性があります。

```js
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`

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

自身のアドレスを返す関数。
渡されたノードに関係なく、コントラクト自身のアドレスを返します。
これにより、このコントラクトをENSレジストリに直接リゾルバとして登録可能です。

**引数**

* `node`
    * 解決対象のノード。

**戻り値**

* `address`
    * このコントラクトのアドレス。

#### `supportsInterface`

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

インターフェースの対応可否を返す関数。
コントラクトが `addr` インターフェース（`0x3b3b57de`）と `supportsInterface` 自身（`0x01ffc9a7`）を実装していることを示します。

**引数**

* `interfaceID`
    * 確認対象のインターフェースID。

**戻り値**

* `bool`
    * 実装されていれば `true`。

#### フォールバック関数

```javascript
function() {
    throw;
}
```

不明な関数呼び出しを拒否するフォールバック関数。
コントラクトに存在しない関数が呼び出された場合、必ず失敗させます。

### Standalone Resolver

このリゾルバはアドレスレコードを保持でき、所有者のみが更新可能です。
デプロイ後、ENSレジストリをこのリゾルバに紐づけ、`setAddr` を呼ぶことで名前にアドレスを設定できます。

```js
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;
    }
}
```

#### `AddrChanged`

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

ノードに関連するアドレスが変更された時に発行されるイベント。
ユーザーやクライアントに、対象ノードのアドレスが更新されたことを通知します。

**パラメータ**

* `node`
    * 更新されたノードのハッシュ。
* `a`
    * 新しいアドレス。

#### `owner`

```javascript
address owner;
```

リゾルバの所有者を記録する変数。
このリゾルバのアドレス更新権限を持つアカウントを保持します。

**パラメータ**

* `owner`
    * 所有者アドレス。

#### `addresses`

```javascript
mapping(bytes32=>address) addresses;
```

ノードごとのアドレスを保持するマッピング。
ENSノードごとに対応するアドレスを保存し、問い合わせに応じて返します。

**パラメータ**

* `bytes32`
    * ノードのハッシュ。
* `address`
    * 設定されたアドレス。

#### `only_owner`

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

所有者だけが実行できるようにする修飾子。
関数呼び出し時、送信者が `owner` でなければ処理を中断します。

#### `Resolver`

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

Standalone Resolverの初期化関数。
デプロイ時に呼び出され、コントラクト作成者を所有者に設定します。

#### `addr`

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

ノードに関連付けられたアドレスを返す関数。
マッピングに保存されているアドレスを返します。

**引数**

* `node`
    * 対象ノード。

**戻り値**

* `address`
    * 登録されているアドレス。

#### `setAddr`

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

ノードのアドレスを設定する関数。
所有者のみ実行でき、アドレスを更新すると `AddrChanged` イベントを発行します。

**引数**

* `node`
    * 対象ノード。
* `addr`
    * 新しいアドレス。

#### `supportsInterface`

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

インターフェース対応可否を返す関数。
`addr` インターフェースと `supportsInterface` をサポートしていることを示します。

**引数**

* `interfaceID`
    * 確認対象のインターフェースID。

**戻り値**

* `bool`
    * 実装されていれば `true`。

#### フォールバック関数

```javascript
function() {
    throw;
}
```

定義されていない関数呼び出しを拒否するフォールバック関数。
存在しない関数呼び出しをすべて失敗させます。

### Public Resolver

このリゾルバは基本的にStandalone Resolverと同じですが、更新権限をENSレジストリに基づいて判定します。
これにより、ENSの登録情報をそのまま利用して所有者判定を行えるため、より分散的な利用が可能です。

```js
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;
    }
}
```

#### `AddrChanged`

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

ノードのアドレスが変更された時に発行されるイベント。
対象ノードのアドレス更新を通知します。

**パラメータ**

* `node`
    * 更新されたノード。
* `a`
    * 新しいアドレス。

#### `ContentChanged`

```javascript
event ContentChanged(bytes32 indexed node, bytes32 hash);
```

ノードに関連付けられたコンテンツが変更された時に発行されるイベント。
コンテンツハッシュが更新されたことを通知します。

**パラメータ**

* `node`
    * 更新対象ノード。
* `hash`
    * 新しいコンテンツハッシュ。

#### `ens`

```javascript
ENS ens;
```

ENSレジストリの参照を保持する変数。
ノードの所有者を確認する際に利用されます。

**パラメータ**

* `ens`
    * ENSコントラクトのインスタンス。

#### `addresses`

```javascript
mapping(bytes32=>address) addresses;
```

ノードとアドレスを紐づけるマッピング。
ENSノードごとにアドレスを管理します。

**パラメータ**

* `bytes32`
    * ノードのハッシュ。
* `address`
    * 設定されたアドレス。

#### `only_owner`

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

ENSレジストリ上の所有者のみが実行できるようにする修飾子。
リゾルバ自身が所有者を持たず、ENSレジストリで登録された所有者に基づいて制御します。

**パラメータ**

* `node`
    * 権限確認の対象ノード。

#### `PublicResolver`

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

Public Resolverの初期化関数。
ENSレジストリのアドレスを受け取り、その参照を保持します。

**引数**

* `ensAddr`
    * ENSコントラクトのアドレス。

#### `addr`

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

ノードのアドレスを返す関数。
ENSノードに紐づけられたアドレスを返します。

**引数**

* `node`
    * 対象ノード。

**戻り値**

* `address`
    * 登録されているアドレス。

#### `setAddr`

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

ノードのアドレスを設定する関数。
ENSレジストリ上でそのノードの所有者である場合のみ実行でき、更新後にイベントを発行します。

**引数**

* `node`
    * 対象ノード。
* `addr`
    * 新しいアドレス。

#### `supportsInterface`

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

インターフェース対応可否を返す関数。
`addr` と `supportsInterface` をサポートしていることを示します。

**引数**

* `interfaceID`
    * 確認対象のインターフェースID。

**戻り値**

* `bool`
    * 実装されていれば `true`。

#### フォールバック関数

```javascript
function() {
    throw;
}
```

定義されていない関数呼び出しを拒否するフォールバック関数。
存在しない関数をすべて失敗させます。

## サンプルRegistrar実装

```js
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`

```javascript
ENS ens;
```

ENSレジストリへの参照を保持する変数。
この変数は、ノードの所有者取得やサブノード所有者設定など、ENSレジストリの機能を呼び出すためのハンドルとして使います。
レジストラはこの参照を通じて、登録処理の最終段で `setSubnodeOwner` を実行します。

**パラメータ**

* `ens`
    * ENSレジストリのコントラクト参照。

#### `rootNode`

```javascript
bytes32 rootNode;
```

このレジストラが管理するルートノード（親ノード）を保持する変数。
管理対象となるドメイン（例：`example.eth`）のノードハッシュを保持します。
登録時には、この `rootNode` とサブラベルをハッシュ化して対象ノードを導出します。

**パラメータ**

* `rootNode`
    * 管理対象の親ノードのハッシュ値。

### 関数

#### `FIFSRegistrar`

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

`FIFSRegistrar` の初期化関数。
レジストラ生成時にENSレジストリのアドレスと、管理対象の `rootNode` を初期化します。
これにより、以降の登録処理で参照すべきレジストリと親ノードが確定します。

**引数**

* `ensAddr`
    * ENSレジストリのアドレス。
* `node`
    * 管理対象とするルートノードのハッシュ。

#### `register`

```javascript
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);
}
```

サブノードを先着で登録する関数。
`rootNode` 配下に新しいサブノードを登録します。
まず `rootNode` と `subnode` から対象ノードを計算し、現在の所有者をENSレジストリから取得します。
所有者が未設定（`0`）であれば無料で登録可能です。
既に所有者がいる場合は、現在の呼び出し元が所有者であるときのみ上書きできます。
条件を満たす場合に、ENSレジストリの `setSubnodeOwner` を呼び出して所有者を `owner` に設定します。

**引数**

* `subnode`
    * 登録したいサブノードのラベルハッシュ。
* `owner`
    * 登録後の所有者アドレス。

## 最後に

今回は「ENSの取得と登録者だけが登録内容を更新できる仕組みを提案している**ENSIP1**」についてまとめてきました！
いかがだったでしょうか？

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