[ERC2390] ENSに地理情報を紐付けられる仕組みを理解しよう！

# はじめに

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

https://amzn.asia/d/gxvJ0Pw

今回は、ENS（Ethereum Name Service）に地理情報ベースの名前解決機能を追加する仕組みを提案している**ERC2390**についてまとめていきます！

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

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

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

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

# 概要

GeoENSは、ENS（Ethereum Name Service）に地理的な「スプリットホライズン（Split Horizon）」の概念を導入することで、地理情報に基づいた名前解決を可能にする提案です。
これは、従来のDNSにおけるGeoDNS（地理的に応じて異なるIPアドレスを返す仕組み）に似ています。

**ERC2390**では、ENSのリゾルバに新しいインターフェースを定義し、ユーザーの地理的位置に応じて異なるリソース情報（例：IPアドレス、IPFSコンテンツハッシュなど）を返す仕組みを標準化します。
主な対象は、**EIP1185**で定義されたDNSリソースレコードを保管するENSリゾルバですが、**ERC1062**で定義されたIPFSコンテンツハッシュなど、他のインターフェースにも応用可能です。

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

https://qiita.com/cardene/items/4a625ced12f68f8606ef

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

https://qiita.com/cardene/items/84dca7cc6174508056fe

Geohashを使って位置情報に基づくリソース（IPアドレス、IPFSハッシュ、スマートコントラクトなど）を登録・取得できるようにすることで、ENSをGeoDNSのように機能させます。

これにより、ユーザーの場所や指定した地域に応じた最適なリソースを返せるようになり、分散型WebやIoT、ローカルサービスなどの地理的文脈を持つユースケースにENSを活用できるようになります。

## 動機

従来のWebでは、Amazon Route53などのGeoDNSシステムが広く使われています。
これにより、以下のような用途が実現されています。

* 地理的に近いコンテンツキャッシュへの誘導（ロードバランシング）
* 地域特化のコンテンツ配信（言語や法律に応じた表示）

しかし、現在のENSでは、地理的な情報を元にした名前解決はサポートされていません。
例えば、**ERC1185**ではENSがIPアドレスを返すことは可能ですが、それが「**どこの地域向けか**」を指定する手段がありません。

GeoENSはこの課題を解決することで、ENSに以下のような新たな可能性をもたらします。

* 現実世界の場所と紐づいたスマートコントラクトやデジタルリソースの探索
* 特定の場所にある物理オブジェクトのアクセス制御
* ENS＋IPFSでの地理・言語特化型Webホスティング
* 物理的な位置情報を持つトークンの発行

また、GeoENSは従来のGeoDNSに比べて以下の点で優れています。

* クエリに含まれる位置情報を直接使用するため、IPアドレスから位置を推定するGeoIPデータベースに依存しないためより正確
* 任意の場所の情報をクエリ可能（自身の位置に限られない）
* 特定の地理範囲に存在する複数のリソース情報（例：IPアドレスのリスト）を取得可能
* 最大8文字のGeoHashを使うことで、約±20メートルの精度でリソースを分岐可能

これにより、より柔軟で高精度な地理的名前解決が、分散型のENSシステム上で実現可能になります。

# 仕様

**ERC2390**では、ENSリゾルバに新たなインターフェースを導入することで、地理空間情報（Geo-Spatial Information）をブロックチェーン上で記録・取得できるようにする仕様を提案しています。
主な対象は、**EIP137**で定義された「アドレスリゾルバ」ですが、この考え方は**EIP1185**（DNSリソースレコード）、**EIP1062**（IPFSコンテンツハッシュ）など、あらゆるリソースタイプにも適用可能です。

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

https://qiita.com/cardene/items/17e6e1a452006f08a197

## Geohashとは？

Geohash（ジオハッシュ）は、緯度と経度の情報をビット列として交互に並べ、それをbase32でエンコードした文字列です。
文字数が長いほど、位置の精度が高くなります。
GeoENSでは、8文字のGeohashを使うことで約±20メートルの精度を実現しています。

## インターフェース

### `setGeoAddr`

```js
function setGeoAddr(bytes32 node, string calldata geohash, address addr) external authorised(node)
```

指定したENSノード（`node`）とGeohash（`geohash`）に対して、リソース（例：コントラクトアドレスやIPアドレスなど）を登録する関数。
Geohashはアドレスごとに一意である必要があり、Geohashの長さは固定で8文字とし、高精度な位置識別を可能にします。
リソースを削除したい場合は、`address(0)` を設定します。

### `geoAddr`

```js
function geoAddr(bytes32 node, string calldata geohash) external view returns (address[] memory ret)
```

指定されたENSノードとGeohashに一致するすべてのリソース（アドレス）を取得する関数。
Geohashを8文字で指定すれば、その正確な地点にあるリソースを取得できます。
より短いGeohash（例：3〜7文字）を指定することで、地理的な範囲（矩形領域）内のリソースを一括して取得できます。

## 実装上の補足

Geohashの形式には、Zオーダー（Z-order curve）、ヒルベルト曲線（Hilbert curve）、またはGoogleのS2ジオメトリライブラリのような高精度なものも使用可能です。
Geohashベースの検索は矩形領域の指定が基本ですが、複数のGeohashを組み合わせることで円形の地理的範囲に対応することも可能です。
ただし、この場合は複数回のクエリが必要になるため、若干の実装の工夫が求められます。

GeoENSはこの仕組みにより、ENSを利用した地理情報に基づく名前解決やリソース管理を、オンチェーンで柔軟かつ精密に実現することができます。

# 補足

## IPFSハッシュの格納と取得の仕組み

ENS（Ethereum Name Service）上でIPFSのコンテンツを指し示すには、IPFSのファイル識別子（Base58形式のCID）をスマートコントラクトに格納する必要があります。
しかし、スマートコントラクトではBase58形式は直接扱えないため、Base58 → Hex形式への変換が必要になります。

ENSのPublic Resolverには、IPFSハッシュを保存・取得するための次の2つのメソッドが用意されています。

```js
function setMultihash(bytes32 node, bytes hash) public only_owner(node);
```

この関数は、Base58で表されるIPFSハッシュをHex形式に変換した後、その値を指定したENSノードに対して保存するために使用します。

```js
function multihash(bytes32 node) public view returns (bytes);
```

この関数は、ENSノードに格納されたHex形式のIPFSハッシュを取得します。
その後、Hex → Base58に変換して、IPFS上のコンテンツにアクセス可能になります。

## テストケース

IPFSハッシュの変換には、JavaScript用の `multihashes` ライブラリを利用することで、簡単にBase58とHexの相互変換が可能です。

ライブラリリンク：
[https://www.npmjs.com/package/multihashes](https://www.npmjs.com/package/multihashes)

### Base58 → Hex への変換

```javascript
import multihash from 'multihashes'

export const toHex = function(ipfsHash) {
  let buf = multihash.fromB58String(ipfsHash);
  return '0x' + multihash.toHexString(buf);
}
```

この関数は、IPFSのCID（Base58形式）を受け取り、ENSで扱えるHex形式の文字列に変換します。
変換結果は、`setMultihash` に渡してブロックチェーン上に保存します。

### Hex → Base58 への変換

```javascript
import multihash from 'multihashes'

export const toBase58 = function(contentHash) {
  let hex = contentHash.substring(2) // '0x'を除去
  let buf = multihash.fromHexString(hex);
  return multihash.toB58String(buf);
}
```

この関数は、`multihash` 関数から取得したHex形式のIPFSハッシュを、ユーザーが一般的に扱うBase58形式に変換します。
これにより、IPFS上の対応するコンテンツにアクセス可能になります。

このような変換処理を通じて、ENSとIPFSの連携が円滑に行えるようになります。
開発者はこの方法を使うことで、分散型Webサイトやリソースの永続的なアドレス指定を、Ethereum上で実現できます。

# 参考実装

GeoENSやIPFS対応ENSリソースの実用的なユースケースとして、ブラウザ拡張機能による実装が提案されています。
この拡張機能を利用することで、一般ユーザーでも簡単にENS名を入力するだけで、分散型のWebリソース（例：IPFS上のWebサイト）にアクセスできるようになります。

これにより、現在の課題である「**通常のWebブラウザからENSベースのWebサイトへアクセスできない**」という問題を解決します。
特別な知識や複雑な設定なしで、DNSのようにENSを使ってWebサイトにアクセスできる体験を提供します。

## 実装リポジトリ

実際に動作する実装は以下のGitHubリポジトリに公開されています。

[https://github.com/PortalNetwork/portal-network-browser-extension](https://github.com/PortalNetwork/portal-network-browser-extension)

このリポジトリには、ENS名の解決、IPFSハッシュの取得と表示、ブラウザのURL入力バーから直接ENSサイトにアクセスできる機能などが含まれています。

## 主な機能

* ブラウザのURLバーから `.eth` ドメインを入力可能
* ENS経由で取得したIPFSハッシュを自動的に解決
* IPFS上の分散型Webサイトを通常のWebサイトと同じように表示
* 拡張機能をインストールするだけで利用可能なシンプルな導入手順

# 引用

Phyrex Tsai <phyrex@portal.network>, Portal Network Team, "ERC-1062: Formalize IPFS hash into ENS(Ethereum Name Service) resolver [DRAFT]," Ethereum Improvement Proposals, no. 1062, May 2018. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-1062.

# 最後に

今回は「ENS（Ethereum Name Service）に地理情報ベースの名前解決機能を追加する仕組みを提案している**ERC2390**」についてまとめてきました！
いかがだったでしょうか？

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