fabric-sdk-go
fabric-sdk-go
参考:
fabric-go-sdk官方网址https://github.com/hyperledger/fabric-sdk-go
博客[属于是启蒙导师了,感谢]:https://sxguan0529.gitbook.io/hyperledger-fabric/
介绍:
pkg目录包含了以下这些软件包:
pkg/fabsdk:Fabric SDK的主软件包。此程序包使您可以基于配置创建上下文。这些上下文由下面列出的客户端软件包使用。参考:https://pkg.go.dev/github.com/hyperledger/fabric-sdk-go/pkg/fabsdkpkg/client/channel:提供通道交易功能。参考:https://pkg.go.dev/github.com/hyperledger/fabric-sdk-go/pkg/client/channelpkg/client/event:提供通道事件功能参考:https://pkg.go.dev/github.com/hyperledger/fabric-sdk-go/pkg/client/eventpkg/client/ledger:启用对渠道基础分类账的查询。参考:https://pkg.go.dev/github.com/hyperledger/fabric-sdk-go/pkg/client/ledgerpkg/client/resmgmt:提供资源管理功能,例如安装链码。参考:https://pkg.go.dev/github.com/hyperledger/fabric-sdk-go/pkg/client/resmgmtpkg/client/msp:启用身份管理功能。参考:https://pkg.go.dev/github.com/hyperledger/fabric-sdk-go/pkg/client/msp
基本工作流程:
- 使用配置实例化
fabsdk实例。 注意:fabsdk维护高速缓存,因此您应最小化fabsdk本身的实例。 - 使用
fabsdk实例基于用户和组织创建上下文。 注意:通道上下文还需要通道ID。 - 使用其New函数创建一个客户端实例,并传递上下文。 注意:您需要为每个上下文创建一个新的客户端实例。
- 使用每个客户提供的功能来创建解决方案。
- 调用
fabsdk.Close()释放资源和缓存。
准备网络环境
在这个项目中我们构建一个有一个排序节点以及一个组织每个组织有一个节点的超级账本网络。
准备证书文件
注:设置GOPATH路径为 /root/go
在$GOPATH/src目录下创建一个名为fabric-go-sdk的文件夹做为项目根目录,在此目录下创建名为fixtures的文件夹存放我们网络相关配置文件,在文件夹中创建一个名为crypto-config.yaml的文件并编辑如下:
OrdererOrgs: # 排序节点配置
- Name: Orderer # 组织名称
Domain: example.com # 组织域名
EnableNodeOUs: true # 设置为true 将在msp目录下生成config.yaml文件
Specs:
- Hostname: orderer # 主机名称
PeerOrgs: # peer节点配置
- Name: Org1
Domain: org1.example.com
EnableNodeOUs: true
Template: # 使用模板,将自动生成hostname,以peer0到peerN-1的顺序生成
Count: 2 # peer节点个数
Users:
Count: 1 # peer节点中的用户数量,除admin外的用户数
使用以下命令生成证书文件:
cryptogen generate --config=crypto-config.yaml
准备通道文件
在fixtures路径下创建一个名为configtx.yaml的文件并编辑如下:
# Copyright IBM Corp. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
#
---
################################################################################
#
# Section: Organizations
#
# - This section defines the different organizational identities which will
# be referenced later in the configuration.
#
################################################################################
Organizations:
# SampleOrg defines an MSP using the sampleconfig. It should never be used
# in production but may be used as a template for other definitions
- &OrdererOrg
# DefaultOrg defines the organization which is used in the sampleconfig
# of the fabric.git development environment
Name: OrdererOrg
# ID to load the MSP definition as
ID: OrdererMSP
# MSPDir is the filesystem path which contains the MSP configuration
MSPDir: crypto-config/ordererOrganizations/example.com/msp
# Policies defines the set of policies at this level of the config tree
# For organization policies, their canonical path is usually
# /Channel/<Application|Orderer>/<OrgName>/<PolicyName>
Policies:
Readers:
Type: Signature
Rule: "OR('OrdererMSP.member')"
Writers:
Type: Signature
Rule: "OR('OrdererMSP.member')"
Admins:
Type: Signature
Rule: "OR('OrdererMSP.admin')"
OrdererEndpoints:
- orderer.example.com:7050
- &Org1
# DefaultOrg defines the organization which is used in the sampleconfig
# of the fabric.git development environment
Name: Org1MSP
# ID to load the MSP definition as
ID: Org1MSP
MSPDir: crypto-config/peerOrganizations/org1.example.com/msp
# Policies defines the set of policies at this level of the config tree
# For organization policies, their canonical path is usually
# /Channel/<Application|Orderer>/<OrgName>/<PolicyName>
Policies:
Readers:
Type: Signature
Rule: "OR('Org1MSP.member')"
Writers:
Type: Signature
Rule: "OR('Org1MSP.member')"
Admins:
Type: Signature
Rule: "OR('Org1MSP.admin')"
Endorsement:
Type: Signature
Rule: "OR('Org1MSP.member')"
# leave this flag set to true.
AnchorPeers:
# AnchorPeers defines the location of peers which can be used
# for cross org gossip communication. Note, this value is only
# encoded in the genesis block in the Application section context
- Host: peer0.org1.example.com
Port: 7051
################################################################################
#
# SECTION: Capabilities
#
# - This section defines the capabilities of fabric network. This is a new
# concept as of v1.1.0 and should not be utilized in mixed networks with
# v1.0.x peers and orderers. Capabilities define features which must be
# present in a fabric binary for that binary to safely participate in the
# fabric network. For instance, if a new MSP type is added, newer binaries
# might recognize and validate the signatures from this type, while older
# binaries without this support would be unable to validate those
# transactions. This could lead to different versions of the fabric binaries
# having different world states. Instead, defining a capability for a channel
# informs those binaries without this capability that they must cease
# processing transactions until they have been upgraded. For v1.0.x if any
# capabilities are defined (including a map with all capabilities turned off)
# then the v1.0.x peer will deliberately crash.
#
################################################################################
Capabilities:
# Channel capabilities apply to both the orderers and the peers and must be
# supported by both.
# Set the value of the capability to true to require it.
Channel: &ChannelCapabilities
# V2_0 capability ensures that orderers and peers behave according
# to v2.0 channel capabilities. Orderers and peers from
# prior releases would behave in an incompatible way, and are therefore
# not able to participate in channels at v2.0 capability.
# Prior to enabling V2.0 channel capabilities, ensure that all
# orderers and peers on a channel are at v2.0.0 or later.
V2_0: true
# Orderer capabilities apply only to the orderers, and may be safely
# used with prior release peers.
# Set the value of the capability to true to require it.
Orderer: &OrdererCapabilities
# V2_0 orderer capability ensures that orderers behave according
# to v2.0 orderer capabilities. Orderers from
# prior releases would behave in an incompatible way, and are therefore
# not able to participate in channels at v2.0 orderer capability.
# Prior to enabling V2.0 orderer capabilities, ensure that all
# orderers on channel are at v2.0.0 or later.
V2_0: true
# Application capabilities apply only to the peer network, and may be safely
# used with prior release orderers.
# Set the value of the capability to true to require it.
Application: &ApplicationCapabilities
# V2_0 application capability ensures that peers behave according
# to v2.0 application capabilities. Peers from
# prior releases would behave in an incompatible way, and are therefore
# not able to participate in channels at v2.0 application capability.
# Prior to enabling V2.0 application capabilities, ensure that all
# peers on channel are at v2.0.0 or later.
V2_0: true
################################################################################
#
# SECTION: Application
#
# - This section defines the values to encode into a config transaction or
# genesis block for application related parameters
#
################################################################################
Application: &ApplicationDefaults
# Organizations is the list of orgs which are defined as participants on
# the application side of the network
Organizations:
- *Org1
# Policies defines the set of policies at this level of the config tree
# For Application policies, their canonical path is
# /Channel/Application/<PolicyName>
Policies:
Readers:
Type: ImplicitMeta
Rule: "ANY Readers"
Writers:
Type: ImplicitMeta
Rule: "ANY Writers"
Admins:
Type: ImplicitMeta
Rule: "MAJORITY Admins"
LifecycleEndorsement:
Type: Signature
Rule: "OR('Org1MSP.member','Org2MSP.member')"
Endorsement:
Type: Signature
Rule: "OR('Org1MSP.member','Org2MSP.member')"
Capabilities:
<<: *ApplicationCapabilities
################################################################################
#
# SECTION: Orderer
#
# - This section defines the values to encode into a config transaction or
# genesis block for orderer related parameters
#
################################################################################
Orderer: &OrdererDefaults
# Orderer Type: The orderer implementation to start
OrdererType: solo
# Addresses used to be the list of orderer addresses that clients and peers
# could connect to. However, this does not allow clients to associate orderer
# addresses and orderer organizations which can be useful for things such
# as TLS validation. The preferred way to specify orderer addresses is now
# to include the OrdererEndpoints item in your org definition
Addresses:
- orderer.example.com:7050
EtcdRaft:
Consenters:
- Host: orderer.example.com
Port: 7050
ClientTLSCert: ../organizations/ordererOrganizations/example.com/orderers/orderer.example.com/tls/server.crt
ServerTLSCert: ../organizations/ordererOrganizations/example.com/orderers/orderer.example.com/tls/server.crt
# Batch Timeout: The amount of time to wait before creating a batch
BatchTimeout: 2s
# Batch Size: Controls the number of messages batched into a block
BatchSize:
# Max Message Count: The maximum number of messages to permit in a batch
MaxMessageCount: 10
# Absolute Max Bytes: The absolute maximum number of bytes allowed for
# the serialized messages in a batch.
AbsoluteMaxBytes: 99 MB
# Preferred Max Bytes: The preferred maximum number of bytes allowed for
# the serialized messages in a batch. A message larger than the preferred
# max bytes will result in a batch larger than preferred max bytes.
PreferredMaxBytes: 512 KB
# Organizations is the list of orgs which are defined as participants on
# the orderer side of the network
Organizations:
# Policies defines the set of policies at this level of the config tree
# For Orderer policies, their canonical path is
# /Channel/Orderer/<PolicyName>
Policies:
Readers:
Type: ImplicitMeta
Rule: "ANY Readers"
Writers:
Type: ImplicitMeta
Rule: "ANY Writers"
Admins:
Type: ImplicitMeta
Rule: "MAJORITY Admins"
# BlockValidation specifies what signatures must be included in the block
# from the orderer for the peer to validate it.
BlockValidation:
Type: ImplicitMeta
Rule: "ANY Writers"
################################################################################
#
# CHANNEL
#
# This section defines the values to encode into a config transaction or
# genesis block for channel related parameters.
#
################################################################################
Channel: &ChannelDefaults
# Policies defines the set of policies at this level of the config tree
# For Channel policies, their canonical path is
# /Channel/<PolicyName>
Policies:
# Who may invoke the 'Deliver' API
Readers:
Type: ImplicitMeta
Rule: "ANY Readers"
# Who may invoke the 'Broadcast' API
Writers:
Type: ImplicitMeta
Rule: "ANY Writers"
# By default, who may modify elements at this config level
Admins:
Type: ImplicitMeta
Rule: "MAJORITY Admins"
# Capabilities describes the channel level capabilities, see the
# dedicated Capabilities section elsewhere in this file for a full
# description
Capabilities:
<<: *ChannelCapabilities
################################################################################
#
# Profile
#
# - Different configuration profiles may be encoded here to be specified
# as parameters to the configtxgen tool
#
################################################################################
Profiles:
TwoOrgsOrdererGenesis:
<<: *ChannelDefaults
Orderer:
<<: *OrdererDefaults
Organizations:
- *OrdererOrg
Capabilities:
<<: *OrdererCapabilities
Consortiums:
SampleConsortium:
Organizations:
- *Org1
TwoOrgsChannel:
Consortium: SampleConsortium
<<: *ChannelDefaults
Application:
<<: *ApplicationDefaults
Organizations:
- *Org1
Capabilities:
<<: *ApplicationCapabilities
生成创始块文件,使用以下命令在当前目录下的channel-artifacts目录下得到一个文件genesis.block
configtxgen -profile TwoOrgsOrdererGenesis -outputBlock ./channel-artifacts/genesis.block -channelID fabric-channel
生成通道文件,使用以下命令在当前目录下的channel-artifacts目录下得到一个文件channel.tx
configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID mychannel
生成组织一锚节点更新文件,使用以下命令在当前目录下的channel-artifacts目录下得到一个文件Org1MSPanchors.tx
configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID mychannel -asOrg Org1MSP
解释:
这三个文件是通过使用configtxgen工具生成的Fabric网络配置文件的不同类型:
genesis.block(创始区块):
该文件是Fabric网络的初始区块,包含了定义Orderer组织和初始通道的配置信息。它是由TwoOrgsOrdererGenesis配置文件生成的。genesis.block中包含了Orderer组织的身份信息以及其他一些全局配置,如Orderer节点的地址、共识算法和初始通道的配置。channel.tx(通道配置交易):
该文件是用于创建通道的配置交易文件。它是由TwoOrgsChannel配置文件生成的。channel.tx文件包含了创建通道所需的配置信息,如通道的名称、参与通道的组织及其身份证书等。通过这个文件,可以创建一个新的通道,并将其部署到Fabric网络的所有参与组织中。Org1MSPanchors.tx(锚节点更新交易):
该文件是用于更新通道中锚节点的配置交易文件。锚节点是指在通道中代表组织的对等节点。该文件是由TwoOrgsChannel配置文件生成的。Org1MSPanchors.tx文件包含了指定组织的锚节点更新信息,用于在通道中传播和更新锚节点配置。这确保了其他组织可以连接到该组织的对等节点并进行通信。
这三个文件在Fabric网络的配置和运行过程中起着重要的作用。创始区块初始化了网络的全局配置,通道配置交易创建了新的通道,而锚节点更新交易则确保了不同组织之间的连接和通信。这些文件在设置和维护Fabric网络时必不可少。
准备docker-compose文件
在fixtures路径下创建一个名为docker-compose.yaml的文件并编辑如下:
# Copyright IBM Corp. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
#
version: '2'
volumes:
orderer.example.com:
peer0.org1.example.com:
peer1.org1.example.com:
networks:
test:
services:
orderer.example.com:
container_name: orderer.example.com
image: hyperledger/fabric-orderer:2.2
environment:
- FABRIC_LOGGING_SPEC=DEBUG
- ORDERER_GENERAL_LISTENADDRESS=0.0.0.0
- ORDERER_GENERAL_LISTENPORT=7050
- ORDERER_GENERAL_GENESISMETHOD=file
- ORDERER_GENERAL_GENESISFILE=/var/hyperledger/orderer/orderer.genesis.block
- ORDERER_GENERAL_LOCALMSPID=OrdererMSP
- ORDERER_GENERAL_LOCALMSPDIR=/var/hyperledger/orderer/msp
# enabled TLS
- ORDERER_GENERAL_TLS_ENABLED=true
- ORDERER_GENERAL_TLS_PRIVATEKEY=/var/hyperledger/orderer/tls/server.key
- ORDERER_GENERAL_TLS_CERTIFICATE=/var/hyperledger/orderer/tls/server.crt
- ORDERER_GENERAL_TLS_ROOTCAS=[/var/hyperledger/orderer/tls/ca.crt]
# - ORDERER_KAFKA_TOPIC_REPLICATIONFACTOR=1
# - ORDERER_KAFKA_VERBOSE=true
# - ORDERER_GENERAL_CLUSTER_CLIENTCERTIFICATE=/var/hyperledger/orderer/tls/server.crt
# - ORDERER_GENERAL_CLUSTER_CLIENTPRIVATEKEY=/var/hyperledger/orderer/tls/server.key
# - ORDERER_GENERAL_CLUSTER_ROOTCAS=[/var/hyperledger/orderer/tls/ca.crt]
working_dir: /opt/gopath/src/github.com/hyperledger/fabric
command: orderer
volumes:
- ./channel-artifacts/genesis.block:/var/hyperledger/orderer/orderer.genesis.block
- ./crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/msp:/var/hyperledger/orderer/msp
- ./crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/tls/:/var/hyperledger/orderer/tls
- orderer.example.com:/var/hyperledger/production/orderer
ports:
- 7050:7050
networks:
- test
peer0.org1.example.com:
container_name: peer0.org1.example.com
image: hyperledger/fabric-peer:2.2
environment:
#Generic peer variables
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
# the following setting starts chaincode containers on the same
# bridge network as the peers
# https://docs.docker.com/compose/networking/
- CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=fixtures_test
- FABRIC_LOGGING_SPEC=DEBUG
#- FABRIC_LOGGING_SPEC=DEBUG
- CORE_PEER_TLS_ENABLED=true
- CORE_PEER_PROFILE_ENABLED=true
- CORE_PEER_TLS_CERT_FILE=/etc/hyperledger/fabric/tls/server.crt
- CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/fabric/tls/server.key
- CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/tls/ca.crt
# Peer specific variabes
- CORE_PEER_ID=peer0.org1.example.com
- CORE_PEER_ADDRESS=peer0.org1.example.com:7051
- CORE_PEER_LISTENADDRESS=0.0.0.0:7051
- CORE_PEER_CHAINCODEADDRESS=peer0.org1.example.com:7052
- CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:7052
- CORE_PEER_GOSSIP_BOOTSTRAP=peer1.org1.example.com:9051
- CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org1.example.com:7051
- CORE_PEER_LOCALMSPID=Org1MSP
- CORE_LEDGER_STATE_STATEDATABASE=CouchDB
- CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb0:5984
- CORE_LEDGER_STATE_COUCHDBCONFIG_USERNAME=admin
- CORE_LEDGER_STATE_COUCHDBCONFIG_PASSWORD=123456
volumes:
- /var/run/:/host/var/run/
- ./crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/msp:/etc/hyperledger/fabric/msp
- ./crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls:/etc/hyperledger/fabric/tls
- peer0.org1.example.com:/var/hyperledger/production
working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
command: peer node start
ports:
- 7051:7051
depends_on:
- orderer.example.com
#- couchdb.org1.example.com
networks:
- test
peer1.org1.example.com:
container_name: peer1.org1.example.com
image: hyperledger/fabric-peer:2.2
environment:
#Generic peer variables
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
# the following setting starts chaincode containers on the same
# bridge network as the peers
# https://docs.docker.com/compose/networking/
- CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=fixtures_test
- FABRIC_LOGGING_SPEC=DEBUG
#- FABRIC_LOGGING_SPEC=DEBUG
- CORE_PEER_TLS_ENABLED=true
- CORE_PEER_PROFILE_ENABLED=true
- CORE_PEER_TLS_CERT_FILE=/etc/hyperledger/fabric/tls/server.crt
- CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/fabric/tls/server.key
- CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/tls/ca.crt
# Peer specific variabes
- CORE_PEER_ID=peer1.org1.example.com
- CORE_PEER_ADDRESS=peer1.org1.example.com:9051
- CORE_PEER_LISTENADDRESS=0.0.0.0:9051
- CORE_PEER_CHAINCODEADDRESS=peer1.org1.example.com:9052
- CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:9052
- CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer1.org1.example.com:9051
- CORE_PEER_GOSSIP_BOOTSTRAP=peer0.org1.example.com:7051
- CORE_PEER_LOCALMSPID=Org1MSP
- CORE_LEDGER_STATE_STATEDATABASE=CouchDB
- CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb1:5984
- CORE_LEDGER_STATE_COUCHDBCONFIG_USERNAME=admin
- CORE_LEDGER_STATE_COUCHDBCONFIG_PASSWORD=123456
volumes:
- /var/run/:/host/var/run/
- ./crypto-config/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/msp:/etc/hyperledger/fabric/msp
- ./crypto-config/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls:/etc/hyperledger/fabric/tls
- peer1.org1.example.com:/var/hyperledger/production
working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
command: peer node start
ports:
- 9051:9051
depends_on:
- orderer.example.com
#- couchdb.org1.example.com
networks:
- test
ca.org1.example.com:
image: hyperledger/fabric-ca:1.4.9
container_name: ca.org1.example.com
environment:
- FABRIC_CA_HOME=/etc/hyperledger/fabric-ca-server
- FABRIC_CA_SERVER_CA_NAME=ca.org1.example.com
- FABRIC_CA_SERVER_CA_CERTFILE=/etc/hyperledger/fabric-ca-server-config/ca.org1.example.com-cert.pem
- FABRIC_CA_SERVER_CA_KEYFILE=/etc/hyperledger/fabric-ca-server-config/priv_sk
- FABRIC_CA_SERVER_TLS_ENABLED=true
- FABRIC_CA_SERVER_TLS_CERTFILE=/etc/hyperledger/fabric-ca-server-config/ca.org1.example.com-cert.pem
- FABRIC_CA_SERVER_TLS_KEYFILE=/etc/hyperledger/fabric-ca-server-config/priv_sk
ports:
- 7054:7054
command: sh -c 'fabric-ca-server start -b admin:adminpw -d'
volumes:
- ./crypto-config/peerOrganizations/org1.example.com/ca/:/etc/hyperledger/fabric-ca-server-config
networks:
- test
couchdb0:
container_name: couchdb0
image: hyperledger/fabric-couchdb:latest
# Populate the COUCHDB_USER and COUCHDB_PASSWORD to set an admin user and password
# for CouchDB. This will prevent CouchDB from operating in an "Admin Party" mode.
environment:
- COUCHDB_USER=admin
- COUCHDB_PASSWORD=123456
# Comment/Uncomment the port mapping if you want to hide/expose the CouchDB service,
# for example map it to utilize Fauxton User Interface in dev environments.
ports:
- "5984:5984"
networks:
- test
couchdb1:
container_name: couchdb1
image: hyperledger/fabric-couchdb:latest
# Populate the COUCHDB_USER and COUCHDB_PASSWORD to set an admin user and password
# for CouchDB. This will prevent CouchDB from operating in an "Admin Party" mode.
environment:
- COUCHDB_USER=admin
- COUCHDB_PASSWORD=123456
# Comment/Uncomment the port mapping if you want to hide/expose the CouchDB service,
# for example map it to utilize Fauxton User Interface in dev environments.
ports:
- "7984:5984"
networks:
- test
配置文件config.yaml
客户端使用sdk与fabric网络交互,需要告诉sdk两类信息:
- 我是谁:即当前客户端的信息,包含所属组织、密钥和证书文件的路径等, 这是每个客户端专用的信息。
- 对方是谁:即fabric网络结构的信息,channel、org、orderer和peer等 的怎么组合起当前fabric网络的,这些结构信息应当与
configytx.yaml中是一致的。这是通用配置,每个客户端都可以拿来使用。另外,这部分信息并不需要是完整fabric网络信息,如果当前客户端只和部分节点交互,那配置文件中只需要包含所使用到的网络信息。
下面是详细的配置文件用法:
1、声明
#
# Copyright SecureKey Technologies Inc. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
#
#
# The network connection profile provides client applications the information about the target
# blockchain network that are necessary for the applications to interact with it. These are all
# knowledge that must be acquired from out-of-band sources. This file provides such a source.
# 网络连接配置文件为客户端应用程序提供目标区块链网络的信息,这是应用程序与之交互所必须的。
# 这些都是必须从带外源获得的信息。该文件提供了这样的源代码。
#
# Schema version of the content. Used by the SDK to apply the corresponding parsing rules.
# 内容的模式版本。SDK用于应用相应的解析规则。
version: 1.0.0
2、客户端部分
#
# The client section used by GO SDK.
# 用于GO SDK的客户端部分。
client:
# Which organization does this application instance belong to? The value must be the name of an org
# defined under "organizations"
# 这个应用程序实例属于哪个组织?这个值必须是定义在"organizations"下的一个组织的名称。
organization: org1
logging:
level: info
# Global configuration for peer, event service and orderer timeouts
# if this this section is omitted, then default values will be used (same values as below)
# 对等节点,事件服务和排序节点超时的全局配置。
# 如果这个部分省略,将会使用默认值(默认值如下所示)。
# peer:
# timeout:
# connection: 10s
# response: 180s
# discovery:
# # Expiry period for discovery service greylist filter
# # The channel client will greylist peers that are found to be offline
# # to prevent re-selecting them in subsequent retries.
# # This interval will define how long a peer is greylisted
# 发现服务灰名单过滤器的过期时间
# 通道客户端需要将掉线的对等节点列为灰名单以阻止重复选择并重试。
# 这个间隔时间将定义多长时间将对等节点列为灰名单。
# greylistExpiry: 10s
# the below timeouts are commented out to use the default values that are found in
# "pkg/fab/endpointconfig.go"
# the client is free to override the default values by uncommenting and resetting
# the values as they see fit in their config file
# 下面的超时时间被注释掉以使用默认值,默认值可以在"pkg/fab/endpointconfig.go"中找到
# 客户端可以根据配置文件的需要取消注释并重新设置这些值,从而覆盖默认值
# timeout:
# connection: 15s
# registrationResponse: 15s
# orderer:
# timeout:
# connection: 15s
# response: 15s
# global:
# timeout:
# query: 180s
# execute: 180s
# resmgmt: 180s
# cache:
# connectionIdle: 30s
# eventServiceIdle: 2m
# channelConfig: 30m
# channelMembership: 30s
# discovery: 10s
# selection: 10m
# Root of the MSP directories with keys and certs.
# 根MSP的keys和certs路径
cryptoconfig:
path: ${FABRIC_SDK_GO_PROJECT_PATH}/${CRYPTOCONFIG_FIXTURES_PATH}
# Some SDKs support pluggable KV stores, the properties under "credentialStore"
# are implementation specific
# 一些SDK支持可插拔的KV存储,"credentialStore"下的属性是具体实现
credentialStore:
# [Optional]. Used by user store. Not needed if all credentials are embedded in configuration
# and enrollments are performed elsewhere.
# [可选的] 用于用户存储。如果所有凭证都嵌入到配置中,并且在其他地方执行登记,则不需要。
path: "/tmp/state-store"
# [Optional]. Specific to the CryptoSuite implementation used by GO SDK. Software-based implementations
# requiring a key store. PKCS#11 based implementations does not.
# [可选的] 特定于GO SDK使用的CryptoSuite实现。基于软件的实现需要密钥存储区。基于PKCS#11的实现则不用。
cryptoStore:
# Specific to the underlying KeyValueStore that backs the crypto key store.
# 特定于支持加密密钥存储的基础KeyValueStore。
path: /tmp/msp
# [Optional] BCCSP config for the client. Used by GO SDK.
# [可选的] 客户端的BCCSP配置。用于GO SDK。
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
# [可选的] 当连接到对等节点、排序节点(用于协商TLS)时使用系统证书池。默认值:false
systemCertPool: true
# [Optional]. Client key and cert for TLS handshake with peers and orderers
# [可选的] 与对等节点和排序节点进行TLS握手的客户端密钥和证书
client:
key:
path: ${FABRIC_SDK_GO_PROJECT_PATH}/${CRYPTOCONFIG_FIXTURES_PATH}/peerOrganizations/tls.example.com/users/User1@tls.example.com/tls/client.key
cert:
path: ${FABRIC_SDK_GO_PROJECT_PATH}/${CRYPTOCONFIG_FIXTURES_PATH}/peerOrganizations/tls.example.com/users/User1@tls.example.com/tls/client.crt
#
# [Optional]. But most apps would have this section so that channel objects can be constructed
# based on the content below. If an app is creating channels, then it likely will not need this
# section.
# [可选的] 但是大多数应用程序都会有此部分,以便可以根据以下内容构造通道对象。
# 如果某个应用正在创建频道,则可能不需要此部分。
3、通道部分
#
# [Optional]. But most apps would have this section so that channel objects can be constructed
# based on the content below. If an app is creating channels, then it likely will not need this
# section.
# [可选的] 但是大多数应用程序都会有此部分,以便可以根据以下内容构造通道对象。
# 如果某个应用正在创建频道,则可能不需要此部分。
channels:
# Default channel is used if channel configuration is missing or if defined channel configuration is missing info
# If channel doesn't define peers then peers from default channel will be used
# If channel doesn't define orderes then orderes from default channel will be used
# If channel doesn't define policies then policies from default channel will be used.
# Also, if channel defines policies and some policy info is missing than that missing info will be filled from default channel.
# 如果缺少通道配置或定义的通道配置缺少信息,则使用默认通道配置
# 如果通道未定义对等节点,则将使用默认通道中的对等节点
# 如果通道未定义排序节点,则将使用默认通道的排序节点
# 如果通道未定义策略,则将使用默认通道中的策略。
# 此外,如果通道定义了策略,并且缺少某些策略信息,那么该缺失信息将从默认通道中填充。
_default:
# [Optional]. list of peers from participating orgs
# [可选的] 参与组织的对等节点列表。
peers:
peer0.org1.example.com:
# [Optional]. will this peer be sent transaction proposals for endorsement? The peer must
# have the chaincode installed. The app can also use this property to decide which peers
# to send the chaincode install request. Default: true
# 这个对等节点是否会被发送交易建议以供背书?对等端必须安装了chaincode。
# 应用程序也可以使用这个属性来决定哪个对等节点发送chaincode安装请求。默认值:true
endorsingPeer: true
# [Optional]. will this peer be sent query proposals? The peer must have the chaincode
# installed. The app can also use this property to decide which peers to send the
# chaincode install request. Default: true
# 这个对等节点会被发送查询建议吗?对等端必须安装了chaincode。
# 应用程序也可以使用这个属性来决定哪个对等节点发送chaincode安装请求。默认值:true
chaincodeQuery: true
# [Optional]. will this peer be sent query proposals that do not require chaincodes, like
# queryBlock(), queryTransaction(), etc. Default: true
# 这个对等节点是否会被发送不需要链码的查询建议,如queryBlock(), queryTransaction()等。默认值:true
ledgerQuery: true
# [Optional]. will this peer be the target of the SDK's listener registration? All peers can
# produce events but the app typically only needs to connect to one to listen to events.
# Default: true
# 这个对等节点会成为SDK侦听器注册的目标吗?所有对等节点都可以产生事件,但应用程序通常只需要连接到一个对等节点就可以监听事件。
# 默认:真
eventSource: true
# [Optional]. The application can use these options to perform channel operations like retrieving channel
# config etc.
# 应用程序可以使用这些选项来执行通道操作,如检索通道配置等。
policies:
#[Optional] options for retrieving discovery info
# 检索发现信息的选项
discovery:
#[Optional] discovery info will be retrieved for these number of random targets
maxTargets: 2
#[Optional] retry options for retriving discovery info
# 发现信息将为这些数目的随机目标检索
retryOpts:
#[Optional] number of retry attempts
# 重试次数
attempts: 4
#[Optional] the back off interval for the first retry attempt
# 第一次重试尝试的后退间隔
initialBackoff: 500ms
#[Optional] the maximum back off interval for any retry attempt
# 任何重试尝试的最大回退间隔
maxBackoff: 5s
#[Optional] he factor by which the initial back off period is exponentially incremented
# 它使初始的回退周期以指数形式增加
backoffFactor: 2.0
#[Optional] options for selection service
# 选择服务选项
selection:
#[Optional] endorser selection sorting strategy. Possible values: [BlockHeightPriority,Balanced]
# 背书者选择排序策略
SortingStrategy: BlockHeightPriority
#[Optional] load-balancer type. Possible values: [RoundRobin,Random]
# 负载平衡器类型
Balancer: RoundRobin
#[Optional] the number of blocks from the highest block number of a group of peers that a peer
#can lag behind and still be considered to be up-to-date. These peers will be sorted using the
#given Balancer. If a peer's block height falls behind this threshold then it will be demoted
#to a lower priority list of peers which will be sorted according to block height.
#Note: This property only applies to BlockHeightPriority sorter.
# 在一组对等节点中,一个对等节点可以落后但仍然被认为是最新的最大块数。
# 这些对等节点将使用给定的平衡器进行排序。
# 如果一个对等节点的块高度低于这个阈值,那么它将被降级到一个较低优先级的对等节点列表中,该列表将根据块高度进行排序。
# 注意:此属性仅适用于BlockHeightPriority排序器。
BlockHeightLagThreshold: 5
#[Optional] options for retrieving channel configuration blocks
# 检索通道配置块的选项
queryChannelConfig:
#[Optional] min number of success responses (from targets/peers)
# 最小成功响应数(来自目标/对等节点)
minResponses: 1
#[Optional] channel config will be retrieved for these number of random targets
# 通道配置将为这些数量的随机目标检索
maxTargets: 1
#[Optional] retry options for query config block
# 查询配置块的重试选项
retryOpts:
#[Optional] number of retry attempts
# 重试次数
attempts: 5
#[Optional] the back off interval for the first retry attempt
initialBackoff: 500ms
#[Optional] the maximum back off interval for any retry attempt
maxBackoff: 5s
#[Optional] he factor by which the initial back off period is exponentially incremented
backoffFactor: 2.0
#[Optional] options for the event service
# 事件服务的选项
eventService:
# [Optional] resolverStrategy specifies the peer resolver strategy to use when connecting to a peer
# resolverStrategy指定连接到对等节点时使用的对等解析器策略
# Possible values: [PreferOrg (default), MinBlockHeight, Balanced]
#
# PreferOrg:
# Determines which peers are suitable based on block height lag threshold, although will prefer the peers in the
# current org (as long as their block height is above a configured threshold). If none of the peers from the current org
# are suitable then a peer from another org is chosen.
# 根据块高度滞后阈值确定哪些对等节点是合适的,尽管会更喜欢当前org中的对等节点(只要它们的块高度高于配置的阈值)。
# 如果当前组织的对等节点中没有一个是合适的,则选择另一个组织的对等节点。
# MinBlockHeight:
# Chooses the best peer according to a block height lag threshold. The maximum block height of all peers is
# determined and the peers whose block heights are under the maximum height but above a provided "lag" threshold are load
# balanced. The other peers are not considered.
# 根据块高度延迟阈值选择最佳的对等节点。所有对等节点的最大块高度被确定,并且其块高度低于最大高度但高于提供的“滞后”阈值的对等节点被负载平衡。
# 其他同行不考虑。
# Balanced:
# Chooses peers using the configured balancer.
# 使用配置的均衡器选择对等节点。
resolverStrategy: PreferOrg
# [Optional] balancer is the balancer to use when choosing a peer to connect to
# 平衡器是在选择要连接的对等点时使用的平衡器
# Possible values: [Random (default), RoundRobin]
balancer: RoundRobin
# [Optional] blockHeightLagThreshold sets the block height lag threshold. This value is used for choosing a peer
# to connect to. If a peer is lagging behind the most up-to-date peer by more than the given number of
# blocks then it will be excluded from selection.
# Note that this parameter is applicable only when minBlockHeightResolverMode is set to ResolveByThreshold.
# blockHeightLagThreshold块高度滞后阈值。此值用于选择要连接的对等点。如果一个对等节点落后于最新的对等点超过给定的块数,那么它将被排除在选择之外。
# 需要注意的是,当minBlockHeightResolverMode设置为ResolveByThreshold时,此参数才会生效
# Default: 5
blockHeightLagThreshold: 2
# [Optional] reconnectBlockHeightLagThreshold - the event client will disconnect from the peer if the peer's
# block height falls behind the specified number of blocks and will reconnect to a better performing peer.
# Note that this parameter is only applicable if peerMonitor is set to Enabled (default).
# reconnectBlockHeightLagThreshold—如果对等节点的块高度低于指定的块数量,事件客户端将断开与对等节点的连接,并重新连接到性能更好的对等节点。
# 注意:只有当“peerMonitor”设置为“Enabled”(默认)时,此参数才有效。
# Default: 10
# NOTES:
# - Setting this value too low may cause the event client to disconnect/reconnect too frequently, thereby
# affecting performance.
reconnectBlockHeightLagThreshold: 5
# [Optional] peerMonitorPeriod is the period in which the connected peer is monitored to see if
# the event client should disconnect from it and reconnect to another peer.
# peerMonitorPeriod是监控已连接的对等节点的周期,以查看事件客户端是否应该断开连接并重新连接到另一个对等节点。
# Default: 0 (disabled) for Balanced resolverStrategy; 5s for PreferOrg and MinBlockHeight strategy
peerMonitorPeriod: 3s
# Mychannel overrides initialBackoff for discovery
# Mychannel覆盖发现的initialBackoff
mychannel:
policies:
discovery:
retryOpts:
#[Required for override] number of retry attempts
attempts: 4
initialBackoff: 1s
# multi-org test channel
orgchannel:
# orderers:
# - orderer.example.com
peers:
peer0.org1.example.com:
endorsingPeer: true
chaincodeQuery: true
ledgerQuery: true
eventSource: true
peer1.org1.example.com:
endorsingPeer: true
chaincodeQuery: true
ledgerQuery: true
eventSource: true
peer0.org2.example.com:
endorsingPeer: true
chaincodeQuery: true
ledgerQuery: true
eventSource: true
4、组织部分
#
# list of participating organizations in this network
# 参与该网络的组织名单
#
organizations:
org1:
mspid: Org1MSP
# This org's MSP store (absolute path or relative to client.cryptoconfig)
# MSP存储路径
cryptoPath: peerOrganizations/org1.example.com/users/{username}@org1.example.com/msp
peers:
- peer0.org1.example.com
- peer1.org1.example.com
# [Optional]. Certificate Authorities issue certificates for identification purposes in a Fabric based
# network. Typically certificates provisioning is done in a separate process outside of the
# runtime network. Fabric-CA is a special certificate authority that provides a REST APIs for
# dynamic certificate management (enroll, revoke, re-enroll). The following section is only for
# Fabric-CA servers.
# 在基于Fabric的网络中,证书颁发机构为识别目的颁发证书。通常,证书提供是在运行时网络之外的单独进程中完成的。
# Fabric-CA是一个特殊的证书颁发机构,它为动态证书管理(注册、撤销、重新注册)提供了一个REST api。以下内容仅适用于Fabric-CA服务器。
certificateAuthorities:
- ca.org1.example.com
- tlsca.org1.example.com
# the profile will contain public information about organizations other than the one it belongs to.
# These are necessary information to make transaction lifecycles work, including MSP IDs and
# peers with a public URL to send transaction proposals. The file will not contain private
# information reserved for members of the organization, such as admin key and certificate,
# fabric-ca registrar enroll ID and secret, etc.
# 这个概要文件将包含关于它所属组织以外的其他组织的公共信息。
# 这些是使事务生命周期正常工作所必需的信息,包括MSP id和使用公共URL发送事务建议的对等节点。
# 该文件将不包含为组织成员保留的私人信息,如管理员密钥和证书、fabric-ca注册商注册ID和秘密等。
org2:
mspid: Org2MSP
# This org's MSP store (absolute path or relative to client.cryptoconfig)
cryptoPath: peerOrganizations/org2.example.com/users/{username}@org2.example.com/msp
peers:
- peer0.org2.example.com
- peer1.org2.example.com
certificateAuthorities:
- ca.org2.example.com
# 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: ordererOrganizations/example.com/users/{username}@example.com/msp
5、排序节点部分
#
# 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.
#
# 要发送事务和通道创建/更新请求的订购者列表。目前只需要一份订单。如果定义了多个,那么SDK将使用哪个是特定于实现的。
# 请查阅每个SDK的文档,了解它对排序的处理。
orderers:
_default:
# common orderer config items between all the orderers in config
# these are standard properties defined by the gRPC library
# they will be passed in as-is to gRPC client constructor
# 这些是gRPC库定义的标准属性,它们将被原样传递给gRPC客户端构造器
grpcOptions:
# 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
# 这些参数的设置应该与服务器上的keepalive策略相协调,因为不兼容的设置可能导致连接关闭。
# 当“keep-alive-time”的持续时间设置为0或更小时,保持活跃的客户端参数将被禁用
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
# 如果address没有定义协议,Allow-insecure将被考虑,如果address为true则GRPC或GRPCS
allow-insecure: false
orderer.example.com:
# [Optional] Default: Infer from hostname
url: orderer.example.com:7050
# these are standard properties defined by the gRPC library
# they will be passed in as-is to gRPC client constructor
# 这些是gRPC库定义的标准属性,它们将原样传递给gRPC客户端构造函数
grpcOptions:
#ssl-target-name-override: orderer.example.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
# 这些参数的设置应该与服务器上的keepalive策略相协调,因为不兼容的设置可能导致连接关闭。
# 当“keep-alive-time”的持续时间设置为0或更小时,保持活跃的客户端参数将被禁用
#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
# 如果address没有定义协议,Allow-insecure将被考虑,如果address为true则GRPC或GRPCS
#allow-insecure: false
tlsCACerts:
# Certificate location absolute path
path: ${FABRIC_SDK_GO_PROJECT_PATH}/${CRYPTOCONFIG_FIXTURES_PATH}/ordererOrganizations/example.com/tlsca/tlsca.example.com-cert.pem
6、对等节点部分
#
# List of peers to send various requests to, including endorsement, query
# and event listener registration.
# 发送各种请求的对等点节点列表,包括背书、查询和事件侦听器注册。
#
peers:
_default:
#common grpc options between all the peers
# 所有对等节点的通用GRPC选项
grpcOptions:
# 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
# 这些参数的设置应该与服务器上的keepalive策略相协调,因为不兼容的设置可能导致连接关闭。
# 当“keep-alive-time”的持续时间设置为0或更小时,保持活跃的客户端参数将被禁用
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
# 如果address没有定义协议,Allow-insecure将被考虑,如果address为true则GRPC或GRPCS
allow-insecure: false
peer0.org1.example.com:
# this URL is used to send endorsement and query requests
# 此URL用于发送背书和查询请求
# [Optional] Default: Infer from hostname
url: peer0.org1.example.com:7051
#grpcOptions:
# ssl-target-name-override: peer0.org1.example.com
tlsCACerts:
# 证书本地绝对路径
path: ${FABRIC_SDK_GO_PROJECT_PATH}/${CRYPTOCONFIG_FIXTURES_PATH}/peerOrganizations/org1.example.com/tlsca/tlsca.org1.example.com-cert.pem
peer1.org1.example.com:
# 此URL用于发送背书和查询请求
url: peer1.org1.example.com:7151
#grpcOptions:
# ssl-target-name-override: peer1.org1.example.com
tlsCACerts:
# Certificate location absolute path
path: ${FABRIC_SDK_GO_PROJECT_PATH}/${CRYPTOCONFIG_FIXTURES_PATH}/peerOrganizations/org1.example.com/tlsca/tlsca.org1.example.com-cert.pem
peer0.org2.example.com:
url: peer0.org2.example.com:8051
#grpcOptions:
# ssl-target-name-override: peer0.org2.example.com
tlsCACerts:
path: ${FABRIC_SDK_GO_PROJECT_PATH}/${CRYPTOCONFIG_FIXTURES_PATH}/peerOrganizations/org2.example.com/tlsca/tlsca.org2.example.com-cert.pem
peer1.org2.example.com:
url: peer1.org2.example.com:9051
#grpcOptions:
# ssl-target-name-override: peer1.org2.example.com
tlsCACerts:
path: ${FABRIC_SDK_GO_PROJECT_PATH}/${CRYPTOCONFIG_FIXTURES_PATH}/peerOrganizations/org2.example.com/tlsca/tlsca.org2.example.com-cert.pem
7、CA部分
#
# 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.
# Fabric- ca是Hyperledger Fabric提供的一种特殊的证书颁发机构,它允许通过REST api进行证书管理。
# 应用程序可以选择使用标准的证书颁发机构而不是Fabric-CA,在这种情况下,将不指定此部分。
#
certificateAuthorities:
ca.org1.example.com:
# [Optional] Default: Infer from hostname
url: https://ca.org1.example.com:7054
# [Optional] The optional server name for target override
#grpcOptions:
# ssl-target-name-override: ca.org1.example.com
tlsCACerts:
# Comma-Separated list of paths
path: ${FABRIC_SDK_GO_PROJECT_PATH}/${CRYPTOCONFIG_FIXTURES_PATH}/peerOrganizations/org1.example.com/tlsca/tlsca.org1.example.com-cert.pem
# Client key and cert for SSL handshake with Fabric CA
client:
key:
path: ${FABRIC_SDK_GO_PROJECT_PATH}/${CRYPTOCONFIG_FIXTURES_PATH}/peerOrganizations/tls.example.com/users/User1@tls.example.com/tls/client.key
cert:
path: ${FABRIC_SDK_GO_PROJECT_PATH}/${CRYPTOCONFIG_FIXTURES_PATH}/peerOrganizations/tls.example.com/users/User1@tls.example.com/tls/client.crt
# Fabric-CA supports dynamic user enrollment via REST APIs. A "root" user, a.k.a registrar, is
# needed to enroll and invoke new users.
# Fabric-CA支持通过REST api动态注册用户。注册和调用新用户需要一个“根”用户,即注册商
registrar:
enrollId: admin
enrollSecret: adminpw
# [Optional] The optional name of the CA.
caName: ca.org1.example.com
tlsca.org1.example.com:
# [Optional] Default: Infer from hostname
url: https://ca.org1.example.com:7154
tlsCACerts:
# Comma-Separated list of paths
path: ${FABRIC_SDK_GO_PROJECT_PATH}/${CRYPTOCONFIG_FIXTURES_PATH}/peerOrganizations/org1.example.com/tlsca/tlsca.org1.example.com-cert.pem
# Client key and cert for SSL handshake with Fabric CA
client:
key:
path: ${FABRIC_SDK_GO_PROJECT_PATH}/${CRYPTOCONFIG_FIXTURES_PATH}/peerOrganizations/tls.example.com/users/User1@tls.example.com/tls/client.key
cert:
path: ${FABRIC_SDK_GO_PROJECT_PATH}/${CRYPTOCONFIG_FIXTURES_PATH}/peerOrganizations/tls.example.com/users/User1@tls.example.com/tls/client.crt
# Fabric-CA supports dynamic user enrollment via REST APIs. A "root" user, a.k.a registrar, is
# needed to enroll and invoke new users.
registrar:
enrollId: admin2
enrollSecret: adminpw2
# [Optional] The optional name of the CA.
caName: tlsca.org1.example.com
ca.org2.example.com:
url: https://ca.org2.example.com:8054
# [Optional] The optional server name for target override
#grpcOptions:
# ssl-target-name-override: ca.org2.example.com
tlsCACerts:
# Comma-Separated list of paths
path: ${FABRIC_SDK_GO_PROJECT_PATH}/${CRYPTOCONFIG_FIXTURES_PATH}/peerOrganizations/org2.example.com/tlsca/tlsca.org2.example.com-cert.pem
# Client key and cert for SSL handshake with Fabric CA
client:
key:
path: ${FABRIC_SDK_GO_PROJECT_PATH}/${CRYPTOCONFIG_FIXTURES_PATH}/peerOrganizations/tls.example.com/users/User1@tls.example.com/tls/client.key
cert:
path: ${FABRIC_SDK_GO_PROJECT_PATH}/${CRYPTOCONFIG_FIXTURES_PATH}/peerOrganizations/tls.example.com/users/User1@tls.example.com/tls/client.crt
# Fabric-CA supports dynamic user enrollment via REST APIs. A "root" user, a.k.a registrar, is
# needed to enroll and invoke new users.
# Fabric-CA支持通过REST api动态注册用户。注册和调用新用户需要一个“根”用户,即注册商。
registrar:
enrollId: admin
enrollSecret: adminpw
# [Optional] The optional name of the CA.
caName: ca.org2.example.com
8、EntityMatchers部分
# EntityMatchers enable substitution of network hostnames with static configurations
# so that properties can be mapped. Regex can be used for this purpose
# UrlSubstitutionExp can be empty which means the same network hostname will be used
# UrlSubstitutionExp can be given same as mapped peer url, so that mapped peer url can be used
# UrlSubstitutionExp can have golang regex matchers like ${1}.local.example.${2}:${3} for pattern
# like peer0.org1.example.com:1234 which converts peer0.org1.example.com to peer0.org1.local.example.com:1234
# sslTargetOverrideUrlSubstitutionExp follow in the same lines as
# SubstitutionExp for the fields gprcOptions.ssl-target-name-override respectively
# In any case mappedHost's config will be used, so mapped host cannot be empty, if entityMatchers are used
# EntityMatchers支持使用静态配置替换网络主机名,以便可以映射属性。
# 正则表达式可以用于此目的
# UrlSubstitutionExp可以为空,这意味着将使用相同的网络主机名
# UrlSubstitutionExp可以与映射的对等节点url相同,这样就可以使用映射的对等url
#entityMatchers:
entityMatchers:
peer:
- pattern: (\w+).org1.example.com:(\d+)
urlSubstitutionExp: ${1}.org1.example.com:${2}
sslTargetOverrideUrlSubstitutionExp: ${1}.org1.example.com
mappedHost: peer0.org1.example.com
- pattern: (\w+).org2.example.com:(\d+)
urlSubstitutionExp: ${1}.org2.example.com:${2}
sslTargetOverrideUrlSubstitutionExp: ${1}.org2.example.com
mappedHost: peer0.org2.example.com
- pattern: (\w+).org1.example.(\w+)
urlSubstitutionExp: peer0.org1.example.com:7051
sslTargetOverrideUrlSubstitutionExp: peer0.org1.example.com
mappedHost: peer0.org1.example.com
- pattern: (\w+).org2.example.(\w+)
urlSubstitutionExp: peer0.org2.example.com:8051
sslTargetOverrideUrlSubstitutionExp: peer0.org2.example.com
mappedHost: peer0.org2.example.com
- pattern: (\w+).example1.(\w+):(\d+)
urlSubstitutionExp: ${1}.org1.example.${2}:${3}
sslTargetOverrideUrlSubstitutionExp: ${1}.org1.example.${2}
mappedHost: peer0.org1.example.com
- pattern: (\w+).org1.example.(\w+):(\d+)
urlSubstitutionExp: peer0.org1.example.com:7051
sslTargetOverrideUrlSubstitutionExp: peer0.org1.example.com
mappedHost: peer0.org1.example.com
orderer:
- pattern: (\w+).example.(\w+)
urlSubstitutionExp: orderer.example.com:7050
sslTargetOverrideUrlSubstitutionExp: orderer.example.com
mappedHost: orderer.example.com
- pattern: (\w+).example2.(\w+)
urlSubstitutionExp: localhost:7050
sslTargetOverrideUrlSubstitutionExp: localhost
mappedHost: orderer.example.com
- pattern: (\w+).example3.(\w+)
urlSubstitutionExp:
sslTargetOverrideUrlSubstitutionExp:
mappedHost: orderer.example.com
- pattern: (\w+).example4.(\w+):(\d+)
urlSubstitutionExp: ${1}.example.${2}:${3}
sslTargetOverrideUrlSubstitutionExp: ${1}.example.${2}
mappedHost: orderer.example.com
certificateAuthority:
- pattern: (\w+).org1.example.(\w+)
urlSubstitutionExp:
sslTargetOverrideUrlSubstitutionExp: ca.example.com
mappedHost: ca.org1.example.com
- pattern: (\w+).org2.example.(\w+)
urlSubstitutionExp:
sslTargetOverrideUrlSubstitutionExp: ca.example.com
mappedHost: ca.org2.example.com
现在我们改写自己的配置文件
version: 1.0.0
client:
organization: org1
logging:
level: info
cryptoconfig:
path: /root/go/src/fabric-go-sdk/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: /root/go/src/fabric-go-sdk/fixtures/crypto-config/peerOrganizations/org1.example.com/users/User1@org1.example.com/tls/client.key
cert:
path: /root/go/src/fabric-go-sdk/fixtures/crypto-config/peerOrganizations/org1.example.com/users/User1@org1.example.com/tls/client.crt
channels:
# multi-org test channel
mychannel:
peers:
peer0.org1.example.com:
endorsingPeer: true
chaincodeQuery: true
ledgerQuery: true
eventSource: true
peer1.org1.example.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: peerOrganizations/org1.example.com/users/{username}@org1.example.com/msp
peers:
- peer0.org1.example.com
- peer1.org1.example.com
# [Optional]. Certificate Authorities issue certificates for identification purposes in a Fabric based
# network. Typically certificates provisioning is done in a separate process outside of the
# runtime network. Fabric-CA is a special certificate authority that provides a REST APIs for
# dynamic certificate management (enroll, revoke, re-enroll). The following section is only for
# Fabric-CA servers.
# the profile will contain public information about organizations other than the one it belongs to.
# These are necessary information to make transaction lifecycles work, including MSP IDs and
# peers with a public URL to send transaction proposals. The file will not contain private
# information reserved for members of the organization, such as admin key and certificate,
# fabric-ca registrar enroll ID and secret, etc.
# org1:
# mspid: org1MSP
# This org's MSP store (absolute path or relative to client.cryptoconfig)
# cryptoPath: peerOrganizations/org1.example.com/users/{username}@org1.example.com/msp
# peers:
# - peer0.org1.example.com
# 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: ordererOrganizations/example.com/users/{username}@example.com/msp
#
# 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: orderer.example.com: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: orderer.example.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: /root/go/src/fabric-go-sdk/fixtures/crypto-config/ordererOrganizations/example.com/tlsca/tlsca.example.com-cert.pem
#
# List of peers to send various requests to, including endorsement, query
# and event listener registration.
#
peers:
peer0.org1.example.com:
# this URL is used to send endorsement and query requests
# [Optional] Default: Infer from hostname
url: peer0.org1.example.com:7051
grpcOptions:
ssl-target-name-override: peer0.org1.example.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: /root/go/src/fabric-go-sdk/fixtures/crypto-config/peerOrganizations/org1.example.com/tlsca/tlsca.org1.example.com-cert.pem
peer1.org1.example.com:
url: peer1.org1.example.com:9051
#grpcOptions:
# ssl-target-name-override: peer0.org1.example.com
grpcOptions:
ssl-target-name-override: peer1.org1.example.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:
path: /root/go/src/fabric-go-sdk/fixtures/crypto-config/peerOrganizations/org1.example.com/tlsca/tlsca.org1.example.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: (\w+).org1.example.com:(\d+)
urlSubstitutionExp: ${1}.org1.example.com:${2}
sslTargetOverrideUrlSubstitutionExp: ${1}.org1.example.com
mappedHost: peer0.org1.example.com
sdk部分
定义所需结构体
在项目路径下新建名为sdkInit的文件夹,此文件夹用于存放实现sdk的代码。在sdkInit路径下新建sdkInfo.go并编辑如下:
package sdkInit
import (
"github.com/hyperledger/fabric-sdk-go/pkg/client/channel"
"github.com/hyperledger/fabric-sdk-go/pkg/client/event"
mspclient "github.com/hyperledger/fabric-sdk-go/pkg/client/msp"
"github.com/hyperledger/fabric-sdk-go/pkg/client/resmgmt"
contextAPI "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context"
)
type OrgInfo struct {
OrgAdminUser string // like "Admin"
OrgName string // like "Org1"
OrgMspId string // like "Org1MSP"
OrgUser string // like "User1"
orgMspClient *mspclient.Client
OrgAdminClientContext *contextAPI.ClientProvider
OrgResMgmt *resmgmt.Client
OrgPeerNum int
//Peers []*fab.Peer
OrgAnchorFile string // like ./channel-artifacts/Org2MSPanchors.tx
}
type SdkEnvInfo struct {
// 通道信息
ChannelID string // like "simplecc"
ChannelConfig string // like os.Getenv("GOPATH") + "/src/github.com/hyperledger/fabric-samples/test-network/channel-artifacts/testchannel.tx"
// 组织信息
Orgs []*OrgInfo
// 排序服务节点信息
OrdererAdminUser string // like "Admin"
OrdererOrgName string // like "OrdererOrg"
OrdererEndpoint string
OrdererClientContext *contextAPI.ClientProvider
// 链码信息
ChaincodeID string
ChaincodeGoPath string
ChaincodePath string
ChaincodeVersion string
ChClient *channel.Client
EvClient *event.Client
}
type Application struct {
SdkEnvInfo *SdkEnvInfo
}
介绍:
mspclient "github.com/hyperledger/fabric-sdk-go/pkg/client/msp"
msp包允许在Fabric网络上创建和更新用户。msp客户端支持以下操作:注册、重注册、登记和获取身份签名。基本工作流为:
- 准备客户端上下文
- 创建msp客户端
- 登记用户
- 注册用户
"github.com/hyperledger/fabric-sdk-go/pkg/client/resmgmt"
resmgmt包用于在Fabric网络中创建和更新资源。它允许管理员创建和/或更新通道,并允许对等节点加入通道。管理员还可以在对等节点上执行链码相关操作,如安装、实例化、升级链码等。基本工作流为:
- 准备客户端上下文
- 创建资源管理器客户端
- 创建新通道
- 节点加入通道
- 查询对等节点的通道,安装/实例化的链代码等。
contextAPI "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context"
- 提供所需上下文接口
初始化及生命周期
在sdkInit路径下新建sdkSetting.go并编辑如下:
package sdkInit
import (
"fmt"
mb "github.com/hyperledger/fabric-protos-go/msp"
pb "github.com/hyperledger/fabric-protos-go/peer"
"github.com/hyperledger/fabric-sdk-go/pkg/client/channel"
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/errors/status"
"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab"
"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/msp"
"github.com/hyperledger/fabric-sdk-go/pkg/core/config"
lcpackager "github.com/hyperledger/fabric-sdk-go/pkg/fab/ccpackager/lifecycle"
"github.com/hyperledger/fabric-sdk-go/pkg/fabsdk"
"github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/common/policydsl"
"strings"
)
func Setup(configFile string, info *SdkEnvInfo) (*fabsdk.FabricSDK, error) {
// Create SDK setup for the integration tests
var err error
sdk, err := fabsdk.New(config.FromFile(configFile))
if err != nil {
return nil, err
}
// 为组织获得Client句柄和Context信息
for _, org := range info.Orgs {
org.orgMspClient, err = mspclient.New(sdk.Context(), mspclient.WithOrg(org.OrgName))
if err != nil {
return nil, err
}
orgContext := sdk.Context(fabsdk.WithUser(org.OrgAdminUser), fabsdk.WithOrg(org.OrgName))
org.OrgAdminClientContext = &orgContext
// New returns a resource management client instance.
resMgmtClient, err := resmgmt.New(orgContext)
if err != nil {
return nil, fmt.Errorf("根据指定的资源管理客户端Context创建通道管理客户端失败: %v", err)
}
org.OrgResMgmt = resMgmtClient
}
// 为Orderer获得Context信息
ordererClientContext := sdk.Context(fabsdk.WithUser(info.OrdererAdminUser), fabsdk.WithOrg(info.OrdererOrgName))
info.OrdererClientContext = &ordererClientContext
return sdk, nil
}
func CreateAndJoinChannel(info *SdkEnvInfo) error {
fmt.Println(">> 开始创建通道......")
if len(info.Orgs) == 0 {
return fmt.Errorf("通道组织不能为空,请提供组织信息")
}
// 获得所有组织的签名信息
signIds := []msp.SigningIdentity{}
for _, org := range info.Orgs {
// Get signing identity that is used to sign create channel request
orgSignId, err := org.orgMspClient.GetSigningIdentity(org.OrgAdminUser)
if err != nil {
return fmt.Errorf("GetSigningIdentity error: %v", err)
}
signIds = append(signIds, orgSignId)
}
// 创建通道
if err := createChannel(signIds, info); err != nil {
return fmt.Errorf("Create channel error: %v", err)
}
fmt.Println(">> 创建通道成功")
fmt.Println(">> 加入通道......")
for _, org := range info.Orgs {
// 加入通道
// Org peers join channel
if err := org.OrgResMgmt.JoinChannel(info.ChannelID, resmgmt.WithRetry(retry.DefaultResMgmtOpts), resmgmt.WithOrdererEndpoint("orderer.example.com")); err != nil {
return fmt.Errorf("%s peers failed to JoinChannel: %v", org.OrgName, err)
}
}
fmt.Println(">> 加入通道成功")
return nil
}
func createChannel(signIDs []msp.SigningIdentity, info *SdkEnvInfo) error {
// Channel management client is responsible for managing channels (create/update channel)
chMgmtClient, err := resmgmt.New(*info.OrdererClientContext)
if err != nil {
return fmt.Errorf("Channel management client create error: %v", err)
}
// create a channel for orgchannel.tx
req := resmgmt.SaveChannelRequest{ChannelID: info.ChannelID,
ChannelConfigPath: info.ChannelConfig,
SigningIdentities: signIDs}
if _, err := chMgmtClient.SaveChannel(req, resmgmt.WithRetry(retry.DefaultResMgmtOpts), resmgmt.WithOrdererEndpoint("orderer.example.com")); err != nil {
return fmt.Errorf("error should be nil for SaveChannel of orgchannel: %v", err)
}
fmt.Println(">>>> 使用每个org的管理员身份更新锚节点配置...")
//do the same get ch client and create channel for each anchor peer as well (first for Org1MSP)
for i, org := range info.Orgs {
req = resmgmt.SaveChannelRequest{ChannelID: info.ChannelID,
ChannelConfigPath: org.OrgAnchorFile,
SigningIdentities: []msp.SigningIdentity{signIDs[i]}}
if _, err = org.OrgResMgmt.SaveChannel(req, resmgmt.WithRetry(retry.DefaultResMgmtOpts), resmgmt.WithOrdererEndpoint("orderer.example.com")); err != nil {
return fmt.Errorf("SaveChannel for anchor org %s error: %v", org.OrgName, err)
}
}
fmt.Println(">>>> 使用每个org的管理员身份更新锚节点配置完成")
//integration.WaitForOrdererConfigUpdate(t, configQueryClient, mc.channelID, false, lastConfigBlock)
return nil
}
func CreateCCLifecycle(info *SdkEnvInfo, sequence int64, upgrade bool, sdk *fabsdk.FabricSDK) error {
if len(info.Orgs) == 0 {
return fmt.Errorf("the number of organization should not be zero.")
}
// Package cc
fmt.Println(">> 开始打包链码......")
label, ccPkg, err := packageCC(info.ChaincodeID, info.ChaincodeVersion, info.ChaincodePath)
if err != nil {
return fmt.Errorf("pakcagecc error: %v", err)
}
packageID := lcpackager.ComputePackageID(label, ccPkg)
fmt.Println(">> 打包链码成功")
// Install cc
fmt.Println(">> 开始安装链码......")
if err := installCC(label, ccPkg, info.Orgs); err != nil {
return fmt.Errorf("installCC error: %v", err)
}
// Get installed cc package
if err := getInstalledCCPackage(packageID, info.Orgs[0]); err != nil {
return fmt.Errorf("getInstalledCCPackage error: %v", err)
}
// Query installed cc
if err := queryInstalled(packageID, info.Orgs[0]); err != nil {
return fmt.Errorf("queryInstalled error: %v", err)
}
fmt.Println(">> 安装链码成功")
// Approve cc
fmt.Println(">> 组织认可智能合约定义......")
if err := approveCC(packageID, info.ChaincodeID, info.ChaincodeVersion, sequence, info.ChannelID, info.Orgs, info.OrdererEndpoint); err != nil {
return fmt.Errorf("approveCC error: %v", err)
}
// Query approve cc
if err:=queryApprovedCC(info.ChaincodeID, sequence, info.ChannelID, info.Orgs);err!=nil{
return fmt.Errorf("queryApprovedCC error: %v", err)
}
fmt.Println(">> 组织认可智能合约定义完成")
// Check commit readiness
fmt.Println(">> 检查智能合约是否就绪......")
if err:=checkCCCommitReadiness(packageID, info.ChaincodeID, info.ChaincodeVersion, sequence, info.ChannelID, info.Orgs); err!=nil{
return fmt.Errorf("checkCCCommitReadiness error: %v", err)
}
fmt.Println(">> 智能合约已经就绪")
// Commit cc
fmt.Println(">> 提交智能合约定义......")
if err:=commitCC(info.ChaincodeID, info.ChaincodeVersion, sequence, info.ChannelID, info.Orgs, info.OrdererEndpoint);err!=nil{
return fmt.Errorf("commitCC error: %v", err)
}
// Query committed cc
if err:=queryCommittedCC(info.ChaincodeID, info.ChannelID, sequence, info.Orgs); err!=nil{
return fmt.Errorf("queryCommittedCC error: %v", err)
}
fmt.Println(">> 智能合约定义提交完成")
// Init cc
fmt.Println(">> 调用智能合约初始化方法......")
if err:=initCC(info.ChaincodeID, upgrade, info.ChannelID, info.Orgs[0], sdk); err!=nil{
return fmt.Errorf("initCC error: %v", err)
}
fmt.Println(">> 完成智能合约初始化")
return nil
}
func packageCC(ccName, ccVersion, ccpath string) (string, []byte, error) {
label := ccName + "_" + ccVersion
desc := &lcpackager.Descriptor{
Path: ccpath,
Type: pb.ChaincodeSpec_GOLANG,
Label: label,
}
ccPkg, err := lcpackager.NewCCPackage(desc)
if err != nil {
return "", nil, fmt.Errorf("Package chaincode source error: %v", err)
}
return desc.Label, ccPkg, nil
}
func installCC(label string, ccPkg []byte, orgs []*OrgInfo) error {
installCCReq := resmgmt.LifecycleInstallCCRequest{
Label: label,
Package: ccPkg,
}
packageID := lcpackager.ComputePackageID(installCCReq.Label, installCCReq.Package)
for _, org := range orgs {
orgPeers, err := DiscoverLocalPeers(*org.OrgAdminClientContext, org.OrgPeerNum)
if err != nil {
fmt.Errorf("DiscoverLocalPeers error: %v", err)
}
if flag, _ := checkInstalled(packageID, orgPeers[0], org.OrgResMgmt); flag == false {
if _, err := org.OrgResMgmt.LifecycleInstallCC(installCCReq, resmgmt.WithTargets(orgPeers...), resmgmt.WithRetry(retry.DefaultResMgmtOpts)); err != nil {
return fmt.Errorf("LifecycleInstallCC error: %v", err)
}
}
}
return nil
}
func getInstalledCCPackage(packageID string, org *OrgInfo) error {
// use org1
orgPeers, err := DiscoverLocalPeers(*org.OrgAdminClientContext, 1)
if err != nil {
return fmt.Errorf("DiscoverLocalPeers error: %v", err)
}
if _, err := org.OrgResMgmt.LifecycleGetInstalledCCPackage(packageID, resmgmt.WithTargets([]fab.Peer{orgPeers[0]}...)); err != nil {
return fmt.Errorf("LifecycleGetInstalledCCPackage error: %v", err)
}
return nil
}
func queryInstalled(packageID string, org *OrgInfo) error {
orgPeers, err := DiscoverLocalPeers(*org.OrgAdminClientContext, 1)
if err != nil {
return fmt.Errorf("DiscoverLocalPeers error: %v", err)
}
resp1, err := org.OrgResMgmt.LifecycleQueryInstalledCC(resmgmt.WithTargets([]fab.Peer{orgPeers[0]}...))
if err != nil {
return fmt.Errorf("LifecycleQueryInstalledCC error: %v", err)
}
packageID1 := ""
for _, t := range resp1 {
if t.PackageID == packageID {
packageID1 = t.PackageID
}
}
if !strings.EqualFold(packageID, packageID1) {
return fmt.Errorf("check package id error")
}
return nil
}
func checkInstalled(packageID string, peer fab.Peer, client *resmgmt.Client) (bool, error) {
flag := false
resp1, err := client.LifecycleQueryInstalledCC(resmgmt.WithTargets(peer))
if err != nil {
return flag, fmt.Errorf("LifecycleQueryInstalledCC error: %v", err)
}
for _, t := range resp1 {
if t.PackageID == packageID {
flag = true
}
}
return flag, nil
}
func approveCC(packageID string, ccName, ccVersion string, sequence int64, channelID string, orgs []*OrgInfo, ordererEndpoint string) error {
mspIDs := []string{}
for _, org := range orgs {
mspIDs = append(mspIDs, org.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,
}
for _, org := range orgs{
orgPeers, err := DiscoverLocalPeers(*org.OrgAdminClientContext, org.OrgPeerNum)
fmt.Printf(">>> chaincode approved by %s peers:\n", org.OrgName)
for _, p := range orgPeers {
fmt.Printf(" %s\n", p.URL())
}
if err!=nil{
return fmt.Errorf("DiscoverLocalPeers error: %v", err)
}
if _, err := org.OrgResMgmt.LifecycleApproveCC(channelID, approveCCReq, resmgmt.WithTargets(orgPeers...), resmgmt.WithOrdererEndpoint(ordererEndpoint), resmgmt.WithRetry(retry.DefaultResMgmtOpts));err != nil {
fmt.Errorf("LifecycleApproveCC error: %v", err)
}
}
return nil
}
func queryApprovedCC(ccName string, sequence int64, channelID string, orgs []*OrgInfo) error {
queryApprovedCCReq := resmgmt.LifecycleQueryApprovedCCRequest{
Name: ccName,
Sequence: sequence,
}
for _, org := range orgs{
orgPeers, err := DiscoverLocalPeers(*org.OrgAdminClientContext, org.OrgPeerNum)
if err!=nil{
return fmt.Errorf("DiscoverLocalPeers error: %v", err)
}
// Query approve cc
for _, p := range orgPeers {
resp, err := retry.NewInvoker(retry.New(retry.TestRetryOpts)).Invoke(
func() (interface{}, error) {
resp1, err := org.OrgResMgmt.LifecycleQueryApprovedCC(channelID, queryApprovedCCReq, resmgmt.WithTargets(p))
if err != nil {
return nil, status.New(status.TestStatus, status.GenericTransient.ToInt32(), fmt.Sprintf("LifecycleQueryApprovedCC returned error: %v", err), nil)
}
return resp1, err
},
)
if err != nil {
return fmt.Errorf("Org %s Peer %s NewInvoker error: %v", org.OrgName, p.URL(), err)
}
if resp==nil{
return fmt.Errorf("Org %s Peer %s Got nil invoker", org.OrgName, p.URL())
}
}
}
return nil
}
func checkCCCommitReadiness(packageID string, ccName, ccVersion string, sequence int64, channelID string, orgs []*OrgInfo) error {
mspIds := []string{}
for _, org := range orgs {
mspIds = append(mspIds, org.OrgMspId)
}
ccPolicy := policydsl.SignedByNOutOfGivenRole(int32(len(mspIds)), mb.MSPRole_MEMBER, mspIds)
req := resmgmt.LifecycleCheckCCCommitReadinessRequest{
Name: ccName,
Version: ccVersion,
//PackageID: packageID,
EndorsementPlugin: "escc",
ValidationPlugin: "vscc",
SignaturePolicy: ccPolicy,
Sequence: sequence,
InitRequired: true,
}
for _, org := range orgs{
orgPeers, err := DiscoverLocalPeers(*org.OrgAdminClientContext, org.OrgPeerNum)
if err!=nil{
fmt.Errorf("DiscoverLocalPeers error: %v", err)
}
for _, p := range orgPeers {
resp, err := retry.NewInvoker(retry.New(retry.TestRetryOpts)).Invoke(
func() (interface{}, error) {
resp1, err := org.OrgResMgmt.LifecycleCheckCCCommitReadiness(channelID, req, resmgmt.WithTargets(p))
fmt.Printf("LifecycleCheckCCCommitReadiness cc = %v, = %v\n", ccName, resp1)
if err != nil {
return nil, status.New(status.TestStatus, status.GenericTransient.ToInt32(), fmt.Sprintf("LifecycleCheckCCCommitReadiness returned error: %v", err), nil)
}
flag := true
for _, r := range resp1.Approvals {
flag = flag && r
}
if !flag {
return nil, status.New(status.TestStatus, status.GenericTransient.ToInt32(), fmt.Sprintf("LifecycleCheckCCCommitReadiness returned : %v", resp1), nil)
}
return resp1, err
},
)
if err != nil {
return fmt.Errorf("NewInvoker error: %v", err)
}
if resp==nil{
return fmt.Errorf("Got nill invoker response")
}
}
}
return nil
}
func commitCC(ccName, ccVersion string, sequence int64, channelID string, orgs []*OrgInfo, ordererEndpoint string) error{
mspIDs := []string{}
for _, org := range orgs {
mspIDs = append(mspIDs, org.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,
}
_, err := orgs[0].OrgResMgmt.LifecycleCommitCC(channelID, req, resmgmt.WithOrdererEndpoint(ordererEndpoint), resmgmt.WithRetry(retry.DefaultResMgmtOpts))
if err != nil {
return fmt.Errorf("LifecycleCommitCC error: %v", err)
}
return nil
}
func queryCommittedCC( ccName string, channelID string, sequence int64, orgs []*OrgInfo) error {
req := resmgmt.LifecycleQueryCommittedCCRequest{
Name: ccName,
}
for _, org := range orgs {
orgPeers, err := DiscoverLocalPeers(*org.OrgAdminClientContext, org.OrgPeerNum)
if err!=nil{
return fmt.Errorf("DiscoverLocalPeers error: %v", err)
}
for _, p := range orgPeers {
resp, err := retry.NewInvoker(retry.New(retry.TestRetryOpts)).Invoke(
func() (interface{}, error) {
resp1, err := org.OrgResMgmt.LifecycleQueryCommittedCC(channelID, req, resmgmt.WithTargets(p))
if err != nil {
return nil, status.New(status.TestStatus, status.GenericTransient.ToInt32(), fmt.Sprintf("LifecycleQueryCommittedCC returned error: %v", err), nil)
}
flag := false
for _, r := range resp1 {
if r.Name == ccName && r.Sequence == sequence {
flag = true
break
}
}
if !flag {
return nil, status.New(status.TestStatus, status.GenericTransient.ToInt32(), fmt.Sprintf("LifecycleQueryCommittedCC returned : %v", resp1), nil)
}
return resp1, err
},
)
if err != nil {
return fmt.Errorf("NewInvoker error: %v", err)
}
if resp==nil{
return fmt.Errorf("Got nil invoker response")
}
}
}
return nil
}
func initCC(ccName string, upgrade bool, channelID string, org *OrgInfo, sdk *fabsdk.FabricSDK) error {
//prepare channel client context using client context
clientChannelContext := sdk.ChannelContext(channelID, fabsdk.WithUser(org.OrgUser), fabsdk.WithOrg(org.OrgName))
// Channel client is used to query and execute transactions (Org1 is default org)
client, err := channel.New(clientChannelContext)
if err != nil {
return fmt.Errorf("Failed to create new channel client: %s", err)
}
// init
_, err = client.Execute(channel.Request{ChaincodeID: ccName, Fcn: "init", Args: nil, IsInit: true},
channel.WithRetry(retry.DefaultChannelOpts))
if err != nil {
return fmt.Errorf("Failed to init: %s", err)
}
return nil
}
介绍:
"github.com/hyperledger/fabric-sdk-go/pkg/core/config":获取所需配置文件
"github.com/hyperledger/fabric-sdk-go/pkg/fabsdk" :fabsdk包允许客户端使用Hyperledger Fabric网络。
添加DiscoverLocalPeers方法
DiscoverLocalPeers方法可以自动查找的所有节点
在sdkInit路径下新建一个名为intergration.go的文件。编辑如下:
package sdkInit
import (
"encoding/hex"
"fmt"
"github.com/hyperledger/fabric-sdk-go/pkg/client/channel"
"github.com/hyperledger/fabric-sdk-go/pkg/client/event"
"github.com/hyperledger/fabric-sdk-go/pkg/common/errors/retry"
"github.com/hyperledger/fabric-sdk-go/pkg/common/errors/status"
contextAPI "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context"
fabAPI "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab"
contextImpl "github.com/hyperledger/fabric-sdk-go/pkg/context"
"github.com/hyperledger/fabric-sdk-go/pkg/fabsdk"
"log"
)
func DiscoverLocalPeers(ctxProvider contextAPI.ClientProvider, expectedPeers int) ([]fabAPI.Peer, error) {
ctx, err := contextImpl.NewLocal(ctxProvider)
if err != nil {
return nil, fmt.Errorf("error creating local context: %v", err)
}
discoveredPeers, err := retry.NewInvoker(retry.New(retry.TestRetryOpts)).Invoke(
func() (interface{}, error) {
peers, serviceErr := ctx.LocalDiscoveryService().GetPeers()
if serviceErr != nil {
return nil, fmt.Errorf("getting peers for MSP [%s] error: %v", ctx.Identifier().MSPID, serviceErr)
}
if len(peers) < expectedPeers {
return nil, status.New(status.TestStatus, status.GenericTransient.ToInt32(), fmt.Sprintf("Expecting %d peers but got %d", expectedPeers, len(peers)), nil)
}
return peers, nil
},
)
if err != nil {
return nil, err
}
return discoveredPeers.([]fabAPI.Peer), nil
}
func (t *SdkEnvInfo) InitService(chaincodeID, channelID string, org *OrgInfo, sdk *fabsdk.FabricSDK) error {
handler := &SdkEnvInfo{
ChaincodeID: chaincodeID,
}
//prepare channel client context using client context
clientChannelContext := sdk.ChannelContext(channelID, fabsdk.WithUser(org.OrgUser), fabsdk.WithOrg(org.OrgName))
// Channel client is used to query and execute transactions (Org1 is default org)
var err error
t.ChClient, err = channel.New(clientChannelContext)
if err != nil {
return err
}
t.EvClient, err = event.New(clientChannelContext, event.WithBlockEvents())
if err != nil {
return err
}
handler.ChClient = t.ChClient
handler.EvClient = t.EvClient
return nil
}
func regitserEvent(client *event.Client, chaincodeID string) (fabAPI.Registration, <-chan *fabAPI.CCEvent) {
eventName := "chaincode-event"
reg, notifier, err := client.RegisterChaincodeEvent(chaincodeID, eventName)
if err != nil {
fmt.Println("注册链码事件失败: %s", err)
}
return reg, notifier
}
func ChainCodeEventListener(c *event.Client, ccID string) fabAPI.Registration {
reg, notifier := regitserEvent(c, ccID)
// consume event
go func() {
for e := range notifier {
log.Printf("Receive cc event, ccid: %v \neventName: %v\n"+
"payload: %v \ntxid: %v \nblock: %v \nsourceURL: %v\n",
e.ChaincodeID, e.EventName, string(e.Payload), e.TxID, e.BlockNumber, e.SourceURL)
}
}()
return reg
}
func TxListener(c *event.Client, txIDCh chan string) {
log.Println("Transaction listener start")
defer log.Println("Transaction listener exit")
for id := range txIDCh {
// Register monitor transaction event
log.Printf("Register transaction event for: %v", id)
txReg, txCh, err := c.RegisterTxStatusEvent(id)
if err != nil {
log.Printf("Register transaction event error: %v", err)
continue
}
defer c.Unregister(txReg)
// Receive transaction event
go func() {
for e := range txCh {
log.Printf("Receive transaction event: txid: %v, "+
"validation code: %v, block number: %v",
e.TxID,
e.TxValidationCode,
e.BlockNumber)
}
}()
}
}
func BlockListener(ec *event.Client) fabAPI.Registration {
// Register monitor block event
beReg, beCh, err := ec.RegisterBlockEvent()
if err != nil {
log.Printf("Register block event error: %v", err)
}
log.Println("Registered block event")
// Receive block event
go func() {
for e := range beCh {
log.Printf("Receive block event:\nSourceURL: %v\nNumber: %v\nHash"+
": %v\nPreviousHash: %v\n\n",
e.SourceURL,
e.Block.Header.Number,
hex.EncodeToString(e.Block.Header.DataHash),
hex.EncodeToString(e.Block.Header.PreviousHash))
}
}()
return beReg
}
链码chaincode
package main
import (
"encoding/json"
"fmt"
"github.com/hyperledger/fabric-chaincode-go/shim"
"github.com/hyperledger/fabric-protos-go/peer"
)
// SimpleAsset implements a simple chaincode to manage an asset
type SimpleAsset struct {
}
type outputEvent struct {
EventName string
}
// Init is called during chaincode instantiation to initialize any
// data. Note that chaincode upgrade also calls this function to reset
// or to migrate data.
func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {
fmt.Printf("init...")
return shim.Success(nil)
}
// Invoke is called per transaction on the chaincode. Each transaction is
// either a 'get' or a 'set' on the asset created by Init function. The Set
// method may create a new asset by specifying a new key-value pair.
func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
// Extract the function and args from the transaction proposal
fn, args := stub.GetFunctionAndParameters()
var result string
var err error
if fn == "set" {
result, err = set(stub, args)
} else { // assume 'get' even if fn is nil
result, err = get(stub, args)
}
if err != nil {
return shim.Error(err.Error())
}
// Return the result as success payload
return shim.Success([]byte(result))
}
// Set stores the asset (both key and value) on the ledger. If the key exists,
// it will override the value with the new one
func set(stub shim.ChaincodeStubInterface, args []string) (string, error) {
if len(args) != 2 {
return "", fmt.Errorf("Incorrect arguments. Expecting a key and a value")
}
err := stub.PutState(args[0], []byte(args[1]))
if err != nil {
return "", fmt.Errorf("Failed to set asset: %s", args[0])
}
event := outputEvent{
EventName: "set",
}
payload, err := json.Marshal(event)
if err != nil {
return "", err
}
err = stub.SetEvent("chaincode-event", payload)
return args[1], nil
}
// Get returns the value of the specified asset key
func get(stub shim.ChaincodeStubInterface, args []string) (string, error) {
if len(args) != 1 {
return "", fmt.Errorf("Incorrect arguments. Expecting a key")
}
value, err := stub.GetState(args[0])
if err != nil {
return "", fmt.Errorf("Failed to get asset: %s with error: %s", args[0], err)
}
if value == nil {
return "", fmt.Errorf("Asset not found: %s", args[0])
}
return string(value), nil
}
// main function starts up the chaincode in the container during instantiate
func main() {
if err := shim.Start(new(SimpleAsset)); err != nil {
fmt.Printf("Error starting SimpleAsset chaincode: %s", err)
}
}
调用链码
get
package sdkInit
import (
"fmt"
"github.com/hyperledger/fabric-sdk-go/pkg/client/channel"
)
func (t *Application) Get(args []string) (string, error) {
response, err := t.SdkEnvInfo.ChClient.Query(channel.Request{ChaincodeID: t.SdkEnvInfo.ChaincodeID, Fcn: args[0], Args: [][]byte{[]byte(args[1])}})
if err != nil {
return "", fmt.Errorf("failed to query: %v", err)
}
return string(response.Payload), nil
}
set
package sdkInit
import (
"github.com/hyperledger/fabric-sdk-go/pkg/client/channel"
)
func (t *Application) Set(args []string) (string, error) {
var tempArgs [][]byte
for i := 1; i < len(args); i++ {
tempArgs = append(tempArgs, []byte(args[i]))
}
request := channel.Request{ChaincodeID: t.SdkEnvInfo.ChaincodeID, Fcn: args[0], Args: [][]byte{[]byte(args[1]), []byte(args[2])}}
response, err := t.SdkEnvInfo.ChClient.Execute(request)
if err != nil {
// 资产转移失败
return "", err
}
//fmt.Println("============== response:",response)
return string(response.TransactionID), nil
}
启动项目
在命令行中进入chaincode路径,并使用以下命令为链码添加依赖包。
go mod init
go mod tidy
go mod vendor
在命令行中进入项目根目录,并使用以下命令为项目添加依赖包。
go mod init
go mod tidy
在命令行中进入fixture路径,并使用以下命令启动网络。
docker-compose up -d
在命令行中进入项目根目录,并使用以下命令build整个项目。
go build
接着使用以下命令运行项目。
./fabric-go-sdk
运行成功后输出:
root@VM-4-5-ubuntu:~/go/src/fabric-go-sdk# ./fabric-go-sdk
>> 开始创建通道......
>>>> 使用每个org的管理员身份更新锚节点配置...
>>>> 使用每个org的管理员身份更新锚节点配置完成
>> 创建通道成功
>> 加入通道......
>> 加入通道成功
>> 开始打包链码......
>> 打包链码成功
>> 开始安装链码......
>> 安装链码成功
>> 组织认可智能合约定义......
>>> chaincode approved by Org1 peers:
peer0.org1.example.com:7051
peer1.org1.example.com:9051
>> 组织认可智能合约定义完成
>> 检查智能合约是否就绪......
LifecycleCheckCCCommitReadiness cc = simplecc, = {map[Org1MSP:true]}
LifecycleCheckCCCommitReadiness cc = simplecc, = {map[Org1MSP:true]}
>> 智能合约已经就绪
>> 提交智能合约定义......
>> 智能合约定义提交完成
>> 调用智能合约初始化方法......
>> 完成智能合约初始化
>> 通过链码外部服务设置链码状态......
>> 设置链码状态完成
2023/10/07 10:54:36 Registered block event
<--- 添加信息 --->: e8e0ce6f1ae591742083476db07432c84177e4932265f3a49bfb26a4a80e5dc3
2023/10/07 10:54:39 Receive cc event, ccid: simplecc
eventName: chaincode-event
payload: {"EventName":"set"}
txid: e8e0ce6f1ae591742083476db07432c84177e4932265f3a49bfb26a4a80e5dc3
block: 5
sourceURL: peer1.org1.example.com:9051
2023/10/07 10:54:39 Receive block event:
SourceURL: peer1.org1.example.com:9051
Number: 5
Hash: 90f4322aa5139681e04324b875cff82654188426774715d18084e0587aa8dab4
PreviousHash: 4706146f1e2338d5634ad0d16d953b0946766b47b4e939dce62115732c73c7c0
2023/10/07 10:54:41 Receive cc event, ccid: simplecc
eventName: chaincode-event
payload: {"EventName":"set"}
txid: f01565ab29f84dccb2c7b542128bf70a058a7ad51a369235fa53ce8b44360ebf
block: 6
sourceURL: peer1.org1.example.com:9051
2023/10/07 10:54:41 Receive block event:
SourceURL: peer1.org1.example.com:9051
Number: 6
Hash: 8c1294f2c5f3fdab064cc05eeeb5ed08b59da758627b8e90c0823b7b3ad362b6
PreviousHash: 4a40e22e8324ef5d89e4b1be7bd5ef772f61a8ee4e2919526a0ceb86b5b86790
<--- 添加信息 --->: f01565ab29f84dccb2c7b542128bf70a058a7ad51a369235fa53ce8b44360ebf
2023/10/07 10:54:43 Receive cc event, ccid: simplecc
eventName: chaincode-event
payload: {"EventName":"set"}
txid: f07c09de8121501886bfcae68c444e5eb47bb600d0e7859b076311f783a96a9e
block: 7
sourceURL: peer1.org1.example.com:9051
2023/10/07 10:54:43 Receive block event:
SourceURL: peer1.org1.example.com:9051
Number: 7
Hash: 5add5b729cdac0305bf9b3f69f9fcdb385c207c7612e40f0fd1a972d3d3786ed
PreviousHash: 8d829e10423decbe439366cfe7cf9bfe438cc6c3177288c9bc3230e7228e4e08
<--- 添加信息 --->: f07c09de8121501886bfcae68c444e5eb47bb600d0e7859b076311f783a96a9e
<--- 查询信息 --->: 789
可能遇到的问题
报错:
# github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/discovery/client
/root/go/pkg/mod/github.com/hyperledger/fabric-sdk-go@v1.0.0/internal/github.com/hyperledger/fabric/discovery/client/api.go:47:38: undefined: discovery.ChaincodeCall
/root/go/pkg/mod/github.com/hyperledger/fabric-sdk-go@v1.0.0/internal/github.com/hyperledger/fabric/discovery/client/client.go:83:63: undefined: discovery.ChaincodeInterest
/root/go/pkg/mod/github.com/hyperledger/fabric-sdk-go@v1.0.0/internal/github.com/hyperledger/fabric/discovery/client/client.go:120:65: undefined: discovery.ChaincodeCall
/root/go/pkg/mod/github.com/hyperledger/fabric-sdk-go@v1.0.0/internal/github.com/hyperledger/fabric/discovery/client/client.go:124:23: undefined: discovery.ChaincodeInterest
/root/go/pkg/mod/github.com/hyperledger/fabric-sdk-go@v1.0.0/internal/github.com/hyperledger/fabric/discovery/client/client.go:229:105: undefined: discovery.ChaincodeCall
/root/go/pkg/mod/github.com/hyperledger/fabric-sdk-go@v1.0.0/internal/github.com/hyperledger/fabric/discovery/client/client.go:247:64: undefined: discovery.ChaincodeCall
/root/go/pkg/mod/github.com/hyperledger/fabric-sdk-go@v1.0.0/internal/github.com/hyperledger/fabric/discovery/client/client.go:604:48: undefined: discovery.ChaincodeInterest
/root/go/pkg/mod/github.com/hyperledger/fabric-sdk-go@v1.0.0/internal/github.com/hyperledger/fabric/discovery/client/client.go:620:35: undefined: discovery.ChaincodeCall
原因及解决方法: github.com/hyperledger/fabric-sdk-go 需要指定 20220117 版本,将 go.mod 文件对应依赖替换如下:
github.com/hyperledger/fabric-sdk-go v1.0.1-0.20220117114400-c848d119936b
报错:
>> 开始创建通道......
>> Create channel and join error: Create channel error: error should be nil for SaveChannel of orgchannel: create channel failed: create channel failed: SendEnvelope failed: calling orderer 'orderer.example.com:7050' failed: Orderer Server Status Code: (400) BAD_REQUEST. Description: error applying config update to existing channel 'mychannel': error authorizing update: error validating ReadSet: proposed update requires that key 【Group】 /Channel/Application be at version 0, but it is currently at version 1
解决:
sudo docker rm -f $(sudo docker ps -aq)
sudo docker network prune
sudo docker volume prune
cd fixtures
docker-compose down -v && docker-compose down --rmi all
报错:
Create channel and join error: Create channel error: error should be nil for SaveChannel of orgchannel: create channel failed: create channel failed: SendEnvelope failed: calling orderer 'orderer.example.com:7050' failed: Orderer Client Status Code: (2) CONNECTION_FAILED. Description: dialing connection on target 【orderer.example.com:7050】: connection is in TRANSIENT_FAILURE
解决:
vi /etc/hosts
# 在文件里面添加以下内容
127.0.0.1 orderer.example.com
127.0.0.1 peer0.org1.example.com
127.0.0.1 peer1.org1.example.com

浙公网安备 33010602011771号