如何在Hyperledger Fabric中使用Go语言实现多链码交互及跨链码调用
Hyperledger Fabric 多链码交互(Go语言实现)
在Hyperledger Fabric中,Go语言开发的链码之间要实现交互,核心依赖Fabric提供的ChaincodeStubInterface接口中的InvokeChaincode方法。以下是具体实现步骤、代码示例和注意事项:
前提条件
- 两个需要交互的链码必须部署在同一个通道上(跨通道调用需要额外配置,但同通道是最常见场景);
- 被调用链码(Callee Chaincode)已经完成部署并正常运行,且你知道它的链码名称(部署时指定的名字)。
核心调用方法:InvokeChaincode
该方法定义在github.com/hyperledger/fabric-chaincode-go/shim包中,签名如下:
InvokeChaincode(chaincodeName string, args [][]byte, channel string) pb.Response
参数说明:
chaincodeName:被调用链码的部署名称;args:传递给被调用链码的参数列表,第一个元素必须是被调用的函数名,后续是函数的入参;channel:目标通道名称,同通道调用传空字符串""即可。
代码示例
1. 被调用链码(CalleeCC)
先实现一个简单的被调用链码,提供资产查询和写入的函数:
package main import ( "fmt" "github.com/hyperledger/fabric-chaincode-go/shim" pb "github.com/hyperledger/fabric-protos-go/peer" ) type CalleeCC struct{} // Init 初始化链码(空实现) func (t *CalleeCC) Init(stub shim.ChaincodeStubInterface) pb.Response { return shim.Success(nil) } // Invoke 处理调用请求 func (t *CalleeCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response { funcName, args := stub.GetFunctionAndParameters() switch funcName { case "SetAsset": if len(args) != 2 { return shim.Error("参数错误:需要assetID和value") } err := stub.PutState(args[0], []byte(args[1])) if err != nil { return shim.Error(fmt.Sprintf("写入资产失败:%v", err)) } return shim.Success([]byte("资产写入成功")) case "GetAsset": if len(args) != 1 { return shim.Error("参数错误:需要assetID") } value, err := stub.GetState(args[0]) if err != nil { return shim.Error(fmt.Sprintf("查询资产失败:%v", err)) } if value == nil { return shim.Error("资产不存在") } return shim.Success(value) default: return shim.Error(fmt.Sprintf("未知函数:%s", funcName)) } } func main() { err := shim.Start(new(CalleeCC)) if err != nil { fmt.Printf("启动CalleeCC失败:%v", err) } }
2. 发起调用的链码(CallerCC)
在这个链码中,通过InvokeChaincode调用CalleeCC的函数:
package main import ( "fmt" "github.com/hyperledger/fabric-chaincode-go/shim" pb "github.com/hyperledger/fabric-protos-go/peer" ) type CallerCC struct{} // Init 初始化链码(空实现) func (t *CallerCC) Init(stub shim.ChaincodeStubInterface) pb.Response { return shim.Success(nil) } // Invoke 处理调用请求 func (t *CallerCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response { funcName, args := stub.GetFunctionAndParameters() switch funcName { case "CallCalleeSetAsset": // 调用CalleeCC的SetAsset函数 if len(args) != 2 { return shim.Error("参数错误:需要assetID和value") } // 构造参数:第一个元素是被调用的函数名,后续是函数入参 calleeArgs := [][]byte{ []byte("SetAsset"), []byte(args[0]), []byte(args[1]), } // 调用链码,同通道传空字符串作为channel参数 resp := stub.InvokeChaincode("calleecc", calleeArgs, "") if resp.Status != shim.OK { return shim.Error(fmt.Sprintf("调用CalleeCC失败:%s", resp.Message)) } return shim.Success(resp.Payload) case "CallCalleeGetAsset": // 调用CalleeCC的GetAsset函数 if len(args) != 1 { return shim.Error("参数错误:需要assetID") } calleeArgs := [][]byte{ []byte("GetAsset"), []byte(args[0]), } resp := stub.InvokeChaincode("calleecc", calleeArgs, "") if resp.Status != shim.OK { return shim.Error(fmt.Sprintf("调用CalleeCC失败:%s", resp.Message)) } return shim.Success(resp.Payload) default: return shim.Error(fmt.Sprintf("未知函数:%s", funcName)) } } func main() { err := shim.Start(new(CallerCC)) if err != nil { fmt.Printf("启动CallerCC失败:%v", err) } }
关键注意事项
- 事务原子性:调用链码的操作属于当前事务的一部分,如果被调用链码执行失败,整个事务(包括调用链码和被调用链码的所有操作)都会回滚;
- 权限控制:确保调用链码的身份(Peer节点或客户端)拥有访问被调用链码的权限,可通过通道的ACL配置实现;
- 避免递归调用:不要在被调用链码中反向调用发起调用的链码,否则会导致死循环;
- 参数格式匹配:传递的参数必须与被调用链码的函数入参完全匹配,否则会触发参数错误;
- 链码名称准确性:
InvokeChaincode中的chaincodeName必须和被调用链码部署时指定的名称一致,否则会提示链码不存在。
内容的提问来源于stack exchange,提问作者Arihant Sameer
相关产品推荐
相关产品推荐

