first-network是fabric官方提供的首个示例。这里对此示例的结构和启动过程做下解析。
结构
使用byfn.sh up
启动后,会出现9个新容器:
IMAGE | NAME | PORTS |
---|---|---|
dev-peer1.org2.example.com-mycc-1.0-26c2ef32838554aac4f7ad6f100aca865e87959c9a126e86d764c8d01f8346ab | dev-peer1.org2.example.com-mycc-1.0 | - |
dev-peer0.org1.example.com-mycc-1.0-384f11f484b9302df90b453200cfb25174305fce8f53f4e94d45ee3b6cab0ce9 | dev-peer0.org1.example.com-mycc-1.0 | - |
dev-peer0.org2.example.com-mycc-1.0-15b571b3ce849066b7ec74497da3b27e54e0df1345daff3951b94245ce09c42b | dev-peer0.org2.example.com-mycc-1.0 | - |
hyperledger/fabric-tools:latest | cli | - |
hyperledger/fabric-orderer:latest | orderer.example.com | 0.0.0.0:7050->7050/tcp |
hyperledger/fabric-peer:latest | peer1.org1.example.com | 0.0.0.0:8051->7051/tcp, 0.0.0.0:8053->7053/tcp |
hyperledger/fabric-peer:latest | peer0.org1.example.com | 0.0.0.0:7051->7051/tcp, 0.0.0.0:7053->7053/tcp |
hyperledger/fabric-peer:latest | peer1.org2.example.com | 0.0.0.0:10051->7051/tcp, 0.0.0.0:10053->7053/tcp |
hyperledger/fabric-peer:latest | peer0.org2.example.com | 0.0.0.0:9051->7051/tcp, 0.0.0.0:9053->7053/tcp |
可见,共启动了4个peer节点,分别属于org1和org2;另有一个orderer节点。
还启动了一个cli,用于控制网络。
在3个peer节点上,安装了chaincode,也以容器形式启动。
分析 byfn.sh
Step.1 byfn.sh generate
generate required certificates and genesis block.
elif [ "${MODE}" == "generate" ]; then ## Generate Artifacts
generateCerts
replacePrivateKey
generateChannelArtifacts
从脚本入口部分可见,generate命令执行了3个函数:
1. generateCerts()
精简代码如下:
# Generates Org certs using cryptogen tool
function generateCerts() {
if [ -d "crypto-config" ]; then
rm -Rf crypto-config
fi
cryptogen generate --config=./crypto-config.yaml
}
可见,这一步先清除crypto-config目录,再调用cryptogen工具,根据crypto-config.yaml生成文件(到crypto-config目录)。
精简的crypto-config.yaml:
# ---------------------------------------------------------------------------
# "OrdererOrgs" - Definition of organizations managing orderer nodes
# ---------------------------------------------------------------------------
OrdererOrgs:
- Name: Orderer
Domain: example.com
Specs:
- Hostname: orderer
# ---------------------------------------------------------------------------
# "PeerOrgs" - Definition of organizations managing peer nodes
# ---------------------------------------------------------------------------
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
可见,定义了一个OrdererOrg,2个PeerOrgs:Org1,Org2;每个PeerOrg各有2个节点。
生成的文件结构:
$ tree crypto-config -L 4 -d
crypto-config
├── ordererOrganizations
│ └── example.com
│ ├── ca
│ ├── msp
│ │ ├── admincerts
│ │ ├── cacerts
│ │ └── tlscacerts
│ ├── orderers
│ │ └── orderer.example.com
│ ├── tlsca
│ └── users
│ └── [email protected]
└── peerOrganizations
├── org1.example.com
│ ├── ca
│ ├── msp
│ │ ├── admincerts
│ │ ├── cacerts
│ │ └── tlscacerts
│ ├── peers
│ │ ├── peer0.org1.example.com
│ │ └── peer1.org1.example.com
│ ├── tlsca
│ └── users
│ ├── [email protected]
│ └── [email protected]
└── org2.example.com
├── ca
├── msp
│ ├── admincerts
│ ├── cacerts
│ └── tlscacerts
├── peers
│ ├── peer0.org2.example.com
│ └── peer1.org2.example.com
├── tlsca
└── users
├── [email protected]
└── [email protected]
2. replacePrivateKey()
精简代码如下:
# Using docker-compose-e2e-template.yaml, replace constants with private key file names
# generated by the cryptogen tool and output a docker-compose.yaml specific to this
# configuration
function replacePrivateKey() {
OPTS="-i"
# 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
}
可见,此函数复制了docker-compose-e2e-template.yaml为docker-compose-e2e.yaml,并根据上一步生成的.../ca/..._sk文件,替换了.yaml中响应路径。
由于之后我们使用docker-compose-cli.yaml启动,不使用CA节点,所以这里生成的文件暂时还用不到。
3. generateChannelArtifacts()
精简代码:
# Generate orderer genesis block, channel configuration transaction and anchor peer update transactions
function generateChannelArtifacts() {
# Generating Orderer Genesis block
# 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
# Generating channel configuration transaction 'channel.tx'
configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID $CHANNEL_NAME
# Generating anchor peer update for Org1MSP
configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP
# Generating anchor peer update for Org2MSP
configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate \
./channel-artifacts/Org2MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org2MSP
可见,此函数功能就是调用configtxgen:
- 生成 orderer genesis block
- 生成 channel configuration transaction
- 为每个组织生成 anchor peer update transactions
最终文件生成在channel-artifacts目录中:
$ tree channel-artifacts/
channel-artifacts/
├── channel.tx
├── genesis.block
├── Org1MSPanchors.tx
└── Org2MSPanchors.tx
执行完generate命令后,我们得到了:
- 一系列密钥和证书在crypto-config目录;
- 一个使用了CA证书的compose配置文件docker-compose-e2e.yaml;
- orderer genesis block、channel configuration transaction、anchor peer update transactions在channel-artifacts目录。
Step.1 byfn.sh up
#Create the network using docker compose
if [ "${MODE}" == "up" ]; then
networkUp
可见,up命令只执行了networkUp()函数。
networkUp()
精简代码:
COMPOSE_FILE=docker-compose-cli.yaml
# Generate the needed certificates, the genesis block and start the network.
function networkUp() {
if [ "${IF_COUCHDB}" == "couchdb" ]; then
IMAGE_TAG=$IMAGETAG docker-compose -f $COMPOSE_FILE -f $COMPOSE_FILE_COUCH up -d 2>&1
else
IMAGE_TAG=$IMAGETAG docker-compose -f $COMPOSE_FILE up -d 2>&1
fi
# now run the end to end script
docker exec cli scripts/script.sh $CHANNEL_NAME $CLI_DELAY $LANGUAGE $CLI_TIMEOUT $VERBOSE
}
可见,这个函数做了两件事:
- docker-compose根据docker-compose-cli.yaml编排启动容器。
- 在cli容器中执行scripts/script.sh脚本。
docker-compose-cli.yaml:
version: '2'
volumes:
orderer.example.com:
peer0.org1.example.com:
peer1.org1.example.com:
peer0.org2.example.com:
peer1.org2.example.com:
networks:
byfn:
services:
orderer.example.com:
extends:
file: base/docker-compose-base.yaml
service: orderer.example.com
container_name: orderer.example.com
networks:
- byfn
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
cli:
container_name: cli
image: hyperledger/fabric-tools:$IMAGE_TAG
tty: true
stdin_open: true
environment:
- GOPATH=/opt/gopath
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
#- CORE_LOGGING_LEVEL=DEBUG
- CORE_LOGGING_LEVEL=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
可见,这个配置文件定义了需要启动的9个容器的相关信息。9个容器被放在同一个docker network:byfn中。
精简的script.sh脚本:
#!/bin/bash
## Create channel
createChannel
## Join all the peers to the channel
joinChannel
## Set the anchor peers for each org in the channel
updateAnchorPeers 0 1
updateAnchorPeers 0 2
## Install chaincode on peer0.org1 and peer0.org2
installChaincode 0 1
installChaincode 0 2
# Instantiate chaincode on peer0.org2
instantiateChaincode 0 2
# Query chaincode on peer0.org1
chaincodeQuery 0 1 100
# Invoke chaincode on peer0.org1 and peer0.org2
chaincodeInvoke 0 1 0 2
## Install chaincode on peer1.org2
installChaincode 1 2
# Query on chaincode on peer1.org2, check if the result is 90
chaincodeQuery 1 2 90
除去细节后,script.sh执行了一系列网络构建工作,包括:
- 创建channel并将所有peers加入channel;
- 设定每个org的anchor peer;
- install chaincode到2个anchor peer,实例化其中一个;
- Query和invoke操作
- install chaincode到另一个节点,并在其上query它
这里进行了一个交易操作,并验证其结果正确。