十一、区块链学习-Hyperledger Fabric (基于release-1.0) 链码开发-marbles管理

Acacia ·
更新时间:2024-11-10
· 714 次阅读

链码开发-marbles管理1. 概述2. marble弹珠管理2.1实现功能2.2chaincode链码2.3编写测试类2.4 跑测试类3 搭建本地测试环境 并测试链码3.1 挂载链码3.2 启动网络环境3.3 进入chaincode容器编译链码 提供链码服务3.4 进入cli容器 安装链码 实例化链码3.5 测试链码4 关闭网络 1. 概述

根据前面十篇文章的介绍,基本已经了解了fabric的网络环境和链码开发 部署 调试的过程。这一篇文章,再次巩固链码开发以及部署调用调试。

2. marble弹珠管理

marble

// 声明弹珠结构体 type Marble struct { // 对象类型 ObjectType string `json:"objectType"` // 弹珠名称 Name string `json:"name"` // 弹珠颜色 Color string `json:"color"` // 弹珠大小 Size int `json:"size"` // 弹珠拥有者 Owner string `json:"owner"` } 2.1实现功能 弹珠创建 弹珠查询 弹珠删除 弹珠交易 弹珠操作历史查询 查询某个用户的弹珠列表 2.2chaincode链码

markbels.go

package main // 引入依赖包 import ( "fmt" "github.com/hyperledger/fabric/core/chaincode/shim" "github.com/hyperledger/fabric/protos/peer" "time" "bytes" "strconv" "encoding/json" ) // 声明结构体 type MarblesChaincode struct{ } // 声明弹珠结构体 type Marble struct { // 对象类型 ObjectType string `json:"objectType"` // 弹珠名称 Name string `json:"name"` // 弹珠颜色 Color string `json:"color"` // 弹珠大小 Size int `json:"size"` // 弹珠拥有者 Owner string `json:"owner"` } // 实例化链码接口 func (t *MarblesChaincode) Init(stub shim.ChaincodeStubInterface) peer.Response { // 这里初始化不做操作 fmt.Println("MarblesChaincode 链码实例化") return shim.Success(nil) } // invoke 操作 func (t *MarblesChaincode) Invoke(stub shim.ChaincodeStubInterface) peer.Response { // 获取调用的方法和参数 fn, args := stub.GetFunctionAndParameters() // 判断分发 业务方法 if fn == "initMarble" { // 调用创建弹珠方法 return t.initMarble(stub,args) }else if fn == "readMarble" { // 调用读取弹珠信息的方法 return t.readMarble(stub,args) }else if fn == "deleteMarble" { // 调用删除弹珠信息 return t.deleteMarble(stub,args) }else if fn == "transferMarble" { // 调用交易弹珠的方法 return t.transferMarble(stub,args) }else if fn == "getMarbleByRange" { // 调用范围查询 return t.getMarbleByRange(stub,args) }else if fn == "queryMarblesByOwner" { // 调用查询用户拥有的弹珠信息 return t.queryMarblesByOwner(stub,args) }else if fn == "queryHistoryForMarble" { // 查询弹珠的历史操作信息 return t.queryHistoryForMarble(stub,args) } // 如果没有对应的方法 返回错误 return shim.Error(fn +" 方法不存在!") } // 创建marble args: name color size owner func (t *MarblesChaincode) initMarble(stub shim.ChaincodeStubInterface,args []string) peer.Response { // 根据名称判断marble是否已经存在 name := args[0] // 查找之前是否存在 名为 name的 marble marbleBytes, err := stub.GetState(name) // 查询错误 if err != nil { return shim.Error(err.Error()) } // 如果marbleBytes 已经存在 if marbleBytes != nil { return shim.Error("名为"+ name + "的弹珠已经存在"); } // 如果不存在 则写入到账本中 color := args[1] size,err := strconv.Atoi(args[2]) owner := args[3] // 组装测结构体 marble := &Marble{"marble",name,color,size,owner} // 将marble 转成json字符串 存储到账本 marbleJsonStr, err := json.Marshal(marble) if err != nil { return shim.Error(err.Error()) } // PutState json信息写入账本 err = stub.PutState(name,marbleJsonStr) if err != nil { return shim.Error(err.Error()) } fmt.Println("创建弹珠成功!!") // 同时创建组合键用于查询 indexName := "owner~record" indexKey, err := stub.CreateCompositeKey(indexName,[]string{ owner,string(marbleJsonStr)}) if err != nil{ return shim.Error(err.Error()) } err = stub.PutState(indexKey,[]byte{0x00}) if err != nil{ return shim.Error(err.Error()) } fmt.Println(indexKey) return shim.Success(nil) } // 读取marble args: marbleName func (t *MarblesChaincode) readMarble(stub shim.ChaincodeStubInterface,args []string) peer.Response { // 获取参数 参数为marble的name name := args[0] // 根据name 读取marble的数据 marbleBytes, err := stub.GetState(name) if err != nil { return shim.Error(err.Error()) } if marbleBytes == nil { return shim.Error(name + " 的弹珠信息不存在") } // 返回信息 return shim.Success(marbleBytes) } // 删除marble args: marbleName func (t *MarblesChaincode) deleteMarble(stub shim.ChaincodeStubInterface,args []string) peer.Response { // 从参数总获取到markble的name name := args[0] // 判断弹珠是否存在 marbleBytes, err := stub.GetState(name) if err != nil { return shim.Error(err.Error()) } if marbleBytes == nil { return shim.Error(name + "的弹珠信息不存在") } // 删除弹珠 err = stub.DelState(name) if err != nil { return shim.Error(err.Error()) } fmt.Println(name + " 弹珠删除成功!") return shim.Success(nil) } // 交易marble args: marbleName newOwner func (t *MarblesChaincode) transferMarble(stub shim.ChaincodeStubInterface,args []string) peer.Response { // 获取到参数 marbleName := args[0] newOwner := args[1] // 检查弹珠是否存在 marbleBytes, err := stub.GetState(marbleName) if err != nil{ return shim.Error(err.Error()) } if marbleBytes == nil { return shim.Error(marbleName + " 的弹珠信息不存在") } // 将账本中的信息转为 Marble 结构体 marbleInfo := Marble{} err = json.Unmarshal(marbleBytes,&marbleInfo) if err != nil { return shim.Error(err.Error()) } // 修改拥有者 marbleInfo.Owner = newOwner // 转为json数据 newMarbleBytes,err := json.Marshal(marbleInfo) if err != nil { return shim.Error(err.Error()) } // 写入账本 err = stub.PutState(marbleName,newMarbleBytes) if err != nil { return shim.Error(err.Error()) } fmt.Println(marbleName +"转给"+ newOwner+ "的弹珠交易完成") return shim.Success(nil) } // 查询某一范围内的marble args:startMarble endMarble func (t *MarblesChaincode) getMarbleByRange(stub shim.ChaincodeStubInterface, args []string) peer.Response { // 获取参数 startMarble := args[0] endMarble := args[1] // 调用查询方法 resultIterator, err := stub.GetStateByRange(startMarble,endMarble) if err!=nil { return shim.Error(err.Error()) } defer resultIterator.Close(); var buffer bytes.Buffer buffer.WriteString("[") isWriteSplit := false // 遍历resultIterator for resultIterator.HasNext() { item,err := resultIterator.Next() if err!= nil { return shim.Error(err.Error()) } if isWriteSplit==true { buffer.WriteString(",") } buffer.WriteString("{\"key\":") buffer.WriteString("\""+item.Key+"\"") buffer.WriteString(",\"record\":") buffer.WriteString(string(item.Value)) buffer.WriteString("}") isWriteSplit = true } buffer.WriteString("]") // 返回结果 return shim.Success(buffer.Bytes()) } // 查询用户拥有的弹珠信息 func (t *MarblesChaincode) queryMarblesByOwner(stub shim.ChaincodeStubInterface,args []string) peer.Response { owner := args[0] fmt.Println("开始查询用户"+owner+"拥有的弹珠信息") indexName := "owner~record" resultIterator,err := stub.GetStateByPartialCompositeKey(indexName ,[]string{owner}) if err != nil { return shim.Error(err.Error()) } defer resultIterator.Close(); var buffer bytes.Buffer buffer.WriteString("[") isWriteSplit := false for resultIterator.HasNext() { item,_ := resultIterator.Next() _,compositeKeyParts,err := stub.SplitCompositeKey(item.Key) if err != nil { shim.Error(err.Error()) } if isWriteSplit==true { buffer.WriteString(",") } buffer.WriteString(compositeKeyParts[1]) isWriteSplit = true } buffer.WriteString("]") // 返回结果 return shim.Success(buffer.Bytes()) // 获取参数 下面是通过couchdb的方式来查询 但是 我本地环境如果 启动了couchdb总是 在容器启动的时候cli容器无法连接couchdb 猜测应该是couchdb在容器中启动未完成 cli就去尝试连接了。 // owner := args[0] // fmt.Println("开始查询用户"+owner+"拥有的弹珠信息") // queryString := fmt.Sprintf("{\"selector\":{\"owner\": %s }}",owner) // resultIterator,err := stub.GetQueryResult(queryString) // if err != nil { // return shim.Error(err.Error()) // } // defer resultIterator.Close(); // var buffer bytes.Buffer // buffer.WriteString("[") // isWriteSplit := false // // 遍历resultIterator // for resultIterator.HasNext() { // item,err := resultIterator.Next() // if err!= nil { // return shim.Error(err.Error()) // } // if isWriteSplit==true { // buffer.WriteString(",") // } // buffer.WriteString("{\"key\":") // buffer.WriteString("\""+item.Key+"\"") // buffer.WriteString(",\"record\":") // buffer.WriteString(string(item.Value)) // buffer.WriteString("}") // isWriteSplit = true // } // buffer.WriteString("]") // // 返回结果 // return shim.Success(buffer.Bytes()) } // 查询用户 func (t *MarblesChaincode) queryHistoryForMarble(stub shim.ChaincodeStubInterface,args []string) peer.Response { // 获取参数 marbleName := args[0] // 获取历史信息 resultIterator,err := stub.GetHistoryForKey(marbleName) if err != nil { return shim.Error(err.Error()) } defer resultIterator.Close() var buffer bytes.Buffer buffer.WriteString("[") isWriteSplit := false // 遍历resultIterator for resultIterator.HasNext() { item,err := resultIterator.Next() if err!= nil { return shim.Error(err.Error()) } if isWriteSplit==true { buffer.WriteString(",") } buffer.WriteString("{\"TxId\":") buffer.WriteString("\""+item.TxId+"\"") buffer.WriteString(",\"Timestamp\":") buffer.WriteString(time.Unix(item.Timestamp.Seconds,int64(item.Timestamp.Nanos)).String()) buffer.WriteString(",\"Value\":") buffer.WriteString(string(item.Value)) buffer.WriteString(",\"IsDelete\":") buffer.WriteString(strconv.FormatBool(item.IsDelete)) buffer.WriteString("}") isWriteSplit = true } buffer.WriteString("]") // 如果没有对应的方法 返回错误 return shim.Success(buffer.Bytes()) } // main 函数 func main (){ err := shim.Start(new (MarblesChaincode)) if err != nil { fmt.Printf("Error start MarblesChaincode") } } 2.3编写测试类

markbels_test.go

package main import( "fmt" "testing" "github.com/hyperledger/fabric/core/chaincode/shim" ) func checkInit(t *testing.T,stub *shim.MockStub,args [][]byte) { res := stub.MockInit("1",args) if (res.Status != shim.OK){ fmt.Println(string(res.Message)) t.FailNow() } } func checkInvoke(t *testing.T,stub *shim.MockStub,args [][]byte) { res := stub.MockInvoke("1",args) if (res.Status != shim.OK){ fmt.Println(string(res.Message)) t.FailNow() } } func checkReadMarble(t *testing.T,stub *shim.MockStub, name string) { res := stub.MockInvoke("1",[][]byte{[]byte("readMarble"),[]byte(name)}) if(res.Status != shim.OK){ fmt.Println(string(res.Message)) t.FailNow() } if(res.Payload == nil){ fmt.Println("checkReadMarble",name,"failed to get value") t.FailNow() } fmt.Println(string(res.Payload)) } func checkReadMarbleByRange(t *testing.T,stub *shim.MockStub, startKey string,endKey string) { res := stub.MockInvoke("1",[][]byte{[]byte("getMarbleByRange"),[]byte(startKey),[]byte(endKey)}) if(res.Status != shim.OK){ fmt.Println(string(res.Message)) t.FailNow() } if(res.Payload == nil){ fmt.Println("checkReadMarbleByRange",startKey,endKey,"failed to get value") t.FailNow() } fmt.Println(string(res.Payload)) } func checkQueryMarblesByOwner(t *testing.T,stub *shim.MockStub, owner string) { res := stub.MockInvoke("1",[][]byte{[]byte("queryMarblesByOwner"),[]byte(owner)}) if(res.Status != shim.OK){ fmt.Println(string(res.Message)) t.FailNow() } if(res.Payload == nil){ fmt.Println("checkQueryMarblesByOwner",owner,"failed to get value") t.FailNow() } fmt.Println(string(res.Payload)) } func checkQueryMarblesHistoryByKey(t *testing.T,stub *shim.MockStub, marbleName string) { res := stub.MockInvoke("1",[][]byte{[]byte("queryHistoryForMarble"),[]byte(marbleName)}) if(res.Status != shim.OK){ fmt.Println(string(res.Message)) t.FailNow() } if(res.Payload == nil){ fmt.Println("checkReadMarbleByRange",marbleName,"failed to get value") t.FailNow() } fmt.Println(string(res.Payload)) } func Test_MarblesChaincode(t *testing.T) { hello := new(MarblesChaincode) stub := shim.NewMockStub("marble",hello) checkInit(t,stub,nil) // name color size owner checkInvoke(t,stub,[][]byte{[]byte("initMarble"),[]byte("marble-1"),[]byte("red"),[]byte("10"),[]byte("LH")}) checkInvoke(t,stub,[][]byte{[]byte("initMarble"),[]byte("marble-2"),[]byte("yellow"),[]byte("11"),[]byte("LH")}) checkInvoke(t,stub,[][]byte{[]byte("initMarble"),[]byte("marble-3"),[]byte("blue"),[]byte("12"),[]byte("WX")}) checkQueryMarblesByOwner(t,stub,"WX") // checkReadMarble(t,stub,"marble-1") // checkInvoke(t,stub,[][]byte{[]byte("transferMarble"),[]byte("marble-1"),[]byte("WX")}) // checkInvoke(t,stub,[][]byte{[]byte("transferMarble"),[]byte("marble-1"),[]byte("LH")}) // checkInvoke(t,stub,[][]byte{[]byte("deleteMarble"),[]byte("marble-1")}) // checkQueryMarblesHistoryByKey(t,stub,"marble-1") // checkInvoke(t,stub,[][]byte{[]byte("initMarble"),[]byte("marble-4"),[]byte("green"),[]byte("12"),[]byte("WX")}) // checkReadMarbleByRange(t,stub,"marble-1","marble-4") // checkReadMarble(t,stub,"marble-1") // checkInvoke(t,stub,[][]byte{[]byte("initMarble"),[]byte("marble-1"),[]byte("red"),[]byte("10"),[]byte("LH")}) // checkReadMarble(t,stub,"marble-2") } 2.4 跑测试类

进入到 marbles.go 和 marbles_test.go所在的目录
我是放到了 $GOPATH/my_chaincode/marbles 目录下

cd $GOPATH/my_chaincode/marbles go test marbles_test.go -v marbles.go --tags=nopkcs11

可以尝试修改marbles_test.go 来测试其他的方法

3 搭建本地测试环境 并测试链码

使用fabric-samples项目中 的chaincode-docker-devmode 环境测试

3.1 挂载链码

将编写好的 marbles.go 和marbles_test.go文件移动到

$GOAPTH/src/github.com/hyperledger/fabric-samples/chaincode

方便挂载到容器中

3.2 启动网络环境

进入到chaincode-docker-devmode目录下

cd $GOPATH/src/github.com/hyperledger/fabric-samples/chaincode-docker-devmode

利用docker-compose启动网络环境

docker-compose -f docker-compose-simple.yaml up

启动完成后的容器列表

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a7b22f7351fa hyperledger/fabric-tools "/bin/bash -c ./scri…" 7 seconds ago Up 5 seconds cli d761b06a034a hyperledger/fabric-ccenv "/bin/bash -c 'sleep…" 7 seconds ago Up 5 seconds chaincode 7b329ef1311f hyperledger/fabric-peer "peer node start --p…" 8 seconds ago Up 6 seconds 0.0.0.0:7051->7051/tcp, 0.0.0.0:7053->7053/tcp peer 6302ffa111e2 hyperledger/fabric-orderer "orderer" 9 seconds ago Up 7 seconds 0.0.0.0:7050->7050/tcp orderer 3.3 进入chaincode容器编译链码 提供链码服务

启动一个新的终端

进入容器

docker exec -it chaincode /bin/bash

进入到挂载go文件目录

cd marbles/

编译程序

go build

编译完成后启动链码服务

CORE_PEER_ADDRESS=peer:7051 CORE_CHAINCODE_ID_NAME=mycc:0 ./marbles 3.4 进入cli容器 安装链码 实例化链码

启动一个新的终端
进入cli容器

docker exec -it cli /bin/bash

安装链码

peer chaincode install -p chaincodedev/chaincode/marbles -n mycc -v 0

初始化链码

peer chaincode instantiate -n mycc -v 0 -c '{"Args":[]}' -C myc 3.5 测试链码

创建三个弹珠

peer chaincode invoke -n mycc -c '{"Args":["initMarble","marble-1","red","10","LH"]}' -C myc peer chaincode invoke -n mycc -c '{"Args":["initMarble","marble-2","yellow","11","LH"]}' -C myc peer chaincode invoke -n mycc -c '{"Args":["initMarble","marble-3","blue","12","WX"]}' -C myc

查询弹珠

peer chaincode query -n mycc -c '{"Args":["readMarble","marble-1"]}' -C myc

范围查询

peer chaincode query -n mycc -c '{"Args":["getMarbleByRange","marble-1","marble-3"]}' -C myc

转让弹珠

peer chaincode invoke -n mycc -c '{"Args":["transferMarble","marble-1","WX"]}' -C myc

再查询marble-1:是否owner变为WX

peer chaincode query -n mycc -c '{"Args":["readMarble","marble-1"]}' -C myc

查询owner所有的弹珠

peer chaincode query -n mycc -c '{"Args":["queryMarblesByOwner","WX"]}' -C myc

查看弹珠修改历史:结果中 可以查询到一次 初始化交易 一次 转让交易

peer chaincode query -n mycc -c '{"Args":["queryHistoryForMarble","marble-1"]}' -C myc 4 关闭网络

在最开始启动网络的终端 按两次 control+c后执行

docker-compose -f docker-compose-simple.yaml down

停止并关闭所有容器


作者:LH_0811



区块链学习 hyperledger RELEASE 学习 fabric 区块链

需要 登录 后方可回复, 如果你还没有账号请 注册新账号