Fabric: 使用InvokeChaincode实现跨通道数据访问
因为工作中遇到一些问题考虑使用Fabric的跨通道链码调用方法InvokeChaincode()来解决,这篇文章主要是记录以下在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
命令创建两个通道channel1
和channel2
,这两个通道将共用所有的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"]}'
Tips:以上这些工作不是本篇的重点,具体过程可以参考其他资料,这里不赘述。
以上准备工作完成后,需要编写新的链码文件的来具体引用InvokeChaincode
方法,并将新的链码部署到通道channel2
上。
2 实现
在test-network
目录下创建一个链码目录sherryChaincode
,并在该目录下创建链码文件mychaincode.go
。其代码如下:
/*
SPDX-License-Identifier: Apache-2.0
*/
package main
import (
"github.com/hyperledger/fabric-contract-api-go/contractapi"
"log"
)
type TestSmartContract struct {
contractapi.Contract
}
func (s *TestSmartContract) QueryData(ctx contractapi.TransactionContextInterface,chaincode_name string,channel_name string,channel_args []string) (string, error) {
chaincodeName := chaincode_name
channelName := channel_name
chaincodeArgs := make([][]byte, len(channel_args))
for i,item := range channel_args {
chaincodeArgs[i]=[]byte(item)
}
response := ctx.GetStub().InvokeChaincode(chaincodeName, chaincodeArgs, channelName)
data := response.Payload
return string(data), nil
}
func main() {
assetChaincode, err := contractapi.NewChaincode(&TestSmartContract{})
if err != nil {
log.Panicf("Error creating asset-transfer-basic chaincode: %v", err)
}
if err := assetChaincode.Start(); err != nil {
log.Panicf("Error starting asset-transfer-basic chaincode: %v", err)
}
}
接着将该链码部署到channel2
通道上,具体如下:
#先跳转到test-network目录下
sudo ./network.sh deployCC -ccn basic_test -ccp ./sherryChaincode -ccl go -c channel2
Tips:注意ccp参数中的目录,定位到go.mod文件所在的路径即可。
接着使用peer chaincode query
命令来验证InvokeChaincode
的执行。
示例1
peer chaincode query -C channel2 -n basic_test -c '{"Args":["QueryData","basic","channel1","[\"GetAllAssets\"]"]}'
其执行情况如下:
示例2
peer chaincode query -C channel2 -n basic_test -c '{"Args":["QueryData","basic","channel1","[\"ReadAsset\",\"asset6\"]"]}'
代码执行结果如下:
至此,实现了数据的跨通道访问。
2.2 补充说明
在编写mychaincode.go
文件时,中途遇到一些错误信息。整理如下:
- 使用
deployCC
命令部署链码时若出现错误,一般都是go语言的语法错误,这里直接根据提示信息修改代码即可。 - 即使
deployCC
命令成功了也不代表链码能运行成功。后续链码的编译和执行错误信息可以通道命令docker查看。先运行docker ps -a
命令,如果出现如下情况,则说明链码依然有错。
这种情况下,可以通过docker日志来查看具体的错误信息。命令如下:
docker logs --details <CONTAINER-ID>
代码修改之后需要重新使用 deployCC
部署。
- 开始
mychaincode.go
文件中只有一个名为queryData
的方法时遇到这样一种错误:Contracts are required to have at least 1 (none-ignored) public method
.
错误原因:go语言中的方法名如果第一个字母为小写,则说明其他包无法使用这个方法,即为私有,在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
}
这种写法会提示错误:Cannot use metadata. Metadata did not match schema
。为了解决这个错误,在最终的代码中将QueryData
的返回值类型改为了(string,error)
。
- 在
QueryData
方法中参数channel_args
用来接收被调用链码中将要执行的方法及其参数,所以这里channel_args
参数应接收的数据类型为切片。但是这里不能用...string
而要用[]string
对channel_args
进行参数说明。同时还要注意peer chaincode query
中-c
中参数中切片部分的写法。
参考资料
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)