はじめに
Hyperledger Fabric Sample試して見たけど、どうやってチェーンコードを書くかという段階で止まってる人向けにHyerledger Fabricのチェーンコードについて簡単な説明をしていこうかと思います。
#スケルトンコード
まず、初めにFabricのチェーンコードのスケルトンコードを見てください。
package main
import (
"encoding/json"
"fmt"
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/protos/peer"
)
type Asset struct {
}
// initialize・Update処理のエントリポイント
func (t *Asset) Init(stub shim.ChaincodeStubInterface) peer.Response {
return shim.Success(nili)
}
// Invoke・Query処理のエントリポイント
func (t *Asset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
return shim.Success(nili)
}
func main() {
if err := shim.Start(new(SimpleAsset)); err != nil {
fmt.Printf("Error starting Asset chaincode: %s", err)
}
}
FabricではInvoke処理とQuery処理は同じ関数が呼ばれます。ただし、台帳への書き込み要求が止められているため、switch文で処理を分岐させるつようがあります。
また、チェーンコードをアップデートする時にInit関数がコールされるので台帳の初期化処理を以前のバージョンで行っていた場合注意が必要です。
GetStateとPutState
この2つがブロックチェーン上へのRead/Writeの関数です。
これさえできれば、簡単なシステムは作れてしまうかと思います。
import "github.com/hyperledger/fabric/core/chaincode/shim"
// 台帳からデータを取得
func getData(key string, stub shim.ChaincodeStubInterface) string {
value := stub.GetState(key) // 引数に指定されたキーに保存されたデータを取得
return value
}
// 台帳にデータを書き込み
func setData(key , value string, stub shim.ChaincodeStubInterface){
stub.PutState(key, value)
}
保存できるデータは数値ではなく文字列です。
複雑なデータを保存したい場合はJSON文字列にパースしてPutStateやGetStateしてください。
実際にコードを書いてみましょう
PutStateとGetStateを用いて台帳に簡単に書くプログラムをためしてみましょう
package main
import (
"fmt"
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/protos/peer"
)
type Asset struct {
}
// initialize・Update処理のエントリポイント
func (t *Asset) Init(stub shim.ChaincodeStubInterface) peer.Response {
return shim.Success(nili)
}
// Invoke・Query処理のエントリポイント
func (t *Asset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
fn, args := stub.GetFunctionAndParameters()
var result string
switch fn {
case "set":
if len(args) != 2 {
return shim.Error("Incorrect arguments. Expecting a key")
}
PutState(args[0], args[1])
result = "success"
case "get":
if len(args) != 1 {
return shim.Error("Incorrect arguments. Expecting a key")
}
result, _ = GetState(args[0])
}
return shim.Success([]byte(result)) // ここの値が表示される
}
func main() {
if err := shim.Start(new(SimpleAsset)); err != nil {
fmt.Printf("Error starting SimpleAsset chaincode: %s", err)
}
}
これで
peer chaincode invoke -o "ordererのアドレス" -C "チャンネル名" -n "チェーンコード名" -c '{"function":"set","Args":["A", "1"]}'
peer chaincode query -o "ordererのアドレス" -C "チャンネル名" -n "チェーンコード名" -c '{"function":"get","Args":["A"]}'
をCli上で入力させれば、AというWalletに1が書き込まれ、Aの中身を見ることができるかと思います。
stub.GetFunctionAndParameters()は-c オプションのfunction とArgsの値を戻り値とする関数です。
ここで、コマンドはqueryで実行していますがInvoke関数のget処理が行われてること、引数を増やしたり減らしたりしたらエラーが出てくることを確認してください。
invokeコマンドのArgsの"1"を任意の値にすることで正しく保存されているかを確認してみてください。
これでチェーンコードがかけるようになりました
実際、チェーンコードを書く上で必要な処理はこれだけで過去のデータを参照し結果を利用した処理を行わない限り、チェーンコードを書く言語のライブラリ関数を利用するだけで作成することができます。
使う可能性がある関数をまとめておきます。
GetHistoryForKey(Key string) //指定したキーのトランザクションの履歴を線形リストとして取得
DelState(key string) //指定したキーの状態を削除する
// package ledger でよく使いそうな関数たち
type PeerLedger interface {
// GetTransactionByIDは、TxIDによってトランザクションを検索する
GetTransactionByID(txID string) (*peer.ProcessedTransaction, error)
//ハッシュ値によりブロックを検索
GetBlockByHash(blockHash []byte) (*common.Block, error)
// TxIDを持つブロックを検索
GetBlockByTxID(txID string) (*common.Block, error)
// GetTxValidationCodeByTxID returns reason code of transaction validation
GetTxValidationCodeByTxID(txID string) (peer.TxValidationCode, error)
}
最後に
今回はHyperledger のチェーンコードを書きました。
次回はNode.jsのSDKでトランザクションの発行についてかければと思います。