Fabricインテリジェント契約example 02詳細解読


引用する
スタンドアロンマルチノードFabricネットワークを導入する際に使用するのはe 2 e_です.cliテストサンプルのスマート契約:example 02、そのパス:/opt/gopath/src/github.com/hyperledger/fabric/aberic/chaincode/go/chaincode_example 02,本章では,この簡単な契約のみを深く解析する.
1、インテリジェント契約の配置
まず、スマート契約をインストールするコマンドは、次のとおりです.
peer chaincode install -n mychannel -p github.com/hyperledger/fabric/aberic/
chaincode/go/chaincode/go/chaincode_example02 -v 1.0

インテリジェント契約のインストールログは次のとおりです.
2020-06-08 07:00:42.296 UTC [msp] GetLocalMSP -> DEBU 001 Returning existing local MSP
2020-06-08 07:00:42.296 UTC [msp] GetDefaultSigningIdentity -> DEBU 002 Obtaining default signing identity
2020-06-08 07:00:42.296 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 003 Using default escc
2020-06-08 07:00:42.296 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 004 Using default vscc
2020-06-08 07:00:42.408 UTC [golang-platform] getCodeFromFS -> DEBU 005 getCodeFromFS github.com/hyperledger/fabric/aberic/chaincode/go/chaincode_example02
2020-06-08 07:00:42.643 UTC [golang-platform] func1 -> DEBU 006 Discarding GOROOT package fmt
2020-06-08 07:00:42.643 UTC [golang-platform] func1 -> DEBU 007 Discarding provided package github.com/hyperledger/fabric/core/chaincode/shim
2020-06-08 07:00:42.643 UTC [golang-platform] func1 -> DEBU 008 Discarding provided package github.com/hyperledger/fabric/protos/peer
2020-06-08 07:00:42.643 UTC [golang-platform] func1 -> DEBU 009 Discarding GOROOT package strconv
2020-06-08 07:00:42.643 UTC [golang-platform] GetDeploymentPayload -> DEBU 00a done
2020-06-08 07:00:42.644 UTC [msp/identity] Sign -> DEBU 00b Sign: plaintext: 0A86070A5C08031A0C089AC4F7F60510...2FBAFE130000FFFFBC6386C1002C0000 
2020-06-08 07:00:42.644 UTC [msp/identity] Sign -> DEBU 00c Sign: digest: B8C34039C216016ABD32E7B74CA92DE3268BF433FB2717666208CF766E75FB1F 
2020-06-08 07:00:42.649 UTC [chaincodeCmd] install -> DEBU 00d Installed remotely response:<status:200 payload:"OK" > 
2020-06-08 07:00:42.649 UTC [main] main -> INFO 00e Exiting.....


2、インスタンス化Chaincode
インストールが完了したら、chaincodeをインスタンス化する必要があります.次のコマンドを実行します.
peer chaincode instantiate -o orderer.example.com:7050 -C mychannel -n 
mychannel -c '{"Args":["init","A","10","B","10"]}' -P "OR ('Org1MSP.member')" -v 1.0

インスタンス化chaincodeのログは次のとおりです.
2020-06-08 07:05:59.323 UTC [msp] GetLocalMSP -> DEBU 001 Returning existing local MSP
2020-06-08 07:05:59.323 UTC [msp] GetDefaultSigningIdentity -> DEBU 002 Obtaining default signing identity
2020-06-08 07:05:59.324 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 003 Using default escc
2020-06-08 07:05:59.324 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 004 Using default vscc
2020-06-08 07:05:59.324 UTC [msp/identity] Sign -> DEBU 005 Sign: plaintext: 0A91070A6708031A0C08D7C6F7F60510...314D53500A04657363630A0476736363 
2020-06-08 07:05:59.324 UTC [msp/identity] Sign -> DEBU 006 Sign: digest: 58805553D0FCD1F5B30140EA706BC2CAB22E56627011C1CD55E3DCBDFDFD4B00 
2020-06-08 07:06:22.303 UTC [msp/identity] Sign -> DEBU 007 Sign: plaintext: 0A91070A6708031A0C08D7C6F7F60510...C6BE9382352C9F3E65C5165CB5442EFF 
2020-06-08 07:06:22.303 UTC [msp/identity] Sign -> DEBU 008 Sign: digest: 92235FAD70B935B6AC7A58928D7B6DF0B768010BC3C5F8910A43DE945FC6788B 
2020-06-08 07:06:22.308 UTC [main] main -> INFO 009 Exiting.....


3、知能契約分析
example 02のソースコードは以下の通りです.
package main
import (
	"fmt"
	"strconv"

	"github.com/hyperledger/fabric/core/chaincode/shim"
	pb "github.com/hyperledger/fabric/protos/peer"
)

// SimpleChaincode example simple Chaincode implementation
type SimpleChaincode struct {
}

func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
	fmt.Println("ex02 Init")
	_, args := stub.GetFunctionAndParameters()
	var A, B string    // Entities
	var Aval, Bval int // Asset holdings
	var err error

	if len(args) != 4 {
		return shim.Error("Incorrect number of arguments. Expecting 4")
	}

	// Initialize the chaincode
	A = args[0]
	Aval, err = strconv.Atoi(args[1])
	if err != nil {
		return shim.Error("Expecting integer value for asset holding")
	}
	B = args[2]
	Bval, err = strconv.Atoi(args[3])
	if err != nil {
		return shim.Error("Expecting integer value for asset holding")
	}
	fmt.Printf("Aval = %d, Bval = %d
"
, Aval, Bval) // Write the state to the ledger err = stub.PutState(A, []byte(strconv.Itoa(Aval))) if err != nil { return shim.Error(err.Error()) } err = stub.PutState(B, []byte(strconv.Itoa(Bval))) if err != nil { return shim.Error(err.Error()) } return shim.Success(nil) } func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response { fmt.Println("ex02 Invoke") function, args := stub.GetFunctionAndParameters() if function == "invoke" { // Make payment of X units from A to B return t.invoke(stub, args) } else if function == "delete" { // Deletes an entity from its state return t.delete(stub, args) } else if function == "query" { // the old "Query" is now implemtned in invoke return t.query(stub, args) } return shim.Error("Invalid invoke function name. Expecting \"invoke\" \"delete\" \"query\"") } // Transaction makes payment of X units from A to B func (t *SimpleChaincode) invoke(stub shim.ChaincodeStubInterface, args []string) pb.Response { var A, B string // Entities var Aval, Bval int // Asset holdings var X int // Transaction value var err error if len(args) != 3 { return shim.Error("Incorrect number of arguments. Expecting 3") } A = args[0] B = args[1] // Get the state from the ledger // TODO: will be nice to have a GetAllState call to ledger Avalbytes, err := stub.GetState(A) if err != nil { return shim.Error("Failed to get state") } if Avalbytes == nil { return shim.Error("Entity not found") } Aval, _ = strconv.Atoi(string(Avalbytes)) Bvalbytes, err := stub.GetState(B) if err != nil { return shim.Error("Failed to get state") } if Bvalbytes == nil { return shim.Error("Entity not found") } Bval, _ = strconv.Atoi(string(Bvalbytes)) // Perform the execution X, err = strconv.Atoi(args[2]) if err != nil { return shim.Error("Invalid transaction amount, expecting a integer value") } Aval = Aval - X Bval = Bval + X fmt.Printf("Aval = %d, Bval = %d
"
, Aval, Bval) // Write the state back to the ledger err = stub.PutState(A, []byte(strconv.Itoa(Aval))) if err != nil { return shim.Error(err.Error()) } err = stub.PutState(B, []byte(strconv.Itoa(Bval))) if err != nil { return shim.Error(err.Error()) } return shim.Success(nil) } // Deletes an entity from state func (t *SimpleChaincode) delete(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) != 1 { return shim.Error("Incorrect number of arguments. Expecting 1") } A := args[0] // Delete the key from the state in ledger err := stub.DelState(A) if err != nil { return shim.Error("Failed to delete state") } return shim.Success(nil) } // query callback representing the query of a chaincode func (t *SimpleChaincode) query(stub shim.ChaincodeStubInterface, args []string) pb.Response { var A string // Entities var err error if len(args) != 1 { return shim.Error("Incorrect number of arguments. Expecting name of the person to query") } A = args[0] // Get the state from the ledger Avalbytes, err := stub.GetState(A) if err != nil { jsonResp := "{\"Error\":\"Failed to get state for " + A + "\"}" return shim.Error(jsonResp) } if Avalbytes == nil { jsonResp := "{\"Error\":\"Nil amount for " + A + "\"}" return shim.Error(jsonResp) } jsonResp := "{\"Name\":\"" + A + "\",\"Amount\":\"" + string(Avalbytes) + "\"}" fmt.Printf("Query Response:%s
"
, jsonResp) return shim.Success(Avalbytes) } func main() { err := shim.Start(new(SimpleChaincode)) if err != nil { fmt.Printf("Error starting Simple chaincode: %s", err) } }

まず、チェーンコード起動はshimパケットのstart関数を呼び出してchaincodeのタイプのパラメータを渡さなければならないので、まずchaincodeタイプインタフェースを見てみましょう.
type chaincode interface{
	Init(stub ChaincodestubInterface) peer.Response
	Invoke(stub ChaincodestubInterface) peer.Response
}

ChainCodeのGoコードにはSimpleChaincodeのようなstructを定義し、そのstructにInitとInvokeの2つの関数を定義し、ChainCodeの起動入口としてmain関数を定義する必要があります.ここで、Initはチェーンコードのインスタンス化またはアップグレード時に呼び出され、Invokeは帳簿データの状態を更新または照会する際に呼び出され、この方法で呼び出しまたは照会に応答するビジネスロジックを実現する必要がある.
3.1、まずInit関数を見る:
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
	fmt.Println("ex02 Init")
	_, args := stub.GetFunctionAndParameters()//GetFunctionAndParameters            
	var A, B string    // Entities
	var Aval, Bval int // Asset holdings
	var err error

	if len(args) != 4 {
		return shim.Error("Incorrect number of arguments. Expecting 4")
	}

	// Initialize the chaincode
	A = args[0]
	Aval, err = strconv.Atoi(args[1])
	if err != nil {
		return shim.Error("Expecting integer value for asset holding")
	}
	B = args[2]
	Bval, err = strconv.Atoi(args[3])
	if err != nil {
		return shim.Error("Expecting integer value for asset holding")
	}
	fmt.Printf("Aval = %d, Bval = %d
"
, Aval, Bval) // Write the state to the ledger err = stub.PutState(A, []byte(strconv.Itoa(Aval))) if err != nil { return shim.Error(err.Error()) } err = stub.PutState(B, []byte(strconv.Itoa(Bval))) if err != nil { return shim.Error(err.Error()) } return shim.Success(nil) }

次のようになります.
_, args := stub.GetFunctionAndParameters()

呼び出し時に渡されるパラメータを取得し、文字列配列のパラメータを2つの部分に分け、配列の最初の文字はFunction、残りはParameter
	if len(args) != 4 {
		return shim.Error("Incorrect number of arguments. Expecting 4")
	}


入力パラメータが4個かどうかを判断し、4個でなければエラーを返す:Incorrect number of arguments.Expecting 4
A = args[0]
	Aval, err = strconv.Atoi(args[1])//strconv       ,strconv.Atoi() int    
	if err != nil {
		return shim.Error("Expecting integer value for asset holding")
	}

A口座(A付与の最初のパラメータ)を取得し、A口座は10の初期資産(Aval付与の2番目のパラメータ、値は10)を付与し、クエリに失敗した場合、エラーを返す:Expecting integer value for asset holding
fmt.Printf("Aval = %d, Bval = %d
"
, Aval, Bval)

ABの残高をログに印刷します.
	err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
	if err != nil {
		return shim.Error(err.Error())
	}

	err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
	if err != nil {
		return shim.Error(err.Error())
	}

口座A,Bの状態を帳簿に記入する
3.2 Invoke関数の解析
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
	fmt.Println("ex02 Invoke")
	function, args := stub.GetFunctionAndParameters()
	if function == "invoke" {
		// Make payment of X units from A to B
		return t.invoke(stub, args)
	} else if function == "delete" {
		// Deletes an entity from its state
		return t.delete(stub, args)
	} else if function == "query" {
		// the old "Query" is now implemtned in invoke
		return t.query(stub, args)
	}

	return shim.Error("Invalid invoke function name. Expecting \"invoke\" \"delete\" \"query\"")
}


次のようになります.
function, args := stub.GetFunctionAndParameters()

functionは実行する関数名を保存し、argsは関数の後のパラメータを保存します.
次にfunctionに応じてinvoke、delete、query関数を実行します.
  • invoke実現振替操作
  • delete実現口座抹消
  • queryアカウント照会を実現する
  • 解析invoke関数
    基本的な文法は以前と似ていて、一つ一つ説明しないで、コードの中で表記します
    func (t *SimpleChaincode) invoke(stub shim.ChaincodeStubInterface, args []string) pb.Response {
    	var A, B string    //    A   B
    	var Aval, Bval int //     
    	var X int          //     
    	var err error
    
    	if len(args) != 3 {
    		return shim.Error("Incorrect number of arguments. Expecting 3")
    	}
    
    	A = args[0]    //    A    
    	B = args[1]    //    B    
    
    	// Get the state from the ledger       A    
    	// TODO: will be nice to have a GetAllState call to ledger
    	Avalbytes, err := stub.GetState(A)
    	if err != nil {
    		return shim.Error("Failed to get state")
    	}
    	if Avalbytes == nil {
    		return shim.Error("Entity not found")
    	}
    	Aval, _ = strconv.Atoi(string(Avalbytes))
    	//       B    
    	Bvalbytes, err := stub.GetState(B)
    	if err != nil {
    		return shim.Error("Failed to get state")
    	}
    	if Bvalbytes == nil {
    		return shim.Error("Entity not found")
    	}
    	Bval, _ = strconv.Atoi(string(Bvalbytes))
    
    	//// X       
    	X, err = strconv.Atoi(args[2])
    	if err != nil {
    		return shim.Error("Invalid transaction amount, expecting a integer value")
    	}
    	//  
    	Aval = Aval - X
    	Bval = Bval + X
    	fmt.Printf("Aval = %d, Bval = %d
    "
    , Aval, Bval) // A err = stub.PutState(A, []byte(strconv.Itoa(Aval))) if err != nil { return shim.Error(err.Error()) } // B err = stub.PutState(B, []byte(strconv.Itoa(Bval))) if err != nil { return shim.Error(err.Error()) } return shim.Success(nil) }

    delete関数の解析
    func (t *SimpleChaincode) delete(stub shim.ChaincodeStubInterface, args []string) pb.Response {
    	if len(args) != 1 {
    		return shim.Error("Incorrect number of arguments. Expecting 1")
    	}
    
    	A := args[0]//      
    
    	//            
    	err := stub.DelState(A)
    	if err != nil {
    		return shim.Error("Failed to delete state")
    	}
    
    	return shim.Success(nil)
    }
    

    解析query関数
    func (t *SimpleChaincode) query(stub shim.ChaincodeStubInterface, args []string) pb.Response {
    	var A string // Entities
    	var err error
    
    	if len(args) != 1 {
    		return shim.Error("Incorrect number of arguments. Expecting name of the person to query")
    	}
    
    	A = args[0]//     
    
    	//            
    	Avalbytes, err := stub.GetState(A)
    	if err != nil {
    		jsonResp := "{\"Error\":\"Failed to get state for " + A + "\"}"
    		return shim.Error(jsonResp)
    	}
    
    	if Avalbytes == nil {
    		jsonResp := "{\"Error\":\"Nil amount for " + A + "\"}"
    		return shim.Error(jsonResp)
    	}
    
    	jsonResp := "{\"Name\":\"" + A + "\",\"Amount\":\"" + string(Avalbytes) + "\"}"
    	fmt.Printf("Query Response:%s
    "
    , jsonResp) // return shim.Success(Avalbytes) }