Hyperledger fabric(v0.6)でchaincodeの開発環境を構築する

※2016/11/17追記
11/10にIBM Blockchainがβ版からv1.0になったので加筆しました。合わせてIBM Blockchainのfabricのversionは0.6.1となっています。


#背景

今話題のオープンソースのブロックチェーンHyperledger fabricですが、環境を手軽に使えるという意味で私のような初心者は普段BluemixからIBM Blockchainのサービス（Starter Developer plan）を使っています。しかし、Bluemix側のサービスの計画メンテやネットワーク不具合などの原因で使用できなくなることが多々あります。例えば、最近だと新規のブロックチェーンのサービス追加が1週間以上に渡って停止されていたりしました。どうもネットワークに問題らしくデプロイに20分かかりますよーとのこと。

![error.png](https://qiita-image-store.s3.amazonaws.com/0/137163/6f1a78a1-7c19-28c2-00fb-27ea9c0b9154.png)


こうなると厄介なのはchaincodeの開発時で、長時間待ってデプロイは成功してもいざinvoke/query等で不具合が出た時は心が折れそうになります。これは一例ですが、他にもBluemix自体のレスポンスの遅さにストレスを感じてしまうことがあります。そうなるとPaaSの有難味はわかりつつも不要なデプロイ済みのchaincodeやブロックを増やさないためにも自前の環境を用意しておきたいところ。そういうわけでとりあえずchaincodeのテスト環境だけfabricのリポジトリから構築してみよう、と[fabricのドキュメント](https://hyperledger-fabric.readthedocs.io/en/latest/)を参考に見よう見まねで作ってみました。


##開発環境構築の方法
公式ドキュメントの[開発環境セットアップ方法](https://hyperledger-fabric.readthedocs.io/en/latest/Setup/Chaincode-setup/)によるとfabricの開発環境構築には大別して3つの方法があります。

- Vagrantによる構築
- Docker for Mac/Windowsを使用する方法
- Docker ToolBoxを使用している場合

後者2つはDocker-composeを利用して各peerをDockerコンテナとして起動する方法です。しかし私の場合会社の開発環境がWindowsマシン(32bit)なのでDockerの動作対象外ということで、今回は1番目のVagrantによる環境構築を行ってみました。

##fabricリポジトリのクローン

とりあえず適当な作業ディレクトリに[fabricのリポジトリ](https://github.com/hyperledger/fabric)をクローンしてきます。

```
cd <WORKING_DIR>
git clone https://github.com/hyperledger/fabric.git
```

なお、fabricのmasterブランチ(v0.7)は現在アーキテクチャ変更の最中で、RESTサービスのpeerからの分離を進めています。2016/11/18時点ではmasterを使ってもREST-APIは未実装で使用できません。chaincodeのインターフェースなども変わっておりv0.6との互換性はないのでテスト目的に合わせたバージョンを取得しましょう。IBM-Blockchainの場合「サービス状況」タブから使用されているfabricのバージョンを調べることができます。

![version2.png](https://qiita-image-store.s3.amazonaws.com/0/137163/f1c4febf-1827-42ee-b38a-bc186c960ccf.png)


コミットハッシュをもとに該当するバージョンを取得します。

```
cd <WORKING_DIR>/fabric
git checkout ee5b85c20f70b07bfd4fb6fb4dd023d49d9cbfc9
```



##VMセットアップ
開発環境用のセットアップファイルのあるディレクトリに移動します。
`cd <WORKING_DIR>/fabric/devenv`

ディレクトリの中にはLinux仮想マシンを立ち上げるためのVagrantfileやセットアップ用のシェルスクリプトが用意されています。そのままで使えますが、仮想サーバ建てるのでリソースはそれなりに食います。嫌だーという人はAWS等に建てましょう。

```:Vagrantfile
vb.customize ['modifyvm', :id, '--memory', '4096']
    vb.cpus = 2
```

ちょっと弄って？1024MB/1CPUという値にしたらうちの会社の貧弱な32bitWindowsマシンでも動いてくれました。（それでも一応chaicodeのテストはできました）
`vagrant up`
初回起動は30分ぐらい待ちます。
仮想サーバが起動したら`vagrant ssh`するだけです。

ここからは仮想サーバ上での作業となります。先程ローカルにクローンしたfabricのディレクトリはVagrantによって仮想サーバ上の$GOPATH/src/hyperledger配下に共有されています。
まずはchaincodeを登録するための開発用peerノードをビルドします。

```
cd $GOPATH/src/github.com/hyperledger/fabric/
make peer
```

ビルドが終わるとpeerコマンドから開発オプションを付けてpeerを起動できます。

`peer node start --peer-chaincodedev`

register & enrollされた特定のユーザにしかトランザクションを実行させたくない、という場合は
`CORE_SECURITY_ENABLED=true CORE_SECURITY_PRIVACY=true peer node start --peer-chaincodedev`
でセキュリティオプションを有効にできます。今回はとにかくchaincodeを動かせればいい、という目的なのでセキュリティ設定はなしで進めます。
上のセキュリティ設定も含めたpeerの設定は`peer/core.yaml`に定義されており`peer node start`実行時に読み込まれます。例えばREST-APIの設定は下記のラインで定義されています。

```:core.yaml
rest:
    # The address that the REST service will listen on for incoming requests.
    address: 0.0.0.0:7050
```

peer自体はLinux仮想マシン上でdockerコンテナとして起動され、Vagrantのポートフォワーディングの設定によってlocalhost:7050でホストマシンからREST-APIを利用できるというわけですね。（REST-APIについては後述）

```:Vagrantfile
 config.vm.network :forwarded_port, guest: 7050, host: 7050 # Openchain REST services
```

##chaincodeの準備
さて、peerだけ立ち上げても主役がいないことには意味が無いのでchaincodeを準備します。peerを起動したのとは別のターミナルを立ち上げて作業をしていきます。
[今回使うchaincode](https://github.com/tyasu/chaincode-sample)

IBM Blockchainではデプロイ時にパスとしてgithubのURLを指定しないといけないのですが、fabricではローカルのchaincodeもデプロイできます。別のターミナルを起動して、`$GOPATH/src/`以下にchaincodeを置くためのディレクトリを作成します。$GOPATH/srcの下であればどこでも良いようですが、ここでは`$GOPATH/src/github.com/hyperledger/fabric/examples/chaincode/go/sample`とします。`sample.go`を配置し、ビルドします。

```
go build
```


##chaincodeの登録
chaincodeの開発モード(devモード)ではchaincode名を指定してdeployする必要があります。正確には`ChaincodeSpec`つまりJSON-RPC 2.0形式のリクエストにおいて`deploy`メソッドに渡す`params`の`chaincodeID.name`フィールドの指定が**必須**です（※）。しかし名前は単に付ければ良いわけではなく、deployの前段階としてchaincodeの**登録(register)**という作業で名前を登録する必要があります。IBM Blockchainには存在しない手順なのでIBM Blockchain利用者は注意しましょう。
とはいっても1行で終わりです。chaincodeをビルドした先で、

`CORE_CHAINCODE_ID_NAME=mycc CORE_PEER_ADDRESS=0.0.0.0:7051 ./sample`

これで実行可能バイナリsampleがmyccという名前で登録できました。命名ルール等はなく、任意の名前が登録可能です。（とはいえ識別しやすい名前を付けましょう）registerに成功すると
` [shim] DEBU : Received REGISTERED, ready for invocations`
というメッセージが出るのでついdeployを飛ばしてinvokeから実行したくなりますが、**register後でもdeployは必要**です。

※ちなみにP2Pのネットワークを立ち上げる本番モード(`net`モード）では`chaincodeID.path`の指定が**必須**です。常に`net`モードで動作しているIBM-Blockchainではgithubのパス等を指定する必要があるというわけです。

##REST-APIへのリクエスト
あとはcurlやらPostmanで先程のREST-APIのエンドポイントに対してdeploy→invoke/queryの順でリクエストを投げていくだけです。

- Block
	- GET /chain/blocks/{Block}
- Blockchain
	- GET /chain
- Chaincode
	- POST /chaincode
- Network
	- GET /network/peers
- Registrar
	- POST /registrar
	- DELETE /registrar/{enrollmentID}
	- GET /registrar/{enrollmentID}
	- GET /registrar/{enrollmentID}/ecert
	- GET /registrar/{enrollmentID}/tcert
- Transactions
	- GET /transactions/{UUID}

今回は`localhost:7050/chaincode`に対するリクエストボディを作成し、PostmanからREST-APIの/chaincodeエンドポイントに対してPOSTしてみます。注意点としては、リクエストパラメータの`ctorMsg`フィールドは今までのIBM Blockchain(v0.5-developer-preview)では下記のように関数名と引数を別々に渡していました。

```
"ctorMsg": {
  "function": "string",
  "args": [
    "string"
      ]
 },
```

しかしv0.6では`function`フィールドは`args`に統合され、関数名を`args[0]`として受取り、引数をargs[1]以降に設定するように変更されています。

```
"ctorMsg": {
  "args": ["string"]
},
```

とはいえv0.6ではv0.5-developer-previewと互換性があり旧形式のリクエストも正常に受け付けられる仕様になっています。（IBM Blockchainのデフォルトではv0.5の形式のリクエストパラメータが設定されます）。しかし今後のバージョンでは廃止されると思われるので今回はv0.6形式でREST-APIリクエストを作成します。

###Deploy
deploy時に初期化関数`init`が実行されます。deployは少なくともargs[0](関数名）があればOKです。今回は引数を取らないので関数名のみを入れています。

```
{
  "jsonrpc": "2.0",
  "method": "deploy",
  "params": {
    "type": 1,
    "chaincodeID":{
        "name": "mycc"
    },
    "ctorMsg": {
      "args": ["init"]
    }
  },
  "id": 1
}
```

無事に帰ってきました。

```
{
  "jsonrpc": "2.0",
  "result": {
    "status": "OK",
    "message": "mycc"
  },
  "id": 1
}
```
###Invoke
次にinvokeのリクエストをPOSTしてみましょう。ここでは単一のキーに文字列を格納するだのトランザクションです。`args[1]`で書き込まれる値を指定します。

```
{
  "jsonrpc": "2.0",
  "method": "invoke",
  "params": {
    "type": 1,
    "chaincodeID": {
      "name": "mycc"
    },
    "ctorMsg": {
      "args": ["write", "This is my first chaincode"]
    }
  },
  "id": 3
}
```

結果が帰ってきたらトランザクションによってfabricにおけるworld state（世界の状態）が変更されたということです。と大げさに言いますが`("key1","This is my first chaincode")`というペアがKVSに格納されたということです。

```
{
  "jsonrpc": "2.0",
  "result": {
    "status": "OK",
    "message": "0b694fb0-6cfb-490b-9f28-e619ac2531f8"
  },
  "id": 3
}
```

###Query
記録された値をqueryで確認してみましょう。`args[1]`でキーを指定してworld stateの値を取得します。

```
{
  "jsonrpc": "2.0",
  "method": "query",
  "params": {
    "type": 1,
    "chaincodeID": {
      "name": "mycc"
    },
    "ctorMsg": {
      "args": ["read", "key1"]
    }
  },
  "id": 3
}
```


```
{
  "jsonrpc": "2.0",
  "result": {
    "status": "OK",
    "message": "This is my first chaincode"
  },
  "id": 3
}
```

デプロイも早く、いちいちタブでログ画面を開かなくてもログを標準出力に出してくれるしテストは格段に快適になりました。これなら不具合が出ても自分でトラブルシューティングできますしね。次はdockerを使ってpeerネットワークやコンセンサスアルゴリズムの検証環境も構築して試してみたいところ。