[ERC181] ENSのリバース解決の仕組みを理解しよう！

# はじめに

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

https://cryptogames.co.jp/

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

https://cryptospells.jp/

今回は、ENSによるEthereum Addressのリバース解決の仕組みを提案している規格である**ERC181**についてまとめていきます！

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

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

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

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

# 概要

このEIPは、イーサリアムブロックチェーンのアドレスに対して、ENSを用いてより親しみやすい名前を割り当てるシステムについて説明しています。
具体的には、イーサリアムのアドレスに、人間が読める形式の名前（例えば「`alice.eth`」）を関連付けることができるようにするためのルールと技術的な枠組みを提案しています。

このシステムでは、**TLD**、**レジストラ**、**リゾルバ**という三つの主要なコンポーネントがあります。TLDはドメイン名の最後の部分（例えば、「`.com`」や「`.eth`」）を指します。
**レジストラ**はドメイン名を管理し、ユーザーがドメイン名を登録できるようにする機能です。
**リゾルバ**は、ドメイン名を実際のイーサリアムアドレスに変換する役割を担います。

この提案の目的は、イーサリアムのアドレスをより使いやすくすることです。
例えば、長くて複雑なアドレス「`0x123...`」の代わりに、「`alice.eth`」のような短くて覚えやすい名前を使用できます。
これにより、ユーザーはアドレスを簡単に覚えたり共有したりできるようになります。
また、アドレスの所有者だけが名前を設定できるため、セキュリティが保たれます。
これはインターネットのドメイン名システムに似ており、よりユーザーフレンドリーなブロックチェーン体験を提供することを目指しています。

# 動機

名前サービスにおける「**フォワード解決**」とは、簡単な名前（例：「`alice.eth`」）を機械が理解するアドレス（例：「`0x123...`」）に変換することです。
一方、「**リバース解決**」はその逆で、機械的なアドレスから人間が理解しやすい名前への変換を行います。
これは、特に次の3つのケースで便利です。

- **アカウント監視アプリ**
    - アプリケーションはアドレスではなく、アカウントの名前を表示することができます。
    - これにより、ユーザーはアカウントをより簡単に識別できます。

- **メタデータの添付**
    - アドレスに関する記述的な情報を追加することで、そのアドレスがどのように見つかったかに関わらず情報を取得できます。

- **名前の権威ある主張**
    - 誰でも名前をアドレスに紐付けることはできますが、アドレスの所有者はリバースレコードを通じて、そのアドレスに対して「**公式**」の名前を設定することができます。

:::note info
リバース解決はイーサリアムのアドレスからユーザーフレンドリーな名前への変換を可能にし、アカウントの所有者が自分のアドレスに対して認識しやすい名前を設定できるようにします。
これにより、アプリケーションやサービスはアドレスではなく、より親しみやすい名前を表示することができ、ブロックチェーン技術の利用をより直感的で使いやすいものにします。
:::

# 仕様

リバースENSレコードについて簡単に説明すると、イーサリアムのアドレスを人間が読める名前に変換するためのシステムです。
このシステムでは、各イーサリアムアドレスに対応するリバースレコードが「`addr.reverse`」という特別なドメイン下に保存されます。

具体的には、イーサリアムアドレスを16進数の形式で小文字に変換し、その後に「`.addr.reverse`」を追加して、リバースレコード用のENS名を作成します。
例えば、ENSレジストリのアドレス「`0x112234455c3a32fd11230c42e7bccd4a84e02010`」の場合、リバースレコードは「`112234455c3a32fd11230c42e7bccd4a84e02010.addr.reverse`」という名前で保存されます。

このシステムを使って、イーサリアムアドレスのリバース解決を行うためには、コントラクト内で16進数エンコーディングが必要です。
つまり、コントラクトはアドレスを16進数の形式に変換し、ENSシステム内でそのアドレスに対応するリバースレコードを見つけるためにこの名前を使用します。
このプロセスにより、イーサリアムアドレスに関連付けられた読みやすい名前を効率的に検索し、取得できるようになります。

## レジスター

「`addr.reverse`」ドメインの管理者であるレジストラは、イーサリアムアドレスに関連するリバースENSレコードの管理を容易にするために、特定のメソッドを提供しています。
以下にメソッドをまとめます。

### `claim(address owner)`
この関数は、あるアカウントxが呼び出すと、ENSレジストリにそのアカウントのアドレス（16進数形式で表され、'.addr.reverse'が付加されたもの）の名前の所有権を別の指定されたアドレスに移転するよう指示します。
そして、移転されたENSレコードの`namehash`を返します。
この機能は、コントラクトが自身のリバースENSエントリを設定する時に、コンストラクタ内のコードを最小限に抑えることができるようにし、コール元が自分以外のオーナーを指定できるようにします。

### `claimWithResolver(address owner, address resolver)`
この関数は、アカウントxによって呼び出された場合、ENSレジストリに対してそのアカウントのアドレスに関連する名前のリゾルバを特定のリゾルバに設定し、その後、名前の所有権を指定されたアドレスに移転し、移転されたENSレコードの`namehash`を返します。
これは、カスタムリゾルバとオーナーを設定する時に、より少ないトランザクションで済むようにするための方法です。

### `setName(string name)` 関数:
この関数は、アカウントxが呼び出すと、そのアカウントのアドレスに関連する名前のリゾルバをデフォルトのリゾルバに設定し、指定された名前をその名前のレコードに設定します。
これは、ユーザーが単一のトランザクションで簡単にリバースレコードを設定できるようにするための方法です。

:::note info
これらのメソッドは、ユーザーやコントラクトが自分のイーサリアムアドレスに関連するリバースENSレコードを簡単かつ効率的に管理し、設定するために存在します。
これにより、ENSシステムを使用してイーサリアムアドレスと人間が読める名前を関連付けるプロセスがより柔軟で使いやすくなります。
:::

## リゾルバーインターフェース

イーサリアムの新しいリゾルバインターフェースには、「`name(bytes32 node)`」という関数が含まれています。
この関数の目的は、特定のENSノードに対する名前を問い合わせることです。
ENSノードとは、イーサリアムアドレスに関連付けられた一意の識別子のことを指します。

- 「`name(bytes32 node)`」関数の役割
   - この関数は、リクエストされたENSノードに登録されている名前を返すことを目的としています。
   - もしリクエストされたノードに名前が設定されていない場合、この関数は空の文字列を返します。

このインターフェースを実装したリゾルバは、イーサリアムアドレスと人間が読める名前の間でのやり取りを助けることができます。
例えば、あるイーサリアムアドレスに関連する人間が読める名前を取得したい場合、この関数を使用してその名前を簡単に検索できます。

インターフェースのIDは「`0x691f3431`」とされています。
これは、将来的にリバースENSレコードに関連する他のレコードタイプが追加される可能性を示唆しており、ENSシステムの拡張性を示しています。

:::note info
このインターフェースは、イーサリアムアドレスに対応する人間が読めるENS名を効率的に検索し取得するための手段を提供します。
これにより、ENSシステムの利用がより簡単かつ効率的になります。
:::

# レジストらの実装

このレジストラはSolidityで書かれており、これまでの仕様を実装しています。

```javascript
pragma solidity ^0.4.10;

import "./AbstractENS.sol";

contract Resolver {
    function setName(bytes32 node, string name) public;
}

/**
 * @dev Provides a default implementation of a resolver for reverse records,
 * which permits only the owner to update it.
 */
contract DefaultReverseResolver is Resolver {
    AbstractENS public ens;
    mapping(bytes32=>string) public name;

    /**
     * @dev Constructor
     * @param ensAddr The address of the ENS registry.
     */
    function DefaultReverseResolver(AbstractENS ensAddr) {
        ens = ensAddr;
    }

    /**
     * @dev Only permits calls by the reverse registrar.
     * @param node The node permission is required for.
     */
    modifier owner_only(bytes32 node) {
        require(msg.sender == ens.owner(node));
        _;
    }

    /**
     * @dev Sets the name for a node.
     * @param node The node to update.
     * @param _name The name to set.
     */
    function setName(bytes32 node, string _name) public owner_only(node) {
        name[node] = _name;
    }
}

contract ReverseRegistrar {
    // namehash('addr.reverse')
    bytes32 constant ADDR_REVERSE_NODE = 0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2;

    AbstractENS public ens;
    Resolver public defaultResolver;

    /**
     * @dev Constructor
     * @param ensAddr The address of the ENS registry.
     * @param resolverAddr The address of the default reverse resolver.
     */
    function ReverseRegistrar(AbstractENS ensAddr, Resolver resolverAddr) {
        ens = ensAddr;
        defaultResolver = resolverAddr;
    }

    /**
     * @dev Transfers ownership of the reverse ENS record associated with the
     *      calling account.
     * @param owner The address to set as the owner of the reverse record in ENS.
     * @return The ENS node hash of the reverse record.
     */
    function claim(address owner) returns (bytes32 node) {
        return claimWithResolver(owner, 0);
    }

    /**
     * @dev Transfers ownership of the reverse ENS record associated with the
     *      calling account.
     * @param owner The address to set as the owner of the reverse record in ENS.
     * @param resolver The address of the resolver to set; 0 to leave unchanged.
     * @return The ENS node hash of the reverse record.
     */
    function claimWithResolver(address owner, address resolver) returns (bytes32 node) {
        var label = sha3HexAddress(msg.sender);
        node = sha3(ADDR_REVERSE_NODE, label);
        var currentOwner = ens.owner(node);

        // Update the resolver if required
        if(resolver != 0 && resolver != ens.resolver(node)) {
            // Transfer the name to us first if it's not already
            if(currentOwner != address(this)) {
                ens.setSubnodeOwner(ADDR_REVERSE_NODE, label, this);
                currentOwner = address(this);
            }
            ens.setResolver(node, resolver);
        }

        // Update the owner if required
        if(currentOwner != owner) {
            ens.setSubnodeOwner(ADDR_REVERSE_NODE, label, owner);
        }

        return node;
    }

    /**
     * @dev Sets the `name()` record for the reverse ENS record associated with
     * the calling account. First updates the resolver to the default reverse
     * resolver if necessary.
     * @param name The name to set for this address.
     * @return The ENS node hash of the reverse record.
     */
    function setName(string name) returns (bytes32 node) {
        node = claimWithResolver(this, defaultResolver);
        defaultResolver.setName(node, name);
        return node;
    }

    /**
     * @dev Returns the node hash for a given account's reverse records.
     * @param addr The address to hash
     * @return The ENS node hash.
     */
    function node(address addr) constant returns (bytes32 ret) {
        return sha3(ADDR_REVERSE_NODE, sha3HexAddress(addr));
    }

    /**
     * @dev An optimised function to compute the sha3 of the lower-case
     *      hexadecimal representation of an Ethereum address.
     * @param addr The address to hash
     * @return The SHA3 hash of the lower-case hexadecimal encoding of the
     *         input address.
     */
    function sha3HexAddress(address addr) private returns (bytes32 ret) {
        addr; ret; // Stop warning us about unused variables
        assembly {
            let lookup := 0x3031323334353637383961626364656600000000000000000000000000000000
            let i := 40
        loop:
            i := sub(i, 1)
            mstore8(i, byte(and(addr, 0xf), lookup))
            addr := div(addr, 0x10)
            i := sub(i, 1)
            mstore8(i, byte(and(addr, 0xf), lookup))
            addr := div(addr, 0x10)
            jumpi(loop, i)
            ret := sha3(0, 40)
        }
    }
}
```

# 引用

Nick Johnson <arachnid@notdot.net>, "ERC-181: ENS support for reverse resolution of Ethereum addresses," Ethereum Improvement Proposals, no. 181, December 2016. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-181.


# 最後に

今回は「ENSによるEthereum Addressのリバース解決の仕組みを提案している規格である**ERC181**」についてまとめてきました！
いかがだったでしょうか？

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