在实际部署时,一般将Fabric SDK作为API的提供者,为上层应用或对外服务提供Fabric区块链网络底层服务。Fabric SDK本质是一个Fabric基础对象,功能的封装集合和客户端框架实现,旨在方便开发人员快速开发出符合产品业务需求的服务模块。
获取 Fabric 和 Fabric CA 的客户端 SDK 包,SDK 文档可以在GoDoc中查看:
go get github.com/hyperledger/fabric-sdk-go
下面使用sdk实现创建通道,加入通道,打包链码,安装链码,升级链码等功能。
配置文件包含client(客户端)、channels(通道)、organizations(组织)、orderers(排序节点列表)、peers(peer列表)、entityMatchers(实例匹配器)六部分信息。
其中client表示当前客户端信息,包含所属组织、密钥和证书文件的路径等, 这是每个client专用的信息。其余部分为fabric网络结构的信息,这些结构信息应当与configytx.yaml中是一致的。这是通用配置,每个客户端都可以拿来使用。另外,这部分信息并不需要是完整fabric网络信息,如果当前client只和部分节点交互,那配置文件中只需要包含所使用到的网络信息。
version: 0.0.0
client:
organization: org1
logging:
level: info
cryptoconfig:
path: ./fixtures/crypto-config
credentialStore:
path: "/tmp/state-store"
cryptoStore:
# Specific to the underlying KeyValueStore that backs the crypto key store.
path: /tmp/msp
BCCSP:
security:
enabled: true
default:
provider: "SW"
hashAlgorithm: "SHA2"
softVerify: true
level: 256
tlsCerts:
# [Optional]. Use system certificate pool when connecting to peers, orderers (for negotiating TLS) Default: false
systemCertPool: true
# [Optional]. Client key and cert for TLS handshake with peers and orderers
client:
key:
path: ./fixtures/crypto-config/peerOrganizations/org1.maakees.com/users/User1@org1.maakees.com/tls/client.key
cert:
path: ./fixtures/crypto-config/peerOrganizations/org1.maakees.com/users/User1@org1.maakees.com/tls/client.crt
channels:
# multi-org test channel
mychannel:
peers:
peer0.org1.maakees.com:
endorsingPeer: true
chaincodeQuery: true
ledgerQuery: true
eventSource: true
peer.org1.maakees.com:
endorsingPeer: true
chaincodeQuery: true
ledgerQuery: true
eventSource: true
policies:
queryChannelConfig:
minResponses: 1
maxTargets: 1
retryOpts:
attempts: 5
initialBackoff: 500ms
maxBackoff: 5s
backoffFactor: 2.0
#
# list of participating organizations in this network
#
organizations:
org1:
mspid: Org1MSP
# This org's MSP store (absolute path or relative to client.cryptoconfig)
cryptoPath: ./fixtures/crypto-config/peerOrganizations/org1.maakees.com/users/{username}@org1.maakees.com/msp
peers:
- peer0.org1.maakees.com
- peer1.org1.maakees.com
# failed to create resmgmt client due to context error: user not found
users:
Admin:
cert:
path: ./fixtures/crypto-config/peerOrganizations/org1.maakees.com/users/Admin@org1.maakees.com/msp/signcerts/Admin@org1.maakees.com-cert.pem
key:
path: ./fixtures/crypto-config/peerOrganizations/org1.maakees.com/users/Admin@org1.maakees.com/msp/keystore/priv_sk
User1:
cert:
path: ./fixtures/crypto-config/peerOrganizations/org1.maakees.com/users/User1@org1.maakees.com/msp/signcerts/User1@org1.maakees.com-cert.pem
key:
path: ./fixtures/crypto-config/peerOrganizations/org1.maakees.com/users/User1@org1.maakees.com/msp/keystore/priv_sk
# Orderer Org name
ordererorg:
# Membership Service Provider ID for this organization
mspID: OrdererMSP
# Needed to load users crypto keys and certs for this org (absolute path or relative to global crypto path, DEV mode)
cryptoPath: ./fixtures/crypto-config/ordererOrganizations/maakees.com/users/Admin@maakees.com/msp
# failed to create resmgmt client due to context error: user not found
users:
Admin:
cert:
path: ./fixtures/crypto-config/ordererOrganizations/maakees.com/users/Admin@maakees.com/msp/signcerts/Admin@maakees.com-cert.pem
key:
path: ./fixtures/crypto-config/ordererOrganizations/maakees.com/users/Admin@maakees.com/msp/keystore/priv_sk
#
# List of orderers to send transaction and channel create/update requests to. For the time
# being only one orderer is needed. If more than one is defined, which one get used by the
# SDK is implementation specific. Consult each SDK's documentation for its handling of orderers.
#
orderers:
orderer.example.com:
# [Optional] Default: Infer from hostname
url: localhost:7050
# these are standard properties defined by the gRPC library
# they will be passed in as-is to gRPC client constructor
grpcOptions:
ssl-target-name-override: orderer0.maakees.com
# These parameters should be set in coordination with the keepalive policy on the server,
# as incompatible settings can result in closing of connection.
# When duration of the 'keep-alive-time' is set to 0 or less the keep alive client parameters are disabled
keep-alive-time: 0s
keep-alive-timeout: 20s
keep-alive-permit: false
fail-fast: false
# allow-insecure will be taken into consideration if address has no protocol defined, if true then grpc or else grpcs
allow-insecure: false
tlsCACerts:
# Certificate location absolute path
path: ./fixtures/crypto-config/ordererOrganizations/maakees.com/tlsca/tlsca.maakees.com-cert.pem
#
# List of peers to send various requests to, including endorsement, query
# and event listener registration.
#
peers:
peer0.org1.maakees.com:
# this URL is used to send endorsement and query requests
# [Optional] Default: Infer from hostname
url: localhost:7051
grpcOptions:
ssl-target-name-override: peer0.org1.maakees.com
# These parameters should be set in coordination with the keepalive policy on the server,
# as incompatible settings can result in closing of connection.
# When duration of the 'keep-alive-time' is set to 0 or less the keep alive client parameters are disabled
keep-alive-time: 0s
keep-alive-timeout: 20s
keep-alive-permit: false
fail-fast: false
# allow-insecure will be taken into consideration if address has no protocol defined, if true then grpc or else grpcs
allow-insecure: false
#grpcOptions:
# ssl-target-name-override: peer0.org1.example.com
tlsCACerts:
# Certificate location absolute path
path: ./fixtures/crypto-config/peerOrganizations/org1.maakees.com/tlsca/tlsca.org1.maakees.com-cert.pem
peer1.org1.maakees.com:
# this URL is used to send endorsement and query requests
# [Optional] Default: Infer from hostname
url: peer1.org1.maakees.com:8051
grpcOptions:
ssl-target-name-override: peer1.org1.maakees.com
# These parameters should be set in coordination with the keepalive policy on the server,
# as incompatible settings can result in closing of connection.
# When duration of the 'keep-alive-time' is set to 0 or less the keep alive client parameters are disabled
keep-alive-time: 0s
keep-alive-timeout: 20s
keep-alive-permit: false
fail-fast: false
# allow-insecure will be taken into consideration if address has no protocol defined, if true then grpc or else grpcs
allow-insecure: false
#grpcOptions:
# ssl-target-name-override: peer0.org1.example.com
tlsCACerts:
# Certificate location absolute path
path: ./fixtures/crypto-config/peerOrganizations/org1.maakees.com/tlsca/tlsca.org1.maakees.com-cert.pem
#
# Fabric-CA is a special kind of Certificate Authority provided by Hyperledger Fabric which allows
# certificate management to be done via REST APIs. Application may choose to use a standard
# Certificate Authority instead of Fabric-CA, in which case this section would not be specified.
#
entityMatchers:
peer:
- pattern: peer0.org1.maakees.(\w+)
urlSubstitutionExp: localhost:7051
sslTargetOverrideUrlSubstitutionExp: peer0.org1.maakees.com
mappedHost: peer0.org1.maakees.com
- pattern: peer1.org1.maakees.(\w+)
urlSubstitutionExp: localhost:8051
sslTargetOverrideUrlSubstitutionExp: peer1.org1.maakees.com
mappedHost: peer1.org1.maakees.com
orderer:
- pattern: (\w+).maakees.(\w+)
urlSubstitutionExp: localhost:7050
sslTargetOverrideUrlSubstitutionExp: orderer0.maakees.com
mappedHost: orderer0.maakees.com
certificateAuthority:
- pattern: (\w+).org1.maakees.(\w+)
urlSubstitutionExp: https://localhost:7054
sslTargetOverrideUrlSubstitutionExp: ca.org1.maakees.com
mappedHost: ca.org1.maakees.com
func InitSdk(configFile string) (*fabsdk.FabricSDK) {
// 通过config.FromFile解析配置文件,然后通过fabsdk.New创建fabric go sdk的入口实例
sdk, err := fabsdk.New(config.FromFile(configFile))
if err != nil {
fmt.Println("初始化fabric sdk失败,error:%v", err)
os.Exit(-1)
}
fmt.Println("Fabric sdk初始化成功!!!")
return sdk
}
func createChannel(sdk *fabsdk.FabricSDK, channelID, channelConfigPath,orgName,orgAdmin string) {
signIds := []msp.SigningIdentity{}
// 为组织创建msp客户端
orgMspClient, err := mspclient.New(sdk.Context(), mspclient.WithOrg(orgName))
if err != nil {
fmt.Errorf("创建组织msp客户端失败,error: %v", err)
os.Exit(-1)
}
// 获取组织签名信息
signId, err := orgMspClient.GetSigningIdentity(orgAdmin)
if err != nil {
fmt.Errorf("GetSigningIdentity error: %v", err)
os.Exit(-1)
}
signIds = append(signIds, signId)
ordererClientContext := sdk.Context(fabsdk.WithUser(initInfo.OrdererAdminUser), fabsdk.WithOrg(initInfo.OrdererOrgName))
chMgmtClient, err := resmgmt.New(ordererClientContext)
if err != nil {
fmt.Errorf("根据orderer节点创建通道管理客户端失败,error: %v", err)
os.Exit(-1)
}
// 保存通道请求的参数
chreq := resmgmt.SaveChannelRequest{
ChannelID: channelID,
ChannelConfigPath: channelConfigPath,
SigningIdentities: signIds,
}
// 创建通道
_, err := chMgmtClient.SaveChannel(chreq)
if err != nil {
fmt.Errorf("SaveChannel error: %v", err)
os.Exit(-1)
}
fmt.Println("创建通道成功!!!")
}
func joinChannel(sdk *fabsdk.FabricSDK, channelID, orgAdmin, orgName, ordererEndpoint string) {
clientContext := sdk.Context(fabsdk.WithUser(orgAdmin), fabsdk.WithOrg(orgName))
// 根据组织创建通道管理客户端实例
orgResMgmt, err := resmgmt.New(clientContext)
if err != nil {
fmt.Errorf("创建通道管理客户端失败,error: %v", err)
os.Exit(-1)
}
if err = orgResMgmt.JoinChannel(channelID, resmgmt.WithRetry(retry.DefaultResMgmtOpts), resmgmt.WithOrdererEndpoint(ordererEndpoint)); err != nil {
fmt.Errorf("%s组织加入通道失败,error: %v", orgName, err)
os.Exit(-1)
}
fmt.Println("组织加入通道成功")
}
func packageCC(ccName, ccVersion, ccPath string) (string, []byte) {
label := ccName + "_" + ccVersion
desc := &lifecycle.Descriptor{
Path: ccPath,
Type: peer.ChaincodeSpec_GOLANG,
Label: label,
}
ccPkg, err := lifecycle.NewCCPackage(desc)
if err != nil {
fmt.Errorf("packageCC error: %v", err)
os.Exit(-1)
}
return label, ccPkg
}
func installCC(sdk *fabsdk.FabricSDK, label string, ccPkg []byte, orgAdmin, orgName string) {
installCCReq := resmgmt.LifecycleInstallCCRequest{
Label: label,
Package: ccPkg,
}
clientContext := sdk.Context(fabsdk.WithUser(orgAdmin), fabsdk.WithOrg(orgName))
orgResMgmt, _ := resmgmt.New(clientContext)
if _,err := orgResMgmt.LifecycleInstallCC(installCCReq, resmgmt.WithRetry(retry.DefaultResMgmtOpts)); err != nil {
fmt.Errorf("LifecycleInstallCC error: %v", err)
os.Exit(-1)
}
}
func queryInstalledCC(orgResMgmt *resmgmt.Client) {
installedCC, err := orgResMgmt.LifecycleQueryInstalledCC()
if err != nil {
fmt.Errorf("LifecycleQueryInstalledCC error: %v", err)
os.Exit(-1)
}
for _, info := range installedCC {
fmt.Printf("label:%s, packageID:%s",info.Label, info.PackageID)
}
}
func getInstalledCCPackage(packageID string, orgResMgmt *resmgmt.Client) {
if _, err := orgResMgmt.LifecycleGetInstalledCCPackage(packageID); err != nil {
fmt.Errorf("LifecycleGetInstalledCCPackage error: %v", err)
os.Exit(-1)
}
fmt.Println("LifecycleGetInstalledCCPackage success")
}
func approveCC(sdk *fabsdk.FabricSDK, packageID, ccName, ccVersion string, sequence int64, channelID string, orgAdmin, orgName, orgMspID, ordererEndpoint string) {
mspIDs := []string{}
mspIDs = append(mspIDs, orgMspID)
ccPolicy := policydsl.SignedByNOutOfGivenRole(int32(len(mspIDs)), mb.MSPRole_MEMBER, mspIDs)
approveCCReq := resmgmt.LifecycleApproveCCRequest{
Name: ccName,
Version: ccVersion,
PackageID: packageID,
Sequence: sequence,
EndorsementPlugin: "escc",
ValidationPlugin: "vscc",
SignaturePolicy: ccPolicy,
InitRequired: true,
}
orgClientContext := sdk.Context(fabsdk.WithUser(orgAdmin), fabsdk.WithOrg(orgName))
ctx, err := context.NewLocal(orgClientContext)
orgPeers, err := ctx.LocalDiscoveryService().GetPeers()
if err != nil {
fmt.Errorf("GetPeers error: %v", err)
os.Exit(-1)
}
orgResMgmt,_ := resmgmt.New(orgClientContext)
if _, err := orgResMgmt.LifecycleApproveCC(channelID, approveCCReq, resmgmt.WithTargets(orgPeers...), resmgmt.WithOrdererEndpoint(ordererEndpoint), resmgmt.WithRetry(retry.DefaultResMgmtOpts)); err != nil {
fmt.Errorf("LifecycleApproveCC error: %v", err)
os.Exit(-1)
}
fmt.Errorf("LifecycleApproveCC success")
}
func queryApprovedCC(sdk *fabsdk.FabricSDK,ccName string, sequence int64, channelID string, orgAdmin, orgName string) {
queryApprovedCCReq := resmgmt.LifecycleQueryApprovedCCRequest{
Name: ccName,
Sequence: sequence,
}
orgClientContext := sdk.Context(fabsdk.WithUser(orgAdmin), fabsdk.WithOrg(orgName))
ctx, _ := context.NewLocal(orgClientContext)
orgResMgmt,_ := resmgmt.New(orgClientContext)
orgPeers, _ := ctx.LocalDiscoveryService().GetPeers()
for _, p := range orgPeers {
resp, err := orgResMgmt.LifecycleQueryApprovedCC(channelID, queryApprovedCCReq, resmgmt.WithTargets(p))
if err != nil {
fmt.Errorf("LifecycleQueryApprovedCC returned error: %v", err)
}
fmt.Println(resp.Name)
}
}
func checkCCCommitReadiness(sdk *fabsdk.FabricSDK,ccName, ccVersion string, sequence int64, channelID string, orgAdmin, orgName, orgMspID string) {
mspIds := []string{"orgMspID"}
ccPolicy := policydsl.SignedByNOutOfGivenRole(int32(len(mspIds)), mb.MSPRole_MEMBER, mspIds)
req := resmgmt.LifecycleCheckCCCommitReadinessRequest{
Name: ccName,
Version: ccVersion,
EndorsementPlugin: "escc",
ValidationPlugin: "vscc",
SignaturePolicy: ccPolicy,
Sequence: sequence,
InitRequired: true,
}
orgClientContext := sdk.Context(fabsdk.WithUser(orgAdmin), fabsdk.WithOrg(orgName))
ctx, _ := context.NewLocal(orgClientContext)
orgResMgmt,_ := resmgmt.New(orgClientContext)
orgPeers, _ := ctx.LocalDiscoveryService().GetPeers()
for _, p := range orgPeers {
resp, err := orgResMgmt.LifecycleCheckCCCommitReadiness(channelID, req, resmgmt.WithTargets(p))
if err != nil {
fmt.Errorf("LifecycleCheckCCCommitReadiness error: %v", err)
os.Exit(-1)
}
for _, r := range resp.Approvals {
if !r {
fmt.Errorf("链码未就绪")
os.Exit(-1)
}
}
}
fmt.Errorf("LifecycleCheckCCCommitReadiness success")
}
func commitCC(ccName, ccVersion string, sequence int64, channelID string, orgs appConf.OrgsConfig, orgAdmin, orgName, orgMspID, ordererEndpoint string) {
mspIDs := []string{"orgMspID"}
ccPolicy := policydsl.SignedByNOutOfGivenRole(int32(len(mspIDs)), mb.MSPRole_MEMBER, mspIDs)
req := resmgmt.LifecycleCommitCCRequest{
Name: ccName,
Version: ccVersion,
Sequence: sequence,
EndorsementPlugin: "escc",
ValidationPlugin: "vscc",
SignaturePolicy: ccPolicy,
InitRequired: true,
}
orgClientContext := sdk.Context(fabsdk.WithUser(orgAdmin), fabsdk.WithOrg(orgName))
orgResMgmt,_ := resmgmt.New(orgClientContext)
_, err := orgResMgmt.LifecycleCommitCC(channelID, req, resmgmt.WithOrdererEndpoint(ordererEndpoint), resmgmt.WithRetry(retry.DefaultResMgmtOpts))
if err != nil {
fmt.Errorf("LifecycleCommitCC error: %v", err)
os.Exit(-1)
}
fmt.Errorf("LifecycleCommitCC success")
}
func InitCC(ccName,orgName,orgUser string) {
clientChannelContext := sdk.ChannelContext(channelID, fabsdk.WithUser(orgUser), fabsdk.WithOrg(orgName))
client, err := channel.New(clientChannelContext)
_, err := client.Execute(channel.Request{ChaincodeID: ccName, Fcn: "Init", Args: nil, IsInit: true},
channel.WithRetry(retry.DefaultChannelOpts))
if err != nil {
fmt.Errorf("Failed to initcc: %s", err)
os.Exit(-1)
}
fmt.Println("合约初始化成功 !!!")
}
升级链码与部署链码的生命周期相同:
Fabric 链码在链码定义中使用 sequence 来跟踪升级。所有的通道成员需要把序列号加一并且同意新的定义来升级链码。