Hyperledger Fabric 1.0 实战(四)多机部署e2e_cli

节点服务和数据存储都使用docker容器进行管理的,docker作为一个轻量级的虚拟化工具,docker容器可以理解为一个最小化的操作系统;每个节点都需要启动一个docker容器,服务和数据是隔离的。单机部署就可以理解为:一个机器上跑了多个虚拟机;多机部署可以理解为把多个虚拟机部署到多个机器上。

本文主要是让大家更深入的理解配置文件,如果已经有一定的理解可以直接看基于kafka的多机部署实例,有完整的配置文件:
https://www.jianshu.com/p/9c84673374a5

以下是整个运行步骤:

 基于 hyperledger/fabric/examples/e2e_cli/架构搭建 4个peer  1个order 的多服务器架构 --深入
    目标机器:
        10.0.200.111    orderer.lychee.com
        10.0.200.113    peer0.org1.lychee.com
        10.0.200.114    peer1.org1.lychee.com
        10.0.200.115    peer0.org2.lychee.com
        10.0.200.116    peer1.org2.lychee.com
    
    1 工具了解(会自动编译)
        configtxlator   配置文件生成工具
        cryptogen   证书生成工具

    2 创建证书与配置文件 文件配置需要格式严格设置
        # 参数通道名称,涉及文件:crypto-config.yaml实体证书信息, configtx.yaml创始区块配置文件,docker-compose-e2e.yaml 证书服务器docker配置,域、节点数等修改时,需要同时修改这几个文件。
        ./generateArtifacts.sh lycheechannel
    
        2.1 generateArtifacts.sh 脚本文件说明
            generateCerts 证书生成
            replacePrivateKey 私钥替换
            generateChannelArtifacts 创始区块配置文件生成

            cryptogen generate --config=./crypto-config.yaml #根据这个文件可以为组织和其中的成员生成数字证书和签名密钥,生成的文件都保存到crypto-config文件夹

            export FABRIC_CFG_PATH=$PWD
            configtxgen -profile TwoOrgsOrdererGenesis -outputBlock ./channel-artifacts/genesis.block   #生成排序服务创世区块
            #TwoOrgsOrdererGenesis为configtx.yaml中的profiles之一。
            #./channel-artifacts/genesis.block为生成的创世块的文件名及保存位置。
            
            configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID mychannel  #生成通道配置创世区块
            #mychannel为通道名称
                #TwoOrgsChannel为configtx.yaml中的profiles之一。
            
            configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID mychannel -asOrg Org1MSP  #生成组织锚节点,锚节点负责代表组织与其他组织中的节点进行 Gossip 通信
            configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org2MSPanchors.tx -channelID mychannel -asOrg Org2MSP
            其中Org1MSP,Org2MSP为组织名称,在configtx.yaml中有设置。
            最终,我们在channel-artifacts文件夹中,应该是能够看到4个文件。
            channel-artifacts/ 
            ├── channel.tx 
            ├── genesis.block 
            ├── Org1MSPanchors.tx 
            └── Org2MSPanchors.tx

            
        2.2 crypto-config.yaml 证书文件说明
            MSP证书是超级账本网络实体的身份标识,实体在通信和交易时使用证书进行签名和验证。生产证书需要crypto-config.yaml文件。定义所有的组织,OrdererOrgs排序服务组织、PeerOrgs节点组织;主要是包括服务所在域名、名称、包含的节点数等,详见文件;

        2.3 configtx.yaml 创始区块配置文件
            生成创世区块依赖文件configtx.yaml。configtx.yaml这个文件里面配置了由2个Org参与的Orderer共识配置TwoOrgsOrdererGenesis,以及由2个Org 参与的Channel配置:TwoOrgsChannel。Orderer可以设置共识的算法是Solo还是Kafka,以及共识时区块大小,超时时间 等,我们使用默认值即可,不用更改。而Peer节点的配置包含了MSP的配置,锚节点的配置。如果我们有更多的Org,或者有更多的Channel,那么 就可以根据模板进行对应的修改。

        2.4 docker-compose-e2e.yaml 文件说明
            此文件为docker-compose创建启动docker的配置文件;定义了两个ca证书服务容器;关于docker启动配置参数说明,见docker-compose配置文件说明.txt。在多机部署的时候,不再需要此文件;单机部署的时候可以直接用这个文件编译:
            ./network_setup.sh up 
            关于network_setup.sh脚本,up即启动容器,down则使用docker ps 枚举后在用docker rm。5台节点的启动看后续配置文件说明。  

    3 docker-compose常用命令

        docker-compose up ,这两个容器都是在前台运行的。我们可以指定-d命令以daemon的方式启动容器。除此之外,docker-compose还支持下面参数:
            --verbose :输出详细信息
            -f 制定一个非docker-compose.yml命名的yaml文件
            -p 设置一个项目名称(默认是directory名)
        docker-compose的动作包括:
            build :构建服务
            kill -s SIGINT :给服务发送特定的信号。
            logs :输出日志
            port :输出绑定的端口
            ps :输出运行的容器
            pull :pull服务的image
            rm :删除停止的容器
            run : 运行某个服务,例如docker-compose run web python manage.py shell
            start :运行某个服务中存在的容器。
            stop :停止某个服务中存在的容器。
            up :create + run + attach容器到服务。
            scale :设置服务运行的容器数量。例如:docker-compose scale web=2 worker=3   

    4 分别配置docker-compose.yaml文件

        4.1 配置orderer节点yaml
            cp docker-compose-cli.yaml docker-compose-orderer.yaml
            #orderer服务器上我们只需要保留order设置,其他peer和cli设置都可以删除。orderer可以不设置extra_hosts。注意域、名称等一致性。详见实例文件

        4.2 配置peer节点的yaml
            cp docker-compose-cli.yaml docker-compose-peer.yaml
            #修改docker-compose-peer.yaml,去掉orderer的配置,只保留一个peer和cli,因为我们要多级部署,节点与节点之前又是通过主机名通讯,所以需要修改容器中的host文件,也就是extra_hosts设置,修改后的peer配置如下:
            peer0.org1.example.com:
                container_name: peer0.org1.example.com
                extends:
                        file:  base/docker-compose-base.yaml
                        service: peer0.org1.example.com
                extra_hosts:
                    - "orderer.example.com:10.174.13.185" # host:ip 在peer容器的/etc/hosts增加记录。             
            cli也需要能够和各个节点通讯,所以cli下面也需要添加extra_hosts设置,去掉无效的依赖,否则容易启动失败,启动顺序非常重要,并且去掉command这一行(调用scripts.sh命令,这个是创建通道、部署链码测试脚本),因为我们是每个peer都会有个对应的客户端,也就是cli,所以我只需要去手动执行一次命令,而不是自动运行(创建通道等只需要运行一次)。主要定义了一些环境变量、卷轴挂载、网络说明等。
            注意路径映射,否则容易找不到文件。其他peer节点复制后将名称、地址修改即可。

        4.3 docker-compose-base.yaml 文件说明
            所有peer和orderer都依赖这个文件,这个是基础的extends扩展;主要定义一些环境变量msp和tls位置、msp和tls卷轴映射、端口映射、gossip地址等。这个文件也要修改,因为是多机部署,所以端口映射可以改为一样。


    5 多节点分发配置
        每个机器都要求安卓docker等工具,所以建议在一个环境配置好后,保存镜像,然后再用此镜像创建其他节点。
        分发后,需要修改其他peer节点的docker-compose-peer.yaml文件,主要是修改名称和依赖。
    
    6 启动Fabric
    
        5.6.1 启动orderer节点
            docker-compose -f docker-compose-orderer.yaml up -d
            #启动后,可以用docker ps 查看启动的容器,会有一个为orderer.lychee.com的节点

        5.6.2 启动peer节点
            docker-compose -f docker-compose-peer.yaml up –d
            #启动后,可以看到两个容器,peer0.org1.lychee.com和lycheecli两个容器。
            #登陆其他节点,同样的命令启动。

    7 创建通道和部署、实例化、测试链码

        7.1 创建channel测试chaincode
            选择任意一个peer,执行一下命令:
            docker exec -it lycheecli bash  #切入到lycheecli容器中;结果类似:
            root@b41e67d40583:/opt/gopath/src/github.com/hyperledger/fabric/peer#
            
            ./scripts/script.sh mychannel   #创建名称为mychannel的通道并测试链码
        
        7.2 script.sh脚本文件说明(摘取部分命令说明,详见配置文件)
            #在orderer上创建一个channel,channel配置文件为./channel-artifacts/channel.tx
            peer channel create -o orderer.lychee.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/channel.tx >&log.txt 
            
            #更新通道上锚节点信息
            peer channel update -o orderer.lychee.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/${CORE_PEER_LOCALMSPID}anchors.tx >&log.txt
            
            #加入通道,尝试5次
            peer channel join -b $CHANNEL_NAME.block  >&log.txt
            
            #安装链码,先要配置环境
            peer chaincode install -n mycc -v 1.0 -p github.com/hyperledger/fabric/lychees/chaincode/go/chaincode_lychee02 >&log.txt

            #实例化链码,先要配置环境
            peer chaincode instantiate -o orderer.lychee.com: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

            #链码调用,查询
            peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}' >&log.txt

            #链码调用,invoke
            peer chaincode invoke -o orderer.lychee.com:7050 -C $CHANNEL_NAME -n mycc -c '{"Args":["invoke","a","b","10"]}' >&log.txt
    
    8 结果
        ===================== All GOOD, End-2-End execution completed ===================== 
        出现以上内容,则这简单的5个节点的多机部署就OK了

以下是所有的配置文件:

crypto-config.yaml实体证书信息
# 
# Copyright IBM Corp. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
#

# ---------------------------------------------------------------------------
# "OrdererOrgs" - Definition of organizations managing orderer nodes
# ---------------------------------------------------------------------------
OrdererOrgs:
  # ---------------------------------------------------------------------------
  # Orderer
  # ---------------------------------------------------------------------------
  - Name: Orderer
    Domain: lychee.com
    # ---------------------------------------------------------------------------
    # "Specs" - See PeerOrgs below for complete description
    # ---------------------------------------------------------------------------
    Specs:
      - Hostname: orderer
# ---------------------------------------------------------------------------
# "PeerOrgs" - Definition of organizations managing peer nodes
# ---------------------------------------------------------------------------
PeerOrgs:
  # ---------------------------------------------------------------------------
  # Org1
  # ---------------------------------------------------------------------------
  - Name: Org1
    Domain: org1.lychee.com
    # ---------------------------------------------------------------------------
    # "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}}"
    #
    #                 which obtains its values from the Spec.Hostname and
    #                 Org.Domain, respectively.
    # ---------------------------------------------------------------------------
    # Specs:
    #   - Hostname: foo # implicitly "foo.org1.lychee.com"
    #     CommonName: foo27.org5.lychee.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
    # ---------------------------------------------------------------------------
    Template:
      Count: 2
      # Start: 5
      # Hostname: {{.Prefix}}{{.Index}} # default
    # ---------------------------------------------------------------------------
    # "Users"
    # ---------------------------------------------------------------------------
    # Count: The number of user accounts _in addition_ to Admin
    # ---------------------------------------------------------------------------
    Users:
      Count: 1
  # ---------------------------------------------------------------------------
  # Org2: See "Org1" for full specification
  # ---------------------------------------------------------------------------
  - Name: Org2
    Domain: org2.lychee.com
    Template:
      Count: 2
    Users:
      Count: 1

configtx.yaml创始区块配置文件
# Copyright IBM Corp. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
#

---
################################################################################
#
#   Profile
#
#   - Different configuration profiles may be encoded here to be specified
#   as parameters to the configtxgen tool
#
################################################################################
Profiles:

    TwoOrgsOrdererGenesis:
        Orderer:
            <<: *OrdererDefaults
            Organizations:
                - *OrdererOrg
        Consortiums:
            SampleConsortium:
                Organizations:
                    - *Org1
                    - *Org2
    TwoOrgsChannel:
        Consortium: SampleConsortium
        Application:
            <<: *ApplicationDefaults
            Organizations:
                - *Org1
                - *Org2

################################################################################
#
#   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/lychee.com/msp

    - &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.lychee.com/msp

        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.lychee.com
              Port: 7051

    - &Org2
        # DefaultOrg defines the organization which is used in the sampleconfig
        # of the fabric.git development environment
        Name: Org2MSP


        # ID to load the MSP definition as
        ID: Org2MSP

        MSPDir: crypto-config/peerOrganizations/org2.lychee.com/msp

        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.lychee.com
              Port: 7051

################################################################################
#
#   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
    # Available types are "solo" and "kafka"
    OrdererType: solo

    Addresses:
        - orderer.lychee.com: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: 98 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

    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:

generateArtifacts.sh运行脚本
#!/bin/bash +x
#
# Copyright IBM Corp. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
#


#set -e

CHANNEL_NAME=$1
: ${CHANNEL_NAME:="mychannel"}
echo $CHANNEL_NAME

export FABRIC_ROOT=$PWD/../..
export FABRIC_CFG_PATH=$PWD
echo

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)}')

## Using docker-compose template replace private key file names with constants
function replacePrivateKey () {
        ARCH=`uname -s | grep Darwin`
        if [ "$ARCH" == "Darwin" ]; then
                OPTS="-it"
        else
                OPTS="-i"
        fi

        cp docker-compose-e2e-template.yaml docker-compose-e2e.yaml

        CURRENT_DIR=$PWD
        cd crypto-config/peerOrganizations/org1.lychee.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.lychee.com/ca/
        PRIV_KEY=$(ls *_sk)
        cd $CURRENT_DIR
        sed $OPTS "s/CA2_PRIVATE_KEY/${PRIV_KEY}/g" docker-compose-e2e.yaml
}

## Generates Org certs using cryptogen tool
function generateCerts (){
        CRYPTOGEN=$FABRIC_ROOT/release/$OS_ARCH/bin/cryptogen

        if [ -f "$CRYPTOGEN" ]; then
            echo "Using cryptogen -> $CRYPTOGEN"
        else
            echo "Building cryptogen"
            make -C $FABRIC_ROOT release
        fi

        echo
        echo "##########################################################"
        echo "##### Generate certificates using cryptogen tool #########"
        echo "##########################################################"
        $CRYPTOGEN generate --config=./crypto-config.yaml
        echo
}

## Generate orderer genesis block , channel configuration transaction and anchor peer update transactions
function generateChannelArtifacts() {

        CONFIGTXGEN=$FABRIC_ROOT/release/$OS_ARCH/bin/configtxgen
        if [ -f "$CONFIGTXGEN" ]; then
            echo "Using configtxgen -> $CONFIGTXGEN"
        else
            echo "Building configtxgen"
            make -C $FABRIC_ROOT release
        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!
        $CONFIGTXGEN -profile TwoOrgsOrdererGenesis -outputBlock ./channel-artifacts/genesis.block

        echo
        echo "#################################################################"
        echo "### Generating channel configuration transaction 'channel.tx' ###"
        echo "#################################################################"
        $CONFIGTXGEN -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID $CHANNEL_NAME

        echo
        echo "#################################################################"
        echo "#######    Generating anchor peer update for Org1MSP   ##########"
        echo "#################################################################"
        $CONFIGTXGEN -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP

        echo
        echo "#################################################################"
        echo "#######    Generating anchor peer update for Org2MSP   ##########"
        echo "#################################################################"
        $CONFIGTXGEN -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org2MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org2MSP
        echo
}

generateCerts
replacePrivateKey
generateChannelArtifacts

docker-compose-peer.yaml docker peer容器配置文件
# Copyright IBM Corp. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
#

version: '2'

services:

  peer0.org1.lychee.com:
    container_name: peer0.org1.lychee.com
    extends:
      file:  base/docker-compose-base.yaml
      service: peer0.org1.lychee.com
    extra_hosts:
      - "orderer.lychee.com:10.0.200.111"
  lycheecli:
    container_name: lycheecli
    image: hyperledger/fabric-tools
    tty: true
    environment:
      - GOPATH=/opt/gopath
      - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
      - CORE_LOGGING_LEVEL=DEBUG
      - CORE_PEER_ID=lycheecli
      - CORE_PEER_ADDRESS=peer0.org1.lychee.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.lychee.com/peers/peer0.org1.lychee.com/tls/server.crt
      - CORE_PEER_TLS_KEY_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.lychee.com/peers/peer0.org1.lychee.com/tls/server.key
      - CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.lychee.com/peers/peer0.org1.lychee.com/tls/ca.crt
      - CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.lychee.com/users/[email protected]/msp
    working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
    volumes:
        - /var/run/:/host/var/run/
        - ../chaincode/go/:/opt/gopath/src/github.com/hyperledger/fabric/lychees/chaincode/go
        - ./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:
      - peer0.org1.lychee.com
    extra_hosts:
      - "orderer.lychee.com:10.0.200.111"
      - "peer0.org1.lychee.com:10.0.200.113"
      - "peer1.org1.lychee.com:10.0.200.114"
      - "peer0.org2.lychee.com:10.0.200.115"
      - "peer1.org2.lychee.com:10.0.200.116"

docker-compose-base.yaml docker peer基本配置
# Copyright IBM Corp. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
#

version: '2'

services:

  orderer.lychee.com:
    container_name: orderer.lychee.com
    image: hyperledger/fabric-orderer
    environment:
      - ORDERER_GENERAL_LOGLEVEL=debug
      - ORDERER_GENERAL_LISTENADDRESS=0.0.0.0
      - 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]
    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/lychee.com/orderers/orderer.lychee.com/msp:/var/hyperledger/orderer/msp
    - ../crypto-config/ordererOrganizations/lychee.com/orderers/orderer.lychee.com/tls/:/var/hyperledger/orderer/tls
    ports:
     - 7050:7050

  peer0.org1.lychee.com:
    container_name: peer0.org1.lychee.com
    extends:
      file: peer-base.yaml
      service: peer-base
    environment:
      - CORE_PEER_ID=peer0.org1.lychee.com
      - CORE_PEER_ADDRESS=peer0.org1.lychee.com:7051
      - CORE_PEER_CHAINCODELISTENADDRESS=peer0.org1.lychee.com:7052
      - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org1.lychee.com:7051
      - CORE_PEER_LOCALMSPID=Org1MSP
    volumes:
        - /var/run/:/host/var/run/
        - ../crypto-config/peerOrganizations/org1.lychee.com/peers/peer0.org1.lychee.com/msp:/etc/hyperledger/fabric/msp
        - ../crypto-config/peerOrganizations/org1.lychee.com/peers/peer0.org1.lychee.com/tls:/etc/hyperledger/fabric/tls
    ports:
      - 7051:7051
      - 7052:7052
      - 7053:7053

  peer1.org1.lychee.com:
    container_name: peer1.org1.lychee.com
    extends:
      file: peer-base.yaml
      service: peer-base
    environment:
      - CORE_PEER_ID=peer1.org1.lychee.com
      - CORE_PEER_ADDRESS=peer1.org1.lychee.com:7051
      - CORE_PEER_CHAINCODELISTENADDRESS=peer1.org1.lychee.com:7052
      - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer1.org1.lychee.com:7051
      - CORE_PEER_GOSSIP_BOOTSTRAP=peer0.org1.lychee.com:7051
      - CORE_PEER_LOCALMSPID=Org1MSP
    volumes:
        - /var/run/:/host/var/run/
        - ../crypto-config/peerOrganizations/org1.lychee.com/peers/peer1.org1.lychee.com/msp:/etc/hyperledger/fabric/msp
        - ../crypto-config/peerOrganizations/org1.lychee.com/peers/peer1.org1.lychee.com/tls:/etc/hyperledger/fabric/tls

    ports:
      - 7051:7051
      - 7052:7052
      - 7053:7053
  peer0.org2.lychee.com:
    container_name: peer0.org2.lychee.com
    extends:
      file: peer-base.yaml
      service: peer-base
    environment:
      - CORE_PEER_ID=peer0.org2.lychee.com
      - CORE_PEER_ADDRESS=peer0.org2.lychee.com:7051
      - CORE_PEER_CHAINCODELISTENADDRESS=peer0.org2.lychee.com:7052
      - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org2.lychee.com:7051
      - CORE_PEER_LOCALMSPID=Org2MSP
    volumes:
        - /var/run/:/host/var/run/
        - ../crypto-config/peerOrganizations/org2.lychee.com/peers/peer0.org2.lychee.com/msp:/etc/hyperledger/fabric/msp
        - ../crypto-config/peerOrganizations/org2.lychee.com/peers/peer0.org2.lychee.com/tls:/etc/hyperledger/fabric/tls
    ports:
      - 7051:7051
      - 7052:7052
      - 7053:7053
  peer1.org2.lychee.com:
    container_name: peer1.org2.lychee.com
    extends:
      file: peer-base.yaml
      service: peer-base
    environment:
      - CORE_PEER_ID=peer1.org2.lychee.com
      - CORE_PEER_ADDRESS=peer1.org2.lychee.com:7051
      - CORE_PEER_CHAINCODELISTENADDRESS=peer1.org2.lychee.com:7052
      - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer1.org2.lychee.com:7051
      - CORE_PEER_GOSSIP_BOOTSTRAP=peer0.org2.lychee.com:7051
      - CORE_PEER_LOCALMSPID=Org2MSP
    volumes:
        - /var/run/:/host/var/run/
        - ../crypto-config/peerOrganizations/org2.lychee.com/peers/peer1.org2.lychee.com/msp:/etc/hyperledger/fabric/msp
        - ../crypto-config/peerOrganizations/org2.lychee.com/peers/peer1.org2.lychee.com/tls:/etc/hyperledger/fabric/tls
    ports:
      - 7051:7051
      - 7052:7052
      - 7053:7053

scripts.sh测试脚本
#!/bin/bash
# Copyright London Stock Exchange Group All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
#
echo
echo " ____    _____      _      ____    _____           _____   ____    _____ "
echo "/ ___|  |_   _|    / \    |  _ \  |_   _|         | ____| |___ \  | ____|"
echo "\___ \    | |     / _ \   | |_) |   | |    _____  |  _|     __) | |  _|  "
echo " ___) |   | |    / ___ \  |  _ <    | |   |_____| | |___   / __/  | |___ "
echo "|____/    |_|   /_/   \_\ |_| \_\   |_|           |_____| |_____| |_____|"
echo

CHANNEL_NAME="$1"
: ${CHANNEL_NAME:="mychannel"}
: ${TIMEOUT:="60"}
COUNTER=1
MAX_RETRY=5
ORDERER_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/lychee.com/orderers/orderer.lychee.com/msp/tlscacerts/tlsca.lychee.com-cert.pem

echo "Channel name : "$CHANNEL_NAME

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.lychee.com/peers/peer0.org1.lychee.com/tls/ca.crt
                CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.lychee.com/users/[email protected]/msp
                if [ $1 -eq 0 ]; then
                        CORE_PEER_ADDRESS=peer0.org1.lychee.com:7051
                else
                        CORE_PEER_ADDRESS=peer1.org1.lychee.com:7051
                fi
        else
                CORE_PEER_LOCALMSPID="Org2MSP"
                CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.lychee.com/peers/peer0.org2.lychee.com/tls/ca.crt
                CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.lychee.com/users/[email protected]/msp
                if [ $1 -eq 2 ]; then
                        CORE_PEER_ADDRESS=peer0.org2.lychee.com:7051
                else
                        CORE_PEER_ADDRESS=peer1.org2.lychee.com:7051
                fi
        fi

        env |grep CORE
}

createChannel() {
        setGlobals 0

        if [ -z "$CORE_PEER_TLS_ENABLED" -o "$CORE_PEER_TLS_ENABLED" = "false" ]; then
                peer channel create -o orderer.lychee.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/channel.tx >&log.txt
        else
                peer channel create -o orderer.lychee.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/channel.tx --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA >&log.txt
        fi
        res=$?
        cat log.txt
        verifyResult $res "Channel creation failed"
        echo "===================== Channel \"$CHANNEL_NAME\" is created successfully ===================== "
        echo
}

updateAnchorPeers() {
        PEER=$1
        setGlobals $PEER

        if [ -z "$CORE_PEER_TLS_ENABLED" -o "$CORE_PEER_TLS_ENABLED" = "false" ]; then
                peer channel update -o orderer.lychee.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/${CORE_PEER_LOCALMSPID}anchors.tx >&log.txt
        else
                peer channel update -o orderer.lychee.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/${CORE_PEER_LOCALMSPID}anchors.tx --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA >&log.txt
        fi
        res=$?
        cat log.txt
        verifyResult $res "Anchor peer update failed"
        echo "===================== Anchor peers for org \"$CORE_PEER_LOCALMSPID\" on \"$CHANNEL_NAME\" is updated successfully ===================== "
        sleep 5
        echo
}


## Sometimes Join takes time hence RETRY atleast for 5 times
joinWithRetry () {
        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`
                echo "PEER$1 failed to join the channel, Retry after 2 seconds"
                sleep 2
                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 2
                echo
        done
}

installChaincode () {
        PEER=$1
        setGlobals $PEER
        peer chaincode install -n mycc -v 1.0 -p github.com/hyperledger/fabric/lychees/chaincode/go/chaincode_lychee02 >&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
        # 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.lychee.com: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.lychee.com: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
  while test "$(($(date +%s)-starttime))" -lt "$TIMEOUT" -a $rc -ne 0
  do
     sleep 3
     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
  if test $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
                peer chaincode invoke -o orderer.lychee.com:7050 -C $CHANNEL_NAME -n mycc -c '{"Args":["invoke","a","b","10"]}' >&log.txt
        else
                peer chaincode invoke -o orderer.lychee.com: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
}


## Create channel
echo "Creating channel..."
createChannel

## 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..."
updateAnchorPeers 0
echo "Updating anchor peers for org2..."
updateAnchorPeers 2

## Install chaincode on Peer0/Org1 and Peer2/Org2
echo "Installing chaincode on org1/peer0..."
installChaincode 0
echo "Install chaincode on org2/peer2..."
installChaincode 2

#Instantiate chaincode on Peer2/Org2
echo "Instantiating chaincode on org2/peer2..."
instantiateChaincode 2

#Query on chaincode on Peer0/Org1
echo "Querying chaincode on org1/peer0..."
chaincodeQuery 0 100

#Invoke on chaincode on Peer0/Org1
echo "Sending invoke transaction on org1/peer0..."
chaincodeInvoke 0

## Install chaincode on Peer3/Org2
echo "Installing chaincode on org2/peer3..."
installChaincode 3

#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, End-2-End execution completed ===================== "
echo

echo
echo " _____   _   _   ____            _____   ____    _____ "
echo "| ____| | \ | | |  _ \          | ____| |___ \  | ____|"
echo "|  _|   |  \| | | | | |  _____  |  _|     __) | |  _|  "
echo "| |___  | |\  | | |_| | |_____| | |___   / __/  | |___ "
echo "|_____| |_| \_| |____/          |_____| |_____| |_____|"
echo

exit 0

chaincode_example02.go智能合约代码
/*
Copyright IBM Corp. 2016 All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

         http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

//WARNING - this chaincode's ID is hard-coded in chaincode_example04 to illustrate one way of
//calling chaincode from a chaincode. If this example is modified, chaincode_example04.go has
//to be modified as well with the new ID of chaincode_example02.
//chaincode_example05 show's how chaincode ID can be passed in as a parameter instead of
//hard-coding.

import (
    "fmt"
    "strconv"

    "github.com/hyperledger/fabric/core/chaincode/shim"
    pb "github.com/hyperledger/fabric/protos/peer"
)

// SimpleChaincode example simple Chaincode implementation
type SimpleChaincode struct {
}

func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
    fmt.Println("ex02 Init")
    _, args := stub.GetFunctionAndParameters()
    var A, B string    // Entities
    var Aval, Bval int // Asset holdings
    var err error

    if len(args) != 4 {
        return shim.Error("Incorrect number of arguments. Expecting 4")
    }

    // Initialize the chaincode
    A = args[0]
    Aval, err = strconv.Atoi(args[1])
    if err != nil {
        return shim.Error("Expecting integer value for asset holding")
    }
    B = args[2]
    Bval, err = strconv.Atoi(args[3])
    if err != nil {
        return shim.Error("Expecting integer value for asset holding")
    }
    fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)

    // Write the state to the ledger
    err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
    if err != nil {
        return shim.Error(err.Error())
    }

    err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
    if err != nil {
        return shim.Error(err.Error())
    }

    return shim.Success(nil)
}

func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
    fmt.Println("ex02 Invoke")
    function, args := stub.GetFunctionAndParameters()
    if function == "invoke" {
        // Make payment of X units from A to B
        return t.invoke(stub, args)
    } else if function == "delete" {
        // Deletes an entity from its state
        return t.delete(stub, args)
    } else if function == "query" {
        // the old "Query" is now implemtned in invoke
        return t.query(stub, args)
    }

    return shim.Error("Invalid invoke function name. Expecting \"invoke\" \"delete\" \"query\"")
}

// Transaction makes payment of X units from A to B
func (t *SimpleChaincode) invoke(stub shim.ChaincodeStubInterface, args []string) pb.Response {
    var A, B string    // Entities
    var Aval, Bval int // Asset holdings
    var X int          // Transaction value
    var err error

    if len(args) != 3 {
        return shim.Error("Incorrect number of arguments. Expecting 3")
    }

    A = args[0]
    B = args[1]

    // Get the state from the ledger
    // TODO: will be nice to have a GetAllState call to ledger
    Avalbytes, err := stub.GetState(A)
    if err != nil {
        return shim.Error("Failed to get state")
    }
    if Avalbytes == nil {
        return shim.Error("Entity not found")
    }
    Aval, _ = strconv.Atoi(string(Avalbytes))

    Bvalbytes, err := stub.GetState(B)
    if err != nil {
        return shim.Error("Failed to get state")
    }
    if Bvalbytes == nil {
        return shim.Error("Entity not found")
    }
    Bval, _ = strconv.Atoi(string(Bvalbytes))

    // Perform the execution
    X, err = strconv.Atoi(args[2])
    if err != nil {
        return shim.Error("Invalid transaction amount, expecting a integer value")
    }
    Aval = Aval - X
    Bval = Bval + X
    fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)

    // Write the state back to the ledger
    err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
    if err != nil {
        return shim.Error(err.Error())
    }

    err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
    if err != nil {
        return shim.Error(err.Error())
    }

    return shim.Success(nil)
}

// Deletes an entity from state
func (t *SimpleChaincode) delete(stub shim.ChaincodeStubInterface, args []string) pb.Response {
    if len(args) != 1 {
        return shim.Error("Incorrect number of arguments. Expecting 1")
    }

    A := args[0]

    // Delete the key from the state in ledger
    err := stub.DelState(A)
    if err != nil {
        return shim.Error("Failed to delete state")
    }

    return shim.Success(nil)
}

// query callback representing the query of a chaincode
func (t *SimpleChaincode) query(stub shim.ChaincodeStubInterface, args []string) pb.Response {
    var A string // Entities
    var err error

    if len(args) != 1 {
        return shim.Error("Incorrect number of arguments. Expecting name of the person to query")
    }

    A = args[0]

    // Get the state from the ledger
    Avalbytes, err := stub.GetState(A)
    if err != nil {
        jsonResp := "{\"Error\":\"Failed to get state for " + A + "\"}"
        return shim.Error(jsonResp)
    }

    if Avalbytes == nil {
        jsonResp := "{\"Error\":\"Nil amount for " + A + "\"}"
        return shim.Error(jsonResp)
    }

    jsonResp := "{\"Name\":\"" + A + "\",\"Amount\":\"" + string(Avalbytes) + "\"}"
    fmt.Printf("Query Response:%s\n", jsonResp)
    return shim.Success(Avalbytes)
}

func main() {
    err := shim.Start(new(SimpleChaincode))
    if err != nil {
        fmt.Printf("Error starting Simple chaincode: %s", err)
    }
}

以上便是所有文件,一定要注意格式和路径,否则就会出现bad_request

你可能感兴趣的:(Hyperledger Fabric 1.0 实战(四)多机部署e2e_cli)