Node.jsとNEM-sdkを利用して暗号資産XEMのばらまきサイトを超簡単に作る。

いろいろ広まりだしてきた仮想通貨の話題。
でも、それってどれだけ便利なの？ネットバンキングで事足りることない？
そう思う人も多いかと思います。

送金する側にとっては、送金速度や手数料が安くなる程度の実感しかないかもしれませんが、
大きく異なってくるのはお金を受け取る側にさまざまな多様性が生まれることです。
銀行とやりとりするための堅牢なシステムを構築する必要がないため、
１回限り、あるいは少額の販売でも柔軟な通貨の受け取りが可能になります。

ただ、実際ビットコインなどで送金システムを組もうとすると、
安定したAPIサーバを独自で構築する必要があり、ライブラリは十分あるものの
高度に熟練したサーバメンテナンスの知識が必要になり、
結局は大企業かあるいはそれに依存した形でしかシステムを構築できなかったりします。

ところが仮想通貨NEMだと状況は異なります。
APIの設計が非常に優れており、開発者はNEMを構築するスーパーノードにアクセスするだけで
アプリケーションが完成してしまいます。
IT革命で情報がそこらじゅうに落ちている感覚が広まったかと思いますが、
次に起こる情報革命では「そこらじゅうに受け渡し可能なパラメータが落ちている」という感覚が生まれると思います。

それを簡単に実現できるのがNEMです。

まずは、Faucet(蛇口)と呼ばれるばらまきサイトを作成してみました。
サーバ側のプログラムをとりあえず、はっつけておきます。
Node.js の知識が若干必要です。
プログラム的にはNodeをゲームなどのサーバ側で利用している方はすぐに理解できる内容だと思います。
説明は都度追加していきますのでしばらくお待ちください。


```js:faucet.js
const url        = require('url');
const express    = require('express');
const bodyParser = require('body-parser');
const nem        = require("nem-sdk").default;

const FAUCET_ADDRESS = "NCQN6FPL7W7SQIP7K5JNNS6XV2LFOR7JW35XPPJZ";
const FAUCET_PRIKEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";

var app = express();
app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());
app.use(function(req, res, next){
    res.header("Access-Control-Allow-Origin","*");
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    next();
})
app.listen(1337);

var NODES = Array(
"http://alice2.nem.ninja",
"http://alice3.nem.ninja",
"http://alice4.nem.ninja",
"http://alice5.nem.ninja",
"http://alice6.nem.ninja",
"http://alice7.nem.ninja"
);

var faucet_amount = 0;
var address_expire = {};
var address_win = {};

function getEndpoint(){
	
	var node_url = NODES[Math.floor(Math.random() * NODES.length)];
	return node_url;
}

setInterval(() => {

	var endpoint = nem.model.objects.create("endpoint")(getEndpoint(), nem.model.nodes.defaultPort);
	nem.com.requests.account.data(endpoint, FAUCET_ADDRESS).then(function(res) {
		faucet_amount = Math.floor(res.account.balance / 1000000);
	});
	
	for (var key in address_expire) {
		if(address_expire[key] < Date.now()){
			delete address_expire[key];
		}
	}
		
}, 15000);

app.get('/info', (req, res) => {

	var q = url.parse(req.url,true).query;

	res.writeHead(200, {'Content-Type': 'application/json; charset=utf-8'});
	if(address_win[q.address]){
		res.write('{"status":"win","amount":' + address_win[q.address] + '}');
		delete address_win[q.address];
	
	}else{
		res.write('{"status":"lose"}');
	}
	res.end();
});

app.get('/slot', (req, res,next) => {

	var q = url.parse(req.url,true).query;
	if(q.address  in address_expire){

		address_expire[q.address] += 60 * 1000;
		return;
	}else{
		address_expire[q.address] = Date.now() + 60 * 1000;
	}

	if(Math.random() < 0.5 && faucet_amount > 10){

		let transferTx = nem.model.objects.create("transferTransaction")(q.address, 5, "Qiita Faucet");
		let common     = nem.model.objects.create("common")("", FAUCET_PRIKEY);
		let transactionEntity = nem.model.transactions.prepare("transferTransaction")(common, transferTx, nem.model.network.data.mainnet.id);
		nem.model.transactions.send(common, transactionEntity, endpoint)
			.then(function(res){
				address_win[q.address] = 5;
			});
	}

	res.writeHead(200, {'Content-Type': 'text/plain; charset=utf-8'});
	res.write("ok");
	res.end();
});

```

##解説
簡単なプログラムのように見えて、実は拡張しやすいように勘所は抑えています。

- 抽選と当選通知を非同期で分離
- 頻繁に抽選されないようなスパム対策
- Faucetの残高が無くなると当選できなくなる

追加で実装が必要な要素は以下の通りです。

- DBなどを利用したデータ永続化
- ログ出力による運営の安定化
- 当選確率などの調整


それでは！


----
以下のNEMアドレスは、全てNEM上でエンジニアが活動できるための資金に使用します。
これから私が書く記事に共感していただけましたら、ご寄附いただけると助かります。

NEM ADDRESS: NBZNQL-2JDWTG-UAW237-PXV4SS-XSPORY-43GUSW-GSB7