NEMDNSについて。

なぜかツイッターアカウントが凍結されてしまったそらりすです。
とりあえずTwitterアカウント連携していたものに軒並みログインできなくなって大変困っております。
QiitaもTwitter連携しておりメールアドレスがどれに紐づいているかわからなかったのでとりあえず再作成しました。
その結果アドベントカレンダーで書こうと思っていたNEM-DNS周りのお話ができなくなってしまったのですが自分から枠を奪って書かせていただきます。

#NEM DNSとは
どのバージョンからはあまり覚えていないのですが、Nanowalletのサービス欄にいつの間にか増えていました。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/329151/10ccc769-fd9c-13e7-4580-f19d3907e019.png)

https://github.com/aenima86/NEM-DNS
どうやら「namespaceを使ってDNSみたいなことをしよう。」というもののようです。

Namespaceをもとにポインターアドレスというものを作成し、そこに対して書き込まれたメッセージの中で、Namespaceの所有者が書き込んだ最新のメッセージをDNSの情報としています。

#実装
なぜかNanowalletにあるNEM DNSの実装とGithubにあるNEM DNSの実装は挙動が少し違いました。コードを見る限りは同じなんですけどね。
なので少しコードを変えています。
また、コードはNEM-SDKを使用しています。

まず、Namespaceの所有者を調べます。

```javascript
async function getNamespaceOwner(namespace) {
	let endpoint = nem.model.objects.create("endpoint")("http://xxx.xxx.xxx.xxx", 7890);
	let res = await nem.com.requests.namespace.info(endpoint, namespace);
	return res.owner;
}
```
```
{
    "owner": "NCWIGGBM5JJB24JI4GYNDP2XNKDPK2GMLMWSBGYD",
    "fqn": "soralis",
    "height": 1751425
}
```
soralisの所有者は``NCWIGGBM5JJB24JI4GYNDP2XNKDPK2GMLMWSBGYD``です。

次に、ポインターアドレスを取得します。

```javascript
function getPointerAddress(namespace){
	let passphrase = sha256(namespace);

	//githubにはない部分
	passphrase = nem.utils.convert.hex2ua(passphrase);
	passphrase = nem.utils.convert.ua2words(passphrase, 32);
	//ここまで

	let privateKey =  nem.crypto.helpers.derivePassSha(passphrase, 1).priv;
	let keyPair = nem.crypto.keyPair.create(privateKey);
	let publicKey = keyPair.publicKey.toString();
	let address = nem.model.address.toAddress(publicKey, nem.model.network.data.testnet.id);
	return address;
}
```
soralisのポインターアドレスは``NCDV4PXSQ6JM5E3QDQD3FZY7OKS2W3LLXDVGINAS``です。
(上の2行がないと``NBZJ2S57SUCOCQ7OVEWFFCUWFANEZXLCYUHJPFHN``になりました。)

次に、ポインターアドレスに対して送信されたトランザクションを取得します。

```javascript

async function getPointerTransactions(namespace) {
	const endpoint = nem.model.objects.create("endpoint")("http://xxx.xxx.xxx.xxx", 7890);
	const address = getPointerAccount(namespace);
	let res = await nem.com.requests.account.transactions.incoming(endpoint, address);
	return res;
}
```

```
{
    "data": [
        {
            "meta": {
                "innerHash": {},
                "id": 2505688,
                "hash": {
                    "data": "a483c347b6527a17188dfd4128b24f8ee56fe5c44150b31a64a42d6db68bfd82"
                },
                "height": 1751440
            },
            "transaction": {
                "timeStamp": 106027303,
                "amount": 0,
                "signature": "0b72b4197707980114c1e8d5d981a2af2ffc5bbd874a4453fd3a143999c107a62a52f8c749fe07842e77490950ae9ae6ce52d94315ef04142783b6cfec92be05",
                "fee": 200000,
                "recipient": "NCDV4PXSQ6JM5E3QDQD3FZY7OKS2W3LLXDVGINAS",
                "type": 257,
                "deadline": 106113703,
                "message": {
                    "payload": "7b22646e73223a22796573222c22697031223a22736f72616c69732e6f7267222c2022656d61696c223a22736f72616c69732e6e656d40676d61696c2e636f6d227d",
                    "type": 1
                },
                "version": 1744830465,
                "signer": "c47171b8feeaa6f8938182f9ac287f26cb1cbe828fac2726112c010b70465f7e"
            }
        },
        {
            "meta": {
                "innerHash": {},
                "id": 2505648,
                "hash": {
                    "data": "f6e4bb00710e312e779160207ff2d0cfa2bce3ed17d5e494247e6d3c0be825f6"
                },
                "height": 1751429
            },
            "transaction": {
                "timeStamp": 106026701,
                "amount": 0,
                "signature": "724e76401f73a8497597de1aaf8ff86e6e05f41d2bdb50b1ad4eb7a7701733af9d2a5f791970e407fb37e33db56dcb4f53fa24210cb820118d2b23ff86d0e601",
                "fee": 150000,
                "recipient": "NCDV4PXSQ6JM5E3QDQD3FZY7OKS2W3LLXDVGINAS",
                "type": 257,
                "deadline": 106113101,
                "message": {
                    "payload": "7b22646e73223a22796573222c22697031223a223132372e302e302e31222c20226f74686572223a2254657374227d",
                    "type": 1
                },
                "version": 1744830465,
                "signer": "c47171b8feeaa6f8938182f9ac287f26cb1cbe828fac2726112c010b70465f7e"
            }
        }
    ]
}
```

この中から最新のDNSデータを取得していきます。

```javascript
function convertFromHex(hex) {
	let hex = hex.toString(); //force conversion
	let str = '';
	for (var i = 0; i < hex.length; i += 2)
		str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
	return str;
}

function getAccountFromPublickey(publicKey) {
	const res = nem.model.address.toAddress(publicKey, nem.model.network.data.mainnet.id)
	return res;
}

function isJSON(arg) {
	arg = (typeof arg === "function") ? arg() : arg;
	if (typeof arg !== "string") {
		return false;
	}
	try {
		arg = (!JSON) ? eval("(" + arg + ")") : JSON.parse(arg);
		return true;
	} catch (e) {
		return false;
	}
}

async function getDNSdata(){
	const ownerAcc = await getNamespaceOwner(namespace)
	const incomingTransactions = await getPointerTransactions(namespace);
	if (incomingTransactions.data.length <= 0) {
		return "not found";
	}
	for (var i in incomingTransactions.data) {
		let senderAcc =  getAccountFromPublickey(incomingTransactions.data[i].transaction.signer);
		if (senderAcc == ownerAcc) {
			break;
		}
	}
	let senderAcc =  getAccountFromPublickey(incomingTransactions.data[i].transaction.signer);
	if (senderAcc != ownerAcc) {
		return "not found";
	}
	
	let payload = convertFromHex(incomingTransactions.data[i].transaction.message.payload);

	if (!isJSON(payload)) {
		return "not found";
	}
	payload = JSON.parse(payload);
	return payload;
}
```
メッセージが正しい形式じゃない場合や持ち主以外がメッセージを送ってきている場合があるのでしっかりチェックしましょう。
また、書いてありませんがページング処理も必要になってくるかもしれません。


``{"dns":"yes","ip1":"soralis.org", "email":"soralis.nem@gmail.com"}``

これで書き込まれたDNSのデータが取得できます。
あとはこのデータを煮るなり焼くなり隙にすればOKです。

ipXにIPアドレスかドメインが入るのでそこにリダイレクトしろ～みたいな感じですね。


#疑問点や今後やってほしいこと
DNS名乗るならAとかCNAMEとかちゃんとしてほしいです。ip1にCNAMEっぽいのとIPが入る仕様はやめてほしい。


メッセージに平文で入っているのでポインターアドレスが分からなくてもDNSの中身が分かってしまう。
DNSとしてこれってどうなのって思っていまいます。
→メッセージをPAの秘密鍵で暗号化すればいいのでは？


へんなChrome拡張作るんじゃなくてDNSサーバーを公開してほしい。そうじゃないと実用性がないです・・・
私もとりあえずDNSサーバーもどきを書いたんですが動かないので誰か助けてほしい。
https://github.com/soralis-nem/nem-dns-proxy

とりあえず以上です。