• Fabric: 使用InvokeChaincode实现跨通道数据访问


    因为工作中遇到一些问题考虑使用Fabric的跨通道链码调用方法InvokeChaincode()来解决,这篇文章主要是记录以下在Fabric测试网络中InvokeChaincode()的使用过程及遇到的问题。

    1 前期准备

    1.1 认识InvokeChaincode

      InvokeChaincode的作用是调用指定的链码。而被调用的链码与执行 InvokeChaincode的链码在或不在同一个通道上时,其能发挥的作用不同。具体规则如下:

    • 如果与被调用的链码在同一个通道上,它只是将调用的链码读取集和写入集添加到调用事务中。
    • 如果与被调用的链码在不同的通道上,则只有响应返回给调用的链码;来自被调用链码的任何putState()调用都不会对账本产生任何影响(我的理解是,这种情况下只可以读数据不能写数据)。

    另外,InvokeChaincode的参数及其返回值如下:

    • 参数chaincodeName: string类型,目标链码名称。
    • 参数args: [][]byte型,参数列表。
    • 参数channel: string类型,目标链码所在的通道名称。如果channel值为"",则表示当前通道。
    • 返回值peer.Response: peer.Pesponse类型的数据是对peer节点操作的响应结果,通过处理peer.Response类型的数据,可以获取操作执行的状态、事件以及返回的值。也就是说,可以从peer.Response中抽取出目标链码的返回值。peer.Response数据通常包含以下字段:
    字段名含义
    Status操作的状态码 ,操作成功为200
    Payload操作的返回结果,其类型为:[]byte
    Message操作的错误信息,仅在出错时有效
    TxId操作所在的交易ID
    Proposal操作所在的提案
    1.2 准备工作

       为了能减少创建节点、通道等这些繁琐的工作,这里将充分利用Fabric中的测试网络test-network。在实现跨通道的数据访问之前,需要先将基本的环境搭建完成。具体包括以下:

    • 使用./network.sh up命令创建peer节点、Orderer节点和cli客户端。
    • 使用./network.sh createChannel -c命令创建两个通道channel1channel2,这两个通道将共用所有的peer和Orderer节点。
    • 配置peer CLI, 将peer CLI绑定到Org1上的peer0节点上。
    • fabric-samples中提供的go语言链码示例部署到channel1中,并使用peer chaincode invoke调用链码的InitLedger方法往链码的world state中写入数据。具体代码如下:
    #将asset-transfer-basic/chaincode-go中的链码部署到channel1中
    ./network.sh deployCC -ccn basic -ccp ../asset-transfer-basic/chaincode-go -ccl go -c channel1
    #执行InitLedger()
    peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" -C channel1 -n basic --peerAddresses localhost:7051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt" --peerAddresses localhost:9051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt" -c '{"function":"TransferAsset","Args":["asset6","Christopher"]}'
    #查询world state中的所有数据
    peer chaincode query -C channel1 -n basic -c '{"Args":["GetAllAssets"]}'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Tips:以上这些工作不是本篇的重点,具体过程可以参考其他资料,这里不赘述。

    以上准备工作完成后,需要编写新的链码文件的来具体引用InvokeChaincode方法,并将新的链码部署到通道channel2上。

    2 实现

    2.1 具体实现

    • 先编写链码文件test_invoke.go,具体代码如下:
    package chaincode
    
    import (
    	"github.com/hyperledger/fabric-contract-api-go/contractapi"
    )
    
    type TestSmartContract struct {
    	contractapi.Contract
    }
    
    func (s *TestSmartContract) QueryData(ctx contractapi.TransactionContextInterface) (string, error) {
    	//这里为了简单,把要传递给InvokeChaincode()的参数在函数体固定了,实际应用中需要改为从peer chaincode中接收参数
    	chaincodeName := "basic"
    	channelName := "channel1"
    	chaincodeArgs := make([][]byte,1)
    	chaincodeArgs[0]=[]byte("GetAllAssets")
    
    	response := ctx.GetStub().InvokeChaincode(chaincodeName, chaincodeArgs, channelName)
    	data := response.Payload
    	return string(data), nil
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • test_invoke.go文件放到fabric-samples/asset-transfer-basic/chaincode-go/chaincode目录下
    • test_invoke.go文件执行权限。
    sudo chmod -R 777 test_invoke.go
    
    • 1
    • 修改fabric-samples/asset-transfer-basic/assetTransfer.go文件。将文件中NewChaincode中的参数改为&chaincode.TestSmartContract{}即可。Tips:test_invoke.go文件的主要作用就是定义结构体类型:TestSmartContract。
      在这里插入图片描述
    • 将链码部署到channel2中,这里将这个链码命名为basic_test。
    ./network.sh deployCC -ccn basic_test -ccp ../asset-transfer-basic/chaincode-go -ccl go -c channel2
    
    • 1
    • 接下来在渠道channel2上调用链码basic_test上的QueryData来实现跨通道数据访问。
    peer chaincode query -C channel2 -n basic_test -c '{"Args":["QueryData"]}'
    
    • 1

    结果如下(成功返回了channel1中的world state数据了)。至此,就完成了跨通道的数据访问。
    在这里插入图片描述

    2.2 补充说明

      在编写test_invoke.go文件时,中途遇到一些错误信息。整理如下:

    • 使用deployCC命令部署链码时若出现错误,一般都是go语言的语法错误,这里直接根据提示信息修改代码即可。
    • 即使 deployCC命令成功了也不代表链码能运行成功。后续链码的编译和执行错误信息可以通道命令docker查看。先运行docker ps -a命令,如果出现如下情况,则说明链码依然有错。
      在这里插入图片描述
      这种情况下,可以通过docker日志来查看具体的错误信息。命令如下:
    docker logs --details <CONTAINER-ID>
    
    • 1

    代码修改之后需要重新使用 deployCC部署。

    • 开始test_invoke.go文件中只有一个名为queryData的方法时遇到这样一种错误: Contracts are required to have at least 1 (none-ignored) public method.
      错误原因:go语言中的方法名如果第一个字母为小写,则说明其他包无法使用这个方法,即为私有。所以这个错误只需要将方法名的首字母大写即可,即QueryData。这里建议,链码中的方法名都尽可能首字母大写。
    • InvokeChaincode方法的返回值类型为peer.Response,如果QueryData直接返回peer.Response,比如采用如下写法:
    func (s *TestSmartContract) QueryData(ctx contractapi.TransactionContextInterface) peer.Response {
    	chaincodeName := "basic"
    	channelName := "channel1"
    	chaincodeArgs := make([][]byte,1)
    	chaincodeArgs[0]=[]byte("GetAllAssets")
    
    	response := ctx.GetStub().InvokeChaincode(chaincodeName, chaincodeArgs, channelName)
    	return response
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    这种写法会提示错误:Cannot use metadata. Metadata did not match schema。为了解决这个错误,在最终的代码中将QueryData的返回值类型改为了(string,error)

    参考资料

    1. https://blog.csdn.net/weixin_41946008/article/details/123044664
    2. https://hyperledger-fabric.readthedocs.io/en/latest/test_network.html
  • 相关阅读:
    华为OD机试 - 事件推送(Java 2023 B卷 100分)
    事件监听 & 页面滚动(页面滚动到某一位置时显示隐藏某元素,Vue环境)
    #define定义标识符详解
    asp.net酒店管理系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio
    Yolov5网络修改教程(将backbone修改为EfficientNet、MobileNet3、RegNet等)
    超简单 轮播图片 html css
    电商数据|淘宝商品数据接口接入|参数|获取商品订单物流|电商数据分析
    BGP基本配置
    JavaSE阶段常见面试题(二)
    MySQL/Oracle用逗号分割的id怎么实现in (逗号分割的id字符串)。find_in_set(`id`, ‘1,2,3‘) 函数,
  • 原文地址:https://blog.csdn.net/yeshang_lady/article/details/134292625