LoginSignup
3
1

More than 5 years have passed since last update.

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

Posted at

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://qiita.com/kai_kou/items/e02e34dd9abb26219a7e

懸念として開発時とデプロイ時で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://qiita.com/kai_kou/items/e02e34dd9abb26219a7e

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1