1 first-network示例概述
Build Your First network提供了一个 fabric 的示例网络。该示例网络中由两个组织构成,每个组织维护两个 peer 节点,演示了基于链码查询2个账户余额与转账操作。
- 1个Orderer节点:默认使用 solo 共识
- 4个Peer节点: 顺序编号Peer0-Peer3,并加上组织名称
- 1个CLI命令行客户端节点:使用cryptogen和configtxgen工具生成配置文件、证书文件、创世区块等
first-network 中有一个启动脚本 byfn.sh,利用构建的 Docker 镜像快速启动网络。该脚本会启动一个 orderer 节点和四个归属两个不同组织的 peer 节点,还将启动一个cli运行脚本,它将 peer 节点加入通道(Channel)、部署和实例化链码,并根据已部署的链码驱动交易执行,以下是byfn.sh的帮助文档。
function printHelp() {
echo "Usage: "
echo " byfn.sh [-c ] [-t ] [-d ] [-f ] [-s ] [-l ] [-o ] [-i ] [-a] [-n] [-v]"
echo " - one of 'up', 'down', 'restart', 'generate' or 'upgrade'"
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 " - 'upgrade' - upgrade the network from version 1.3.x to 1.4.0"
echo " -c - channel name to use (defaults to \"mychannel\")"
echo " -t - CLI timeout duration in seconds (defaults to 10)"
echo " -d - delay duration in seconds (defaults to 3)"
echo " -f - specify which docker-compose file use (defaults to docker-compose-cli.yaml)"
echo " -s - the database backend to use: goleveldb (default) or couchdb"
echo " -l - the chaincode language: golang (default) or node"
echo " -o - the consensus-type of the ordering service: solo (default), kafka, or etcdraft"
echo " -i - the tag to be used to launch the network (defaults to \"latest\")"
echo " -a - launch certificate authorities (no certificate authorities are launched by default)"
echo " -n - do not deploy chaincode (abstore chaincode is deployed by default)"
echo " -v - verbose mode"
echo " byfn.sh -h (print this message)"
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 generate -c mychannel"
echo " byfn.sh up -c mychannel -s couchdb"
echo " byfn.sh up -c mychannel -s couchdb -i 1.4.0"
echo " byfn.sh up -l node"
echo " byfn.sh down -c mychannel"
echo " byfn.sh upgrade -c mychannel"
echo
echo "Taking all defaults:"
echo " byfn.sh generate"
echo " byfn.sh up"
echo " byfn.sh down"
}
执行./byfn.sh up -o etcdraft启动脚本,指定使用raft共识算法
- -c:设置通道名称,默认为 mychannel
- -t:设置 CLI 超时参数
- -l:设置智能合约的语言,默认是 go
- -o:设置排序服务方式,默认是 solo
- -s:设置使用的数据库,默认是goleveldb
- -f:指定docker-compose文件,默认是docker-compose-cli.yaml
2 生成系统初始化启动的相关配置文件
通过cryptogen工具生成组织成员关系和身份证书、密钥等文件。调用configtxgen工具生成节点与通道配置文件,包括Orderer节点上系统通道的创世区块文件genesis.block,新建应用通道的配置交易文件channel.tx、组织锚节点配置更新交易文件Org1MSPanchors.tx与Org2MSPanchors.tx等。
用户执行network_setup.sh脚本启动Fabric网络,该脚本调用networkUp函数,该命令会检查网络实体的证书是否生成,如果没有则首先生成相关证书目录。
# Generate the needed certificates, the genesis block and start the network.
function networkUp() {
checkPrereqs
# generate artifacts if they don't exist
if [ ! -d "crypto-config" ]; then
generateCerts
replacePrivateKey
generateChannelArtifacts
fi
COMPOSE_FILES="-f ${COMPOSE_FILE}"
if [ "${CERTIFICATE_AUTHORITIES}" == "true" ]; then
COMPOSE_FILES="${COMPOSE_FILES} -f ${COMPOSE_FILE_CA}"
export BYFN_CA1_PRIVATE_KEY=$(cd crypto-config/peerOrganizations/org1.example.com/ca && ls *_sk)
export BYFN_CA2_PRIVATE_KEY=$(cd crypto-config/peerOrganizations/org2.example.com/ca && ls *_sk)
fi
if [ "${CONSENSUS_TYPE}" == "kafka" ]; then
COMPOSE_FILES="${COMPOSE_FILES} -f ${COMPOSE_FILE_KAFKA}"
elif [ "${CONSENSUS_TYPE}" == "etcdraft" ]; then
COMPOSE_FILES="${COMPOSE_FILES} -f ${COMPOSE_FILE_RAFT2}"
fi
if [ "${IF_COUCHDB}" == "couchdb" ]; then
COMPOSE_FILES="${COMPOSE_FILES} -f ${COMPOSE_FILE_COUCH}"
fi
IMAGE_TAG=$IMAGETAG docker-compose ${COMPOSE_FILES} up -d 2>&1
docker ps -a
if [ $? -ne 0 ]; then
echo "ERROR !!!! Unable to start network"
exit 1
fi
if [ "$CONSENSUS_TYPE" == "kafka" ]; then
sleep 1
echo "Sleeping 10s to allow $CONSENSUS_TYPE cluster to complete booting"
sleep 9
fi
if [ "$CONSENSUS_TYPE" == "etcdraft" ]; then
sleep 1
echo "Sleeping 15s to allow $CONSENSUS_TYPE cluster to complete booting"
sleep 14
fi
# now run the end to end script
docker exec cli scripts/script.sh $CHANNEL_NAME $CLI_DELAY $LANGUAGE $CLI_TIMEOUT $VERBOSE $NO_CHAINCODE
if [ $? -ne 0 ]; then
echo "ERROR !!!! Test failed"
exit 1
fi
}
2.1 组织成员关系与身份认证等文件
generateCerts主要执行了cryptogen generate --config=./crypto-config.yaml
根据crypto-config.yaml生成网络成员组织结构和对应的身份证书、签名私钥等文件,并保存到默认的crypto-config目录,身份证书等文件在对应的目录msp/中,TLS证书与密钥文件保存到tls/中。
function generateCerts() {
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
set -x
cryptogen generate --config=./crypto-config.yaml
res=$?
set +x
if [ $res -ne 0 ]; then
echo "Failed to generate certificates..."
exit 1
fi
echo
echo "Generate CCP files for Org1 and Org2"
./ccp-generate.sh
}
crypto-config.yaml主要包含的fabric排序节点证书配置以及fabric组织证书配置。
Template
模板定义节点的配置模式
Specs
另外一种配置模式
Count
节点总数
Hostname
全限定域名 命名格式
Domain
域名
Users
添加到管理员的用户帐户数
采用Specs模式配置了5个排序节点
OrdererOrgs:
- Name: Orderer
Domain: example.com
EnableNodeOUs: true
Specs:
- Hostname: orderer
- Hostname: orderer2
- Hostname: orderer3
- Hostname: orderer4
- Hostname: orderer5
采用Template模式配置了两个组织,每个组织2套公私钥和证书,包含普通User数量为1
PeerOrgs:
- Name: Org1
Domain: org1.example.com
EnableNodeOUs: true
Template:
Count: 2
Users:
Count: 1
- Name: Org2
Domain: org2.example.com
EnableNodeOUs: true
Template:
Count: 2
Users:
Count: 1
ordererOrganizations目录:包含Orderer组织类型的身份证书.pem文件、签名私钥文件_sk文件、TLS证书(证书.crt文件和密钥.key文件)
peerOrganizations目录:包含Peer组织类型的身份证书.pem文件、签名私钥文件_sk文件、TLS证书(证书.crt文件和密钥.key文件)
这些文件通过docker-compose工具,基于docker-compose-cil.yaml、docker-compose-base.yaml等配置文件,将目录作为挂载卷到容器的指定目录。
调用replacePrivateKey基于docker-compose-e2e-template.yaml文件创建新的配置文件docker-compose-e2e.yaml。进入Org1组织的CA目录,获取私钥文件名作为PRIV_KEY,替换docker-compose-e2e.yaml文件的CA1_PRIVATE_KEY。同理替换CA2_PRIVATE_KEY。
function replacePrivateKey() {
# sed on MacOSX does not support -i flag with a null extension. We will use
# 't' for our back-up's extension and delete 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
}
2.2 节点与通道配置文件
执行generateChannelArtifacts函数,使用configtxgen工具基于configtx.yaml创建节点与通道配置文件,包括Orderer通道的创世区块,应用通道配置配置交易文件channel.tx,锚节点配置更新交易文件Org1MSPanchors.tx和Org2MSPanchors.tx
Orderer系统通道的创世区块
执行configtxgen命令,创建Orderer创世区块文件genesis.block
configtxgen -profile TwoOrgsOrdererGenesis -channelID $SYS_CHANNEL -outputBlock ./channel-artifacts/genesis.block
新建应用通道的配置交易文件
执行configtxgen命令,创建应用通道的配置交易文件channel.tx,后续执行peer channel create读取文件。
configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID $CHANNEL_NAME
锚节点配置更新交易文件
执行configtxgen命令,创建锚节点配置更新交易文件Org1MSPanchors.tx和Org2MSPanchors.tx,后续执行peer channel update进行更新。
configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP
configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org2MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org2MSP
返回到network_setup.sh脚本,判断是否启用了CouchDB标志位(默认关闭),使用docker-Compose工具执行docker-Compose-cil.yaml文件,启动Fabric网络。
3 启动Orderer服务节点
继续分析network_setup.sh脚本,默认COMPOSE_FILES是docker-compose-cli.yaml文件,当设置了kafka共识或者raft共识,则使用对应的yaml文件。
docker-compose -f docker-compose-cli.yaml up -d 2>&1
Orderer节点继承了docker-compose-base.yaml中的orderer.example.com配置属性,Orderer节点容器启动时执行如下命令
orderer # 默认启动orderer start子命令
配置文件 | 说明 |
---|---|
msp/* | 组织成员身份证书、签名私钥 |
tls/* | TLS认证证书、密钥文件 |
orderer.gennesis.block | 系统通道创世区块 |
docker-compose-cil.yaml文件
orderer.example.com:
extends:
file: base/docker-compose-base.yaml
service: orderer.example.com
container_name: orderer.example.com
networks:
- byfn
继续追踪base/docker-compose-base.yaml,找到orderer.example.com服务,挂载目录和暴露7050端口。
orderer.example.com:
container_name: orderer.example.com
extends:
file: peer-base.yaml
service: orderer-base
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
orderer的配置还是引入peer-base.yaml中的orderer-base服务
orderer-base:
image: hyperledger/fabric-orderer:$IMAGE_TAG
environment:
# 日志级别INFO
- FABRIC_LOGGING_SPEC=INFO
# 监听地址0.0.0.0
- ORDERER_GENERAL_LISTENADDRESS=0.0.0.0
# 加载方式为文件
- ORDERER_GENERAL_GENESISMETHOD=file
# 指定创始区块的目录
- ORDERER_GENERAL_GENESISFILE=/var/hyperledger/orderer/orderer.genesis.block
# 排序节点MSPID
- ORDERER_GENERAL_LOCALMSPID=OrdererMSP
# 排序节点msp目录
- 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
4 启动Peer节点
4个Peer节点继承了docker-compose-base.yaml中对应容器名称的配置属性,Peer节点容器启动时执行如下命令
peer node start
配置文件 | 说明 |
---|---|
core.yaml | 通用配置、账本配置 |
msp/* | 组织成员身份证书、签名私钥 |
tls/* | TLS认证证书、密钥文件 |
channel.tx | 应用通道配置交易文件 |
Org1MSPanchors.tx | Org1MSP组织锚节点配置更新交易文件 |
Org2MSPanchors.tx | Org2MSP组织锚节点配置更新交易文件 |
docker-compose-cil.yaml文件中Peer节点配置。
peer0.org1.example.com:
container_name: peer0.org1.example.com
extends:
file: base/docker-compose-base.yaml
service: peer0.org1.example.com
networks:
- byfn
peer1.org1.example.com:
container_name: peer1.org1.example.com
extends:
file: base/docker-compose-base.yaml
service: peer1.org1.example.com
networks:
- byfn
peer0.org2.example.com:
container_name: peer0.org2.example.com
extends:
file: base/docker-compose-base.yaml
service: peer0.org2.example.com
networks:
- byfn
peer1.org2.example.com:
container_name: peer1.org2.example.com
extends:
file: base/docker-compose-base.yaml
service: peer1.org2.example.com
networks:
- byfn
docker-compose-base.yaml文件中Peer节点的配置。
peer0.org1.example.com:
container_name: peer0.org1.example.com
extends:
file: peer-base.yaml
service: peer-base
environment:
- 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:8051
- CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org1.example.com:7051
- CORE_PEER_LOCALMSPID=Org1MSP
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
ports:
- 7051:7051
引入peer-base.yaml中的peer-base服务
peer-base:
image: hyperledger/fabric-peer:$IMAGE_TAG
environment:
- 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=${COMPOSE_PROJECT_NAME}_byfn
- FABRIC_LOGGING_SPEC=INFO
#- FABRIC_LOGGING_SPEC=DEBUG
- CORE_PEER_TLS_ENABLED=true
- CORE_PEER_GOSSIP_USELEADERELECTION=true
- CORE_PEER_GOSSIP_ORGLEADER=false
- 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
working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
command: peer node start
5 CLI客户端执行script脚本
docker-compose-cil.yaml文件中CLI客户端配置。启动完 orderer 节点、peer 节点和 CLI 容器之后,实际是调用 script.sh 脚本,该脚本是在 CLI 容器中执行,CLI 容器其实就是用户客户端,只不过是命令行客户端,运行在容器中。默认情况下CLI 的身份是 admin.org1,连接 peer0.org1 节点,执行 script.sh 脚本
cli:
container_name: cli
image: hyperledger/fabric-tools:$IMAGE_TAG
tty: true
stdin_open: true
environment:
- SYS_CHANNEL=$SYS_CHANNEL
- GOPATH=/opt/gopath
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
#- FABRIC_LOGGING_SPEC=DEBUG
- FABRIC_LOGGING_SPEC=INFO
- CORE_PEER_ID=cli
- CORE_PEER_ADDRESS=peer0.org1.example.com:7051
- CORE_PEER_LOCALMSPID=Org1MSP
- CORE_PEER_TLS_ENABLED=true
- CORE_PEER_TLS_CERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.crt
- CORE_PEER_TLS_KEY_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.key
- 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/[email protected]/msp
working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
command: /bin/bash
volumes:
- /var/run/:/host/var/run/
- ./../chaincode/:/opt/gopath/src/github.com/chaincode
- ./crypto-config:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/
- ./scripts:/opt/gopath/src/github.com/hyperledger/fabric/peer/scripts/
- ./channel-artifacts:/opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts
depends_on:
- orderer.example.com
- peer0.org1.example.com
- peer1.org1.example.com
- peer0.org2.example.com
- peer1.org2.example.com
networks:
- byfn
script.sh脚本顺序执行默认的测试流程,包括创建新的应用通道、添加节点、更新锚节点、安装链码、实例化链码、调用链码、查询链码等操作。
## Check for orderering service availablility
echo "Check orderering service availability..."
checkOSNAvailability //测试Orderer节点是否可用
## Create channel
echo "Creating channel..."
createChannel //创建应用通道
## Join all the peers to the channel
echo "Having all peers join the channel..."
joinChannel //添加Peer节点到指定的应用通道
//更新通道中每个组织的锚节点配置
## Set the anchor peers for each org in the channel
echo "Updating anchor peers for org1..."
updateAnchorPeers 0
echo "Updating anchor peers for org2..."
updateAnchorPeers 2
//Peer0/Org1与Peer2/Org2中安装链码
## Install chaincode on Peer0/Org1 and Peer2/Org2
echo "Installing chaincode on org1/peer0..."
installChaincode 0
echo "Install chaincode on org2/peer0..."
installChaincode 2
//Peer2/Org2中实例化链码
#Instantiate chaincode on Peer2/Org2
echo "Instantiating chaincode on org2/peer2..."
instantiateChaincode 2
//Peer0/Org1上查询链码,检查是否100元
#Query on chaincode on Peer0/Org1
echo "Querying chaincode on org1/peer0..."
chaincodeQuery 0 100
//Peer0/Org1上调用链码
#Invoke on chaincode on Peer0/Org1
echo "Sending invoke transaction on org1/peer0..."
chaincodeInvoke 0
//Peer3/Org2上安装链码
## Install chaincode on Peer3/Org2
echo "Installing chaincode on org2/peer3..."
installChaincode 3
//Peer3/Org2上查询链码,是否90元
#Query on chaincode on Peer3/Org2, check if the result is 90
echo "Querying chaincode on org2/peer3..."
chaincodeQuery 3 90
5.1 创建新的应用通道
Fabric要求创建、加入与更新通道的权限必须具有通道组织的管理员身份。调用setGlobals设置全局环境变量,CLI客户端能够灵活切换指定容器的管理员角色,可以直接连接并操作指定的Peer节点,先切换到Peer0/Org1节点。
接着采用Org1管理员身份执行peer指令,将通道配置文件Channel.tx发送给Orderer节点,创建mychananel的应用通道。如果创建成功,则返回一个创世区块block,它会存储在 peer 节点的文件系统中,包含 channel.tx 指定的通道配置信息。
createChannel() {
setGlobals 0 1
if [ -z "$CORE_PEER_TLS_ENABLED" -o "$CORE_PEER_TLS_ENABLED" = "false" ]; then
set -x
peer channel create -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/channel.tx >&log.txt
res=$?
set +x
else
set -x
peer channel create -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/channel.tx --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA >&log.txt
res=$?
set +x
fi
cat log.txt
verifyResult $res "Channel creation failed"
echo "===================== Channel '$CHANNEL_NAME' created ===================== "
echo
}
5.2 Peer节点加入应用通道
遍历所有节点,调用setGlobals切换指定节点,执行peer指令,将Org1组织包含的Peer0/Org1和Peer1/Org1节点加入mychananel应用通道,将创世区块mychananel.block设置成命令行参数。Org2组织类似。
joinChannel () {
for org in 1 2; do
for peer in 0 1; do
joinChannelWithRetry $peer $org
echo "===================== peer${peer}.org${org} joined channel '$CHANNEL_NAME' ===================== "
sleep $DELAY
echo
done
done
}
joinChannelWithRetry 函数中的 setGlobals 是设置 CLI 容器的环境变量的函数。例如:setGlobals 1 2 设置 CLI 的身份为 admin.org2,连接 peer1.org2 节点。
使用 peer channel join 命令让节点加入通道,$CHANNEL_NAME.block 就是前面创建通道成功时返回的区块,该区块在上面 org1.peer0 创建通道时保持在 CLI 容器内,所以能直接使用。节点成功加入通道后会创建 CHANNEL_NAME.block 开头的链。
joinChannelWithRetry() {
PEER=$1
ORG=$2
setGlobals $PEER $ORG
set -x
peer channel join -b $CHANNEL_NAME.block >&log.txt
res=$?
set +x
cat log.txt
if [ $res -ne 0 -a $COUNTER -lt $MAX_RETRY ]; then
COUNTER=$(expr $COUNTER + 1)
echo "peer${PEER}.org${ORG} failed to join the channel, Retry after $DELAY seconds"
sleep $DELAY
joinChannelWithRetry $PEER $ORG
else
COUNTER=1
fi
verifyResult $res "After $MAX_RETRY attempts, peer${PEER}.org${ORG} has failed to join channel '$CHANNEL_NAME' "
}
5.3 更新应用通道上组织的锚节点配置
一个组织只能有一个锚节点,节点加入通道后才能进行更新,连续两次调用updateAnchorPeers,更新两个组织的锚节点配置,用Org1管理身份更新Peer0/Org1的配置,并指定锚节点配置更新文件Org1MSPanchors.tx,Org2组织同理。
updateAnchorPeers() {
PEER=$1
ORG=$2
setGlobals $PEER $ORG
if [ -z "$CORE_PEER_TLS_ENABLED" -o "$CORE_PEER_TLS_ENABLED" = "false" ]; then
set -x
peer channel update -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/${CORE_PEER_LOCALMSPID}anchors.tx >&log.txt
res=$?
set +x
else
set -x
peer channel update -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/${CORE_PEER_LOCALMSPID}anchors.tx --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA >&log.txt
res=$?
set +x
fi
cat log.txt
verifyResult $res "Anchor peer update failed"
echo "===================== Anchor peers updated for org '$CORE_PEER_LOCALMSPID' on channel '$CHANNEL_NAME' ===================== "
sleep $DELAY
echo
}
6 安装、实例化与调用链码
6.1 安装链码
连续两次调用installChaincode分别在Peer0/Org1和Peer2/Org2中安装chaincode_example02链码,并将链码命名为“mycc”且版本1.0,如果链码安装成功,在指定安装目录/var/hyperledger/production/chaincodes下存在name.version的链码文件。
installChaincode() {
PEER=$1
ORG=$2
setGlobals $PEER $ORG
VERSION=${3:-1.0}
set -x
peer chaincode install -n mycc -v ${VERSION} -l ${LANGUAGE} -p ${CC_SRC_PATH} >&log.txt
res=$?
set +x
cat log.txt
verifyResult $res "Chaincode installation on peer${PEER}.org${ORG} has failed"
echo "===================== Chaincode is installed on peer${PEER}.org${ORG} ===================== "
echo
}
6.2 实例化链码
实例化链码必须在安装过链码的节点上进行,同一个通道内所有节点上相同的实例化数据在通道账本中是共享的,用户只需要在任意一个Peer节点上成功执行一次实例化链码操作,并通过排序打包后将实例化数据广播到其他节点上,通道内所有合法节点都可以访问该链码的实例化数据。
实例化将链码添加到通道上,启动目标节点的容器,初始化与链码相关的初始值,这里的初始值为 ["a","100","b","200"]。”实例化“ 过程会产生链码的容器,例如: dev-peer0-org1.example.com-mycc-1.0 。实例化过程需要指定背书策略,通过 -P 参数设置,这里的策略定义为 AND ('Org1MSP.peer','Org2MSP.peer') ,表示任何交易必须要有 org1 和 org2 节点的共同背书。
instantiateChaincode() {
PEER=$1
ORG=$2
setGlobals $PEER $ORG
VERSION=${3:-1.0}
# while 'peer chaincode' command can get the orderer endpoint from the peer
# (if join was successful), let's supply it directly as we know it using
# the "-o" option
if [ -z "$CORE_PEER_TLS_ENABLED" -o "$CORE_PEER_TLS_ENABLED" = "false" ]; then
set -x
peer chaincode instantiate -o orderer.example.com:7050 -C $CHANNEL_NAME -n mycc -l ${LANGUAGE} -v ${VERSION} -c '{"Args":["init","a","100","b","200"]}' -P "AND ('Org1MSP.peer','Org2MSP.peer')" >&log.txt
res=$?
set +x
else
set -x
peer chaincode instantiate -o orderer.example.com:7050 --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA -C $CHANNEL_NAME -n mycc -l ${LANGUAGE} -v 1.0 -c '{"Args":["init","a","100","b","200"]}' -P "AND ('Org1MSP.peer','Org2MSP.peer')" >&log.txt
res=$?
set +x
fi
cat log.txt
verifyResult $res "Chaincode instantiation on peer${PEER}.org${ORG} on channel '$CHANNEL_NAME' failed"
echo "===================== Chaincode is instantiated on peer${PEER}.org${ORG} on channel '$CHANNEL_NAME' ===================== "
echo
}
6.3 调用链码
Peer0/Org1上调用链码查询函数chaincodeQuery和链码调用函数chaincodeQuery。查看A的余额,并从账户A中向账户B中转账10元。
chaincodeInvoke() {
parsePeerConnectionParameters $@
res=$?
verifyResult $res "Invoke transaction failed on channel '$CHANNEL_NAME' due to uneven number of peer and org parameters "
# while 'peer chaincode' command can get the orderer endpoint from the
# peer (if join was successful), let's supply it directly as we know
# it using the "-o" option
if [ -z "$CORE_PEER_TLS_ENABLED" -o "$CORE_PEER_TLS_ENABLED" = "false" ]; then
set -x
peer chaincode invoke -o orderer.example.com:7050 -C $CHANNEL_NAME -n mycc $PEER_CONN_PARMS -c '{"Args":["invoke","a","b","10"]}' >&log.txt
res=$?
set +x
else
set -x
peer chaincode invoke -o orderer.example.com:7050 --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA -C $CHANNEL_NAME -n mycc $PEER_CONN_PARMS -c '{"Args":["invoke","a","b","10"]}' >&log.txt
res=$?
set +x
fi
cat log.txt
verifyResult $res "Invoke execution on $PEERS failed "
echo "===================== Invoke transaction successful on $PEERS on channel '$CHANNEL_NAME' ===================== "
echo
}
6.4 查询链码
Peer3/Org2上安装链码,执行查询A的余额,检查余额是否为90元,如果是说明转账成功。
chaincodeQuery() {
PEER=$1
ORG=$2
setGlobals $PEER $ORG
EXPECTED_RESULT=$3
echo "===================== Querying on peer${PEER}.org${ORG} on channel '$CHANNEL_NAME'... ===================== "
local rc=1
local starttime=$(date +%s)
# continue to poll
# we either get a successful response, or reach TIMEOUT
while
test "$(($(date +%s) - starttime))" -lt "$TIMEOUT" -a $rc -ne 0
do
sleep $DELAY
echo "Attempting to Query peer${PEER}.org${ORG} ...$(($(date +%s) - starttime)) secs"
set -x
peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}' >&log.txt
res=$?
set +x
test $res -eq 0 && VALUE=$(cat log.txt | awk '/Query Result/ {print $NF}')
test "$VALUE" = "$EXPECTED_RESULT" && let rc=0
# removed the string "Query Result" from peer chaincode query command
# result. as a result, have to support both options until the change
# is merged.
test $rc -ne 0 && VALUE=$(cat log.txt | egrep '^[0-9]+$')
test "$VALUE" = "$EXPECTED_RESULT" && let rc=0
done
echo
cat log.txt
if test $rc -eq 0; then
echo "===================== Query successful on peer${PEER}.org${ORG} on channel '$CHANNEL_NAME' ===================== "
else
echo "!!!!!!!!!!!!!!! Query result on peer${PEER}.org${ORG} is INVALID !!!!!!!!!!!!!!!!"
echo "================== ERROR !!! FAILED to execute End-2-End Scenario =================="
echo
exit 1
fi
}