LINE Blockchain を Node.js からたたきはじめてみた

## 試してみた。

どこかに nodejs SDK があるかもしれませんが、たぶん api exprorer のサンプルが python だけっぽいけどすぐに node.js も追加されそうな気がしますが、ちょっと生でたたきはじめてみました。


## 先に、かるいまとめ

ひととおり、ドキュメントを読んだり、チュートリアルを試してみました。

もしかしたら、もう一段階深いパートナーシップ契約やアライアンスがあるのかもしれませんが、一般的な技術者が、デベロッパー申請し認可いただき、チュートリアルなどを試し、比較的簡単に知りえることができたレベルでの2020年9月現在の個人的な理解をここに記しておきます。

間違いとかあったら、ごめんなさい。
コメント欄などでご指摘いただいたら修正したいとおもっておりますので、お手柔らかに。

また、進化スピードが激しい分野だとおもいますので、すぐにこの記事は劣化する可能性が高いです。

| LINE Blockchain  | 項目 | 一般的な公開型の Ethereum Blockchain |
|:-----------:|:------------:|:------------:|
| 情報見当たらず。非公開なのかしら。プライベートブロックチェーンだからすこし安くなるのかな。どうかな。わかんない。でも安定はしそうというか安定を期待。| GAS（手数料） | この8月は高騰しつつ急降下したり不安定 |
| プライベートブロックチェーンだからすこし速いんじゃないかな。本番環境つかえてないからわからない。| トランザクション処理速度 | GAS価格とも関係するけど最近つまりぎみらしい |
| 日本語対応しているし、運転免許証など必要だけどつくりやすい| WALLET| 英語でニーモニックやら秘密鍵やら案内されたり、ちょっと敷居が高いかもしれない|
| WALLETをつくれるのは20歳以上かしら。やや使いづらいシーンもありそう。18歳以上のWALLETできないかなぁ。| 年齢制限 | とくにないとおもう|
| LINEアカウントと紐づくこともあり、必ずしも高くはない | 匿名性| 高い |
| 友達におくったりがとても簡単| 送る宛先指定 | 匿名性の高さと相反するところ |
| fungible, non-fungible, composable token が用意されていて、必要最低限かしら| 自由度| まぁ、がんばれば、いろいろつくれる|
| できることが限られている分、python や nodejs でさくっと開発しやすい| 開発の難易度| まぁ、がんばれば、いろいろつくれる|
| 絶対対応しているはずなんだけど、技術文書など見つけられず。今後の拡張・公開に期待なのかしら| Dapp| まぁ、がんばれば、いろいろつくれる|
| 手順についての記載がみつからず| 本番リリース| まぁ、がんばれば、いろいろつくれる|

LINE Blockchain の開発環境は、手触り感はとてもよいけど、いつどこまで公開あるいは解放されるかわかんないから、本気の開発はまだ時期尚早なのかしら？どうかしら？

あ、チュートリアルのドキュメントに、いくつか誤字とか、サンプルアプリにバグとか、散見されました。
でも、ちょっとかんがえたらわかるような間違いだから、ド致命的ではないです。



## nodejs なら、こんなかんじで呼び出す

```bash
$ npm install axios
```

```node.js
const crypto = require('crypto');
const axios = require('axios').default;

const apikey = 'apikey'
const secret = 'secret';

axios.defaults.baseURL = endpoint;

function jsonToQueryString(json, path) {
    // TODO: 配列対応 https://docs-blockchain.line.biz/api-guide/Authentication?id=example-4
    if (json && Object.keys(json).length > 0) {
        return (path.indexOf('?') == -1 ? '?' : '&') +
            Object.keys(json).map(function (key) {
                // return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]);
                return key + '=' + json[key];
            }).join('&');
    } else {
        return '';
    }

}

async function callAPI(method, path, params) {
    const nonce = crypto.randomBytes(16).toString('hex').slice(4, 12);
    const timestamp = Date.now();

    // POST は昇順にならべる必要あり。GET はドキュメントにとくに注記がなかった気がする。
    const orderedParams = {};
    if (params) {
        Object.keys(params).sort().forEach(function(key) {
            orderedParams[key] = params[key];
        });
    }

    const signatureBase = `${nonce}${timestamp}${method}${path}${jsonToQueryString(orderedParams, path)}`;
    console.log(signatureBase);

    const signature = crypto.createHmac('sha512', secret)
        .update(signatureBase)
        .digest('base64');

    console.log(signature);

    const headers = {
        'service-api-key': apikey,
        'nonce': nonce,
        'timestamp': timestamp,
        'signature': signature

    };

    const config= {
        method: method,
        url: path,
        baseURL: endpoint, 
        headers: headers
        data: orderedParams
    };

    try {
        let result = await axios.request(config);
            console.log(result.data.responseData)
    } catch (err) {
        if (err.response) {
            console.error(err.response.status, err.response.statusText);
            console.log(err.response.data);
            console.log(err.response.headers);
            console.log(err.response.config);
        } else {
            console.log(err);
        }
    }
}


(async()=>{

    // 4b-a) Retrieve service wallet transaction history
    // $ curl -v -X GET https://test-api.blockchain.line.me/v1/wallets/{walletAddress}/transactions?type=token/MsgMint \
    //     -H 'service-api-key: {API Key}' \
    //     -H 'nonce: {nonce}' \
    //     -H 'timestamp: {timestamp}' \
    //     -H 'signature: {signature}'
    const walletAddress = 'walletAddress';
    let path= `/v1/wallets/${walletAddress}/transactions?type=token/MsgMint`
    await callAPI('GET', path);

    // b) Retrieve a transaction with txHash
    // curl -v -X GET https://test-api.blockchain.line.me/v1/transactions/{transaction_hash} \
    //     -H 'service-api-key: {API Key}' \
    //     -H 'nonce: {nonce}' \
    //     -H 'timestamp: {timestamp}' \
    //     -H 'signature: {signature}'
    const transactionHash = 'transactionHash'; // tx hash
    path = `/v1/transactions/${transactionHash}`;
    await callAPI('GET', path);

    // Step 2-2) Mint the non-fungible item token
    // curl -v -X GET https://test-api.blockchain.line.me/v1/item-tokens/{contractId}/non-fungibles/{tokenType}/mint \
    //     -H 'service-api-key: {API Key}' \
    //     -H 'nonce: {nonce}' \
    //     -H 'timestamp: {timestamp}' \
    //     -H 'signature: {signature}' \
    //     -d '
    //     {
    //         "ownerAddress": "{owner wallet address}",
    //         "ownerSecret": "{owner wallet secret}",
    //         "name": "{token id name}",
    //         "toAddress": "{to address}"
    //     }
    //     '
    const ownerWalletAddress = walletAddress;
    const ownerWalletSecret = 'walletSecret';
    const contractId = 'xxxxxxxx';
    const tokenType = '10000001';
    const tokenIdNname = 'xxxxxxxxxx';
    const toAddress = walletAddress;
    path = `/v1/item-tokens/${contractId}/non-fungibles/${tokenType}/mint`;
    // the request body should be added after keys are sorted in the ascending order.
    await callAPI('POST', path, {
        "meta": "hogehoge",
        "name": tokenIdNname,
        "ownerAddress": ownerWalletAddress,
        "ownerSecret": ownerWalletSecret,
        "toAddress": toAddress
    });

})();
```