share facebook facebook twitter menu hatena pocket slack

2019.06.18 TUE

Hyperledger Fabricのチェーンコード(Node.js)をTypeScriptで実装できるようにしてみた

甲斐 甲

WRITTEN BY 甲斐 甲

Hyperledger Fabricのチェーンコードを実装するにはGo、Node.js、Javaが利用できますが、Node.jsを利用するならTypeScriptで実装したい!ので、環境を作ってみました。

Hyperledger Fabric 入門, 第 5 回: チェーンコードの書き方
https://www.ibm.com/developerworks/jp/cloud/library/cl-hyperledger-fabric-basic-5/index.html

調べてみる

やってる人いないかなぁと調べてみたらおられました!
自前で型定義されてます。oh…

Hyperledger FabricのChaincodeをNode.jsでかく(TypeScript) – Qiita
https://qiita.com/Huruikagi/items/0321460f467847e7438e

少し古い記事だったので、状況が変わってないかなとチェーンコードのリポジトリを見てみたら変わってました!

hyperledger/fabric-chaincode-node: Read-only mirror of
https://gerrit.hyperledger.org/r/#/admin/projects/fabric-chaincode-node https://github.com/hyperledger/fabric-chaincode-node

v1.3.0でTypeScript対応されてました!

fabric-chaincode-node/v1.3.0.txt at release-1.4 · hyperledger/fabric-chaincode-node
https://github.com/hyperledger/fabric-chaincode-node/blob/release-1.4/release_notes/v1.3.0.txt

Typescript definitions are now included.

環境構築してみる

下記を参考にしてTypeScriptの実装をJavaScriptにビルドできるようにします。

TypeScript + Node.js プロジェクトのはじめかた2019 – Qiita
https://qiita.com/notakaos/items/3bbd2293e2ff286d9f49

# fabric-shimがNode.js v9.0.0以下でしか利用できないので
> node -v
v8.10.0

> npm -v
5.6.0

> mkdir 任意のディレクトリ
> cd 任意のディレクトリ
> mkdir src
> mkdir dist
> touch package.json

fabric-chaincode-nodeリポジトリのREADME.mdを参考にしてpackage.jsonを用意します。
fabric-shimのバージョンを~1.4.1としています。

package.json

{
    "name": "fabric-chaincode",
    "version": "1.0.0",
    "description": "My first exciting chaincode implemented in node.js",
    "engines": {
        "node": ">=8.4.0",
        "npm": ">=5.3.0"
    },
    "scripts": {
        "start": "node chaincode.js"
    },
    "engine-strict": true,
    "license": "Apache-2.0",
    "dependencies": {
        "fabric-shim": "~1.4.1"
    }
}

必要なライブラリをインストールする

> npm install

[grpc] Success: "/Users/kai/dev/livelock/livelock-blockchain/use-typescript-with-chaincode/node_modules/grpc/src/node/extension_binary/node-v64-darwin-x64-unknown/grpc_node.node" is installed via remote
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN fabric-chaincode@1.0.0 No repository field.

added 193 packages from 113 contributors and audited 324 packages in 165.543s
found 0 vulnerabilities


> npm install --save-dev typescript @types/node

npm WARN fabric-chaincode@1.0.0 No repository field.

+ @types/node@12.0.4
+ typescript@3.5.1
added 2 packages from 25 contributors, updated 1 package and audited 326 packages in 13.708s
found 0 vulnerabilities

tsconfig.json を生成して編集する

> npx tsc --init

tsconfig.json

{
  "compilerOptions": {
    "target": "ES2017",
    "module": "commonjs",
    "outDir": "./dist",
    "strict": true,
    "moduleResolution": "node",
    "esModuleInterop": true
  },
  "include": [
    "src/**/*"
  ]
}

チェーンコードの実装をする

> touch src/main.ts

ざっくりと実装します。
TypeScriptでどう実装したらよいかは下記が参考になりました。

fabric-chaincode-node/chaincode.ts at release-1.4 · hyperledger/fabric-chaincode-node
https://github.com/hyperledger/fabric-chaincode-node/blob/release-1.4/fabric-shim/test/typescript/chaincode.ts

Invoke メソッドでメソッド名からconst method = (this as any)[fcn];でメソッドを動的に取得してるのがなんともですが、ひとまずお試しなので^^

src/main.ts

import {
  Shim,
  ChaincodeInterface,
  ChaincodeStub,
  ChaincodeResponse,
} from 'fabric-shim';


class Chaincode implements ChaincodeInterface {

  async Init(stub: ChaincodeStub): Promise<ChaincodeResponse> {
    let hello_chaincode = {
      message: "hello chaincode!"
    };
    await stub.putState("hello", Buffer.from(JSON.stringify(hello_chaincode)));
    return Shim.success();
  }


  async Invoke(stub: ChaincodeStub): Promise<ChaincodeResponse> {
    const {fcn, params} =  stub.getFunctionAndParameters();
    const method = (this as any)[fcn];
    if (!method) {
      console.error('no function of name:' + fcn + ' found');
      throw new Error('Received unknown function ' + fcn + ' invocation');
    }

    try {
      let payload = await method(stub, params);
      return Shim.success(payload);
    } catch (err) {
      console.log(err);
      return Shim.error(err);
    }
  }


  async queryHello(stub: ChaincodeStub, args: string[]): Promise<Buffer> {
    if (args.length != 1) {
      throw new Error('Incorrect number of arguments. Expecting key ex: hello');
    }
    let key = args[0];

    let valueAsBytes = await stub.getState(key);
    if (!valueAsBytes || valueAsBytes.toString().length <= 0) {
      throw new Error(key + ' does not exist: ');
    }
    console.log(valueAsBytes.toString());
    return valueAsBytes;
  }


  async putHello(stub: ChaincodeStub, args: string[]): Promise<void> {
    if (args.length != 1) {
      throw new Error('Incorrect number of arguments. Expecting 1');
    }

    var data = JSON.parse(args[0]);

    await stub.putState(data.key, Buffer.from(JSON.stringify(data)));
  }
};

Shim.start(new Chaincode());

ビルドしてみる

npx tscコマンドでビルドしてみます。

> npx tsc

無事にビルドできたらdistフォルダにjsファイルが作成されているはずです。

dist/main.js

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const fabric_shim_1 = require("fabric-shim");
class Chaincode {
    async Init(stub) {
        let hello_chaincode = {
            message: "hello chaincode!"
        };
        await stub.putState("hello", Buffer.from(JSON.stringify(hello_chaincode)));
        return fabric_shim_1.Shim.success();
    }
    async Invoke(stub) {
        const { fcn, params } = stub.getFunctionAndParameters();
        const method = this[fcn];
        if (!method) {
            console.error('no function of name:' + fcn + ' found');
            throw new Error('Received unknown function ' + fcn + ' invocation');
        }
        try {
            let payload = await method(stub, params);
            return fabric_shim_1.Shim.success(payload);
        }
        catch (err) {
            console.log(err);
            return fabric_shim_1.Shim.error(err);
        }
    }
    async queryHello(stub, args) {
        if (args.length != 1) {
            throw new Error('Incorrect number of arguments. Expecting key ex: hello');
        }
        let key = args[0];
        let valueAsBytes = await stub.getState(key);
        if (!valueAsBytes || valueAsBytes.toString().length <= 0) {
            throw new Error(key + ' does not exist: ');
        }
        console.log(valueAsBytes.toString());
        return valueAsBytes;
    }
    async putHello(stub, args) {
        if (args.length != 1) {
            throw new Error('Incorrect number of arguments. Expecting 1');
        }
        var data = JSON.parse(args[0]);
        await stub.putState(data.key, Buffer.from(JSON.stringify(data)));
    }
}
;
fabric_shim_1.Shim.start(new Chaincode());

チェーンコードのデプロイができるようにする

Peerノードのチェーンコードをデプロイするにはチェーンコードのjsファイルとpackage.json が必要となるので、package.jsonを用意します。

> touch dist/package.json

先に作成したpackage.jsonが利用できたら良かったのですが、Peerにデプロイする際にfabric-shimのバージョンが1.4.1だとPeerノードでのnpm install時にfailed: cache mode is 'only-if-cached' but no cached response available.エラーが出てしまったので1.2.1がインストールされるようにしています。

私が検証で利用したブロックチェーンネットワークがAmazon Managed Blockchainで構築したもので、Hyperledger Fabricのバージョンが1.2.1なのが原因かもしれません。。。

package.json

{
    "name": "fabric-chaincode",
    "version": "1.0.0",
    "description": "My first exciting chaincode implemented in node.js",
    "engines": {
        "node": ">=8.4.0",
        "npm": ">=5.3.0"
    },
    "scripts": {
        "start": "node main.js"
    },
    "engine-strict": true,
    "license": "Apache-2.0",
    "dependencies": {
        "fabric-shim": "~1.2.1"
  }
}

動作確認してみる

下記の手順で構築したブロックチェーンネットワークに対して、チェーンコードをデプロイしたところ、無事に動作しました。

Amazon Managed BlockchainでHyperledger Fabricのブロックチェーンネットワークを構築してみた – Qiita
https://cloudpack.media/46963

懸念として開発時とデプロイ時でfabric-shimのバージョンが異なるので利用するAPIによってはバージョン違いによる不整合があるかもしれません。(未確認)

ひとまず、TypeScriptでも実装できなくはないというご参考までに^^

参考

Hyperledger Fabric 入門, 第 5 回: チェーンコードの書き方
https://www.ibm.com/developerworks/jp/cloud/library/cl-hyperledger-fabric-basic-5/index.html

Hyperledger FabricのChaincodeをNode.jsでかく(TypeScript) – Qiita
https://qiita.com/Huruikagi/items/0321460f467847e7438e

hyperledger/fabric-chaincode-node: Read-only mirror of
https://gerrit.hyperledger.org/r/#/admin/projects/fabric-chaincode-node https://github.com/hyperledger/fabric-chaincode-node

fabric-chaincode-node/v1.3.0.txt at release-1.4 · hyperledger/fabric-chaincode-node
https://github.com/hyperledger/fabric-chaincode-node/blob/release-1.4/release_notes/v1.3.0.txt

TypeScript + Node.js プロジェクトのはじめかた2019 – Qiita
https://qiita.com/notakaos/items/3bbd2293e2ff286d9f49

fabric-chaincode-node/chaincode.ts at release-1.4 · hyperledger/fabric-chaincode-node
https://github.com/hyperledger/fabric-chaincode-node/blob/release-1.4/fabric-shim/test/typescript/chaincode.ts

Amazon Managed BlockchainでHyperledger Fabricのブロックチェーンネットワークを構築してみた – Qiita
https://cloudpack.media/46963

元記事はこちら

Hyperledger Fabricのチェーンコード(Node.js)をTypeScriptで実装できるようにしてみた

甲斐 甲

甲斐 甲

2018/7にJOIN。 最近の好みはサーバレスです。なんでもとりあえず試します。

cloudpack

cloudpackは、Amazon EC2やAmazon S3をはじめとするAWSの各種プロダクトを利用する際の、導入・設計から運用保守を含んだフルマネージドのサービスを提供し、バックアップや24時間365日の監視/障害対応、技術的な問い合わせに対するサポートなどを行っております。
AWS上のインフラ構築およびAWSを活用したシステム開発など、案件のご相談はcloudpack.jpよりご連絡ください。