[ブロックチェーン] BluemixでOpen Blockchainを動作させよう!

BluemixでOpen Blockchainが使えるようになりました。Open BlockchainはEthreum同様コードを埋め込むタイプのスマートコントラクトが実装されているブロックチェーンです。この記事では、このコードを実行させる手順を解説します。


# Bluemixの準備
2016年3月ごろよりBluemixにブロックチェーンのサービスがβ版ではありますが、提供されるようになりました。この記事ではこのブロックチェーンサービスを使用します。そのため以下の準備が必要です。

* Bluemixのアカウント
	* 現在は30日間限定ですが無料でしかもクレジットカードの登録も必要なくBluemixのアカウントを作成できます。
* ブロックチェーンサービスの起動
	* 組織、地域、スペースを作成が必要です。
	* その後の手順は[こちら](http://qiita.com/ko-he-8/items/f2fa92d951f17a659e92)を行ってください。


以上を行うと以下のような画面が表示されます。
![bc0616.png](https://qiita-image-store.s3.amazonaws.com/0/86684/143e1af1-0641-9097-09d4-4401f1e7d5b3.png)


これで3台のノード(検証ノード×2 + 認証ノード)が自動的に構築されOpen Blockchainが利用可能になります。

# Open Blockchain
Bluemixのブロックチェーンサービスは内部でOpen Blockchainが動いています。Open Blockchainはオープンソースなので自分でノードを立てることもできますが、Bluemixを使うとお手軽なのでこちらをここでは使用します。



## Chaincode
Open Blockchainでは取引をユーザが定義します。そして取引の定義が記述されたものをChaincodeと呼びます。

Chaincodeの中身はソースコードで現在はGo言語でのみ記述することができ以下の3つの関数で構築されます。

* Init関数
	* 初期化処理を記述する
	* 後述のデプロイ操作時に実行される
* Invoke関数
	* 一般的な取引に当たるようなものを記述する
* Query関数
	* データの参照を行うような処理を記述する


# Open Blockchainを使う

## ベースURIとenrollIDの確認


Bluemixのブロックチェーンサービスを起動した時の画面を開き下図赤枠のAPIsのタブを開いてください。

![base.png](https://qiita-image-store.s3.amazonaws.com/0/86684/0783d96e-4f11-050c-9d9b-11d640fa77f4.png "base.png")

* ベースURI

この青枠がAPIのベースURIです。たまに表示されないことがあるのでその時は更新やサービスの再起動などを試してください。


* enrollID

緑枠をクリックするとenrollIDとそのシークレットキーの組が表示されます。(ユーザによって権限が違うかもしれません。dashboarduser_*****というユーザだとこの後の操作を行えることを確認しています。)


## ログイン
Open Blockchainではセキュリティの項目を有効にしているとChaincodeの実行にログインしているenrollIDが必要になります。
そのためにまずはログインします。

ログインでは

```
<ベースURI>/registrar

# 例) https://ab12cde3-45gh-6i78-900-abcf1234567d_vp1-api.blockchain.ibm.com:443/registrar 
```

に対し以下のjsonを投げることになります

```json
{ 
  "enrollId": "<enrollID>", 
  "enrollSecret": "<シークレットキー>" 
} 
```

なのでcurlでやると以下のような感じになります。

```
curl -H "Content-type: application/json" \
-X POST \
-d '{ "enrollId": "<enrollID>", 
"enrollSecret": "<シークレットキー>" 
} \
' <ベースURI>/registrar
```


そして結果が

```
{"OK": "Login successful for user '********'."}
```

というように返ってきます。
これでログインは完了です。

## Chaincodeの実行
Open Blockchainでトランザクションは以下の3つのステップで行われます。

1. Chaincodeのデプロイ
	* ブロックチェーン上にChaincodeを取り込ませることでChaincodeで定義された取引(Init, Invoke, Query)が実行可能になる
	* デプロイを行うと同時にInit関数が実行される
* トランザクションの実行
	* デプロイしたChaincodeのInvoke関数を呼び出しトランザクションを生成する
* データの参照
	* トランザクションにより追加・変更されたデータを参照するためQuery関数を呼び出す


ここからは用意したChaincodeのサンプル
https://github.com/ko-he-/tech-circle_blockchain/tree/master/example02
を使って上記3ステップを行なっていきます。

## Chaincodeのサンプル
今回実行するChaincodeのサンプル
https://github.com/ko-he-/tech-circle_blockchain/tree/master/example02
は以下のものです。

```go:
package main

import (
        "errors"
        "fmt"
        
        "github.com/hyperledger/fabric/core/chaincode/shim"
        )

type SimpleChaincode struct {
}

func (t *SimpleChaincode) Init(stub *shim.ChaincodeStub, function string, args []string) ([]byte, error) {
    if len(args) != 0 {
        fmt.Println("Error: Incorrect number of arguments. Expecting 0")
        return nil, errors.New("Incorrect number of arguments. Expecting 0")
    }
    
    return nil, nil
}

func (t *SimpleChaincode) Invoke(stub *shim.ChaincodeStub, function string, args []string) ([]byte, error) {
    var key string
    var value string
    var err error
    
    if len(args) != 2 {
        return nil, errors.New("Incorrect number of arguments. Expecting 2")
    }
    
    key = args[0]
    value = args[1]
    valbyte := []byte(value)
    
    
    err = stub.PutState(key, valbyte)
    if err != nil {
        return nil, err
    }
    
    
    return nil, nil
}


func (t *SimpleChaincode) Query(stub *shim.ChaincodeStub, function string, args []string) ([]byte, error) {
    if function != "query" {
        return nil, errors.New("Invalid query function name. Expecting \"query\"")
    }
    var key string
    var err error
    
    if len(args) != 1 {
        return nil, errors.New("Incorrect number of arguments. Expecting name of the person to query")
    }
    
    key = args[0]
    
    valbytes, err := stub.GetState(key)
    if err != nil {
        jsonResp := "{\"Error\":\"Failed to get state for " + key + "\"}"
        return nil, errors.New(jsonResp)
    }
    
    if valbytes == nil {
        jsonResp := "{\"Error\":\"Nil amount for " + key + "\"}"
        return nil, errors.New(jsonResp)
    }
    
    message := "Key:" + key + " Value:" + string(valbytes)
    return []byte(message), nil
}

func main() {
    err := shim.Start(new(SimpleChaincode))
    if err != nil {
        fmt.Printf("Error starting Simple chaincode: %s", err)
    }
}
```

このChaincodeでは

* Init関数
	* なにもしない
* Invoke関数
	* ブロックチェーン上にkeyとvalueのペアで文字列を保存
* Qquery関数
	* keyからvalueを呼び出し表示

を行なっています。

## Chaincodeのデプロイ(Init)

Chaincodeをデプロイして定義した取引を有効にしていきます。

デプロイでは、Chaincodeがある場所のpathを指定して実行します。今回はGithub上に置いてあるのでそこを指定します。また、Go言語のファイル自体ではなく、それが入っているフォルダを指定することも注意してください。

APIの実行は

```
<ベースURI>/chaincode

# 例) https://ab12cde3-45gh-6i78-900-abcf1234567d_vp1-api.blockchain.ibm.com:443/chaincode 
```

に対し以下のjsonを投げることになります。
この後のInvoke、Queryの実行もAPIのエンドポイントは同じです。


```json
{
  "jsonrpc": "2.0",
  "method": "deploy",
  "params": {
    "type": 1,
    "chaincodeID": {
      "path": "https://github.com/ko-he-/tech-circle_blockchain/example02"
    },
    "ctorMsg": {
      "function": "init",
      "args": [
      ]
    },
    "secureContext": "<ユーザID>"
  },
  "id": 0
}
```

なのでcurlでやると以下のような感じになります。

```
curl -H "Content-type: application/json" \
-X POST \
-d '{
  "jsonrpc": "2.0",
  "method": "deploy",
  "params": {
    "type": 1,
    "chaincodeID": {
      "path": "https://github.com/ko-he-/tech-circle_blockchain/example02"
    },
    "ctorMsg": {
      "function": "init",
      "args": [
      ]
    },
    "secureContext": "<ユーザID>"
  },
  "id": 0
}' \
<ベースURI>/chaincode
```

返ってくる結果は

```
{"jsonrpc":"2.0","result":{"status":"OK","message":"**************"}
```

このmessageの値がchaincodeIDになります。
この後のInvoke、QueryではこのchaincodeIDを使ってトランザクションを生成します。

### jsonのパラメータの解説

先ほど入力したjsonのパラメータは以下のものでした。
デプロイとこの後のInvoke・QueryではChaincodeIDの中が変わるので注意してください。

<table border=0 cellpadding=0 cellspacing=0 width=366 style='border-collapse:
 collapse;table-layout:fixed;width:366pt'>
 <col class=xl65 width=55 style='mso-width-source:userset;mso-width-alt:2346;
 width:55pt'>
 <col class=xl65 width=92 style='mso-width-source:userset;mso-width-alt:3925;
 width:92pt'>
 <col class=xl65 width=40 style='mso-width-source:userset;mso-width-alt:1706;
 width:40pt'>
 <col class=xl65 width=179 style='mso-width-source:userset;mso-width-alt:7637;
 width:179pt'>
 <tr height=21 style='height:21.0pt'>
  <td colspan=3 height=21 class=xl66 width=187 style='border-right:.5pt dotted #7F7F7F;
  height:21.0pt;width:187pt'>jsonrpc</td>
  <td class=xl69 width=179 style='border-left:none;width:179pt'>JSON-RPCのバージョン</td>
 </tr>
 <tr height=21 style='height:21.0pt'>
  <td colspan=3 height=21 class=xl66 width=187 style='border-right:.5pt dotted #7F7F7F;
  height:21.0pt;width:187pt'>method</td>
  <td class=xl69 width=179 style='border-top:none;border-left:none;width:179pt'>実行するメソッド名</td>
 </tr>
 <tr height=21 style='height:21.0pt'>
  <td rowspan=3 height=63 class=xl70 width=55 style='border-bottom:1.5pt solid #7F7F7F;
  height:63.0pt;border-top:none;width:55pt'>params</td>
  <td colspan=2 class=xl71 width=132 style='border-right:.5pt dotted #7F7F7F;
  border-left:none;width:132pt'>type</td>
  <td class=xl69 width=179 style='border-top:none;border-left:none;width:179pt'>Chaincodeのタイプ(1はGo言語)</td>
 </tr>
 <tr height=21 style='height:21.0pt'>
  <td rowspan=2 height=42 class=xl73 width=92 style='border-bottom:1.5pt solid #7F7F7F;
  height:42.0pt;border-top:none;width:92pt'>chaincodeID</td>
  <td class=xl74 width=40 style='border-top:none;border-left:none;width:40pt'>path</td>
  <td class=xl69 width=179 style='border-top:none;border-left:none;width:179pt'>Chaincodeのパス
  (デプロイのみ)</td>
 </tr>
 <tr height=21 style='height:21.0pt'>
  <td height=21 class=xl77 width=40 style='height:21.0pt;border-top:none;
  border-left:none;width:40pt'>name</td>
  <td class=xl78 width=179 style='border-top:none;border-left:none;width:179pt'>ChaincodeのID(デプロイ以外)</td>
 </tr>
 <tr height=21 style='height:21.0pt'>
  <td height=21 class=xl79 width=55 style='height:21.0pt;border-top:none;
  width:55pt'>ctorMsg</td>
  <td colspan=2 class=xl71 width=132 style='border-right:.5pt dotted #7F7F7F;
  border-left:none;width:132pt'>function, args</td>
  <td class=xl69 width=179 style='border-top:none;border-left:none;width:179pt'>実行時にChaincodeに渡される引数</td>
 </tr>
 <tr height=21 style='height:21.0pt'>
  <td colspan=3 height=21 class=xl66 width=187 style='border-right:.5pt dotted #7F7F7F;
  height:21.0pt;width:187pt'>secureContext</td>
  <td class=xl69 width=179 style='border-top:none;border-left:none;width:179pt'>実行するユーザID</td>
 </tr>
 <tr height=20 style='height:20.0pt'>
  <td colspan=3 rowspan=2 height=40 class=xl80 width=187 style='border-right:
  .5pt dotted #7F7F7F;border-bottom:1.5pt solid #7F7F7F;height:40.0pt;
  width:187pt'>id</td>
  <td class=xl83 width=179 style='border-top:none;border-left:none;width:179pt'>requestとresponseの組を</td>
 </tr>
 <tr height=20 style='height:20.0pt'>
  <td height=20 class=xl87 width=179 style='height:20.0pt;border-left:none;
  width:179pt'>識別するためのもの</td>
 </tr>

</table>


## トランザクションの実行(Invoke)

先ほどデプロイしたChaincodeを実行します。
まずはデータの保存のInvoke関数を呼び出します。

先ほどと同じく

```
<ベースURI>/chaincode

# 例) https://ab12cde3-45gh-6i78-900-abcf1234567d_vp1-api.blockchain.ibm.com:443/chaincode 
```

に対し以下のjsonを投げることになります。


```json
{
  "jsonrpc": "2.0",
  "method": "invoke",
  "params": {
    "type": 1,
    "chaincodeID": {
      "name": "<Chaincode ID>"
    },
    "ctorMsg": {
      "function": "invoke",
      "args": [
        "A101", "Book"
      ]
    },
    "secureContext": "<ユーザID>"
  },
  "id": 0
}
```

なのでcurlでやると以下のような感じになります。

```
curl -H "Content-type: application/json" \
-X POST \
-d '{
  "jsonrpc": "2.0",
  "method": "invoke",
  "params": {
    "type": 1,
    "chaincodeID": {
      "name": "<Chaincode ID>"
    },
    "ctorMsg": {
      "function": "invoke",
      "args": [
        "A101", "Book"
      ]
    },
    "secureContext": "<ユーザID>"
  },
  "id": 0
}' \
<ベースURI>/chaincode
```

返ってくる結果は

```
{"jsonrpc":"2.0","result":{"status":"OK","message":"******"},"id":0}
```

このmessageの値はこのトランザクションのUUIDになりますがこの後特に使用することはありません。

### ワールドステート
先ほどのInvokeの実行では"A101"というkeyに"Book"というvalueの
ペアで保存しました。このようにブロックチェーン上で保存されるデータのことをOpen Blockchainではワールドステートと呼びます。

ワールドステートはこのようにkeyとvalueのペアでかつvalueはbyte配列になっている必要があり、このinvoke関数でも一度、文字列のvalueをbyte配列に変換しています。



## データの参照(Query)

Invokeで保存した"A101"というkeyからそのvalueを参照します。

先ほどと同じく

```
<ベースURI>/chaincode

# 例) https://ab12cde3-45gh-6i78-900-abcf1234567d_vp1-api.blockchain.ibm.com:443/chaincode 
```

に対し以下のjsonを投げることになります。


```json
{
  "jsonrpc": "2.0",
  "method": "query",
  "params": {
    "type": 1,
    "chaincodeID": {
      "name": "<Chaincode ID>"
    },
    "ctorMsg": {
      "function": "query",
      "args": [
        "A101"
      ]
    },
    "secureContext": "<ユーザID>"
  },
  "id": 0
}
```

なのでcurlでやると以下のような感じになります。

```
curl -H "Content-type: application/json" \
-X POST \
-d '{
  "jsonrpc": "2.0",
  "method": "query",
  "params": {
    "type": 1,
    "chaincodeID": {
      "name": "<Chaincode ID>"
    },
    "ctorMsg": {
      "function": "query",
      "args": [
        "A101"
      ]
    },
    "secureContext": "<ユーザID>"
  },
  "id": 0
}' \
<ベースURI>/chaincode
```

返ってくる結果は

```
{"jsonrpc":"2.0","result":{"status":"OK","message":"Key:A101 Value:Book"},"id":0}
```

となり、messageが"Key:A101 Value:Book"隣っているので先ほど実行したInvokeの結果を参照できたことが確認できました。

# 終わり
以上がOpen Blockchainの基本操作です。
次はよりChaincodeの書き方などについてまとめたいと思います。