You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 04:35:29