Hyperledger Fabric のチェーンコードを実装する

[オープンソースのブロックチェーン Hyperledger Fabric を試してみる](http://qiita.com/kyrieleison/items/ec5223bbe6140822df4a) の続き

Hyperledger Fabric のチェーンコードを書いてみたときのメモです。
Bluemix 上の [IBM Blockchain](https://new-console.ng.bluemix.net/catalog/services/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`: 開発前のスケルトンチェーンコード(エラー処理とログ出力のみで何もしません)
    - https://github.com/IBM-Blockchain/learn-chaincode/tree/master/start
- `finished`: 開発後のチェーンコード(キーとバリューを受取って保持するプログラムが追加されています)
    - https://github.com/IBM-Blockchain/learn-chaincode/tree/master/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/blob/master/examples/chaincode/go/chaincode_example02/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