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
としています。
{
"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
{
"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];
でメソッドを動的に取得してるのがなんともですが、ひとまずお試しなので^^
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ファイルが作成されているはずです。
"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
なのが原因かもしれません。。。
{
"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