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つのステップで行われます。
- 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
は以下のものです。
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の書き方などについてまとめたいと思います。