はじめに
『DApps開発入門』という本や色々記事を書いているかるでねです。
今回は、ENS(Ethereum Name Service)でEthereum以外のブロックチェーンアドレスも解決可能にする仕組みを提案しているERC2304についてまとめていきます!
以下にまとめられているものを翻訳・要約・補足しながらまとめていきます。
他にも様々なEIPについてまとめています。
概要
ERC2304は、ENS(Ethereum Name Service)のリゾルバにおける addr フィールドに対して、異なるブロックチェーンのアドレスも扱えるようにするための新しい引数形式を追加する提案です。
これにより、Ethereum以外のブロックチェーンのアドレスもENSを通じて解決(リゾルブ)できるようになります。
動機
ENSはマルチコイン対応ウォレットの普及に伴い、Ethereum以外のチェーンのアドレスも扱う必要性が出てきています。 ウォレット開発者からは、ENSを用いて他のブロックチェーンのアドレスも解決できる機能の要望が寄せられていました。 ERC2304は、そのようなニーズに応えるために、クライアント間で一貫した方法で非Ethereumチェーンのアドレスを登録・取得できる仕組みを標準化することを目的としています。
仕様
関数
ERC2304では、ENSリゾルバに以下のような新しい関数が追加されます。
addr
function addr(bytes32 node, uint coinType) public view returns (bytes memory) {
return _addresses[node][coinType];
}任意のコインタイプのアドレスを取得する関数。
この関数は、特定のENSノード(node)と暗号通貨の種類(coinType)を指定することで、その通貨に対応するアドレスをバイナリ形式で返します。
coinType は SLIP-0044 に定義された仮想通貨の識別番号を用います。
未登録の場合は空バイト列が返されます。
この関数のEIP165におけるインターフェースIDは 0xf1cb7e06 です。
EIP165については以下の記事を参考にしてください。
引数
node- 対象となるENSノード。
coinType- 仮想通貨の種類(SLIP-0044に準拠)。
戻り値
bytes- 対応するアドレスのバイナリ形式。
setAddr
function setAddr(bytes32 node, uint coinType, bytes memory a) public authorised(node) {
emit AddressChanged(node, coinType, a);
if (coinType == COIN_TYPE_ETH) {
emit AddrChanged(node, bytesToAddress(a));
}
_addresses[node][coinType] = a;
}任意のコインタイプのアドレスを登録する関数。
与えられたENSノードとコインタイプに対してアドレスを登録または更新します。Ethereum(60)の場合は従来の AddrChanged イベントも併せて発行します。
引数
node- アドレスを登録するENSノード。
coinType- コインの種類(SLIP-0044に準拠)。
a- 登録するアドレス(バイナリ形式)。
イベント
新たに以下のイベントが定義されます。
AddressChanged
event AddressChanged(bytes32 indexed node, uint coinType, bytes newAddress);コインタイプを指定したアドレスが変更された時に発行されるイベント。 ENSノードに紐づく特定の暗号通貨アドレスが変更された場合に発行され、マルチコイン対応の新しい形式に対応しています。
パラメータ
node- ENS名の namehash(ノード)。
coinType- 暗号通貨のコインタイプ(SLIP-0044に準拠)。
newAddress- 新しく設定されたアドレス(バイナリ形式)。
アドレスのバイナリ形式とエンコーディング
ENSに保存するアドレスは、各ブロックチェーンにおけるネイティブなバイナリ形式である必要があります。 つまり、一般的なテキスト形式でのチェックサム付きアドレスではなく、バイト列としての形式です。
以下は代表的なチェーンの coinType とそのエンコーディング方式の一部です。
| 暗号通貨 | Coin Type | エンコーディング形式 |
|---|---|---|
| Bitcoin | 0 | P2PKH (0x00), P2SH (0x05), SegWit(bech32のbc) |
| Ethereum | 60 | ChecksummedHex(EIP-55) |
| Litecoin | 2 | P2PKH (0x30), P2SH (0x32, 0x05), SegWit(ltc) |
| Dogecoin | 3 | P2PKH (0x1e), P2SH (0x16) |
| Ethereum Classic | 61 | ChecksummedHex |
| Rootstock | 137 | ChecksummedHex(RSKIP-60) |
| Ripple | 144 | base58checkまたはX-address(タグ付き) |
| Bitcoin Cash | 145 | CashAddr(bech32派生) |
| Binance Chain | 714 | Bech32(プレフィックスはbnb) |
各アドレス形式の詳細
-
P2PKH / P2SH Bitcoinなどで使われる形式。Base58Checkでエンコードされ、デコード後はバージョンバイト+20バイトのハッシュとして構成されます。
-
SegWit (Bech32) witness version と witness program に基づく形式。人間が読める部分(例:
bc)と機械可読な部分から構成されます。 -
ChecksummedHex Ethereum系で使われる形式で、
0xを除いた後の16進文字列をデコードした20バイトのデータを使用します。 チェックサム付き(EIP-55, RSKIP-60)の形式でないアドレスは警告またはエラー扱いとします。 -
Ripple Ripple固有のBase58Check形式を使用し、
r-addressやX-addressをデコードしてバイナリ保存します。 -
CashAddr(Bitcoin Cash) Bitcoin Cash用に定義されたbech32派生形式で、デコード後のP2PKH/P2SH形式で保存します。
-
Bech32(Binance等)
bnbなどのプレフィックスを持つbech32形式で、デコードされたバイト列をそのまま保存します。
実装例
pragma solidity ^0.5.8;
contract AddrResolver is ResolverBase {
bytes4 constant private ADDR_INTERFACE_ID = 0x3b3b57de;
bytes4 constant private ADDRESS_INTERFACE_ID = 0xf1cb7e06;
uint constant private COIN_TYPE_ETH = 60;
event AddrChanged(bytes32 indexed node, address a);
event AddressChanged(bytes32 indexed node, uint coinType, bytes newAddress);
mapping(bytes32=>mapping(uint=>bytes)) _addresses;
/**
* Sets the address associated with an ENS node.
* May only be called by the owner of that node in the ENS registry.
* @param node The node to update.
* @param a The address to set.
*/
function setAddr(bytes32 node, address a) external authorised(node) {
setAddr(node, COIN_TYPE_ETH, addressToBytes(a));
}
/**
* Returns the address associated with an ENS node.
* @param node The ENS node to query.
* @return The associated address.
*/
function addr(bytes32 node) public view returns (address) {
bytes memory a = addr(node, COIN_TYPE_ETH);
if(a.length == 0) {
return address(0);
}
return bytesToAddress(a);
}
function setAddr(bytes32 node, uint coinType, bytes memory a) public authorised(node) {
emit AddressChanged(node, coinType, a);
if(coinType == COIN_TYPE_ETH) {
emit AddrChanged(node, bytesToAddress(a));
}
_addresses[node][coinType] = a;
}
function addr(bytes32 node, uint coinType) public view returns(bytes memory) {
return _addresses[node][coinType];
}
function supportsInterface(bytes4 interfaceID) public pure returns(bool) {
return interfaceID == ADDR_INTERFACE_ID || interfaceID == ADDRESS_INTERFACE_ID || super.supportsInterface(interfaceID);
}
}- Ethereum用アドレス (
coinType = 60) のショートカット関数としてaddr(bytes32)を引き続きサポート - マッピング構造
mapping(bytes32 => mapping(uint => bytes))を用いて、ノードとコインタイプに応じたアドレスを保存 setAddrにてアドレス登録時にAddressChangedを必ず発行supportsInterfaceにて EIP-165 に基づいたインターフェース識別を実装
ADDR_INTERFACE_ID
bytes4 constant private ADDR_INTERFACE_ID = 0x3b3b57de;ENSの従来の addr(bytes32) 関数に対応するインターフェースID。
この識別子は、EIP165 準拠の supportsInterface 関数において、Ethereumアドレスの取得をサポートしているかを識別するために使用されます。
ADDRESS_INTERFACE_ID
bytes4 constant private ADDRESS_INTERFACE_ID = 0xf1cb7e06;マルチコインアドレス対応の addr(bytes32,uint) 関数に対応するインターフェースID。
このIDは、EIP165 に基づき、複数のブロックチェーンのアドレスをサポートするリゾルバであることを示します。
COIN_TYPE_ETH
uint constant private COIN_TYPE_ETH = 60;Ethereumのコインタイプを示す定数。 SLIP-0044 によって定義されたEthereumのコインタイプ番号で、60番が割り当てられています。
AddrChanged
event AddrChanged(bytes32 indexed node, address a);Ethereumアドレスが変更された時に発行されるイベント。
従来の addr(bytes32) の形式に対応したアドレスが変更された際に使用されます。
ERC137の仕様に準拠しています。
ERC137については以下の記事を参考にしてください。
パラメータ
node- ENS名の namehash(ノード)。
a- Ethereumアドレス。
_addresses
mapping(bytes32 => mapping(uint => bytes)) _addresses;ENSノードごとのコインタイプに対応するアドレスを保存するマッピング。
_addresses[node][coinType] の形式でアクセスすることで、任意のコインのバイナリ形式アドレスを取得または保存することができます。
addr
function addr(bytes32 node) public view returns (address) {
bytes memory a = addr(node, COIN_TYPE_ETH);
if (a.length == 0) {
return address(0);
}
return bytesToAddress(a);
}ENSノードに紐づくEthereumアドレスを取得する関数。
Ethereum(coinType 60)に限定された addr(bytes32) の形式で、対応するバイナリアドレスを取得し、デコードしてEthereumアドレスとして返します。
引数
node- Ethereumアドレスを取得したいENSノード。
戻り値
address- 紐づいたEthereumアドレス(登録がなければ address(0))。
setAddr
function setAddr(bytes32 node, address a) external authorised(node) {
setAddr(node, COIN_TYPE_ETH, addressToBytes(a));
}Ethereumアドレスを設定する関数。
coinType = 60(Ethereum)を使って、指定されたENSノードにEthereumアドレスを登録します。
内部ではバイナリ変換を行い、マルチコイン形式の setAddr を呼び出します。
引数
node- アドレスを登録するENSノード。
a- Ethereumアドレス。
supportsInterface
function supportsInterface(bytes4 interfaceID) public pure returns (bool) {
return interfaceID == ADDR_INTERFACE_ID || interfaceID == ADDRESS_INTERFACE_ID || super.supportsInterface(interfaceID);
}このコントラクトが指定されたインターフェースをサポートしているかどうかを確認する関数。
EIP165に準拠した形式で、従来の addr(bytes32) とERC2304で提案される addr(bytes32,uint) の両方をサポートしているかを判定します。
引数
interfaceID- インターフェース識別子(bytes4形式)。
戻り値
bool- 指定のインターフェースをサポートしていれば
true。そうでなければfalse。
- 指定のインターフェースをサポートしていれば
互換性
ERC2304は、EIP137で定義されている addr(bytes32) インターフェースとの互換性をもつよう設計されています。
具体的には、既存のENSリゾルバがこのEIPの新仕様に対応する場合、以下の点を満たす必要があります。
addr(bytes32 node)の戻り値は、addr(bytes32 node, uint coinType)をcoinType = 60(Ethereumのコインタイプ)として呼び出した場合と常に同じ値を返す必要があります。AddrChangedイベント(EIP137のイベント)が発行されるような操作では、必ずAddressChangedイベント(ERC2304で定義されたイベント)も同時に発行される必要があります。この時、coinTypeは60を指定します。- 逆に、
coinType = 60のAddressChangedイベントが発行された場合も、必ずAddrChangedイベントを発行する必要があります。
このように設計することで、既存のENS利用者やツールが影響を受けることなく、新しいマルチコイン対応機能を取り入れることができます。
引用
Nick Johnson nick@ens.domains, "ERC-2304: Multichain address resolution for ENS [DRAFT]," Ethereum Improvement Proposals, no. 2304, September 2019. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-2304.
最後に
今回は「ENS(Ethereum Name Service)でEthereum以外のブロックチェーンアドレスも解決可能にする仕組みを提案しているERC2304」についてまとめてきました! いかがだったでしょうか?
質問などがある方は以下のTwitterのDMなどからお気軽に質問してください!
他の媒体でも情報発信しているのでぜひ他も見ていってください!

