Because I encountered some problems at work, I considered using Fabric’s cross-channel chain code calling method InvokeChaincode() to solve them. This article mainly records the following usage process of InvokeChaincode() and the problems encountered in the Fabric test network.
1 Preparation
1.1 Understanding InvokeChaincode
The function of InvokeChaincode
is to call the specified chain code. When the called chaincode and the chaincode executing InvokeChaincode
are or are not on the same channel, they can play different roles. The specific rules are as follows:
- If on the same channel as the called chaincode, it simply adds the calling chaincode’s read set and write set to the calling transaction.
- If on a different channel than the called chaincode, only the response is returned to the calling chaincode; any putState() calls from the called chaincode will have no impact on the ledger(My understanding is that in this case, data can only be read but not written).
In addition, the parameters and return values of InvokeChaincode
are as follows:
- Parameter chaincodeName: string type, target chaincode name.
- Parameter args: [][]byte type, parameter list.
- Parameter channel: string type, the name of the channel where the target chain code is located. If the channel value is “”, it indicates the current channel.
- Return value peer.Response: The peer.Pesponse type data is the response result to the peer node operation. By processing the peer.Response type data, you can obtain the status, events and returned values of the operation execution. In other words, the return value of the target chain code can be extracted from peer.Response. peer.Response data usually contains the following fields:
Field name | Meaning |
---|---|
Status | The status code of the operation, if the operation is successful, it is 200 |
Payload | The return result of the operation, its type is: []byte |
Message | The error message of the operation, only Valid on error |
TxId | Transaction ID where the operation is located |
Proposal | The proposal where the operation is located |
1.2 Preparation
In order to reduce the tedious work of creating nodes, channels, etc., we will make full use of the test network test-network
in Fabric. Before realizing cross-channel data access, the basic environment needs to be set up first. Specifically include the following:
- Use the
./network.sh up
command to create the peer node, Orderer node and cli client. - Use the
./network.sh createChannel -c
command to create two channelschannel1
andchannel2
. These two channels will share all peers and Orderers. node. - Configure the peer CLI and bind the peer CLI to the peer0 node on Org1.
- Deploy the go language chaincode sample provided in
fabric-samples
tochannel1
, and usepeer chaincode invoke
to call the chaincode’sThe InitLedger
method writes data to the world state of the chaincode. The specific code is as follows:
#Deploy the chaincode in asset-transfer-basic/chaincode-go to channel1 ./network.sh deployCC -ccn basic -ccp ../asset-transfer-basic/chaincode-go -ccl go -c channel1 #Execute 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"]}' #Query all data in world state peer chaincode query -C channel1 -n basic -c '{"Args":["GetAllAssets"]}'
Tips: The above work is not the focus of this article. You can refer to other materials for the specific process, so I will not go into details here.
After the above preparations are completed, you need to write a new chaincode file to specifically reference the InvokeChaincode
method, and deploy the new chaincode to the channel channel2
.
2 Implementation
Create a chaincode directory sherryChaincode
in the test-network
directory, and create a chaincode file mychaincode.go
in this directory. The code is as follows:
/* 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) } \t response := ctx.GetStub().InvokeChaincode(chaincodeName, chaincodeArgs, channelName) data := response.Payload return string(data), nil }c func main() {<!-- --> assetChaincode, err := contractapi.NewChaincode( & amp;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) } }
Then deploy the chaincode to the channel2
channel, as follows:
#Jump to the test-network directory first sudo ./network.sh deployCC -ccn basic_test -ccp ./sherryChaincode -ccl go -c channel2
Tips: Pay attention to the directory in the cc parameter and locate the path where the go.mod file is located.
Then use the peer chaincode query
command to verify the execution of InvokeChaincode
.
Example 1
peer chaincode query -C channel2 -n basic_test -c '{"Args":["QueryData","basic","channel1","["GetAllAssets ]"]}'
Its implementation is as follows:
Example 2
peer chaincode query -C channel2 -n basic_test -c '{"Args":["QueryData","basic","channel1","["ReadAsset ","asset6"]"]}'
The code execution results are as follows:
At this point, cross-channel access to data is achieved.
2.2 Supplementary instructions
While writing the mychaincode.go
file, I encountered some error messages. It is organized as follows:
- If an error occurs when deploying the chain code using the
deployCC
command, it is usually a syntax error in the Go language. Here you can simply modify the code according to the prompt information. - Even if the
deployCC
command succeeds, it does not mean that the chain code can run successfully. Subsequent chain code compilation and execution error information can be viewed through the channel command docker. First run thedocker ps -a
command. If the following situation occurs, it means that the chain code is still wrong.
In this case, you can view the specific error information through the docker log. The command is as follows:
docker logs --details <CONTAINER-ID>
After the code is modified, it needs to be deployed again using deployCC
.
- When starting the
mychaincode.go
file with only one method namedqueryData
, I encountered this error:Contracts are required to have at least 1 (none-ignored ) public method
.
Reason for the error: If the first letter of the method name in the Go language is lowercase, it means that other packages cannot use this method, that is, it is private. In the Go language, there must be a public method. So for this error, just capitalize the first letter of the method name, i.e.QueryData
. - The return value type of the
InvokeChaincode
method ispeer.Response
. IfQueryData
directly returnspeer.Response
, for example, as follows Writing method:
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 }
This way of writing will prompt an error: Cannot use metadata. Metadata did not match schema
. In order to solve this error, the return value type of QueryData
was changed to (string,error)
in the final code.
- In the
QueryData
method, the parameterchannel_args
is used to receive the method and its parameters to be executed in the called chain code, so thechannel_args
parameter here should receive The data type is slice. However,...string
cannot be used here but[]string
must be used to specify parameters forchannel_args
. At the same time, pay attention to the writing method of the slice part of the parameter inpeer chaincode query
.
Reference materials
- https://blog.csdn.net/weixin_41946008/article/details/123044664
- https://hyperledger-fabric.readthedocs.io/en/latest/test_network.html