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で実装できるようにしてみた」