Posted at

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

More than 3 years have passed since last update.

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


Bluemixの準備

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


  • Bluemixのアカウント


    • 現在は30日間限定ですが無料でしかもクレジットカードの登録も必要なくBluemixのアカウントを作成できます。



  • ブロックチェーンサービスの起動


    • 組織、地域、スペースを作成が必要です。

    • その後の手順はこちらを行ってください。



以上を行うと以下のような画面が表示されます。

これで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のタブを開いてください。


  • ベース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を投げることになります

{ 

"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関数が実行される



  2. トランザクションの実行


    • デプロイしたChaincodeのInvoke関数を呼び出しトランザクションを生成する



  3. データの参照


    • トランザクションにより追加・変更されたデータを参照するため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

は以下のものです。

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のエンドポイントは同じです。

{

"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の中が変わるので注意してください。






jsonrpc
JSON-RPCのバージョン


method
実行するメソッド名


params
type
Chaincodeのタイプ(1はGo言語)


chaincodeID
path
Chaincodeのパス
(デプロイのみ)


name
ChaincodeのID(デプロイ以外)


ctorMsg
function, args
実行時にChaincodeに渡される引数


secureContext
実行するユーザID


id
requestとresponseの組を


識別するためのもの


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

先ほどデプロイしたChaincodeを実行します。

まずはデータの保存のInvoke関数を呼び出します。

先ほどと同じく

<ベースURI>/chaincode

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

に対し以下の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を投げることになります。

{

"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の書き方などについてまとめたいと思います。