节点服务和数据存储都使用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