オープンソースのブロックチェーン Hyperledger Fabric を試してみる の続き
Hyperledger Fabric のチェーンコードを書いてみたときのメモです。
Bluemix 上の IBM Blockchain を使っています。
途中までのメモ書きなので、また随時書き足していく予定です。
チェーンコードとは
チェーンコードは Fabric ネットワーク上にデプロイされ、トランザクションを処理するプログラムコードです。
Fabric では、スマートコントラクトをチェーンコードとして実現しています。
今現在、Fabric がサポートしているチェーンコードの開発言語は Go と Java で、JavaScript も近い将来に追加されるとのことです。
今回は Go を使ってチェーンコードを書いていきます。
開発言語については Q&A で取り上げられています:
http://hyperledger-fabric.readthedocs.io/en/latest/FAQ/chaincode_FAQ/
チェーンコードを書いてみる
今回は、IBM Blockchain の learn-chaincode にチェーンコード実装のチュートリアルがあるため、それに沿って進めてみます。
https://github.com/IBM-Blockchain/learn-chaincode
サンプルのチェーンコードをクローンする
まず https://github.com/IBM-Blockchain/learn-chaincode をフォークし、ローカル環境の $GOPATH/src/github.om/<GITHUB_ID>/
にクローンします。
% cd $GOPATH
% mkdir -p src/github.com/<YOUR_GITHUB_ID_HERE>/
% cd src/github.com/<YOUR_GITHUB_ID_HERE>/
% git clone https://github.com/<YOUR_GITHUB_ID_HERE>/learn-chaincode.git
GOPATH は Go のワークスペースの場所を指します。未設定の場合は、以下のように設定してください。
(GOPATH について: http://golang-jp.org/doc/code.html#GOPATH)
export GOPATH=$HOME/.go
export PATH=$PATH:$GOPATH/bin
その他、Go の環境セットアップについては以降割愛します。Go についての基礎はこちら。
- Go 言語のインストール http://golang.jp/install
- Go コードの書き方 http://golang-jp.org/doc/code.html
クローンしたリポジトリには、start
と finished
の二つのバージョンのチェーンコードがあります。
-
start
: 開発前のスケルトンチェーンコード(エラー処理とログ出力のみで何もしません) -
finished
: 開発後のチェーンコード(キーとバリューを受取って保持するプログラムが追加されています)
start
にあるチェーンコードを変更しつつ、迷ったら finished
を都度参照するという形で進めます。
ローカル環境でビルドを確認する
次に start
のチェーンコードをローカル環境でビルドしてみて、正常に実行可能かの確認をします。
% cd $GOPATH/src/github.com/<YOUR_GITHUB_ID_HERE>/learn-chaincode/start
% go build ./
chaincode_start.go:23:2: cannot find package "github.com/hyperledger/fabric/core/chaincode/shim" in any of:
/Users/<USER_NAME>/.anyenv/envs/goenv/versions/1.4/src/github.com/hyperledger/fabric/core/chaincode/shim (from $GOROOT)
/Users/<USER_NAME>/.go/src/github.com/hyperledger/fabric/core/chaincode/shim (from $GOPATH)
ビルドするとエラーが出ました。環境設定が上手くいっていないようなので、以下を確認します。
https://github.com/IBM-Blockchain/learn-chaincode/blob/6827af167c8e46c289c1cb23d96b36fe97846875/docs/setup.md
Hyperledger Fabric が必要なようなので、クローンします。
Bluemix 上の IBM Blokchain では v0.5-developer-preview
が使用されているので、それをローカルにクローンします。
% mkdir -p $GOPATH/github.com/hyperledger
% cd $GOPATH/github.com/hyperledger
% git clone -b v0.5-developer-preview https://github.com/hyperledger-archives/fabric.git
また、v0.5-developer-preview
に対応しているのは v1.0
ブランチなので、そちらに切り替えます。
% cd $GOPATH/src/github.com/<YOUR_GITHUB_ID_HERE>/learn-chaincode
% git checkout v1.0
これで再度ビルドします。
% cd $GOPATH/src/github.com/<YOUR_GITHUB_ID_HERE>/learn-chaincode/start
% go build ./
% ls
chaincode_start.go start
今度は上手くいきました。ビルド結果の start
が作成されています。
チェーンコードを書き換える
作成したコードはこちらにあります。
https://github.com/kyrieleison/learn-chaincode/blob/v1.0/start/chaincode_start.go
チェインコードを書くときには、3つの関数(init
, invoke
, query
)と main
関数が必要です。
-
init
- チェーンコードをデプロイしたときに呼び出される関数で、チェーンコードに必要な初期化処理を行います
- 今回は初期化処理として、与えられた引数を
hello_world
キーに対するバリューとしてステートに追加しています
-
invoke
- 何か実作業を行うための関数を呼び出したいときに呼び出す関数で、チェーン上のブロックにグループ分けされたトランザクションとして記録されます。
function
パラメータを受取って、その関数を実行するようにします - 今回は
function
パラメータとしてwrite
を受け取ったとき、二つの引数のうち一つをキー、もう一つをそのキーのバリューとしてステートに書き込みます
- 何か実作業を行うための関数を呼び出したいときに呼び出す関数で、チェーン上のブロックにグループ分けされたトランザクションとして記録されます。
-
query
- チェーンコードのステートを照会するときに呼び出す関数で、キーバリュー型のステートを取得します
- 今回は
function
パラメータとしてread
を受け取ったとき、その引数をキーとしてステートを取得し、バリューを返します
REST API で確認する
チェーンコードがデプロイ・実行・参照できるかを、IBM Blockchain の REST API で確認してみます。
チェーンコードの操作にはユーザログインが必要なので、Registrar API で任意のユーザにログインします。
POST https://b43adcdd35ba429d9a5045427432ee82-vp2.us.blockchain.ibm.com:444/registrar
{
"enrollId": "<ENROLL_ID>",
"enrollSecret": "<ENROLL_SECRET>"
}
{
"OK": "Login successful for user '<ENROLL_ID>'."
}
無事にログインできたら、Chaincode API でチェインコードをデプロイします。chaincodeID.path
には、自分のフォークしたリポジトリのパスを指定します。
POST https://b43adcdd35ba429d9a5045427432ee82-vp2.us.blockchain.ibm.com:444/chaincode
{
"jsonrpc": "2.0",
"method": "deploy",
"params": {
"type": 1,
"chaincodeID": {
"path": "https://github.com/kyrieleison/learn-chaincode/start"
},
"ctorMsg": {
"function": "init",
"args": [
"hi there"
]
},
"secureContext": "<ENROLL_ID>"
},
"id": 1
}
{
"jsonrpc": "2.0",
"error": {
"code": -32001,
"message": "Deployment failure",
"data": "Error when deploying chaincode: Error getting chaincode package bytes: Error getting code 'go get' failed with error: 'exit status 2'\n# github.com/kyrieleison/learn-chaincode/start\n/go/_usercode_/440950172/src/github.com/kyrieleison/learn-chaincode/start/chaincode_start.go:41: undefined: shim.ChaincodeStubInterface\n/go/_usercode_/440950172/src/github.com/kyrieleison/learn-chaincode/start/chaincode_start.go:50: undefined: shim.ChaincodeStubInterface\n/go/_usercode_/440950172/src/github.com/kyrieleison/learn-chaincode/start/chaincode_start.go:63: undefined: shim.ChaincodeStubInterface\n"
},
"id": 1
}
デプロイが失敗しました。
undefined: shim.ChaincodeStubInterface...
というエラーが出ていますが、これは v2.0
のAPIです。
v1.0
のブランチに対してコードをプッシュしていて、しかし パスに指定していた https://github.com/kyrieleison/learn-chaincode/start
はデフォルトブランチである master
ブランチのコードを go get
するため、v2.0
のコードが読み込まれてエラーになったようです。
そこで、リポジトリにある v1.0
ブランチを、デフォルトブランチに変更して再度実行してみました。
POST https://b43adcdd35ba429d9a5045427432ee82-vp2.us.blockchain.ibm.com:444/chaincode
{
"jsonrpc": "2.0",
"method": "deploy",
"params": {
"type": 1,
"chaincodeID": {
"path": "https://github.com/kyrieleison/learn-chaincode/start"
},
"ctorMsg": {
"function": "init",
"args": [
"hi there"
]
},
"secureContext": "<ENROLL_ID>"
},
"id": 1
}
{
"jsonrpc": "2.0",
"result": {
"status": "OK",
"message": "25b17bfcb3236793de06648ca4a094e2922af5f21ff68431486205b1c59e3fbede189133b4a71ba04af63ee061a11ef7a705f6a4ed01409377bf705865b0c51c"
},
"id": 1
}
無事にデプロイに成功し、チェーンコードIDが返ってきました。
続けて、チェーンコードを実行してみます。chaincodeID
に返ってきたハッシュを指定し、function
に実行したい関数名(ここでは write
)、args
に関数へ渡したい引数(ここでは hi there
と go away
)を指定します。
POST https://b43adcdd35ba429d9a5045427432ee82-vp2.us.blockchain.ibm.com:444/chaincode
{
"jsonrpc": "2.0",
"method": "invoke",
"params": {
"type": 1,
"chaincodeID": {
"name": "25b17bfcb3236793de06648ca4a094e2922af5f21ff68431486205b1c59e3fbede189133b4a71ba04af63ee061a11ef7a705f6a4ed01409377bf705865b0c51c"
},
"ctorMsg": {
"function": "write",
"args": [
"hi there", "go away"
]
},
"secureContext": "<ENROLL_ID>"
},
"id": 1
}
{
"jsonrpc": "2.0",
"result": {
"status": "OK",
"message": "a9e2faeb-feb2-4753-a0a4-ab3f9738f165"
},
"id": 1
}
チェーンコードが実行され、トランザクションの UUID が返ってきました。
最後はステートが更新されたのかを確認するため、ステートを参照します。
実行時と同様に chaincodeID
に返ってきたハッシュを指定し、function
に実行したい関数名(ここでは read
)、args
に関数へ渡したい引数(ここでは hi there
というキー)を指定します。
POST https://b43adcdd35ba429d9a5045427432ee82-vp2.us.blockchain.ibm.com:444/chaincode
{
"jsonrpc": "2.0",
"method": "query",
"params": {
"type": 1,
"chaincodeID": {
"name": "25b17bfcb3236793de06648ca4a094e2922af5f21ff68431486205b1c59e3fbede189133b4a71ba04af63ee061a11ef7a705f6a4ed01409377bf705865b0c51c"
},
"ctorMsg": {
"function": "read",
"args": [
"hi there"
]
},
"secureContext": "<ENROLL_ID>"
},
"id": 1
}
{
"jsonrpc": "2.0",
"result": {
"status": "OK",
"message": "go away"
},
"id": 1
}
hi there
というキーに対して、go away
というバリューが返ってきました。
これでチェーンコードが実行されたことできちんとステートが更新されていることが分かりました。
チェーンコードを書くのに役立つコードやドキュメント
Hyperledger Fabric の examples/chaincode
に Go と Java のサンプルコードがあるので、どういったことができるのか参考になると思います。
公式ドキュメントでは chaincode_example02.go がよく使われています。
https://github.com/hyperledger/fabric/tree/master/examples/chaincode
また、ソースコードは fablic/core/chaincode
にあります。
https://github.com/hyperledger/fabric/tree/master/core/chaincode
チェーンコードAPIのドキュメントはこちらにあるので、本格的に始める場合は参照すると良さそうです。
https://godoc.org/github.com/hyperledger/fabric/core/chaincode/shim