# Copyright IBM Corp. All Rights Reserved. # # SPDX-License-Identifier: Apache-2.0 # # 注意这个文件里一点指定 MSP 的 ID 的地方都没有。 # --------------------------------------------------------------------------- # "OrdererOrgs" - Definition of organizations managing orderer nodes # --------------------------------------------------------------------------- OrdererOrgs: # --------------------------------------------------------------------------- # Orderer # --------------------------------------------------------------------------- # 这里的 Name 是 MemberShip 的 ID,大致就是一个法律实体作为会员。 -Name:Orderer Domain:ORDERER_DOMAIN # --------------------------------------------------------------------------- # "Specs" - See PeerOrgs below for complete description # --------------------------------------------------------------------------- Specs: # orderer docker 容器的名字 -Hostname:orderer # --------------------------------------------------------------------------- # "PeerOrgs" - Definition of organizations managing peer nodes # --------------------------------------------------------------------------- PeerOrgs: # --------------------------------------------------------------------------- # Org1 # --------------------------------------------------------------------------- -Name:Org1 # 组织1的完整 domain Domain:ORG1_DOMAIN # --------------------------------------------------------------------------- # "Specs" # --------------------------------------------------------------------------- # Uncomment this section to enable the explicit definition of hosts in your # configuration. Most users will want to use Template, below # # Specs is an array of Spec entries. Each Spec entry consists of two fields: # - Hostname: (Required) The desired hostname, sans the domain. # - CommonName: (Optional) Specifies the template or explicit override for # the CN. By default, this is the template: # # "{{.Hostname}}.{{.Domain}}" # 这个 CommonName 搞不好是拼出来的完整容器名,比如peer0.org1.com 之类的。 # # which obtains its values from the Spec.Hostname and # Org.Domain, respectively. # --------------------------------------------------------------------------- # Specs: # - Hostname: foo # implicitly "foo.ORG1_DOMAIN" # CommonName: foo27.org5.example.com # overrides Hostname-based FQDN set above # - Hostname: bar # - Hostname: baz # --------------------------------------------------------------------------- # "Template" # --------------------------------------------------------------------------- # Allows for the definition of 1 or more hosts that are created sequentially # from a template. By default, this looks like "peer%d" from 0 to Count-1. # You may override the number of nodes (Count), the starting index (Start) # or the template used to construct the name (Hostname). # # Note: Template and Specs are not mutually exclusive. You may define both # sections and the aggregate nodes will be created for you. Take care with # name collisions # --------------------------------------------------------------------------- # 这里的容器名就不是用 Hostname 指定出来的,而是用 spec 推导出来的了。具体还是看文档,这里就是规定这个组织里有多少个 peer,peer 用什么名字。现在这个名字就是 peer0,peer1 的形式。当然都可以改。 Template: Count:2 # Start: 5 # Hostname: {{.Prefix}}{{.Index}} # default # --------------------------------------------------------------------------- # "Users" # --------------------------------------------------------------------------- # Count: The number of user accounts _in addition_ to Admin # --------------------------------------------------------------------------- # 这个users 本身就是指的非 admin 的 user 要创建出多少套 identity 证书来。 Users: Count:1 # --------------------------------------------------------------------------- # Org2: See "Org1" for full specification # --------------------------------------------------------------------------- -Name:Org2 Domain:ORG2_DOMAIN Template: Count:2 Users: Count:1 -Name:Org3 Domain:ORG3_DOMAIN Template: Count:2 Users: Count:1
################################################################################ # # 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/ORDERER_DOMAIN/msp
-&Org1 # DefaultOrg defines the organization which is used in the sampleconfig # of the fabric.git development environment Name:Org1MSP
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_DOMAIN Port:7051
-&Org2 # DefaultOrg defines the organization which is used in the sampleconfig # of the fabric.git development environment Name:Org2MSP
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.ORG2_DOMAIN Port:7051
################################################################################ # # SECTION: Orderer # # - This section defines the values to encode into a config transaction or # genesis block for orderer related parameters # ################################################################################ # 这里就是 anchor 了。 Orderer:&OrdererDefaults
# Orderer Type: The orderer implementation to start # Available types are "solo" and "kafka" OrdererType:solo
Addresses: -orderer.ORDERER_DOMAIN:7050
# 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:99MB
# 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:512KB
Kafka: # Brokers: A list of Kafka brokers to which the orderer connects # NOTE: Use IP:port notation Brokers: -127.0.0.1:9092
# Organizations is the list of orgs which are defined as participants on # the orderer side of the network Organizations:
################################################################################ # # 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:
# Copyright IBM Corp. All Rights Reserved. # # SPDX-License-Identifier: Apache-2.0 # # 当前 compose 语法已经升级到了 version 3了。 version:'2'
# 官方文档的释义,大义是顶级的 volume 元素是为了在不同的 service 之间共享 volume 而准备的。 # You can mount a host path as part of a definition for a single service, and there is no need to define it in the top level volumes key. # But, if you want to reuse a volume across multiple services, then define a named volume in the top-level volumes key. Use named volumes with services, swarms, and stack files. volumes: # 这个语法相当于声明了若干个命名卷。不同的容器之间把相同的卷挂载到自己本地的路径里,就相当于打通了两个容器的数据共享。 # 注意,命名卷的开头并不是路径形式的,所以不要求当前的 host 有这个名字的绝对路径或者相对路径。 orderer.ORDERER_DOMAIN: peer0.org1.example.com: peer1.org1.example.com: peer0.org2.example.com: peer1.org2.example.com:
# # Copyright IBM Corp All Rights Reserved # # SPDX-License-Identifier: Apache-2.0 #
# This script will orchestrate a sample end-to-end execution of the Hyperledger # Fabric network. # # The end-to-end verification provisions a sample Fabric network consisting of # two organizations, each maintaining two peers, and a “solo” ordering service. # # This verification makes use of two fundamental tools, which are necessary to # create a functioning transactional network with digital signature validation # and access control: # # * cryptogen - generates the x509 certificates used to identify and # authenticate the various components in the network. # * configtxgen - generates the requisite configuration artifacts for orderer # bootstrap and channel creation. # # Each tool consumes a configuration yaml file, within which we specify the topology # of our network (cryptogen) and the location of our certificates for various # configuration operations (configtxgen). Once the tools have been successfully run, # we are able to launch our network. More detail on the tools and the structure of # the network will be provided later in this document. For now, let's get going...
# prepending $PWD/../bin to PATH to ensure we are picking up the correct binaries # this may be commented out to resolve installed version of tools if desired export PATH=${PWD}/../bin:${PWD}:$PATH # 这个环境变量很重要,这里的 export 保证这个变量是在 environment 里。可以被子进程(另一个 shell 脚本继承)。 # 并不是所有的父进程里的 variable 都会被子进程看到的。 export FABRIC_CFG_PATH=${PWD}
# Print the usage message functionprintHelp () { echo"Usage: " echo" byfn.sh -m up|down|restart|generate [-c <channel name>] [-t <timeout>] [-d <delay>] [-f <docker-compose-file>] [-s <dbtype>] [-i <imagetag>]" echo" byfn.sh -h|--help (print this message)" echo" -m <mode> - one of 'up', 'down', 'restart' or 'generate'" echo" - 'up' - bring up the network with docker-compose up" echo" - 'down' - clear the network with docker-compose down" echo" - 'restart' - restart the network" echo" - 'generate' - generate required certificates and genesis block" echo" -c <channel name> - channel name to use (defaults to \"mychannel\")" echo" -t <timeout> - CLI timeout duration in microseconds (defaults to 10000)" echo" -d <delay> - delay duration in seconds (defaults to 3)" echo" -f <docker-compose-file> - specify which docker-compose file use (defaults to docker-compose-cli.yaml)" echo" -s <dbtype> - the database backend to use: goleveldb (default) or couchdb" echo" -i <imagetag> - pass the image tag to launch the network using the tag: 1.0.1, 1.0.2, 1.0.3, 1.0.4 (defaults to latest)" echo echo"Typically, one would first generate the required certificates and " echo"genesis block, then bring up the network. e.g.:" echo echo" byfn.sh -m generate -c mychannel" echo" byfn.sh -m up -c mychannel -s couchdb" echo" byfn.sh -m up -c mychannel -s couchdb -i 1.0.6" echo" byfn.sh -m down -c mychannel" echo echo"Taking all defaults:" echo" byfn.sh -m generate" echo" byfn.sh -m up" echo" byfn.sh -m down" }
# Ask user for confirmation to proceed functionaskProceed () { read -p "Continue (y/n)? " ans case"$ans"in y|Y ) # 只有这个地方会正常走下去 echo"proceeding ..." # 这个双分号是 case 的休止符 ;; n|N ) echo"exiting..." exit 1 ;; * ) # 这里会递归 echo"invalid response" askProceed ;; esac }
# Obtain CONTAINER_IDS and remove them # TODO Might want to make this optional - could clear other containers functionclearContainers () { # -a 表示输出全部容器,-q 表示只输出容器 id,这样就可以迭代使用了 # 结果大概是这样 # 8a25b455f05e # d2d84545a902 # c67d337e5bd9 # e5cbd8457caf # 9ee6a1cb33c1 # a0df580d4633 # cb6b441b083f # 0c87d55710c8 # c24f3eb2e0e0 CONTAINER_IDS=$(docker ps -aq) if [ -z "$CONTAINER_IDS" -o "$CONTAINER_IDS" == " " ]; then echo"---- No containers available for deletion ----" else # 直接拿这个列表来 rm,而不用再 xargs 了。 docker rm -f $CONTAINER_IDS fi }
# Delete any images that were generated as a part of this setup # specifically the following images are often left behind: # TODO list generated image naming patterns functionremoveUnwantedImages() { # 用 awk 来截取多段输出的好例子。 # 注意在这里我们可以看到不要乱改 peer 的名字,不然不能在这里搞到所有的 image_id。 # $()的形式可以把命令求和为一个匿名变量。 DOCKER_IMAGE_IDS=$(docker images | grep "dev\|none\|test-vp\|peer[0-9]-" | awk '{print $3}') if [ -z "$DOCKER_IMAGE_IDS" -o "$DOCKER_IMAGE_IDS" == " " ]; then echo"---- No images available for deletion ----" else docker rmi -f $DOCKER_IMAGE_IDS fi }
# Generate the needed certificates, the genesis block and start the network. functionnetworkUp () { # generate artifacts if they don't exist # 确认一个目录是否存在的命令 if [ ! -d "crypto-config" ]; then # 第一步,生成证书。其实不只是生成证书,还生成证书相关的东西。 generateCerts replacePrivateKey generateChannelArtifacts fi if [ "${IF_COUCHDB}" == "couchdb" ]; then IMAGE_TAG=$IMAGETAG CHANNEL_NAME=$CHANNEL_NAME TIMEOUT=$CLI_TIMEOUT DELAY=$CLI_DELAY docker-compose -f $COMPOSE_FILE -f $COMPOSE_FILE_COUCH up -d 2>&1 else # 这一行让 Docker-Compose 来按照配置文件装配容器配置,生成由容器组成的项目网络 # cli 容器起来以后,会自己去创建频道、一个一个地把容器加入网络中。一个一个地初始化链码,一个一个地测试链码。 IMAGE_TAG=$IMAGETAG CHANNEL_NAME=$CHANNEL_NAME TIMEOUT=$CLI_TIMEOUT DELAY=$CLI_DELAY docker-compose -f $COMPOSE_FILE up -d 2>&1 fi if [ $? -ne 0 ]; then echo"ERROR !!!! Unable to start network" # 这个命令就是去读一个容器启动的 command 在 console 里的输出。 docker logs -f cli exit 1 fi docker logs -f cli }
# Tear down running network functionnetworkDown () { docker-compose -f $COMPOSE_FILE down --volumes docker-compose -f $COMPOSE_FILE -f $COMPOSE_FILE_COUCH down --volumes # Don't remove the generated artifacts -- note, the ledgers are always removed if [ "$MODE" != "restart" ]; then # Bring the containers down deleting their volumes #Cleanup the chaincode containers clearContainers #Cleanup images removeUnwantedImages # remove orderer block and other channel configuration transactions and certs rm -rf channel-artifacts/*.block channel-artifacts/*.tx crypto-config # remove the docker-compose yaml file that was customized to the example rm -f docker-compose-e2e.yaml fi }
# 但这个文件有什么用呢?docker-compose-e2e-template.yaml # Using docker-compose-e2e-template.yaml, replace constants with private key file names # generated by the cryptogen tool and output a docker-compose.yaml specific to this # configuration functionreplacePrivateKey () { # sed on MacOSX does not support -i flag with a null extension. We will use # 't' for our back-up's extension and depete it at the end of the function ARCH=`uname -s | grep Darwin` if [ "$ARCH" == "Darwin" ]; then OPTS="-it" else OPTS="-i" fi
# Copy the template to the file that will be modified to add the private key cp docker-compose-e2e-template.yaml docker-compose-e2e.yaml
# The next steps will replace the template's contents with the # actual values of the private key file names for the two CAs. # 把当前的父文件夹记住 CURRENT_DIR=$PWD cd crypto-config/peerOrganizations/org1.example.com/ca/ PRIV_KEY=$(ls *_sk) cd"$CURRENT_DIR" sed $OPTS"s/CA1_PRIVATE_KEY/${PRIV_KEY}/g" docker-compose-e2e.yaml cd crypto-config/peerOrganizations/org2.example.com/ca/ PRIV_KEY=$(ls *_sk) cd"$CURRENT_DIR" sed $OPTS"s/CA2_PRIVATE_KEY/${PRIV_KEY}/g" docker-compose-e2e.yaml # If MacOSX, remove the temporary backup of the docker-compose file if [ "$ARCH" == "Darwin" ]; then rm docker-compose-e2e.yamlt fi }
# X.509只是 PKI 密码学架构的一个实现。 # ca-cert 是根证书。 # 为什么 count 必须放在这个文件里面。 # 这个函数不止生成证书,还有 keystore。 # 私钥签署,公钥验证!而不是公钥签署,私钥验证! # We will use the cryptogen tool to generate the cryptographic material (x509 certs) # for our various network entities. The certificates are based on a standard PKI # implementation where validation is achieved by reaching a common trust anchor. # # Cryptogen consumes a file - ``crypto-config.yaml`` - that contains the network # topology and allows us to generate a library of certificates for both the # Organizations and the components that belong to those Organizations. Each # Organization is provisioned a unique root certificate (``ca-cert``), that binds # specific components (peers and orderers) to that Org. Transactions and communications # within Fabric are signed by an entity's private key (``keystore``), and then verified # by means of a public key (``signcerts``). You will notice a "count" variable within # this file. We use this to specify the number of peers per Organization; in our # case it's two peers per Org. The rest of this template is extremely # self-explanatory. # # After we run the tool, the certs will be parked in a folder titled ``crypto-config``.
# Generates Org certs using cryptogen tool functiongenerateCerts (){ which cryptogen if [ "$?" -ne 0 ]; then echo"cryptogen tool not found. exiting" exit 1 fi echo echo"##########################################################" echo"##### Generate certificates using cryptogen tool #########" echo"##########################################################" if [ -d "crypto-config" ]; then rm -Rf crypto-config fi cryptogen generate --config=./crypto-config.yaml if [ "$?" -ne 0 ]; then echo"Failed to generate certificates..." exit 1 fi echo }
# 每一个新的 org 都要有一个 anchor 节点。这也就意味着不仅配置文件里要有相关的配置,也意味着初始化步骤里 # 要有相关的配置命令。 # 把三件事做起来: # 1 生成创世区块。 2 生成频道配置 3 配置锚节点。 # # The `configtxgen tool is used to create four artifacts: orderer **bootstrap # block**, fabric **channel configuration transaction**, and two **anchor # peer transactions** - one for each Peer Org. # # The orderer block is the genesis block for the ordering service, and the # channel transaction file is broadcast to the orderer at channel creation # time. The anchor peer transactions, as the name might suggest, specify each # Org's anchor peer on this channel. # # Configtxgen consumes a file - ``configtx.yaml`` - that contains the definitions # for the sample network. There are three members - one Orderer Org (``OrdererOrg``) # and two Peer Orgs (``Org1`` & ``Org2``) each managing and maintaining two peer nodes. # This file also specifies a consortium - ``SampleConsortium`` - consisting of our # two Peer Orgs. Pay specific attention to the "Profiles" section at the top of # this file. You will notice that we have two unique headers. One for the orderer genesis # block - ``TwoOrgsOrdererGenesis`` - and one for our channel - ``TwoOrgsChannel``. # These headers are important, as we will pass them in as arguments when we create # our artifacts. This file also contains two additional specifications that are worth # noting. Firstly, we specify the anchor peers for each Peer Org # (``peer0.org1.example.com`` & ``peer0.org2.example.com``). Secondly, we point to # the location of the MSP directory for each member, in turn allowing us to store the # root certificates for each Org in the orderer genesis block. This is a critical # concept. Now any network entity communicating with the ordering service can have # its digital signature verified. # # This function will generate the crypto material and our four configuration # artifacts, and subsequently output these files into the ``channel-artifacts`` # folder. # # If you receive the following warning, it can be safely ignored: # # [bccsp] GetDefault -> WARN 001 Before using BCCSP, please call InitFactories(). Falling back to bootBCCSP. # # You can ignore the logs regarding intermediate certs, we are not using them in # this crypto implementation.
# Generate orderer genesis block, channel configuration transaction and # anchor peer update transactions functiongenerateChannelArtifacts() { # 检测一个命令是不是存在 which configtxgen if [ "$?" -ne 0 ]; then echo"configtxgen tool not found. exiting" exit 1 fi
echo"##########################################################" echo"######### Generating Orderer Genesis block ##############" echo"##########################################################" # Note: For some unknown reason (at least for now) the block file can't be # named orderer.genesis.block or the orderer will fail to launch! # 1 生成创世区块 configtxgen -profile TwoOrgsOrdererGenesis -outputBlock ./channel-artifacts/genesis.block if [ "$?" -ne 0 ]; then echo"Failed to generate orderer genesis block..." exit 1 fi echo echo"#################################################################" echo"### Generating channel configuration transaction 'channel.tx' ###" echo"#################################################################" # 2 生成频道配置,实际上就是 channel 事务。 configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID $CHANNEL_NAME if [ "$?" -ne 0 ]; then echo"Failed to generate channel configuration transaction..." exit 1 fi
echo echo"#################################################################" echo"####### Generating anchor peer update for Org1MSP ##########" echo"#################################################################" # configtxgen -help 可以看到它的各种 options。其中 outputAnchorPeersUpdate 只能在创建缺省频道(实际上自定义频道也可以),和最初建 anchor 时才可以使用。 # 这个 channel 名和 asOrg 参数共同决定 Org1MSPanchors.tx 的内容。 configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP if [ "$?" -ne 0 ]; then echo"Failed to generate anchor peer update for Org1MSP..." exit 1 fi
echo echo"#################################################################" echo"####### Generating anchor peer update for Org2MSP ##########" echo"#################################################################" configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate \ ./channel-artifacts/Org2MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org2MSP if [ "$?" -ne 0 ]; then echo"Failed to generate anchor peer update for Org2MSP..." exit 1 fi echo }
# 定义完函数以后,这里才是程序的起点 # Obtain the OS and Architecture string that will be used to select the correct # native binaries for your platform OS_ARCH=$(echo"$(uname -s|tr '[:upper:]' '[:lower:]'|sed 's/mingw64_nt.*/windows/')-$(uname -m | sed 's/x86_64/amd64/g')" | awk '{print tolower($0)}') # timeout duration - the duration the CLI should wait for a response from # another container before giving up CLI_TIMEOUT=10 #default for delay CLI_DELAY=3 # channel name defaults to "mychannel" CHANNEL_NAME="mychannel" # use this as the default docker-compose yaml definition COMPOSE_FILE=docker-compose-cli.yaml # COMPOSE_FILE_COUCH=docker-compose-couch.yaml # default image tag IMAGETAG="latest" # Parse commandline args whilegetopts"h?m:c:t:d:f:s:i:" opt; do case"$opt"in h|\?) printHelp exit 0 ;; m) MODE=$OPTARG ;; c) CHANNEL_NAME=$OPTARG ;; t) CLI_TIMEOUT=$OPTARG ;; d) CLI_DELAY=$OPTARG ;; f) COMPOSE_FILE=$OPTARG ;; s) IF_COUCHDB=$OPTARG ;; i) IMAGETAG=`uname -m`"-"$OPTARG ;; esac done
# Determine whether starting, stopping, restarting or generating for announce if [ "$MODE" == "up" ]; then EXPMODE="Starting" elif [ "$MODE" == "down" ]; then EXPMODE="Stopping" elif [ "$MODE" == "restart" ]; then EXPMODE="Restarting" elif [ "$MODE" == "generate" ]; then EXPMODE="Generating certs and genesis block for" else printHelp exit 1 fi
# Announce what was requested
if [ "${IF_COUCHDB}" == "couchdb" ]; then echo echo"${EXPMODE} with channel '${CHANNEL_NAME}' and CLI timeout of '${CLI_TIMEOUT}' using database '${IF_COUCHDB}'" else echo"${EXPMODE} with channel '${CHANNEL_NAME}' and CLI timeout of '${CLI_TIMEOUT}'" fi # ask for confirmation to proceed askProceed
#Create the network using docker compose if [ "${MODE}" == "up" ]; then networkUp elif [ "${MODE}" == "down" ]; then## Clear the network networkDown elif [ "${MODE}" == "generate" ]; then## Generate Artifacts generateCerts replacePrivateKey generateChannelArtifacts elif [ "${MODE}" == "restart" ]; then## Restart the network networkDown networkUp else printHelp exit 1 fi
# verify the result of the end-to-end test verifyResult () { if [ $1 -ne 0 ] ; then echo"!!!!!!!!!!!!!!! "$2" !!!!!!!!!!!!!!!!" echo"========= ERROR !!! FAILED to execute End-2-End Scenario ===========" echo exit 1 fi }
setGlobals () {
if [ $1 -eq 0 -o $1 -eq 1 ] ; then CORE_PEER_LOCALMSPID="Org1MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp # 传进来不同的命令,更改的核心环境变量指标是容器的 endpoint 地址。也就是CORE_PEER_ADDRESS。相同组织的 enpoint 使用的是相同的 MSP 密码学目录。 if [ $1 -eq 0 ]; then CORE_PEER_ADDRESS=peer0.org1.example.com:7051 else CORE_PEER_ADDRESS=peer1.org1.example.com:7051 # 此处有重复,疑为错误。但 github 上原版的代码就是这样写的。 CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp fi else CORE_PEER_LOCALMSPID="Org2MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp if [ $1 -eq 2 ]; then CORE_PEER_ADDRESS=peer0.org2.example.com:7051 else CORE_PEER_ADDRESS=peer1.org2.example.com:7051 fi fi
## Sometimes Join takes time hence RETRY atleast for 5 times joinWithRetry () { # peer channel join 实际上是消耗四个环境变量作为把 peer 加入 channel 的依据,所以外部传进来的环境变量到此几乎可说是无用的。 # CORE_PEER_MSPCONFIGPATH # CORE_PEER_ADDRESS # CORE_PEER_LOCALMSPID # CORE_PEER_TLS_ROOTCERT_FILE peer channel join -b $CHANNEL_NAME.block >&log.txt res=$? cat log.txt if [ $res -ne 0 -a $COUNTER -lt $MAX_RETRY ]; then COUNTER=` expr$COUNTER + 1` # 这的 peer 是顺序 peer,和实际的容器名是不一样的。 echo"PEER$1 failed to join the channel, Retry after 2 seconds" # bash sleep 的妙用 sleep$DELAY # 递归重试 joinWithRetry $1 else COUNTER=1 fi verifyResult $res"After $MAX_RETRY attempts, PEER$ch has failed to Join the Channel" }
joinChannel () { for ch in 0 1 2 3; do setGlobals $ch joinWithRetry $ch echo"===================== PEER$ch joined on the channel \"$CHANNEL_NAME\" ===================== " sleep$DELAY echo done }
installChaincode () { PEER=$1 setGlobals $PEER # 这里这个 p 就是在cli 容器内可以看到的 chaincode 的 go 文件路径了。n 则是链码的合约名字。 # 这个链码为什么不需要经过编译,真是奇也怪哉。 peer chaincode install -n mycc -v 1.0 -p github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02 >&log.txt res=$? cat log.txt verifyResult $res"Chaincode installation on remote peer PEER$PEER has Failed" echo"===================== Chaincode is installed on remote peer PEER$PEER ===================== " echo }
instantiateChaincode () { PEER=$1 setGlobals $PEER # 用硬编码的方式好过用接口的方式来读写 orderer endpoint 的位置。 # while 'peer chaincode' command can get the orderer endpoint from the peer (if join was successful), # lets supply it directly as we know it using the "-o" option if [ -z "$CORE_PEER_TLS_ENABLED" -o "$CORE_PEER_TLS_ENABLED" = "false" ]; then # 在链码初始化的时候,制定背书策略。 # 供反射调用 peer chaincode instantiate -o orderer.ORDERER_DOMAIN:7050 -C $CHANNEL_NAME -n mycc -v 1.0 -c '{"Args":["init","a","100","b","200"]}' -P "OR ('Org1MSP.member','Org2MSP.member')" >&log.txt else peer chaincode instantiate -o orderer.ORDERER_DOMAIN:7050 --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA -C $CHANNEL_NAME -n mycc -v 1.0 -c '{"Args":["init","a","100","b","200"]}' -P "OR ('Org1MSP.member','Org2MSP.member')" >&log.txt fi res=$? cat log.txt verifyResult $res"Chaincode instantiation on PEER$PEER on channel '$CHANNEL_NAME' failed" echo"===================== Chaincode Instantiation on PEER$PEER on channel '$CHANNEL_NAME' is successful ===================== " echo }
chaincodeQuery () { PEER=$1 echo"===================== Querying on PEER$PEER on channel '$CHANNEL_NAME'... ===================== " setGlobals $PEER local rc=1 local starttime=$(date +%s)
# continue to poll # we either get a successful response, or reach TIMEOUT whiletest"$(($(date +%s)-starttime))" -lt "$TIMEOUT" -a $rc -ne 0 do sleep$DELAY echo"Attempting to Query PEER$PEER ...$(($(date +%s)-starttime)) secs" peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}' >&log.txt test $? -eq 0 && VALUE=$(cat log.txt | awk '/Query Result/ {print $NF}') test"$VALUE" = "$2" && let rc=0 done echo cat log.txt iftest$rc -eq 0 ; then echo"===================== Query on PEER$PEER on channel '$CHANNEL_NAME' is successful ===================== " else echo"!!!!!!!!!!!!!!! Query result on PEER$PEER is INVALID !!!!!!!!!!!!!!!!" echo"================== ERROR !!! FAILED to execute End-2-End Scenario ==================" echo exit 1 fi }
chaincodeInvoke () { PEER=$1 setGlobals $PEER # while 'peer chaincode' command can get the orderer endpoint from the peer (if join was successful), # lets supply it directly as we know it using the "-o" option if [ -z "$CORE_PEER_TLS_ENABLED" -o "$CORE_PEER_TLS_ENABLED" = "false" ]; then # -c 是调用的消息的构造函数的参数。后台基本上是用反射什么的来执行调用的。 peer chaincode invoke -o orderer.ORDERER_DOMAIN:7050 -C $CHANNEL_NAME -n mycc -c '{"Args":["invoke","a","b","10"]}' >&log.txt else peer chaincode invoke -o orderer.ORDERER_DOMAIN:7050 --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA -C $CHANNEL_NAME -n mycc -c '{"Args":["invoke","a","b","10"]}' >&log.txt fi res=$? cat log.txt verifyResult $res"Invoke execution on PEER$PEER failed " echo"===================== Invoke transaction on PEER$PEER on channel '$CHANNEL_NAME' is successful ===================== " echo }
## Join all the peers to the channel echo"Having all peers join the channel..." joinChannel
## Set the anchor peers for each org in the channel echo"Updating anchor peers for org1..." # 这两个函数调用,要仔细更改当前的核心环境变量为两个组织预先生成好的 anchor 节点的 endpoint。 updateAnchorPeers 0 echo"Updating anchor peers for org2..." updateAnchorPeers 2
# 只在 peer0 和 peer2上安装合约 ## Install chaincode on Peer0/Org1 and Peer2/Org2 echo"Installing chaincode on org1/peer0..." installChaincode 0 echo"Install chaincode on org2/peer2..." installChaincode 2
# 只在 peer2上初始化合约 #Instantiate chaincode on Peer2/Org2 echo"Instantiating chaincode on org2/peer2..." instantiateChaincode 2
# 只在 peer0 上查询合约 #Query on chaincode on Peer0/Org1 echo"Querying chaincode on org1/peer0..." chaincodeQuery 0 100
# 只在 peer0 上调用合约 #Invoke on chaincode on Peer0/Org1 echo"Sending invoke transaction on org1/peer0..." chaincodeInvoke 0
# 在 peer3 上追加安装合约 ## Install chaincode on Peer3/Org2 echo"Installing chaincode on org2/peer3..." installChaincode 3
# 在 peer3 上追加查询合约。可见追加进来的合约调用结果可以被明确查询到。 #Query on chaincode on Peer3/Org2, check if the result is 90 echo"Querying chaincode on org2/peer3..." chaincodeQuery 3 90
echo echo"========= All GOOD, BYFN execution completed =========== " echo