千家信息网

HyperLeger Fabric开发(八)——HyperLeger Fabric链码开发测试

发表于:2025-02-02 作者:千家信息网编辑
千家信息网最后更新 2025年02月02日,HyperLeger Fabric开发(八)--HyperLeger Fabric链码开发测试一、链码实例SACC项目链码实例如下:package mainimport ( "fmt" "gi
千家信息网最后更新 2025年02月02日HyperLeger Fabric开发(八)——HyperLeger Fabric链码开发测试

HyperLeger Fabric开发(八)--HyperLeger Fabric链码开发测试

一、链码实例

SACC项目链码实例如下:

package mainimport (   "fmt"   "github.com/hyperledger/fabric/core/chaincode/shim"   "github.com/hyperledger/fabric/protos/peer")// SimpleAsset implements a simple chaincode to manage an assettype 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 mainimport (   "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

0