文章目录

  • 摘要
  • 一、fabric-go-sdk各个封装函数的功能介绍
    • 1.1 查询指定节点通道是否已经存在(函数: QuerySavedChannel(...) )
    • 1.2 创建并加入通道(函数: CreateChannel(...) )
    • 1.3 查询指定节点的指定链码是否已经存在(函数: QueryInstalledCC(...) )
    • 1.4 在指定节点安装链码(函数: InstallChaincode(...) )
    • 1.5 链码实例化(函数: ChaincodeInit(...) )
    • 1.6 调用链码(添加数据:函数: InvokeCC(...) )
    • 1.7 调用链码(查询操作:函数: QueryCC(...) )
  • 二、链码
    • 2.1 向区块链中添加数据
    • 2.2 查询区块链账本中的数据
  • 三、后端服务器的接口
    • 3.1 main函数
    • 3.2 FabricNetworkInit()函数
    • 3.3 AddData()函数
    • 3.4 QueryData()函数

摘要

上周的问题,多次尝试解决不了后,重构了下代码,成功解决了问题,事后分析,应该是有个节点没有成功加入通道,安装链码时,该节点没有安装,但链码执行时,选中的执行背书策略的节点是该节点,从而导致的调用链码错误。解决问题后,又遇到了几个小问题,解决所有问题后,用fabric-go-sdk成功实现了调用链码,向区块链账本中插入数据和查询数据的操作。并用Gin框架搭建好了育苗组织基本的后端服务器接口。还需要搭建养殖户组织的后端服务器接口、政府职能组织的后端服务器接口、普通用户组织的后端服务器接口,就基本完成了基于区块链溯源系统的后端服务器开发。


一、fabric-go-sdk各个封装函数的功能介绍

1.1 查询指定节点通道是否已经存在(函数: QuerySavedChannel(…) )

输入资源管理客户端、带查询节点域名、待查询通道,若该通道已存在,则返回true,负责返回false。
该函数是先用resmgmt.WithTargetEndpoints(c.Peer)创建要查询节点的请求reqPeer,再用sourceClient.QueryChannels(reqPeer)查询结果保存至数组channelInfo,再遍历查找待查询的通道ID是否已经存在。

func QuerySavedChannel(sourceClient *resmgmt.Client, info InfoSdk, c InstallCcInfo) (bool, error) {reqPeer := resmgmt.WithTargetEndpoints(c.Peer)channelInfo, err := sourceClient.QueryChannels(reqPeer)if err != nil {return false, fmt.Errorf("failed to query channel already exists: %v", err)}for _, v := range channelInfo.Channels {if v.ChannelId == info.ChannelID {return true, nil}}return false, nil
}

1.2 创建并加入通道(函数: CreateChannel(…) )

输入实例化的fabricsdk-sdk、资源管理客户端sourceClient、组织名、管理员名、通道ID、通道文件路径、通道组织域名,创建通道,并将组织加入通道。
该函数是通过mspclient.New(sdk.Context(), mspclient.WithOrg(info.OrgName))创建MSP客户端mspClient,再用mspClient.GetSigningIdentity(info.Admin)获取组织管理员身份信息adminIdentity,用resmgmt.SaveChannelRequest{ChannelID: info.ChannelID, ChannelConfigPath: info.ChannelConfigPath, SigningIdentities: []msp.SigningIdentity{adminIdentity}}封装创建通道请求channelReq,用sourceClient.SaveChannel(channelReq, resmgmt.WithRetry(retry.DefaultResMgmtOpts), resmgmt.WithOrdererEndpoint(info.OrgOrdererName))创建通道,最后用sourceClient.JoinChannel(info.ChannelID, resmgmt.WithRetry(retry.DefaultResMgmtOpts), resmgmt.WithOrdererEndpoint(info.OrgOrdererName))将组织加入通道。

// 输入实例化的`fabricsdk-sdk`、资源管理客户端`sourceClient`、组织名、管理员名、通道ID、通道文件路径、通道组织域名,创建通道,并将组织加入通道。
func CreateChannel(sdk *fabsdk.FabricSDK, sourceClient *resmgmt.Client, info InfoSdk) error {// New creates a new Client instancemspClient, err := mspclient.New(sdk.Context(), mspclient.WithOrg(info.OrgName))if err != nil {return fmt.Errorf("根据指定的 OrgName 创建 Org MSP 客户端实例失败: %v", err)}//  Returns: signing identityadminIdentity, err := mspClient.GetSigningIdentity(info.Admin)if err != nil {return fmt.Errorf("获取指定id的签名标识失败: %v", err)}// SaveChannelRequest holds parameters for save channel requestchannelReq := resmgmt.SaveChannelRequest{ChannelID: info.ChannelID, ChannelConfigPath: info.ChannelConfigPath, SigningIdentities: []msp.SigningIdentity{adminIdentity}}// save channel response with transaction ID_, err = sourceClient.SaveChannel(channelReq, resmgmt.WithRetry(retry.DefaultResMgmtOpts), resmgmt.WithOrdererEndpoint(info.OrgOrdererName))if err != nil {return fmt.Errorf("创建应用通道失败: %v", err)}fmt.Println("通道已成功创建,")// allows for peers to join existing channel with optional custom options (specific peers, filtered peers). If peer(s) are not specified in options it will default to all peers that belong to client's MSP.err = sourceClient.JoinChannel(info.ChannelID, resmgmt.WithRetry(retry.DefaultResMgmtOpts), resmgmt.WithOrdererEndpoint(info.OrgOrdererName))if err != nil {return fmt.Errorf("Peers加入通道失败: %v", err)}fmt.Println("peers 已成功加入通道.")return nil
}

1.3 查询指定节点的指定链码是否已经存在(函数: QueryInstalledCC(…) )

输入资源管理客户端、查询节点、查询链码名,给定节点上的链码已经存在就返回true,否则返回false。
该函数是用resmgmt.WithTargetEndpoints(c.Peer)封装查询节点请求得到reqPeers,再用sourceClient.QueryInstalledChaincodes(reqPeers)进行查询,将结果保存在queryResult中。

//输入资源管理客户端、查询节点、查询链码名,给定节点上的链码已经存在就返回true,否则返回false
func QueryInstalledCC(sourceClient *resmgmt.Client, c InstallCcInfo) (bool, error) {reqPeers := resmgmt.WithTargetEndpoints(c.Peer)queryResult, err := sourceClient.QueryInstalledChaincodes(reqPeers)if err != nil {return false, fmt.Errorf("failed to query installed chaincode: %v", err)}for _, v := range queryResult.Chaincodes {if v.Name == c.CCID {return true, nil}}return false, nil
}

1.4 在指定节点安装链码(函数: InstallChaincode(…) )

输入资源管理客户端、链码路径、gopath路径、链码ID、链码版本、待安装链码的节点,在指定的组织上安装链码,并指定链码ID、版本。
该函数是用gopackager.NewCCPackage(c.CCPath, c.GoPath)封装链码包得到ccPkg,用resmgmt.WithTargetEndpoints(c.Peer, c.Peer2)封装节点请求包得到reqPeer,最后用sourceClient.InstallCC(installReq, reqPeer),安装链码。

installReq := resmgmt.InstallCCRequest{Name:    c.CCID,Path:    c.CCPath,Version: c.CCVersion,Package: ccPkg,}

封装安装链码的请求包得到installReq

//输入资源管理客户端、链码路径、gopath路径、链码ID、链码版本、待安装链码的节点,在指定的组织上安装链码,并指定链码ID、版本。
func InstallChaincode(sourceClient *resmgmt.Client, c InstallCcInfo) error {ccPkg, err := gopackager.NewCCPackage(c.CCPath, c.GoPath)if err != nil {return fmt.Errorf("package chaincode failed: %v", err)}installReq := resmgmt.InstallCCRequest{Name:    c.CCID,Path:    c.CCPath,Version: c.CCVersion,Package: ccPkg,}reqPeer := resmgmt.WithTargetEndpoints(c.Peer, c.Peer2)_, err = sourceClient.InstallCC(installReq, reqPeer)if err != nil {return fmt.Errorf("Install chaincode failed: %v", c.Peer)}fmt.Println("Install chaincode sucessful")return nil
}

1.5 链码实例化(函数: ChaincodeInit(…) )

输入资源管理客户端、链码ID、链码路径、链码版本、初始化Args、背书策略,实例化链码。
该函数是用ccPolicy, err :=policydsl.FromString(c.Policy)封装背书策略得到ccPolicy,用

 req := resmgmt.InstantiateCCRequest{Name:    c.CCID,Path:    c.CCPath,Version: c.CCVersion,Args:    c.InitArgs,Policy:  ccPolicy,}

封装实例化链码请求包得到req,再用resmgmt.WithTargetEndpoints(c.Peer, c.Peer2)封装待安装链码的节点请求包得到reqPeers,最后用sourceClient.InstantiateCC(c.ChannelID, req, reqPeers)再指定节点实例化链码。

//输入资源管理客户端、链码ID、链码路径、链码版本、初始化Args、背书策略,实例化链码
func ChaincodeInit(sourceClient *resmgmt.Client, c InstallCcInfo) error {ccPolicy, err := policydsl.FromString(c.Policy)if err != nil {return fmt.Errorf("create policy failed: %v", err)}req := resmgmt.InstantiateCCRequest{Name:    c.CCID,Path:    c.CCPath,Version: c.CCVersion,Args:    c.InitArgs,Policy:  ccPolicy,}reqPeers := resmgmt.WithTargetEndpoints(c.Peer, c.Peer2)_, err = sourceClient.InstantiateCC(c.ChannelID, req, reqPeers)if err != nil {return fmt.Errorf("init chaincode failed: %v", err)}fmt.Println("init chaincode sucessful")return nil
}

1.6 调用链码(添加数据:函数: InvokeCC(…) )

输入通道管理客户端、链码ID、调用链码的函数、待执行的调用指令,返回已经存储的序列化后的数据。
该函数是用

req := channel.Request{ChaincodeID: c.CCID,Fcn:         c.Fcn,Args:        c.InvokeArgs,}

封装调用链码请求得到req,再用channel.WithTargetEndpoints(c.Peer)得到调用通道的请求包reqPeers,最后用cc.Execute(req, reqPeers)执行调用链码的操作。

//输入通道管理客户端、链码ID、调用链码的函数、待执行的调用指令,返回已经存储的序列化后的数据
func InvokeCC(cc *channel.Client, c InstallCcInfo) ([]byte, error) {req := channel.Request{ChaincodeID: c.CCID,Fcn:         c.Fcn,Args:        c.InvokeArgs,}reqPeers := channel.WithTargetEndpoints(c.Peer)result, err := cc.Execute(req, reqPeers)if err != nil {return nil, fmt.Errorf("invoke chaincode failed: %v", err)}var d GenerateCroperr = json.Unmarshal(result.Payload, &d)if err != nil {return nil, fmt.Errorf("failed to unmarshal in InvokeCC")}fmt.Printf("Invoke chaincode sucessful already saved: %v\n", d)return result.Payload, nil
}

1.7 调用链码(查询操作:函数: QueryCC(…) )

输入通道管理客户端、链码ID、查询数据需要调用的函数、查询指令,返回查询后的数据(序列化的)
该函数先利用

 req := channel.Request{ChaincodeID: c.CCID,Fcn:         c.QueryFcn,Args:        c.QueryArgs,}

封装链码查询请求包得到req,然后用cc.Query(req)得到查询到的数据包data。

func QueryCC(cc *channel.Client, c InstallCcInfo) ([]byte, error) {req := channel.Request{ChaincodeID: c.CCID,Fcn:         c.QueryFcn,Args:        c.QueryArgs,}data, err := cc.Query(req)if err != nil {return nil, fmt.Errorf("failed to invoke chaincode: %v", err)}return data.Payload, nil
}

二、链码

2.1 向区块链中添加数据

输入序列化后的要被保存的数据,通过stub.PutState()函数将数据保存到区块链账本中。
添加数据主要是利用stub.PutState(data.ID, d)函数,其中data.ID(string类型)是该条数据的Key值,d是准备存储的数据([]byte)类型,是序列化的数据。

func PutData(stub shim.ChaincodeStubInterface, data GenerateCrop) ([]byte, bool) {d, err := json.Marshal(data)if err != nil {return nil, false}err = stub.PutState(data.ID, d)if err != nil {return nil, false}return d, true
}
func (t *GenerateChaincode) AddData(stub shim.ChaincodeStubInterface, args []string) peer.Response {var data GenerateCroperr := json.Unmarshal([]byte(args[0]), &data)if err != nil {return shim.Error("data Unmarshal failed")}d, e := PutData(stub, data)if e == false {return shim.Error("data saved failed")}if d == nil {return shim.Error("failed to marshal data")}return shim.Success(d)
}

2.2 查询区块链账本中的数据

输入要查询的数据ID,通过stub.GetState()查询该ID下最新的数据,再通过stub.GetHistoryForKey()溯源该ID的所有数据,保存至结构体中,通过JSON.Marshal()将数据序列化后返回。

func (t *GenerateChaincode) QueryDataByID(stub shim.ChaincodeStubInterface, ID string) peer.Response {queryResult, err := stub.GetState(ID)if err != nil {return shim.Error("failed to query data")}if queryResult == nil {return shim.Error("not found datas")}var UnmarResult GenerateCroperr = json.Unmarshal(queryResult, &UnmarResult)if err != nil {return shim.Error("failed to UnmarResult in QueryDataByID")}iterator, err := stub.GetHistoryForKey(ID)if err != nil {return shim.Error("根据指定的身份证号码查询对应的历史变更数据失败")}defer iterator.Close()// 迭代处理var historys []HistoryItemvar hisGenerate GenerateCropfor iterator.HasNext() {hisData, err := iterator.Next()if err != nil {return shim.Error("获取edu的历史变更数据失败")}var historyItem HistoryItemhistoryItem.TxId = hisData.TxIderr = json.Unmarshal(hisData.Value, &hisGenerate)if err != nil {shim.Error("failed to Unmarshal in QueryDataByID with GetGistoryForKey")}if hisData.Value == nil {var empty GenerateCrophistoryItem.Generate = empty} else {historyItem.Generate = hisGenerate}historys = append(historys, historyItem)}UnmarResult.History = historys// 返回result, err := json.Marshal(UnmarResult)if err != nil {return shim.Error("序列化edu信息时发生错误")}return shim.Success(result)
}

三、后端服务器的接口

3.1 main函数

  1. 该函数首先调用FabricNetworkInit()函数,创建sdk、资源管理客户端、创建通道并让组织加入通道、安装并初始化链码。
  2. 用Gin框架搭建路由组,利用CorsMiddle.CorsMiddleware()解决Cors跨域问题。
  3. 访问127.0.0.1:6060/generate/addData并传入待存储数据,会调用函数AddData()将数据存入区块链中。
  4. 访问127.0.0.1:6060/generate/queryData并传入待查询数据的Key值将执行QueryData()函数在区块链中查询数据,并返回查询结果。
func main() {//1. init fabric networkerr := FabricNetworkInit()defer Manage.Sdk.Close()if err != nil {fmt.Println(err.Error())}r := gin.Default()r.Use(CorsMiddle.CorsMiddleware())//2. insert datar.POST("generate/addData", AddData)//3. query datar.GET("generate/queryData", QueryData)r.Run(":6060")}

3.2 FabricNetworkInit()函数

进行链码调用前的初始化操作,如创建Sdk、创建资源管理客户端、创建通道等。

func FabricNetworkInit() error {//1. 创建SDKManage.Sdk, _ = SetupSDK(Sdkinfo.ConfigFilePath)//2. 创建资源管理客户端。(可用该客户端创建通道,安装链码等操作)Manage.Sc, err = SetupResmg(Manage.Sdk, Sdkinfo)if err != nil {return err}//3. 查询节点ccinfo.Peer是否已加入通道sdkinfo.ChannelID,没加入则创建通道queryChannelResult, err := QuerySavedChannel(Manage.Sc, Sdkinfo, Ccinfo)if err != nil {return err}if !queryChannelResult {err = CreateChannel(Manage.Sdk, Manage.Sc, Sdkinfo)if err != nil {return err}} else {fmt.Printf("channel '%v' already exist\n", Sdkinfo.ChannelID)}//4. 查询链码ccinfo.CCID在节点ccinfo.Peer上是否已安装,没安装则安装并初始化queryResult, err := QueryInstalledCC(Manage.Sc, Ccinfo)if err != nil {fmt.Println(err.Error())return err}if !queryResult {err = InstallChaincode(Manage.Sc, Ccinfo)if err != nil {return fmt.Errorf("install chaincode failed: %v", err)}err = ChaincodeInit(Manage.Sc, Ccinfo)if err != nil {return fmt.Errorf("init chaincode failed: %v", err)}} else {fmt.Printf("chaincode with name '%v' already exists\n", Ccinfo.CCID)}//5. 创建通道管理客户端。(可用该客户端调用链码)Manage.Cc, err = SetupChannelMg(Manage.Sdk, Ccinfo.ChannelID, Sdkinfo.User, Sdkinfo.OrgName)if err != nil {return fmt.Errorf("failed to Create channel client: %v", err)}return nil}

3.3 AddData()函数

该函数接收JSON类型数据,并调用1.6中的函数 (InvokeCC(…) ) 将数据存储至区块链账本中。

func AddData(c *gin.Context) {chick, err := json.Marshal(ChickData)if err != nil {c.JSON(400, gin.H{"err": err.Error(),})}Ccinfo.InvokeArgs = [][]byte{chick}_, err = InvokeCC(Manage.Cc, Ccinfo)if err != nil {c.JSON(400, gin.H{"err": err.Error(),})}c.JSON(http.StatusOK, gin.H{"err": nil,})
}

3.4 QueryData()函数

该函数接受待查询数据的Key值(ID),调用1.7中的查询链码函数( QueryCC(…) ),查询数据,并将数据返回。

func QueryData(c *gin.Context) {Ccinfo.QueryArgs = [][]byte{[]byte(ChickData.ID)}data, err := QueryCC(Manage.Cc, Ccinfo)if err != nil {c.JSON(400, gin.H{"err":  err.Error(),"data": nil,})}var d GenerateCroperr = json.Unmarshal(data, &d)if err != nil {c.JSON(400, gin.H{"err":  err.Error(),"data": nil,})}c.JSON(http.StatusOK, gin.H{"err":  nil,"data": d,})}

WDK学习笔记_基于区块链溯源系统的后端接口开发相关推荐

  1. 基于区块链溯源系统后端开发

    文章目录 摘要 一.程序各模块功能简介 1.1 goSdk0_1 1.2 org_chaincode 二.各接口功能详细介绍 2.1 Generate(育种组织) 2.1.1 添加数据 2.1.2 查 ...

  2. 区块链溯源系统架构---区块链工作笔记002

    区块链溯源系统,实际上就是对区块链技术的一种实践 我们可以把区块链当成一种存储系统.之前我们存储的时候都是把数据存储到存储系统中.但是之前存储到数据库系统中的数据属于中心化存储.这种存储方式很难保证数 ...

  3. 区块链溯源系统开发:为何百度、阿里纷纷押注区块链溯源

    一.聊聊国内的溯源需求 2018年国内刮起一股区块链的热潮,各地政府和各大企业纷纷试水,把主要精力放在寻找真实场景.推出落地项目上.而溯源是最有应用前景的区块链落地领域之一. 传统国家级溯源平台利用的 ...

  4. 基于区块链的医疗记录存储系统研究与开发

    参考转自 https://www.qklbishe.com/ 基于区块链的医疗记录存储系统研究与开发   摘    要 随着互联网的飞速发展,医疗行业呈现出信息化的发展趋势.EMR(电子病历)记录了患 ...

  5. 「涪陵榨菜」使用区块链溯源系统?回应:看榨菜集团的安排

    榨菜上链溯源. 文 |  秦晓峰 出品 |  Odaily星球日报(ID:o-daily) 据国际在线重庆频道消息,重庆数资区块链研究院与重庆市涪陵区榨菜管理办公室达成合作,共建涪陵榨菜区块链溯源与品 ...

  6. WDK学习笔记_区块链项目实现_MAE

    文章目录 摘要 项目:区块链凤鸡溯源项目的实现 实现总流程 1.1 编写区块链网络配置文件 1.1.1 证书配置文件(crypto-config) 总体逻辑 详情 代码 1.1.2 创世区块及通道配置 ...

  7. 区块链学习笔记:DAY01 区块链的技术原理

    其实很早之前就听过区块链,也看过有关区块链的介绍,那个时候的理解主要还是一句话:分布式记账 然后开始关注比特币,听了有几年了,对于其来历.用途其实一直都是一知半解. 这次的课算是第一次以一个学员的身份 ...

  8. 动力电池溯源追溯系统_来溯有源的区块链溯源系统、追溯系统为什么做的好?值得推荐!...

    产品溯源已经不是一个新的概念,但由于"区块链"这个话题持续的升温,加上蚂蚁区块链的上市,大家一直都在期待着区块链的落地场景及应用.于是"基于区块链的产品溯源防伪" ...

  9. 区块链学习笔记二之区块链的加密技术

    概述 区块链最常见的用途是消除交易双方的中间环节.举个例子来说,学位认证的过程.当你投递简历到企业时,企业一般需要验证你的学位在类似于学信网等第三方验证平台可查,这相当于依托第三方验证平台验证你的过往 ...

最新文章

  1. K12(在线学习的平台)
  2. python 程序设计思维_Python程序设计与算法思维
  3. java怎样生成文档_java中如何创建文档中心的目录
  4. 科大星云诗社动态20210302
  5. Netty--Future和Promise
  6. java 文件通配符_Java中泛型通配符的使用方法示例
  7. 基于python的聊天室_Python实现文字聊天室
  8. 剑指offer——01二维数组中的查找.
  9. MySQL删除重复数据保留1条
  10. Ubuntu搭建Anki服务器
  11. 自动血压呼吸检测仪技术方案
  12. android信鸽推送demo_腾讯信鸽推送(java版)
  13. 2021年中国研究生数学建模竞赛F题航空公司机组优化排班问题思路参考代码
  14. Plant Ecology Journal Club, 2018
  15. JS 不可逆加密后半部分,去混淆还原代码。
  16. (六)CRAFT----2019CVPR论文解读
  17. 电脑如何安装无线网卡?
  18. Vue.js 组件 - 组件间的循环引用
  19. 王者荣耀微信转qq服务器,王者荣耀转区qq转微信可以吗 qq转区微信可以吗
  20. Windows重定向技术【文件重定向与注册表重定向】

热门文章

  1. java_多线程下载
  2. CP15 中的寄存器
  3. socket连接超时问题
  4. 浮点数的表示方法及换算技巧
  5. 解决NoteExpress无法在Word中插入引用文献
  6. RFID-MFRC522射频识别模块,S50卡M1
  7. 自定义控件 仿微信朋友圈文字展开全文功能
  8. Vue3+Quasar实现ins风格图片墙
  9. Ubuntu查看主机名和修改主机名
  10. JS/jQuery 遍历对象属性