Fabric-SDK-GO时提供 的Go语言开发包,应用程序可以利用Fabric-SDK-GO与Fabric网络进行交互并访问链码。
pkg目录是fabric go sdk的主要实现,doc文档介绍了不同目录所提供的功能,以及给出了接口调用案例:
编写config.yaml配置 文件,给应用程序所使用的Fabric-SDK-Go配置相关参数及Fabric 组件的通信地址。
使用 配置实例化fabsdk实例
注意:fabsdk维护缓存,因此尽量减少fabsdk本身的实例
使用fabric实例基于用户和组织创建上下文
注意:通道上下文还需要通道ID
使用其New函数创建一个客户端实例,并传递上下文。
注意:需要为每一个上下文创建一个 新的客户端实例;
使用每个客户提供的功能来创建您的解决方案
调用fabsdk.Close()释放资源和内存。
client使用sdk与fabric网络交互,需要告诉sdk两类信息:
我是谁,即当前client的信息,包含所属组织、密钥和证书文件的路径等,这是每个 client专用的信息。
对方是谁:即fabric网络结构的信息,channel、org、orderer和peer等怎么组合其 当前fabric网络的,这些结构信息英语configtx.yaml中是一致的。这是通用配置,每个客户端都可以拿来使用。另外,这部分信息并不需要是完整fabric网络信息,如果当前client只是和部分节点交互,那配置 文件只需要 包含所使用到的网络信息。
fabric go sdk目前本身使用go modules管理以来,从go mod可知,以来的一些包制定了具体的版本,如果你的项目以来的版本和fabric go sdk依赖的版本不同,会产生编译问题。
因此建议项目也使用go modules管理依赖,然后相同的软件包可以使用相同的版本,可以这样操作:
通过config.FromFile解析配置文件,然后通过fabsdk.New创建fabric go sdk的入口实例
import "github.com/hyperledger/fabric go sdk/pkg/core/config"
import "github.com/hyperledger/fabric go sdk/pkg/fabsdk"
sdk, err := fabsdk.New(config.FromFile(c.ConfigPath))
if err != nil {
log.Panicf("failed to create fabric sdk: %s", err)
}
管理员账号才能进行Hyperledger fabric网络的管理操作,所以创建资源管理客户端一定要使用管理员账号。
通过fabsdk.WithOrg(“Org1”)和fabsdk.WithUser(“Admin”)指定Org1的Admin账户 ,使用sdk.Context创建clientProvider,然后通过resmgmt.New创建fabric-SDK-GO资源管理客户端。
import "github.com/hyperledger/fabric go sdk/pkg/client/resmgmt"
rcp := sdk.Context(fabsdk.WithUser("Admin"), fabsdk.WithOrg("Org1"))
rc, err := resmgmt.New(rcp)
if err != nil {
log.Panicf("failed to create resource client: %s", err)
}
使用用户账号创建fabric-SDK-GO的通道客户端,以便进行fabric链码的调用和查询,使用sdk.ChannelContext创建channelProvider,需要指定channelID和 用户User1,然后通过channel.New创建通道客户端,这个通道客户端就是调用channelID对应channel上的链码的channel client。
两种方法可以创建通道客户端:
方法一:
import "github.com/hyperledger/fabric go sdk/pkg/client/channel"
ccp := sdk.ChannelContext(ChannelID, fabsdk.WithUser("User1"))
cc, err := channel.New(ccp)
if err != nil {
log.Panicf("failed to create channel client: %s", err)
}
方法 二:
// New creates a new Client instance
mspClient, err := mspclient.New(sdk.Context(), mspclient.WithOrg(info.OrgName))
if err != nil {
return fmt.Errorf("根据指定的 OrgName 创建 Org MSP 客户端实例失败: %v", err)
}
// Returns: signing identity
adminIdentity, err := mspClient.GetSigningIdentity(info.OrgAdmin)
if err != nil {
return fmt.Errorf("获取指定id的签名标识失败: %v", err)
}
// SaveChannelRequest holds parameters for save channel request
channelReq := resmgmt.SaveChannelRequest{ChannelID:info.ChannelID, ChannelConfigPath:info.ChannelConfig, SigningIdentities:[]msp.SigningIdentity{adminIdentity}}
// save channel response with transaction ID
_, err = resMgmtClient.SaveChannel(channelReq, resmgmt.WithRetry(retry.DefaultResMgmtOpts), resmgmt.WithOrdererEndpoint(info.OrdererOrgName))
if err != nil {
return fmt.Errorf("创建应用通道失败: %v", err)
}
fmt.Println("通道已成功创建,")
err = info.OrgResMgmt.JoinChannel(
info.ChannelID,
resmgmt.WithRetry(retry.DefaultResMgmtOpts),
resmgmt.WithOrdererEndpoint(info.OrdererOrgName)
)
if err != nil {
return fmt.Errorf("Peers加入通道失败: %v", err)
}
fmt.Println("peers 已成功加入通道.")
安装Fabric链码使用资源管理客户端的InstallCC接口,需要指定resmgmt.InstallCCRequest以及在哪些peers上面安装。resmgmt.InstallCCRequest指明了链码ID、链码路径 、链码版本以及打包后的链码。
打包链码需要使用 到链码路径 CCPath和GoPath,GoPath即本机的$GOPATH,CCPath是相对于GoPath的相对路径,如果路径设置不对,会造成sdk找不到链码。
fmt.Println("开始安装链码......")
// creates new go lang chaincode package
ccPkg, err := gopackager.NewCCPackage(info.ChaincodePath, info.ChaincodeGoPath)
if err != nil {
return nil, fmt.Errorf("创建链码包失败: %v", err)
}
// contains install chaincode request parameters
installCCReq := resmgmt.InstallCCRequest{Name: info.ChaincodeID, Path: info.ChaincodePath, Version: ChaincodeVersion, Package: ccPkg}
/*可以制定安装在哪个peer节点上
reqPeers := resmgmt.WithTargetEndpoints("peer0.org1.example.com")
resps, err := rc.InstallCC(req, reqPeers)
*/
// allows administrators to install chaincode onto the filesystem of a peer
_, err = info.OrgResMgmt.InstallCC(installCCReq, resmgmt.WithRetry(retry.DefaultResMgmtOpts))
if err != nil {
return nil, fmt.Errorf("安装链码失败: %v", err)
}
fmt.Println("指定的链码安装成功")
实例化链码需要使用fabric go sdk的资源管理客户端都得InstantiateCC接口,需要通过ChannelID、resmgmt.InstantiateCCRequest和peers,知名在哪个channel上实例化链码,请求包含了链码的ID、路径、版本以及初始化参数和背书策略,背书策略 可以通过cauthdsl.FromString生成。
方法一:
// endorser policy
org1OrOrg2 := "OR('Org1MSP.member','Org2MSP.member')"
ccPolicy, err := cauthdsl.FromString(org1OrOrg2)
if err != nil {
return errors.WithMessage(err, "gen policy from string error")
}
// new request
args := packArgs([]string{"init", "a", "100", "b", "200"})
req := resmgmt.InstantiateCCRequest{
Name: c.CCID,
Path: c.CCPath,
Version: v,
Args: args,
Policy: ccPolicy,
}
// send request and handle response
reqPeers := resmgmt.WithTargetEndpoints("peer0.org1.example.com")
resp, err := rc.InstantiateCC(ChannelID, req, reqPeers)
if err != nil {
return errors.WithMessage(err, "instantiate chaincode error")
}
方法二:
// returns a policy that requires one valid
ccPolicy := policydsl.SignedByAnyMember([]string{"org1.kevin.kongyixueyuan.com"})
instantiateCCReq := resmgmt.InstantiateCCRequest{Name: info.ChaincodeID, Path: info.ChaincodePath, Version: ChaincodeVersion, Args: [][]byte{[]byte("init")}, Policy: ccPolicy}
// instantiates chaincode with optional custom options (specific peers, filtered peers, timeout). If peer(s) are not specified
_, err = info.OrgResMgmt.InstantiateCC(info.ChannelID, instantiateCCReq, resmgmt.WithRetry(retry.DefaultResMgmtOpts))
if err != nil {
return nil, fmt.Errorf("实例化链码失败: %v", err)
}
fmt.Println("链码实例化成功")
升级链码和实例化链码是非常相似的,不同点只在请求是 resmgmt.UpgradeCCRequest,调用的接口是rc.UpgradeCC:
// endorser policy
org1AndOrg2 := "AND('Org1MSP.member','Org2MSP.member')"
ccPolicy, err := c.genPolicy(org1AndOrg2)
if err != nil {
return errors.WithMessage(err, "gen policy from string error")
}
// new request
args := packArgs([]string{"init", "a", "100", "b", "200"})
req := resmgmt.UpgradeCCRequest{
Name: c.CCID,
Path: c.CCPath,
Version: v,
Args: args,
Policy: ccPolicy,
}
// send request and handle response
reqPeers := resmgmt.WithTargetEndpoints("peer0.org1.example.com")
resp, err := rc.UpgradeCC(ChannelID, req, reqPeers)
if err != nil {
return errors.WithMessage(err, "instantiate chaincode error")
}
使用通道客户端的Execute调用链码,使用入参channel.Request和peers知名要让哪些peer上执行链码,进行背书。channel.Request指明了要 调用的链码,以及链码内要Invoke的函数args,args是序列化 的结果,序列化是自定义的,只要链码能够按照相同的规则进行反序列化即可。
// new channel request for invoke
args := packArgs([]string{"a", "b", "10"})
req := channel.Request{
ChaincodeID: c.CCID,
Fcn: "invoke",
Args: args,
}
// send request and handle response
// peers is needed
reqPeers := channel.WithTargetEndpoints("peer0.org1.example.com")
resp, err := cc.Execute(req, reqPeers)
if err != nil {
return errors.WithMessage(err, "invoke chaincode error")
}
log.Printf("invoke chaincode tx: %s", resp.TransactionID)
查询和调用链码是非常相似的,使用相同的channel.Request,指明了Invoke链码中的query函数。然后调用cc.Query进行查询操作,这样节点不会对请求进行背书:
// new channel request for query
req := channel.Request{
ChaincodeID: c.CCID,
Fcn: "query",
Args: packArgs([]string{keys}),
}
// send request and handle response
reqPeers := channel.WithTargetEndpoints(peer)
resp, err := cc.Query(req, reqPeers)
if err != nil {
return errors.WithMessage(err, "query chaincode error")
}
log.Printf("query chaincode tx: %s", resp.TransactionID)
log.Printf("result: %v", string(resp.Payload))
(1)客户端实现
package sdkInit
import (
"fmt"
"github.com/astaxie/beego/logs"
mspclient "github.com/hyperledger/fabric-sdk-go/pkg/client/msp"
"github.com/hyperledger/fabric-sdk-go/pkg/client/resmgmt"
"github.com/hyperledger/fabric-sdk-go/pkg/common/errors/retry"
"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/msp"
"github.com/hyperledger/fabric-sdk-go/pkg/core/config"
"github.com/hyperledger/fabric-sdk-go/pkg/fabsdk"
"github.com/hyperledger/fabric-sdk-go/pkg/client/channel"
"github.com/hyperledger/fabric-sdk-go/pkg/fab/ccpackager/gopackager"
"github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/common/policydsl"
)
const ChaincodeVersion = "1.0"
//initialized fabric sdk
func SetupSDK(ConfigFile string, initialized bool) (*fabsdk.FabricSDK, error) {
if initialized {
//logs.Error("Fabric SDK has been initialized")
return nil, fmt.Errorf("Fabric SDK has been initialized")
}
sdk, err := fabsdk.New(config.FromFile(ConfigFile))
if err != nil {
//logs.Error("Instantiation Fabric SDK failed")
return nil, fmt.Errorf("Instantiation Fabric SDK failed: %v", err)
}
logs.Informational("Fabric SDK is initialized successfully")
return sdk, nil
}
// create channel and join peers
func CreateChannel(sdk *fabsdk.FabricSDK, info *InitInfo) error {
clientContext := sdk.Context(fabsdk.WithUser(info.OrgAdmin), fabsdk.WithOrg(info.OrgName))
if clientContext == nil {
return fmt.Errorf("Failed to create client context based on organization name and administrator user")
}
// New returns a resource management client instance.
resMgmtClient, err := resmgmt.New(clientContext)
if err != nil {
return fmt.Errorf("Failed to create resource management client by client context: %v", err)
}
// New creates a new Client instance
mspClient, err := mspclient.New(sdk.Context(), mspclient.WithOrg(info.OrgName))
if err != nil {
return fmt.Errorf("Failed to create Org MSP client by specified OrgName: %v", err)
}
// Returns: signing identity
adminIdentity, err := mspClient.GetSigningIdentity(info.OrgAdmin)
if err != nil {
return fmt.Errorf("Failed to get the signature of the specified ID: %v", err)
}
// SaveChannelRequest holds parameters for save channel request
channelReq := resmgmt.SaveChannelRequest{ChannelID: info.ChannelID, ChannelConfigPath: info.ChannelConfig, SigningIdentities: []msp.SigningIdentity{adminIdentity}}
// save channel response with transaction ID
_, err = resMgmtClient.SaveChannel(channelReq, resmgmt.WithRetry(retry.DefaultResMgmtOpts), resmgmt.WithOrdererEndpoint(info.OrdererOrgName))
if err != nil {
return fmt.Errorf("Failed to create channle: %v", err)
}
logs.Informational("Create channel successful")
info.OrgResMgmt = resMgmtClient
// allows for peers to join existing channel with optional custom options (specific peers, filtered peers). If peer(s) are not specified in options it will default to all peers that belong to client's MSP.
err = info.OrgResMgmt.JoinChannel(info.ChannelID, resmgmt.WithRetry(retry.DefaultResMgmtOpts), resmgmt.WithOrdererEndpoint(info.OrdererOrgName))
if err != nil {
return fmt.Errorf("Peers failed to join channel: %v", err)
}
logs.Informational("Peers join channel successful")
return nil
}
//install and instantiate chaincode
func InstallAndInstantiateCC(sdk *fabsdk.FabricSDK, info *InitInfo) (*channel.Client, error) {
logs.Informational("Start to install chaincode")
// creates new go lang chaincode package
ccPkg, err := gopackager.NewCCPackage(info.ChaincodePath, info.ChaincodeGoPath)
if err != nil {
return nil, fmt.Errorf("Failed to create chaincode package: %v", err)
}
// contains install chaincode request parameters
installCCReq := resmgmt.InstallCCRequest{Name: info.ChaincodeID, Path: info.ChaincodePath, Version: ChaincodeVersion, Package: ccPkg}
// allows administrators to install chaincode onto the filesystem of a peer
_, err = info.OrgResMgmt.InstallCC(installCCReq, resmgmt.WithRetry(retry.DefaultResMgmtOpts))
if err != nil {
return nil, fmt.Errorf("Failed to install chaincode: %v", err)
}
logs.Informational("Install chaincode successful")
logs.Informational("Start to instantiate chaincode")
// returns a policy that requires one valid
ccPolicy := policydsl.SignedByAnyMember([]string{"org1.institution.com"})
instantiateCCReq := resmgmt.InstantiateCCRequest{Name: info.ChaincodeID, Path: info.ChaincodePath, Version: ChaincodeVersion, Args: [][]byte{[]byte("init")}, Policy: ccPolicy}
// instantiates chaincode with optional custom options (specific peers, filtered peers, timeout). If peer(s) are not specified
_, err = info.OrgResMgmt.InstantiateCC(info.ChannelID, instantiateCCReq, resmgmt.WithRetry(retry.DefaultResMgmtOpts))
if err != nil {
return nil, fmt.Errorf("Failed to instantiate chaincode: %v", err)
}
logs.Informational("Instantiate chaincode successful")
clientChannelContext := sdk.ChannelContext(info.ChannelID, fabsdk.WithUser(info.UserName), fabsdk.WithOrg(info.OrgName))
// returns a Client instance. Channel client can query chaincode, execute chaincode and register/unregister for chaincode events on specific channel.
channelClient, err := channel.New(clientChannelContext)
if err != nil {
return nil, fmt.Errorf("Failed to create channel context: %v", err)
}
logs.Informational("Create channel client successful ,you can use it to execute transactions.")
return channelClient, nil
}
func ChannelClient(sdk *fabsdk.FabricSDK, info *InitInfo) (*channel.Client,error){
clientChannelContext := sdk.ChannelContext(info.ChannelID, fabsdk.WithUser(info.UserName), fabsdk.WithOrg(info.OrgName))
// returns a Client instance. Channel client can query chaincode, execute chaincode and register/unregister for chaincode events on specific channel.
channelClient, err := channel.New(clientChannelContext)
if err != nil {
return nil, fmt.Errorf("Failed to create channel context: %v", err)
}
logs.Informational("Create channel client successful ,you can use it to execute transactions.")
return channelClient, nil
}
(2)调用客户端
package main
import (
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
_ "github.com/lib/pq"
"jingjinjiapi/controllers"
_ "jingjinjiapi/routers"
"jingjinjiapi/sdkInit"
"os"
)
const (
//config of SDK
configFile = "config.yaml"
//mark whehter the client is initialized
initialized = false
//the chaincode id
EduCC = "educc"
DOC_TYPE = "insObj"
)
func main() {
//setting loggers level and location
logs.SetLogger("file", `{"filename":"logs/jingjinji_beego.log"}`)
logs.SetLevel(logs.LevelInformational)
logs.Info("setting logs level : information")
//initialized the information of sdk
initInfo := &sdkInit.InitInfo{
ChannelID: "institutionchannel",
ChannelConfig: os.Getenv("GOPATH") + "/src/jingjinjiapi/fixtures/artifacts/channel.tx",
OrgAdmin: "Admin",
OrgName: "Org1",
OrdererOrgName: "orderer.institution.com",
ChaincodeID: EduCC,
ChaincodeGoPath: os.Getenv("GOPATH"),
ChaincodePath: "jingjinjiapi/chaincode/",
UserName: "User1",
}
var serviceSetup controllers.ServiceSetup
//initialize SDK,use the function:fabsdk.new
sdk, err := sdkInit.SetupSDK(configFile, initialized)
if err != nil {
logs.Error(err.Error())
return
}
//free the resource until the main program finish
defer sdk.Close()
flag := false
if flag{
//create channel and add the peer node to the channel
err = sdkInit.CreateChannel(sdk, initInfo)
if err != nil {
logs.Error(err.Error())
return
}
//install chaincode and instantiate chaincode
channelClient, err := sdkInit.InstallAndInstantiateCC(sdk, initInfo)
if err != nil {
logs.Error(err.Error())
return
}
serviceSetup.ChaincodeID = EduCC
serviceSetup.Client = channelClient
}else{
channelClient, err := sdkInit.ChannelClient(sdk,initInfo)
if err != nil {
logs.Error(err.Error())
return
}
serviceSetup.ChaincodeID = EduCC
serviceSetup.Client = channelClient
}
logs.Informational(serviceSetup)
//===========================================//
//start Testing .............................................
//start service
beego.Router("/v1/institution", &controllers.InstitutionController{Setup: &serviceSetup})
}