HyperLeger Fabric開發(八)——HyperLeger Fabric鏈碼開發測試
HyperLeger Fabric開發(八)——HyperLeger Fabric鏈碼開發測試
一、鏈碼例項
SACC專案鏈碼例項如下:
package main import ( "fmt" "github.com/hyperledger/fabric/core/chaincode/shim" "github.com/hyperledger/fabric/protos/peer" ) // SimpleAsset implements a simple chaincode to manage an asset type SimpleAsset struct { } // Init方法在鏈碼例項化或鏈碼升級期間呼叫 func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response { // 從交易提案獲取引數 args := stub.GetStringArgs() if len(args) != 2 { return shim.Error("Incorrect arguments. Expecting a key and a value") } // 儲存資產到賬本 err := stub.PutState(args[0], []byte(args[1])) if err != nil { return shim.Error(fmt.Sprintf("Failed to create asset: %s", args[0])) } return shim.Success(nil) } // Invoke方法在鏈碼上執行每次交易時被呼叫 func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response { // 從交易提案提取引數 fn, args := stub.GetFunctionAndParameters() var result string var err error if fn == "set" { result, err = set(stub, args) } else { // assume 'get' even if fn is nil result, err = get(stub, args) } if err != nil { return shim.Error(err.Error()) } // Return the result as success payload return shim.Success([]byte(result)) } // 在賬本上使用key-value設定資產,如果key存在,使用新的值更新 func set(stub shim.ChaincodeStubInterface, args []string) (string, error) { if len(args) != 2 { return "", fmt.Errorf("Incorrect arguments. Expecting a key and a value") } err := stub.PutState(args[0], []byte(args[1])) if err != nil { return "", fmt.Errorf("Failed to set asset: %s", args[0]) } return args[1], nil } // 獲取指定資產key的值 func get(stub shim.ChaincodeStubInterface, args []string) (string, error) { if len(args) != 1 { return "", fmt.Errorf("Incorrect arguments. Expecting a key") } value, err := stub.GetState(args[0]) if err != nil { return "", fmt.Errorf("Failed to get asset: %s with error: %s", args[0], err) } if value == nil { return "", fmt.Errorf("Asset not found: %s", args[0]) } return string(value), nil } // 例項化期間在容器啟動鏈碼 func main() { if err := shim.Start(new(SimpleAsset)); err != nil { fmt.Printf("Error starting SimpleAsset chaincode: %s", err) } }
二、鏈碼的單元測試
1、鏈碼的單元測試簡介
鏈碼開發完畢後,並不需要在區塊鏈環境中部署鏈碼才能進行除錯,可以利用shim.MockStub來編寫單元測試程式碼,直接在Go語言開發環境(無Fabric區塊鏈網路的環境)中除錯。
2、鏈碼測試程式碼編寫
進入sacc目錄,新建一個編寫測試程式碼的sacc_test.go檔案
package main import ( "testing" "github.com/hyperledger/fabric/core/chaincode/shim" "fmt" ) func TestSet(t *testing.T) { //模擬鏈碼部署 scc := new(SimpleAsset) stub := shim.NewMockStub("SimpleAsset", scc) mockInit(t, stub, [][]byte{[]byte("user1"), []byte("0")}) //呼叫鏈碼的交易方法 invokeSet(t, stub, []string{"user1", "10000"}) invokeSet(t, stub, []string{"user1", "1000"}) } func mockInit(t *testing.T, stub *shim.MockStub, args [][]byte) { res := stub.MockInit("1", args) if res.Status != shim.OK { fmt.Println("Init failed", string(res.Message)) t.FailNow() } } func invokeSet(t *testing.T, stub *shim.MockStub, args []string) { // invoke呼叫 res := stub.MockInvoke("1", [][]byte{[]byte("set"), []byte(args[0]),[]byte(args[1])}) fmt.Println("set(" + args[0]+","+ args[1]+")") if res.Status != shim.OK { fmt.Println("invoke set failed:", args[0], string(res.Message)) t.FailNow() } } func TestGet(t *testing.T) { //模擬鏈碼部署 scc := new(SimpleAsset) stub := shim.NewMockStub("SimpleAsset", scc) mockInit(t, stub, [][]byte{[]byte("user1"), []byte("10000")}) //呼叫鏈碼 invokeGet(t, stub, []string{"user1"}) } func invokeGet(t *testing.T, stub *shim.MockStub, args []string) { // invoke呼叫 res := stub.MockInvoke("1", [][]byte{[]byte("get"), []byte(args[0])}) fmt.Println("get(" + args[0]+ ")" + "->" + string(res.Payload)) if res.Status != shim.OK { fmt.Println("invoke get failed:", args[0], string(res.Message)) t.FailNow() } } // output: //=== RUN TestSet //set(user1,10000) //set(user1,1000) //--- PASS: TestSet (0.00s) //=== RUN TestGet //get(user1)->10000 //--- PASS: TestGet (0.00s) //PASS
3、進行單元測試
在鏈碼目錄sacc下執行單元測試go test -v sacc_test.go sacc.go
三、鏈碼的開發環境測試
1、啟動鏈碼開發除錯環境
開啟終端T1,進入chaincode-docker-devmode目錄,啟動Fabric網路:docker-compose -f docker-compose-simple.yaml up
2、編譯並啟動鏈碼
開啟終端T2,進入chaincode容器docker exec -it chaincode bash
進入SACC(簡單資產鏈碼)專案目錄:cd sacc
編譯鏈碼:go build -o sacc
啟動鏈碼:CORE_PEER_ADDRESS=peer:7052 CORE_CHAINCODE_ID_NAME=sacc:0 ./sacc
3、操作鏈碼
開啟終端T3,進入客戶端cli容器:docker exec -it cli bash
安裝鏈碼:peer chaincode install -p chaincodedev/chaincode/sacc -n sacc -v 0
安裝成功列印資訊:Installed remotely response:<status:200 payload:"OK"
例項化鏈碼:peer chaincode instantiate -n sacc -v 0 -c '{"Args":["user1","100"]}' -C myc
通道名稱必須是myc(創世區塊和配置交易的檔名稱為myc),否則將報錯資訊:Error: error getting channel (testchannel) orderer endpoint: error bad proposal response 500: access denied for [GetConfigBlock][testchannel]: Failed to get policy manager for channel [testchannel]
呼叫鏈碼,設定使用者user1的賬戶餘額:peer chaincode invoke -n sacc -c '{"Args":["set", "user1", "1000"]}' -C myc
查詢使用者user1的餘額:peer chaincode query -n sacc -c '{"Args":["get","user1"]}' -C myc
4、打包鏈碼
通過將鏈碼相關資料進行封裝, 可以實現對其進行打包和簽名操作。
打包鏈碼:peer chaincode package -n sacc -p chaincodedev/chaincode/sacc -v 0 -s -S -i "AND('OrgA.admin')" ccpack.out
-s: 建立角色支援的CC部署規範包, 而不是原始的CC部署規範
-S: 如果建立CC部署規範方案角色支援,也與本地MSP簽名
-i: 指定例項化策略
打包後的檔案, 可以直接用於install操作:peer chaincode install ccpack.out
對一個打包檔案進行簽名操作(添加當前MSP簽名到簽名列表中)peer chaincode signpackage ccpack.out signedccpack.out
5、升級鏈碼
退出cli容器,停止chaincode容器。
在終端T2中重新進入chaincode容器:docker exec -it chaincode bash
進入SACC專案:cd sacc
編譯鏈碼:go build -o sacc
啟動鏈碼:CORE_PEER_ADDRESS=peer:7052 CORE_CHAINCODE_ID_NAME=sacc:1 ./sacc
將鏈碼sacc從版本0升級為版本1
開啟終端T3,進入cli容器:docker exec -it cli bash
安裝鏈碼:peer chaincode install -p chaincodedev/chaincode/sacc -n sacc -v 1
升級鏈碼:peer chaincode upgrade -n sacc -v 1 -c '{"Args":["user1", "10000"]}' -C myc
在對某鏈碼程式碼升級前,推薦先將所有該鏈碼的容器停止,並從Peer上備份並移除舊鏈碼部署檔案,然後先在個別Peer節點上部署新鏈碼,對原有資料進行測試,成功後再在其它節點上進行升級操作
查詢使用者user1的賬戶餘額:peer chaincode query -n sacc -c '{"Args":["get","user1"]}' -C myc
6、關閉Fabric網路
刪除所有在執行的容器:docker rm -f $(docker ps -aq)
清理Faric網路快取:docker network prune