【Blockchain】Bitcoin Hands-on~Node.jsでアドレス発行からコイン送金まで~

今回は「Bitcoinをプログラムから動かす~アドレス発行から送金まで~」ということで、Bitcoinのtestnetを用いて、ビットコインアドレスの発行からコインの送金を行うまでのサンプルコードをご紹介していきます。

タイトルに`Hands-on`とあるように、誰でも触れるように丁寧に解説したつもりなので、読むだけで終わらせずぜひ一度触ってみてください！

※ソースはgithubにまとめてあります
→ https://github.com/daiki44/bitcoin-handson

# <目次>
1. 環境一覧
2. 環境構築
3. ビットコインアドレスを生成する
4. 残高を確認する
5. ビットコインを送金する
6. おわりに
7. 参考文献

# 1．環境一覧
- 使用OS
    - MacOS High Sierra v10.13.4
- 使用言語・ライブラリとバージョン
    - Node.js v9.5.0
        - npm v5.8.0
    - HTML5
    - jQuery v3.3.1

# 2．環境構築
Node.jsをインストールしていきます。
今回はMacの`Homebrew`を使うので、`nodebrew`を用いていますが、他OSの方は普通にインストールしていただければ問題ありません。

```bash
# nodebrewのインストール
$ brew install nodebrew

# setup後、「Export a path to nodebrew:」と表示されるので、PATHを追記するのを忘れずに！
$ nodebrew setup
$ nodebrew install-binary stable


# インストールできたか確認
$ node -v
v8.11.1

$ npm -v
5.8.0
```

作業ディレクトリへ移動し、ライブラリをインストールしていきます。

```bash
# 作業ディレクトリを作成し、移動
$ mkdir bitcoin
$ cd bitcoin

# Bitcore周りで使用
$ npm install bitcore-explorers

# Node.jsでリクエストを受け付けるために使用
$ npm install router
$ npm install finalhandler
$ npm install body-parser
```

## 「npm ERR! Error: CERT_UNTRUSTED」が発生したら…
SSLのバリデーションチェックをレジストリに対して行っていることが原因なので、一度、バリデーションを外してあげてください。
終わった後は元に戻すのを忘れずに！

```bash
# 設定変更
$ npm config set strict-ssl false

### ここでインストール ###

# 設定戻す
$ npm config set strict-ssl true
```
これで環境は整ったので、次に実装へ移ります。

# 3．ビットコインアドレスを生成する
ビットコインの送金を行うためには、ビットコインアドレスや署名用の秘密鍵が必要となりますので、まずはビットコインアドレスを生成していきます。

今回はサーバーサイドでビットコインアドレスを生成して、ブラウザに表示するという流れで作っていきます。
![getNewAddress.gif](https://qiita-image-store.s3.amazonaws.com/0/178137/76c84492-7acd-dc4b-325e-c6fb6f0afd29.gif)


```js:getNewAddress.js
// 今回はtestnetを使っていきます
// PORT番号は適当です
const NETWORK_TYPE = 'testnet';
const PORT = '28332';

// HTTPサーバー用
var http = require('http');
var router = require('router');
var finalhandler = require('finalhandler');
var bodyParser = require('body-parser');

// bitcore
var bitcore = require('bitcore-lib');

// routing設定
var appRouter = router();
appRouter.get('/getNewAddress', function(req, res) {
    // 秘密鍵の生成
    var privateKey = new bitcore.PrivateKey(NETWORK_TYPE);
    // 秘密鍵から公開鍵を生成
    var publicKey = privateKey.toPublicKey();
    // 公開鍵からビットコインアドレスを生成
    var bitcoinAddress = publicKey.toAddress(NETWORK_TYPE);

    // レスポンス用のjsonを生成
    var json = JSON.stringify({
        "privateKey": privateKey.toString(),
        "publicKey": publicKey.toString(),
        "bitcoinAddress": bitcoinAddress.toString()
    });

    res.setHeader('Content-Type', 'Content-type: application/json');
    res.end(json);
});

// サーバー起動
var server = http.createServer(function onRequest(req, res) {
    // Set CORS headers
    res.setHeader('Access-Control-Allow-Origin', '*');
	res.setHeader('Access-Control-Request-Method', '*');
	res.setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET');
	res.setHeader('Access-Control-Allow-Headers', '*');

    appRouter(req, res, finalhandler(req, res));
}).listen(PORT);

console.log("The server has been started.");
```

```bash
# Node.jsの起動
$ node getNewAddress.js
```

サーバーサイドを書き終えたら、続いてフロントを作っていきます。

```html:getNewAddress.html
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>ビットコインアドレスを生成する</title>
</head>
<body>
    <main>
        <p id="privateKey">秘密鍵</p>
        <p id="publicKey">公開鍵</p>
        <p id="bitcoinAddress">ビットコインアドレス</p>
        <button type="button" id="createAddressButton">アドレスを生成</button>
    </main>

    <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
    <script>
    $(document).ready(function() {
        $('#createAddressButton').click(function() {
            $.ajax({
                type: 'GET',
                url: 'http://localhost:28332/getNewAddress/',
                dataType: 'json'
            }).done(function(res) {
                $('#privateKey').text('秘密鍵: ' + res.privateKey);
                $('#publicKey').text('公開鍵: ' + res.publicKey);
                $('#bitcoinAddress').text('ビットコインアドレス: ' + res.bitcoinAddress);
            }).fail(function(res) {
                alert('Error');
            });
        });
    });
    </script>
</body>
</html>
```

これでビットコインのtestnetにおける秘密鍵、公開鍵、ビットコインアドレスが手に入りました。
この後、送金を行う際にビットコインアドレスや秘密鍵を使用するので、好きな値を2セット（送信用と受信用）どこかに保存しておいてください。

# 4．ビットコインを手に入れる
ビットコインアドレスが手に入ったので、続いて送金を行うためのビットコインを入手していきます。

testnetのビットコインは通貨としての価値がないため、無料で配布されています。
以下サイトにビットコインアドレスを入力して、ビットコインを手に入れてください。
[Faucet - Bitcoin TestNet sandbox](https://testnet.manu.backend.hamburg/faucet)

# 5．残高を確認する
ビットコインが確実に手に入ったかどうか、残高を確認するためのプログラムを作っていきます。
今回はサーバーサイドへビットコインアドレスをPOSTして、その残高を画面に返すという流れで作成します。

![getBalance (15-36-06).gif](https://qiita-image-store.s3.amazonaws.com/0/178137/b41631b1-1fdd-87e8-5176-1c9f201eadb3.gif)


```js:getBalance.js
/**
 * getNewAddress.jsと同じように定数やライブラリを読み込み
 */

// ビットコインアドレスの残高取得
appRouter.post('/getBalance', function(req, res) {
    // 受け取ったパラメーター:
    var params = req.body;
    var bitcoinAddress = params.bitcoinAddress;

    // ビットコインアドレスに紐づく情報を取得
    insight.address(bitcoinAddress, function (e, info) {
        // レスポンス用
        var json;

        if (e) {
            console.log('エラーが発生しました');
            console.log(e);

            json = JSON.stringify({
                'code': ERROR_CODE
            });
        } else {
            json = JSON.stringify({
                'code': SUCCESS_CODE,
                'balance': info.balance
            });
        }

        res.setHeader('Content-Type', 'Content-type: application/json');
        res.end(json);
    });
});

/**
 * getNewAddress.jsと同じようにサーバーを起動する
 */
```

```bash
# Node.jsの起動
$ node getBalance.js
```

```html:getBalance.html
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>残高を確認する</title>
</head>
<body>
    <main>
        <p id="bitcoinAddress">ビットコインアドレス</p>
        <p id="balance">残高</p>
        <button type="button" id="getBalanceButton">残高を取得</button>
    </main>

    <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
    <script>
    $(document).ready(function() {
        // こちらの値を自分のビットコインアドレスへ変更してください
        const BITCOIN_ADDRESS = 'mpaW7RmJRpC9iAGmYiDk5YmMVMahpFAv3e';
        $('#bitcoinAddress').text('ビットコインアドレス: ' + BITCOIN_ADDRESS);

        $('#getBalanceButton').click(function() {
            $.ajax({
                type: 'POST',
                url: 'http://localhost:28332/getBalance/',
                dataType: 'json',
                data: {
                    bitcoinAddress: BITCOIN_ADDRESS
                }
            }).done(function(res) {
                $('#balance').text('残高: ' + res.balance + ' Satoshi');
            }).fail(function(res) {
                alert('Error');
            });
        });
    });
    </script>
</body>
</html>
```

# 6．ビットコインを送金する
testnetで使えるビットコインが手に入ったので、最後にビットコインを送金してみましょう。
サーバーサイドへ送金元、送金先のビットコインアドレスと署名用の秘密鍵をPOSTして、トランザクションを発行後、トランザクションIDを画面へ返すという流れで作っていきます。
※発行されたトランザクションIDは[BlockExplorer](https://testnet.blockexplorer.com/)という、コインのやり取りを全てWebで確認できるページへ遷移できるようにリンクにしてあります。

![sendToAddress.gif](https://qiita-image-store.s3.amazonaws.com/0/178137/7eaa8a47-c1fc-bbda-1315-9e53dbfe398d.gif)


ビットコインの送金は以下の手順で行われるので、手順を意識しながらソースコードを読んでみてください。
1. 送金元のビットコインアドレスが持つ、未使用のトランザクション（Unspent Transaction Output 通称UTXO）を収集する
2. UTXOを用いて送金用のトランザクションを生成し、秘密鍵で署名を行う
3. 生成したトランザクションをブロックチェーン上にブロードキャストする

```js:sendToAddress.js
/**
 * getNewAddress.jsと同じように定数やライブラリを読み込み
 */

// ビットコインの送金
appRouter.post('/sendToAddress', function(req, res) {
    // 受け取ったパラメーター:
    var params = req.body;
    var privateKey = params.privateKey;
    var fromBitcoinAddress = params.fromBitcoinAddress;
    var toBitcoinAddress = params.toBitcoinAddress;
    var amount = Number(params.amount);

    // 未使用のトランザクションを取得(Unspent Transaction Output)
    insight.getUnspentUtxos(fromBitcoinAddress, function(e, utxos) {
        if (e) {
            console.log(e);
        } else {
            // 発行するトランザクションを生成
            var tx = new bitcore.Transaction()
            .fee(10000)
            .from(utxos)
            .to(toBitcoinAddress, amount)
            .change(fromBitcoinAddress)
            .sign(privateKey);

            // 生成したトランザクションをブロードキャストする
            insight.broadcast(tx, function(e, txId) {
                // レスポンス用
                var json;

                if (e) {
                    console.log(e);

                    json = JSON.stringify({
                        'code': ERROR_CODE
                    });
                } else {
                    json = JSON.stringify({
                        'code': SUCCESS_CODE,
                        'txId': txId
                    });
                }

                res.setHeader('Content-Type', 'Content-type: application/json');
                res.end(json);
            });
        }
    });
});

/**
 * getNewAddress.jsと同じようにサーバーを起動する
 */
```

```bash
# Node.jsの起動
$ node sendToAddress.js
```

```html:sendToAddress.html
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>ビットコインを送金する</title>
</head>
<body>
    <main>
        <p>
            <input type="text" name="privateKey" placeholder="送金元の秘密鍵">
        </p>
        <p>
            <input type="text" name="fromBitcoinAddress" placeholder="送金元のビットコインアドレス">
        </p>
        <p>
            <input type="text" name="toBitcoinAddress" placeholder="送金先のビットコインアドレス">            
        </p>
        <p>
            <input type="text" name="amount" placeholder="送金額">
        </p>
        <p id="txId">
            TXID:
        </p>
        <p>
            <button type="button" id="sendToAddressButton">送金</button>            
        </p>
    </main>

    <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
    <script>
    $(document).ready(function() {
        $('#sendToAddressButton').click(function() {
            $.ajax({
                type: 'POST',
                url: 'http://localhost:28332/sendToAddress/',
                dataType: 'json',
                data: {
                    privateKey: $('input[name="privateKey"]').val(),
                    fromBitcoinAddress: $('input[name="fromBitcoinAddress"]').val(),
                    toBitcoinAddress: $('input[name="toBitcoinAddress"]').val(),
                    amount: $('input[name="amount"]').val()
                }
            }).done(function(res) {
                if (res.code == 500) {
                    alert('エラーが発生しました。');
                    return false;
                }

                // 送金が完了したらBlockExplorerで確認できるようにリンクにする
                var txId = res.txId;
                $('#txId').html('TXID: <a href="https://testnet.blockexplorer.com/tx/' + txId + '" target="_blank">' + txId + '</a>');
            }).fail(function(res) {
                alert('Error');
            });
        });
    });
    </script>
</body>
</html>
```

# 7．おわりに
githubにソースを置いておきましたので、ご興味ある方はCloneして色々と遊んでみてください。
→ https://github.com/daiki44/bitcoin-handson

Qiita上では見やすくするためにサーバーサイドのjsをそれぞれ分けていますが、1ファイルにまとめて実行でもいけるので、github上ではapp.jsとしてまとめています。

このようにBitcoreを使えば簡単にビットコインのブロックチェーンと連携ができますので、この記事を参考にぜひ一度、触ってみてください！


# 参考文献
- [GitHub bitpay/bitcore-lib](https://github.com/bitpay/bitcore-lib)
- [Block Explorer API reference](https://testnet.blockexplorer.com/api-ref)
- [GitHub pillarjs/router](https://github.com/pillarjs/router)
- [Bitcoreでビットコインのトランザクションを生成しブロードキャストする](http://noumenon-th.net/webstrategy/2015/04/21/bitcore-2/)
- [macのhomebrewでnodebrew入れてからnode.jsを入れるまで](https://qiita.com/Alex_mht_code/items/422f5ce10d9c9d5729b7)
- [npm install で SSL Error になった時の対処法。](https://blog.yug1224.com/archives/563d9b67bf652a600632d01e)