Hyperledger Fabric bna deployでコケる
症状
xxxx-xx-xx 07:38:40.662 UTC [chaincode-platform] generateDockerfile -> DEBU 525 FROM ibmblockchain/fabric-baseos:0.3.1 ADD binpackage.tar /usr/local/bin LABEL org.hyperledger.fabric.chaincode.id.name="project-test" \ org.hyperledger.fabric.chaincode.id.version="0.13.0" \ org.hyperledger.fabric.chaincode.type="GOLANG" \ org.hyperledger.fabric.version="1.0.1" \ org.hyperledger.fabric.base.version="0.3.1" ENV CORE_CHAINCODE_BUILDLEVEL=1.0.1 xxxx-xx-xx 07:38:40.695 UTC [util] DockerBuild -> DEBU 526 Attempting build with image ibmblockchain/fabric-ccenv:1.0.1 xxxx-xx-xx 07:39:48.650 UTC [dockercontroller] deployImage -> DEBU 527 Created image: dev-peer0.org1.example.com-project-test-0.13.0-02208064ee2904b44e223320e32de8877802e6faf608fa7043c12fba3162b076 xxxx-xx-xx 07:39:48.650 UTC [dockercontroller] Start -> DEBU 528 start-recreated image successfully xxxx-xx-xx 07:39:48.650 UTC [dockercontroller] createContainer -> DEBU 529 Create container: dev-peer0.org1.example.com-project-test-0.13.0 xxxx-xx-xx 07:39:48.696 UTC [dockercontroller] createContainer -> DEBU 52a Created container: dev-peer0.org1.example.com-project-test-0.13.0-02208064ee2904b44e223320e32de8877802e6faf608fa7043c12fba3162b076 xxxx-xx-xx 07:39:48.871 UTC [dockercontroller] Start -> DEBU 52b Started container dev-peer0.org1.example.com-project-test-0.13.0 xxxx-xx-xx 07:39:48.871 UTC [container] unlockContainer -> DEBU 52c container lock deleted(dev-peer0.org1.example.com-project-test-0.13.0) xxxx-xx-xx 07:43:40.188 UTC [eventhub_producer] validateEventMessage -> DEBU 52d ValidateEventMessage starts for signed event 0xc4216ada10 xxxx-xx-xx 07:43:40.188 UTC [eventhub_producer] deRegisterHandler -> DEBU 52e deregistering event type: BLOCK xxxx-xx-xx 07:43:40.201 UTC [eventhub_producer] Chat -> ERRO 52f error during Chat, stopping handler: rpc error: code = Canceled desc = context canceled
以下の環境変数でchaincodeのコンテナからピアが見えるようになるらしい。
- CORE_PEER_ADDRESSAUTODETECT=true
nodejs用フレームワークnestjsのサンプルを触ってみた(1)
公式docs
https://docs.nestjs.com/
早い話がサーバーサイド版Angular(?)
cats-app example
https://github.com/nestjs/nest/tree/master/examples/01-cats-app
起動
git clone https://github.com/nestjs/nest.git cd ./nest/examples/01-cats-app/ npm i npm run start
localhost:3000
にサーバーが立ち上がります。
とりあえず基本的な部分を使ってみる
その前に cats.controller.ts の中身を一部変更する。
@Post() // @Roles('admin') async create(@Body() createCatDto: CreateCatDto) { this.catsService.create(createCatDto); }
権限の問題を避けるため @Roles
デコレーターをコメントアウトした。 ユーザー権限をメソッド単位で管理できるのは便利そう。
create() メソッドを動かすためのPOSTリクエストを投げてみる。
##Doraemon を追加 $curl -v -H "Accept: application/json" -H "Content-type: application/json" -X POST -d '{ "name":"Doraemon","age":12,"breed":"R-01FR001-MKII"}' http://localhost:3000/cats/ ##Doramichan を追加 $curl -v -H "Accept: application/json" -H "Content-type: application/json" -X POST -d '{ "name":"Doramichan","age":10,"breed":"R-01FR001-MKII"}' http://localhost:3000/cats/
次に findAll() メソッドを動かすためのGETリクエストを投げてみる。
$curl http://localhost:3000/cats/ ##catsが帰ってくる。 {"data":[{"name":"Doraemon","age":12,"breed":"R-01FR001-MKII"},{"name":"Doramichan","age":10,"breed":"R-01FR001-MKII"}]}
こんな感じの簡単なHTTPサーバーになっていることがわかる。
構成
/nest/examples/01-cats-app/src$ tree . ├── app.module.ts ├── cats │ ├── cats.controller.spec.ts │ ├── cats.controller.ts ★ │ ├── cats.module.ts │ ├── cats.service.ts ★ │ ├── cats.service.spec.ts ★(作成) │ ├── dto │ │ └── create-cat.dto.ts │ └── interfaces │ └── cat.interface.ts (略) └── main.ts
★...とりあえず今回見てみたところ
コントローラー
クライアントからリクエストを受けてレスポンスを返すクラス。API と処理を記述。
cats/cats.controller.ts
(略) @Controller('cats') @UseGuards(RolesGuard) @UseInterceptors(LoggingInterceptor, TransformInterceptor) export class CatsController { constructor(private readonly catsService: CatsService) {} @Post() // @Roles('admin') async create(@Body() createCatDto: CreateCatDto) { return this.catsService.create(createCatDto); } @Get() async findAll(): Promise<Cat[]> { return await this.catsService.findAll(); } @Get(':name') async findOne(@Param() params):Promise<Cat> { return await this.catsService.findOne(params.name); } }
constructor で catsService
を消費しているあたり Angular でいう *component.ts
でDIしている感じ。
(参考:https://angular.io/guide/dependency-injection)
最後にGet http://localhost:3000/cats/:name
を追加して、猫を名前で検索できるようにした。
@Param()
は expressでいうところの req.params
で、 params.name のようにプロパティにアクセスできる。
コンポーネント
コントローラー(や他のコンポーネント)にDIされるもの。サービスのコンポーネントにはDBとやりとりするためのSQLやORMを書く。
cat.service.ts
(略) /* 追加 */ import { HttpException } from "@nestjs/core"; @Component() export class CatsService { private readonly cats: Cat[] = []; create(cat: Cat) { this.cats.push(cat); return {"message":"success"}; } findAll(): Cat[] { return this.cats; } findOne(name: String): Cat { let cat: Cat = this.cats.find(cat => cat.name == name); if (!cat) { throw new HttpException('Specified cat does not exist !', HttpStatus.BAD_REQUEST); } else { return cat; } } }
テスト
cats.service
に対するunitテストを書いた。
cat.service.spec.ts
import { Test } from '@nestjs/testing';; import { CatsService } from './cats.service'; import { Cat } from './interfaces/cat.interface'; import { CreateCatDto } from './dto/create-cat.dto'; describe('CatsService', () => { let catsService: CatsService; beforeEach(async () => { const module = await Test.createTestingModule({ components: [CatsService], }).compile(); catsService = module.get<CatsService>(CatsService); }); describe('create Test', () => { it('should return a success message', async () => { let cat: Cat = { "name": "Doraemon", "age": 12, "breed": "R-01FR001-MKII" }; let result = { "message": "success" }; expect(await catsService.create(cat)).toEqual(result); }); }); describe('findAll Test', () => { it('should return an array of cats', async () => { let cat1, cat2: Cat; cat1 = { "name": "Doraemon", "age": 12, "breed": "R-01FR001-MKII" }; cat2 = { "name": "Doramichan", "age": 10, "breed": "R-01FR001-MKII" }; let result = [cat1,cat2]; await catsService.create(cat1); await catsService.create(cat2); expect(await catsService.findAll()).toEqual(result); }); }); describe('findOne Test', () => { it('should retrive a specified cat from cats array', async () => { let cat1, cat2: Cat; cat1 = { "name": "Doraemon", "age": 12, "breed": "R-01FR001-MKII" }; cat2 = { "name": "Doramichan", "age": 10, "breed": "R-01FR001-MKII" }; await catsService.create(cat1); await catsService.create(cat2); expect(await catsService.findOne("Doraemon")).toEqual(cat1); expect(await catsService.findOne("Doramichan")).toEqual(cat2); }); it('should return an error object when incoming name does not exist in cats array', async () => { let cat1, cat2: Cat; cat1 = { "name": "Doraemon", "age": 12, "breed": "R-01FR001-MKII" }; cat2 = { "name": "Doramichan", "age": 10, "breed": "R-01FR001-MKII" }; await catsService.create(cat1); await catsService.create(cat2); expect( () => { catsService.findOne("mi-chan") }).toThrow(); }); }); });
expect(FUNCTION).toThrow();
expectの引数はfunctionなので、無名関数の形で渡す必要がある。
(参考:https://ajsblackbelt.wordpress.com/2014/05/18/jasmine-tests-expect-tothrow/)
最後に作った猫検索を試してみる。
## Doraemon 取得 $curl http://localhost:3000/cats/Doraemon/ {"data":{"name":"Doraemon","age":12,"breed":"R-01FR001-MKII"}} ## Doramichan 取得 $ curl http://localhost:3000curl http://localhost:3000/cats/Doramichan/ {"data":{"name":"Doramichan","age":10,"breed":"R-01FR001-MKII"}} ## mi-chan 取得 $ curl http://localhost:3000/cats/mi-chan/ {"statusCode":400,"message":"Specified cat does not exist !"}
感想
nodejs + express から typescript に移行するのが中々難しいなと思っていたので、nestjsのようなフレームワークの存在は非常にありがたいです。
様々な機能があるみたいなので、今後も使ってみようと思います。
Hyperledger Composer Queryの中でORDER BY が使えない
Hyperledger Composerでクエリ
Hyperledger Composer ではSQLライクにブロックチェーンネットワークから特定のアセットや参加者を取得するためのクエリが準備できる。
(クエリを使う場合、channelに参加するpeerのDBをデフォルトのlevelDBからCouchDBに変更しておく必要がある。)
クエリは queries.qry
の中に以下のように定義する。
query sampleQuery{ description: "sampleQuery" statement: SELECT jp.org.acme.sampleAsset WHERE (_$parameter < fieldName) ORDER BY [fieldName ASC] LIMIT 25 }
ただし、しばらくORDER BYを使うとエラーとなっていた。
解決法
channelに参加しているpeerに紐付いたCouchDBのコンテナ内で以下のコマンドを叩けばOK.
$ curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' -d ' { "index": { "fields": [ { "data.<FIELD_NAME>": "<asc or desc>" } ] }, "type": "json" }' 'http://localhost:5984/<CHANNEL_NAME>/_index'
参考
Hyperledger Composer Node.js SDK から Fabric Network のクエリを実行する場合の定形
コード
var bizNetworkConnection = new BusinessNetworkConnection(); bizNetworkConnection.connect(connectionProfileName, businessNetworkIdentifier, user, password) .then(() => { // exec a query return bizNetworkConnection.query('specificQuery',{parameter:value}); }) .then((results) => { return Promise.all(results.map( (result) => { // output or substitute console.log(result.specificField); })); }) .catch((error) => { throw error; }) .then(() => { return bizNetworkConnection.disconnect(); });;
説明
bizNetworkConnection.query()
は Resource
クラスのアレイを返し、要素の各フィールドにpropertyとしてアクセスできる。
AssetRegistry
や ParticipantRegistry
と勘違いして toJSON
メソッドを使ったらエラーになったため覚書き。
注意点
composer-client@0.13.0
の段階のもの。現在は変更あり。
Writing a Node.js application | Hyperledger Composer
Hyperledger ComposerのBNAファイルをundeployする
BNA(Business Network Archive)ファイルとは
ネットワークピアで実行されるスマートコントラクトのモデル定義(参加者、アセット、トランザクションなど)とトランザクションロジックの情報が書かれたもの。
Hyperledger Composer を使わない場合、GolangでChaincodeと呼ばれるスマートコントラクトを一から書かなければならないが、BNAはそれをラップして便利にした感じ(語弊あり)。
デプロイの仕方についてはまぁまぁ情報があるが、BNAを修正したい場合の情報は少ないのでメモ。
デプロイしたBNAファイルを消す
以下に書き方があるのだが、 card
という概念は比較的最近のもので古いヴァージョンでは対応していない。
https://hyperledger.github.io/composer/reference/composer.network.undeploy.htmlhyperledger.github.io
自分の環境 ver 0.13.0
の場合以下でイケた。
composer network undeploy -n <businessNetworkName> -p <connectionProfileName> -i PeerAdmin -s randomString
ちなみにこの businessNetworkName
には composer-src/package.json
内の"name"
フィールドを書けば良い。
ver 0.13.0 のhelp
/usr/local/bin/composer network undeploy [options] Options: --help Show help [boolean] -v, --version Show version number [boolean] --businessNetworkName, -n The business network name [string] [required] --connectionProfileName, -p The connection profile name [string] [required] --enrollId, -i The enrollment ID of the user [string] [required] --enrollSecret, -s The enrollment secret of the user [string]
注意点
undeployしたネットワークを再度deployした場合その場でエラーは吐かないが、Node.js SDK APIからアクセスできず、businessNetworkNameを別物にするハメになった。
おそらく単にbnaを修正する目的の場合はpackage.json内の"version"を上げて行くのが良いと思われる。
参考
nest.jsが起動しない
症状
nest.jsが npm run start
で起動しない。 initializeが完了という表示のみ。
解決法
typescript
と ts-node
のヴァージョン下げたら動いた。
ts-node version to ~3.2.0 typescript version to ~2.3.4
参考
注意点
Angular で require を使うとき
コンパイルエラーになる
error TS2304: Cannot find name 'require'.
export する class の前に以下を追加
declare var require: any;