Hyperledger Fabric first-network的初始化启动流程(二)

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
}

你可能感兴趣的:(Hyperledger Fabric first-network的初始化启动流程(二))