Fabric-Sample test-network 脚本解析

此脚本将启动一个Hyperledger结构网络,用于测试智能合约和应用程序。

测试网络由两个两个组织。每个组织一个peer节点和一个单节点Raft Orderer服务组成。

用户也可以使用这个脚本创建通道的脚本并且在通道上部署链码。

目录结构

test-network 目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
├─addOrg3 #使用本目录下的脚本文件,动态增加一个组织
│ ├─docker
│ └─fabric-ca
│ └─org3
├─configtx #存放启动fabric网络的通道配置信息
├─docker #存放启动容器的docker-compose模板文件
├─organizations #存放生成的证书信息
│ ├─cryptogen
│ └─fabric-ca
│ ├─ordererOrg
│ ├─org1
│ └─org2
├─scripts #存放整个网络启动、管理的脚本
│ └─org3-scripts
└─system-genesis-block #创世区块

└─network.sh #核心:网络启动流程脚本

└─setOrgEnv.sh #设置各组织的环境变量

scripts脚本目录

1
2
3
4
5
6
7
8
9
10
│  configUpdate.sh  #更新配置信息
│ createChannel.sh #创建、加入通道,配置锚节点
│ deployCC.sh #操作链码的众多操作
│ envVar.sh #为peer节点配置环境变量
│ setAnchorPeer.sh #更新锚节点
│ utils.sh #通用脚本辅助函数,如println

└─org3-scripts #动态增加组织3的脚本
joinChannel.sh
updateChannelConfig.sh

network.sh

整体宏观把握整个通道创建管理的流程。

  • 准备fabric环境
  • 检查fabric环境checkPrereqs
  • 为组织生成证书createOrgs
  • 启动网络networkUp
  • 加入通道createChannel
  • 添加链码deployCC
  • 关闭网络networkDown

将$PWD/./bin预先添加到PATH,以确保获取正确的Fabric的二进制文件
如果需要,可以将其注释掉以解析工具的已安装版本

1
2
3
export PATH=${PWD}/../bin:$PATH
export FABRIC_CFG_PATH=${PWD}/configtx
export VERBOSE=false

引入scripts/utils.sh中的 printHelp 等辅助功能函数

1
. scripts/utils.sh

获取容器ID并将其删除
当您关闭网络时,将调用此函数

1
2
3
4
5
function clearContainers() {
infoln "Removing remaining containers"
docker rm -f $(docker ps -aq --filter label=service=hyperledger-fabric) 2>/dev/null || true
docker rm -f $(docker ps -aq --filter name='dev-peer*') 2>/dev/null || true
}

删除在此设置中生成的所有images
具体而言,以下images通常会被留下:
当您关闭网络时,将调用此函数

1
2
3
4
function removeUnwantedImages() {
infoln "Removing generated chaincode docker images"
docker image rm -f $(docker images -aq --filter reference='dev-peer*') 2>/dev/null || true
}

已知无法与测试网络一起工作的Fabric版本

1
NONWORKING_VERSIONS="^1\.0\. ^1\.1\. ^1\.2\. ^1\.3\. ^1\.4\."

执行一些基本的健全性检查,以确保Fabric的版本正确
二进制文件/images可用。将来,需要对存在性进行额外检查
可以添加go或其他项目的名称。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
function checkPrereqs() {
## Check if your have cloned the peer binaries and configuration files.
peer version > /dev/null 2>&1

if [[ $? -ne 0 || ! -d "../config" ]]; then
errorln "Peer binary and configuration files not found.."
errorln
errorln "Follow the instructions in the Fabric docs to install the Fabric Binaries:"
errorln "https://hyperledger-fabric.readthedocs.io/en/latest/install.html"
exit 1
fi
# use the fabric tools container to see if the samples and binaries match your
# docker images
LOCAL_VERSION=$(peer version | sed -ne 's/^ Version: //p')
DOCKER_IMAGE_VERSION=$(docker run --rm hyperledger/fabric-tools:latest peer version | sed -ne 's/^ Version: //p')

infoln "LOCAL_VERSION=$LOCAL_VERSION"
infoln "DOCKER_IMAGE_VERSION=$DOCKER_IMAGE_VERSION"

if [ "$LOCAL_VERSION" != "$DOCKER_IMAGE_VERSION" ]; then
warnln "Local fabric binaries and docker images are out of sync. This may cause problems."
fi

for UNSUPPORTED_VERSION in $NONWORKING_VERSIONS; do
infoln "$LOCAL_VERSION" | grep -q $UNSUPPORTED_VERSION
if [ $? -eq 0 ]; then
fatalln "Local Fabric binary version of $LOCAL_VERSION does not match the versions supported by the test network."
fi

infoln "$DOCKER_IMAGE_VERSION" | grep -q $UNSUPPORTED_VERSION
if [ $? -eq 0 ]; then
fatalln "Fabric Docker image version of $DOCKER_IMAGE_VERSION does not match the versions supported by the test network."
fi
done

## Check for fabric-ca
if [ "$CRYPTO" == "Certificate Authorities" ]; then

fabric-ca-client version > /dev/null 2>&1
if [[ $? -ne 0 ]]; then
errorln "fabric-ca-client binary not found.."
errorln
errorln "Follow the instructions in the Fabric docs to install the Fabric Binaries:"
errorln "https://hyperledger-fabric.readthedocs.io/en/latest/install.html"
exit 1
fi
CA_LOCAL_VERSION=$(fabric-ca-client version | sed -ne 's/ Version: //p')
CA_DOCKER_IMAGE_VERSION=$(docker run --rm hyperledger/fabric-ca:latest fabric-ca-client version | sed -ne 's/ Version: //p' | head -1)
infoln "CA_LOCAL_VERSION=$CA_LOCAL_VERSION"
infoln "CA_DOCKER_IMAGE_VERSION=$CA_DOCKER_IMAGE_VERSION"

if [ "$CA_LOCAL_VERSION" != "$CA_DOCKER_IMAGE_VERSION" ]; then
warnln "Local fabric-ca binaries and docker images are out of sync. This may cause problems."
fi
fi
}

根据其配置文件为组织生成证书,包括两个peer和一个order节点。Fabric CA生成证书见下文。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
function createOrgs() {
if [ -d "organizations/peerOrganizations" ]; then
rm -Rf organizations/peerOrganizations && rm -Rf organizations/ordererOrganizations
fi

# Create crypto material using cryptogen
if [ "$CRYPTO" == "cryptogen" ]; then
which cryptogen
if [ "$?" -ne 0 ]; then
fatalln "cryptogen tool not found. exiting"
fi
infoln "Generating certificates using cryptogen tool"

infoln "Creating Org1 Identities"

set -x
cryptogen generate --config=./organizations/cryptogen/crypto-config-org1.yaml --output="organizations"
res=$?
{ set +x; } 2>/dev/null
if [ $res -ne 0 ]; then
fatalln "Failed to generate certificates..."
fi

infoln "Creating Org2 Identities"

set -x
cryptogen generate --config=./organizations/cryptogen/crypto-config-org2.yaml --output="organizations"
res=$?
{ set +x; } 2>/dev/null
if [ $res -ne 0 ]; then
fatalln "Failed to generate certificates..."
fi

infoln "Creating Orderer Org Identities"

set -x
cryptogen generate --config=./organizations/cryptogen/crypto-config-orderer.yaml --output="organizations"
res=$?
{ set +x; } 2>/dev/null
if [ $res -ne 0 ]; then
fatalln "Failed to generate certificates..."
fi

fi

# Create crypto material using Fabric CA
if [ "$CRYPTO" == "Certificate Authorities" ]; then
infoln "Generating certificates using Fabric CA"
docker-compose -f $COMPOSE_FILE_CA up -d 2>&1

. organizations/fabric-ca/registerEnroll.sh

while :
do
if [ ! -f "organizations/fabric-ca/org1/tls-cert.pem" ]; then
sleep 1
else
break
fi
done

infoln "Creating Org1 Identities"

createOrg1

infoln "Creating Org2 Identities"

createOrg2

infoln "Creating Orderer Org Identities"

createOrderer

fi

infoln "Generating CCP files for Org1 and Org2"
./organizations/ccp-generate.sh
}

核心:启动网络

使用docker-compose模板启动网络(2 peer和1 order)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function networkUp() {
checkPrereqs
# generate artifacts if they don't exist
if [ ! -d "organizations/peerOrganizations" ]; then
createOrgs
fi

COMPOSE_FILES="-f ${COMPOSE_FILE_BASE}"

if [ "${DATABASE}" == "couchdb" ]; then
COMPOSE_FILES="${COMPOSE_FILES} -f ${COMPOSE_FILE_COUCH}"
fi

DOCKER_SOCK="${DOCKER_SOCK}" docker-compose ${COMPOSE_FILES} up -d 2>&1

docker ps -a
if [ $? -ne 0 ]; then
fatalln "Unable to start network"
fi
}

创建channel,加入org1和org2的peer节点,
并更新每个组织的锚节点,具体的细节参见:scripts/createChannel.sh

1
2
3
4
5
6
7
8
9
10
11
12
function createChannel() {
# Bring up the network if it is not already up.

if [ ! -d "organizations/peerOrganizations" ]; then
infoln "Bringing up network"
networkUp
fi

# now run the script that creates a channel. This script uses configtxgen once
# to create the channel creation transaction and the anchor peer updates.
scripts/createChannel.sh $CHANNEL_NAME $CLI_DELAY $MAX_RETRY $VERBOSE
}

调用脚本以将链码部署到通道,具体的细节参见:scripts/deployCC.sh

1
2
3
4
5
6
7
function deployCC() {
scripts/deployCC.sh $CHANNEL_NAME $CC_NAME $CC_SRC_PATH $CC_SRC_LANGUAGE $CC_VERSION $CC_SEQUENCE $CC_INIT_FCN $CC_END_POLICY $CC_COLL_CONFIG $CLI_DELAY $MAX_RETRY $VERBOSE

if [ $? -ne 0 ]; then
fatalln "Deploying chaincode failed"
fi
}

关闭测试网络

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function networkDown() {
# stop org3 containers also in addition to org1 and org2, in case we were running sample to add org3
DOCKER_SOCK=$DOCKER_SOCK docker-compose -f $COMPOSE_FILE_BASE -f $COMPOSE_FILE_COUCH -f $COMPOSE_FILE_CA down --volumes --remove-orphans
docker-compose -f $COMPOSE_FILE_COUCH_ORG3 -f $COMPOSE_FILE_ORG3 down --volumes --remove-orphans
# Don't remove the generated artifacts -- note, the ledgers are always removed
if [ "$MODE" != "restart" ]; then
# Bring down the network, deleting the volumes
#Cleanup the chaincode containers
clearContainers
#Cleanup images
removeUnwantedImages
# remove orderer block and other channel configuration transactions and certs
docker run --rm -v "$(pwd):/data" busybox sh -c 'cd /data && rm -rf system-genesis-block/*.block organizations/peerOrganizations organizations/ordererOrganizations'
## remove fabric ca artifacts
docker run --rm -v "$(pwd):/data" busybox sh -c 'cd /data && rm -rf organizations/fabric-ca/org1/msp organizations/fabric-ca/org1/tls-cert.pem organizations/fabric-ca/org1/ca-cert.pem organizations/fabric-ca/org1/IssuerPublicKey organizations/fabric-ca/org1/IssuerRevocationPublicKey organizations/fabric-ca/org1/fabric-ca-server.db'
docker run --rm -v "$(pwd):/data" busybox sh -c 'cd /data && rm -rf organizations/fabric-ca/org2/msp organizations/fabric-ca/org2/tls-cert.pem organizations/fabric-ca/org2/ca-cert.pem organizations/fabric-ca/org2/IssuerPublicKey organizations/fabric-ca/org2/IssuerRevocationPublicKey organizations/fabric-ca/org2/fabric-ca-server.db'
docker run --rm -v "$(pwd):/data" busybox sh -c 'cd /data && rm -rf organizations/fabric-ca/ordererOrg/msp organizations/fabric-ca/ordererOrg/tls-cert.pem organizations/fabric-ca/ordererOrg/ca-cert.pem organizations/fabric-ca/ordererOrg/IssuerPublicKey organizations/fabric-ca/ordererOrg/IssuerRevocationPublicKey organizations/fabric-ca/ordererOrg/fabric-ca-server.db'
docker run --rm -v "$(pwd):/data" busybox sh -c 'cd /data && rm -rf addOrg3/fabric-ca/org3/msp addOrg3/fabric-ca/org3/tls-cert.pem addOrg3/fabric-ca/org3/ca-cert.pem addOrg3/fabric-ca/org3/IssuerPublicKey addOrg3/fabric-ca/org3/IssuerRevocationPublicKey addOrg3/fabric-ca/org3/fabric-ca-server.db'
# remove channel and script artifacts
docker run --rm -v "$(pwd):/data" busybox sh -c 'cd /data && rm -rf channel-artifacts log.txt *.tar.gz'
fi
}

其他配置信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# Using crpto vs CA. default is cryptogen
CRYPTO="cryptogen"
# timeout duration - the duration the CLI should wait for a response from
# another container before giving up
MAX_RETRY=5
# default for delay between commands
CLI_DELAY=3
# channel name defaults to "mychannel"
CHANNEL_NAME="mychannel"
# chaincode name defaults to "NA"
CC_NAME="NA"
# chaincode path defaults to "NA"
CC_SRC_PATH="NA"
# endorsement policy defaults to "NA". This would allow chaincodes to use the majority default policy.
CC_END_POLICY="NA"
# collection configuration defaults to "NA"
CC_COLL_CONFIG="NA"
# chaincode init function defaults to "NA"
CC_INIT_FCN="NA"
# use this as the default docker-compose yaml definition
COMPOSE_FILE_BASE=docker/docker-compose-test-net.yaml
# docker-compose.yaml file if you are using couchdb
COMPOSE_FILE_COUCH=docker/docker-compose-couch.yaml
# certificate authorities compose file
COMPOSE_FILE_CA=docker/docker-compose-ca.yaml
# use this as the docker compose couch file for org3
COMPOSE_FILE_COUCH_ORG3=addOrg3/docker/docker-compose-couch-org3.yaml
# use this as the default docker-compose yaml definition for org3
COMPOSE_FILE_ORG3=addOrg3/docker/docker-compose-org3.yaml
#
# chaincode language defaults to "NA"
CC_SRC_LANGUAGE="NA"
# Chaincode version
CC_VERSION="1.0"
# Chaincode definition sequence
CC_SEQUENCE=1
# default database
DATABASE="leveldb"

# Get docker sock path from environment variable
SOCK="${DOCKER_HOST:-/var/run/docker.sock}"
DOCKER_SOCK="${SOCK##unix://}"

最后是读取 命令行参数 执行命令

1
2
3
4
5
6
7
8
9
10
11
12
if [ "${MODE}" == "up" ]; then
networkUp
elif [ "${MODE}" == "createChannel" ]; then
createChannel
elif [ "${MODE}" == "deployCC" ]; then
deployCC
elif [ "${MODE}" == "down" ]; then
networkDown
else
printHelp
exit 1
fi

scripts/createChannel.sh

创建通道的细节。

创建通道的创世区块

1
2
3
4
5
6
7
8
9
10
11
createChannelGenesisBlock() {
which configtxgen
if [ "$?" -ne 0 ]; then
fatalln "configtxgen tool not found."
fi
set -x
configtxgen -profile TwoOrgsApplicationGenesis -outputBlock ./channel-artifacts/${CHANNEL_NAME}.block -channelID $CHANNEL_NAME
res=$?
{ set +x; } 2>/dev/null
verifyResult $res "Failed to generate channel configuration transaction..."
}

创建通道

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
createChannel() {
# 使用org1的证书信息
setGlobals 1
# Poll in case the raft leader is not set yet
local rc=1
local COUNTER=1
while [ $rc -ne 0 -a $COUNTER -lt $MAX_RETRY ] ; do
sleep $DELAY
set -x
osnadmin channel join --channelID $CHANNEL_NAME --config-block ./channel-artifacts/${CHANNEL_NAME}.block -o localhost:7053 --ca-file "$ORDERER_CA" --client-cert "$ORDERER_ADMIN_TLS_SIGN_CERT" --client-key "$ORDERER_ADMIN_TLS_PRIVATE_KEY" >&log.txt
res=$?
{ set +x; } 2>/dev/null
let rc=$res
COUNTER=$(expr $COUNTER + 1)
done
cat log.txt
verifyResult $res "Channel creation failed"
}

加入通道

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
joinChannel() {
FABRIC_CFG_PATH=$PWD/../config/
ORG=$1
# 使用org X的证书信息
setGlobals $ORG
local rc=1
local COUNTER=1
## Sometimes Join takes time, hence retry
while [ $rc -ne 0 -a $COUNTER -lt $MAX_RETRY ] ; do
sleep $DELAY
set -x
peer channel join -b $BLOCKFILE >&log.txt
res=$?
{ set +x; } 2>/dev/null
let rc=$res
COUNTER=$(expr $COUNTER + 1)
done
cat log.txt
verifyResult $res "After $MAX_RETRY attempts, peer0.org${ORG} has failed to join channel '$CHANNEL_NAME' "
}

配置锚节点,具体的细节参见:scripts/setAnchorPeer.sh

1
2
3
4
setAnchorPeer() {
ORG=$1
docker exec cli ./scripts/setAnchorPeer.sh $ORG $CHANNEL_NAME
}

具体执行流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
FABRIC_CFG_PATH=${PWD}/configtx

## Create channel genesis block
infoln "Generating channel genesis block '${CHANNEL_NAME}.block'"
createChannelGenesisBlock

FABRIC_CFG_PATH=$PWD/../config/
BLOCKFILE="./channel-artifacts/${CHANNEL_NAME}.block"

## Create channel
infoln "Creating channel ${CHANNEL_NAME}"
createChannel
successln "Channel '$CHANNEL_NAME' created"

## Join all the peers to the channel
infoln "Joining org1 peer to the channel..."
joinChannel 1
infoln "Joining org2 peer to the channel..."
joinChannel 2

## Set the anchor peers for each org in the channel
infoln "Setting anchor peer for org1..."
setAnchorPeer 1
infoln "Setting anchor peer for org2..."
setAnchorPeer 2

successln "Channel '$CHANNEL_NAME' joined"

scripts/deployCC.sh

部署链码的细节。

打包链码

1
2
3
4
5
6
7
8
9
packageChaincode() {
set -x
peer lifecycle chaincode package ${CC_NAME}.tar.gz --path ${CC_SRC_PATH} --lang ${CC_RUNTIME_LANGUAGE} --label ${CC_NAME}_${CC_VERSION} >&log.txt
res=$?
{ set +x; } 2>/dev/null
cat log.txt
verifyResult $res "Chaincode packaging has failed"
successln "Chaincode is packaged"
}

安装链码(本地安装,未上链)

1
2
3
4
5
6
7
8
9
10
11
installChaincode() {
ORG=$1
setGlobals $ORG
set -x
peer lifecycle chaincode install ${CC_NAME}.tar.gz >&log.txt
res=$?
{ set +x; } 2>/dev/null
cat log.txt
verifyResult $res "Chaincode installation on peer0.org${ORG} has failed"
successln "Chaincode is installed on peer0.org${ORG}"
}

查询已安装的链码(本地)

1
2
3
4
5
6
7
8
9
10
11
12
queryInstalled() {
ORG=$1
setGlobals $ORG
set -x
peer lifecycle chaincode queryinstalled >&log.txt
res=$?
{ set +x; } 2>/dev/null
cat log.txt
PACKAGE_ID=$(sed -n "/${CC_NAME}_${CC_VERSION}/{s/^Package ID: //; s/, Label:.*$//; p;}" log.txt)
verifyResult $res "Query installed on peer0.org${ORG} has failed"
successln "Query installed successful on peer0.org${ORG} on channel"
}

批准链码

1
2
3
4
5
6
7
8
9
10
11
approveForMyOrg() {
ORG=$1
setGlobals $ORG
set -x
peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "$ORDERER_CA" --channelID $CHANNEL_NAME --name ${CC_NAME} --version ${CC_VERSION} --package-id ${PACKAGE_ID} --sequence ${CC_SEQUENCE} ${INIT_REQUIRED} ${CC_END_POLICY} ${CC_COLL_CONFIG} >&log.txt
res=$?
{ set +x; } 2>/dev/null
cat log.txt
verifyResult $res "Chaincode definition approved on peer0.org${ORG} on channel '$CHANNEL_NAME' failed"
successln "Chaincode definition approved on peer0.org${ORG} on channel '$CHANNEL_NAME'"
}

链码上链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
commitChaincodeDefinition() {
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
set -x
peer lifecycle chaincode commit -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "$ORDERER_CA" --channelID $CHANNEL_NAME --name ${CC_NAME} "${PEER_CONN_PARMS[@]}" --version ${CC_VERSION} --sequence ${CC_SEQUENCE} ${INIT_REQUIRED} ${CC_END_POLICY} ${CC_COLL_CONFIG} >&log.txt
res=$?
{ set +x; } 2>/dev/null
cat log.txt
verifyResult $res "Chaincode definition commit failed on peer0.org${ORG} on channel '$CHANNEL_NAME' failed"
successln "Chaincode definition committed on channel '$CHANNEL_NAME'"
}

查询已上链的链码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
queryCommitted() {
ORG=$1
setGlobals $ORG
EXPECTED_RESULT="Version: ${CC_VERSION}, Sequence: ${CC_SEQUENCE}, Endorsement Plugin: escc, Validation Plugin: vscc"
infoln "Querying chaincode definition on peer0.org${ORG} on channel '$CHANNEL_NAME'..."
local rc=1
local COUNTER=1
# continue to poll
# we either get a successful response, or reach MAX RETRY
while [ $rc -ne 0 -a $COUNTER -lt $MAX_RETRY ]; do
sleep $DELAY
infoln "Attempting to Query committed status on peer0.org${ORG}, Retry after $DELAY seconds."
set -x
peer lifecycle chaincode querycommitted --channelID $CHANNEL_NAME --name ${CC_NAME} >&log.txt
res=$?
{ set +x; } 2>/dev/null
test $res -eq 0 && VALUE=$(cat log.txt | grep -o '^Version: '$CC_VERSION', Sequence: [0-9]*, Endorsement Plugin: escc, Validation Plugin: vscc')
test "$VALUE" = "$EXPECTED_RESULT" && let rc=0
COUNTER=$(expr $COUNTER + 1)
done
cat log.txt
if test $rc -eq 0; then
successln "Query chaincode definition successful on peer0.org${ORG} on channel '$CHANNEL_NAME'"
else
fatalln "After $MAX_RETRY attempts, Query chaincode definition result on peer0.org${ORG} is INVALID!"
fi
}

链码初始化(update操作)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
chaincodeInvokeInit() {
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
set -x
fcn_call='{"function":"'${CC_INIT_FCN}'","Args":[]}'
infoln "invoke fcn call:${fcn_call}"
peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "$ORDERER_CA" -C $CHANNEL_NAME -n ${CC_NAME} "${PEER_CONN_PARMS[@]}" --isInit -c ${fcn_call} >&log.txt
res=$?
{ set +x; } 2>/dev/null
cat log.txt
verifyResult $res "Invoke execution on $PEERS failed "
successln "Invoke transaction successful on $PEERS on channel '$CHANNEL_NAME'"
}

链码查询(query操作)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
chaincodeQuery() {
ORG=$1
setGlobals $ORG
infoln "Querying on peer0.org${ORG} on channel '$CHANNEL_NAME'..."
local rc=1
local COUNTER=1
# continue to poll
# we either get a successful response, or reach MAX RETRY
while [ $rc -ne 0 -a $COUNTER -lt $MAX_RETRY ]; do
sleep $DELAY
infoln "Attempting to Query peer0.org${ORG}, Retry after $DELAY seconds."
set -x
peer chaincode query -C $CHANNEL_NAME -n ${CC_NAME} -c '{"Args":["queryAllCars"]}' >&log.txt
res=$?
{ set +x; } 2>/dev/null
let rc=$res
COUNTER=$(expr $COUNTER + 1)
done
cat log.txt
if test $rc -eq 0; then
successln "Query successful on peer0.org${ORG} on channel '$CHANNEL_NAME'"
else
fatalln "After $MAX_RETRY attempts, Query result on peer0.org${ORG} is INVALID!"
fi
}

具体的流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
## package the chaincode
packageChaincode

## Install chaincode on peer0.org1 and peer0.org2
infoln "Installing chaincode on peer0.org1..."
installChaincode 1
infoln "Install chaincode on peer0.org2..."
installChaincode 2

## query whether the chaincode is installed
queryInstalled 1

## approve the definition for org1
approveForMyOrg 1

## check whether the chaincode definition is ready to be committed
## expect org1 to have approved and org2 not to
checkCommitReadiness 1 "\"Org1MSP\": true" "\"Org2MSP\": false"
checkCommitReadiness 2 "\"Org1MSP\": true" "\"Org2MSP\": false"

## now approve also for org2
approveForMyOrg 2

## check whether the chaincode definition is ready to be committed
## expect them both to have approved
checkCommitReadiness 1 "\"Org1MSP\": true" "\"Org2MSP\": true"
checkCommitReadiness 2 "\"Org1MSP\": true" "\"Org2MSP\": true"

## now that we know for sure both orgs have approved, commit the definition
commitChaincodeDefinition 1 2

## query on both orgs to see that the definition committed successfully
queryCommitted 1
queryCommitted 2

## Invoke the chaincode - this does require that the chaincode have the 'initLedger'
## method defined
if [ "$CC_INIT_FCN" = "NA" ]; then
infoln "Chaincode initialization is not required"
else
chaincodeInvokeInit 1 2
fi

scripts/envVar.sh

为组织节点设置环境变量。

预先定义节点的证书信息:

  • orderer的CA证书
  • peer 1,2,3的TLS 证书
  • orderer admin的TLS私钥和签名证书
1
2
3
4
5
6
7
export CORE_PEER_TLS_ENABLED=true
export ORDERER_CA=${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
export PEER0_ORG1_CA=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export PEER0_ORG2_CA=${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
export PEER0_ORG3_CA=${PWD}/organizations/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/tls/ca.crt
export ORDERER_ADMIN_TLS_SIGN_CERT=${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/tls/server.crt
export ORDERER_ADMIN_TLS_PRIVATE_KEY=${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/tls/server.key

根据组织为其配置环境变量

  • peer admin 的 msp
  • peer LOCALMSPID
  • peer TLS 根证书文件
  • peer节点的ip
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# Set environment variables for the peer org
setGlobals() {
local USING_ORG=""
if [ -z "$OVERRIDE_ORG" ]; then
USING_ORG=$1
else
USING_ORG="${OVERRIDE_ORG}"
fi
infoln "Using organization ${USING_ORG}"
if [ $USING_ORG -eq 1 ]; then
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG1_CA
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=localhost:7051
elif [ $USING_ORG -eq 2 ]; then
export CORE_PEER_LOCALMSPID="Org2MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG2_CA
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
export CORE_PEER_ADDRESS=localhost:9051

elif [ $USING_ORG -eq 3 ]; then
export CORE_PEER_LOCALMSPID="Org3MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG3_CA
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org3.example.com/users/Admin@org3.example.com/msp
export CORE_PEER_ADDRESS=localhost:11051
else
errorln "ORG Unknown"
fi

if [ "$VERBOSE" == "true" ]; then
env | grep CORE
fi
}

scripts/setAnchorPeer.sh

锚点被编码到 “配置更新交易”中,以实现不同组织的peer之间的通信并发现通道的所有活跃参与者。

修改配置,生成新的区块,以附加锚节点,具体的细节参见:scripts/configUpdate.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# NOTE: this must be run in a CLI container since it requires jq and configtxlator 
createAnchorPeerUpdate() {
infoln "Fetching channel config for channel $CHANNEL_NAME"
fetchChannelConfig $ORG $CHANNEL_NAME ${CORE_PEER_LOCALMSPID}config.json

infoln "Generating anchor peer update transaction for Org${ORG} on channel $CHANNEL_NAME"

if [ $ORG -eq 1 ]; then
HOST="peer0.org1.example.com"
PORT=7051
elif [ $ORG -eq 2 ]; then
HOST="peer0.org2.example.com"
PORT=9051
elif [ $ORG -eq 3 ]; then
HOST="peer0.org3.example.com"
PORT=11051
else
errorln "Org${ORG} unknown"
fi

set -x

# 修改配置以附加锚节点
# Modify the configuration to append the anchor peer
jq '.channel_group.groups.Application.groups.'${CORE_PEER_LOCALMSPID}'.values += {"AnchorPeers":{"mod_policy": "Admins","value":{"anchor_peers": [{"host": "'$HOST'","port": '$PORT'}]},"version": "0"}}' ${CORE_PEER_LOCALMSPID}config.json > ${CORE_PEER_LOCALMSPID}modified_config.json
{ set +x; } 2>/dev/null

# Compute a config update, based on the differences between
# {orgmsp}config.json and {orgmsp}modified_config.json, write
# it as a transaction to {orgmsp}anchors.tx
createConfigUpdate ${CHANNEL_NAME} ${CORE_PEER_LOCALMSPID}config.json ${CORE_PEER_LOCALMSPID}modified_config.json ${CORE_PEER_LOCALMSPID}anchors.tx
}

将更新的区块上链

1
2
3
4
5
6
7
updateAnchorPeer() {
peer channel update -o orderer.example.com:7050 --ordererTLSHostnameOverride orderer.example.com -c $CHANNEL_NAME -f ${CORE_PEER_LOCALMSPID}anchors.tx --tls --cafile "$ORDERER_CA" >&log.txt
res=$?
cat log.txt
verifyResult $res "Anchor peer update failed"
successln "Anchor peer set for org '$CORE_PEER_LOCALMSPID' on channel '$CHANNEL_NAME'"
}

具体流程:

1
2
3
4
5
6
7
ORG=$1
CHANNEL_NAME=$2
setGlobalsCLI $ORG

createAnchorPeerUpdate

updateAnchorPeer

scripts/configUpdate.sh

更新区块链配置信息。

获取通道配置信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# fetchChannelConfig <org> <channel_id> <output_json>
# Writes the current channel config for a given channel to a JSON file
# NOTE: this must be run in a CLI container since it requires configtxlator
fetchChannelConfig() {
ORG=$1
CHANNEL=$2
OUTPUT=$3

setGlobals $ORG

infoln "Fetching the most recent configuration block for the channel"
set -x
peer channel fetch config config_block.pb -o orderer.example.com:7050 --ordererTLSHostnameOverride orderer.example.com -c $CHANNEL --tls --cafile "$ORDERER_CA"
{ set +x; } 2>/dev/null

infoln "Decoding config block to JSON and isolating config to ${OUTPUT}"
set -x
configtxlator proto_decode --input config_block.pb --type common.Block | jq .data.data[0].payload.data.config >"${OUTPUT}"
{ set +x; } 2>/dev/null
}

创建配置更新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# createConfigUpdate <channel_id> <original_config.json> <modified_config.json> <output.pb>
# Takes an original and modified config, and produces the config update tx
# which transitions between the two
# NOTE: this must be run in a CLI container since it requires configtxlator
createConfigUpdate() {
CHANNEL=$1
ORIGINAL=$2
MODIFIED=$3
OUTPUT=$4

set -x
configtxlator proto_encode --input "${ORIGINAL}" --type common.Config >original_config.pb
configtxlator proto_encode --input "${MODIFIED}" --type common.Config >modified_config.pb
configtxlator compute_update --channel_id "${CHANNEL}" --original original_config.pb --updated modified_config.pb >config_update.pb
configtxlator proto_decode --input config_update.pb --type common.ConfigUpdate >config_update.json
echo '{"payload":{"header":{"channel_header":{"channel_id":"'$CHANNEL'", "type":2}},"data":{"config_update":'$(cat config_update.json)'}}}' | jq . >config_update_in_envelope.json
configtxlator proto_encode --input config_update_in_envelope.json --type common.Envelope >"${OUTPUT}"
{ set +x; } 2>/dev/null
}

peer的admin对配置更新签名

1
2
3
4
5
6
7
8
9
10
11
# signConfigtxAsPeerOrg <org> <configtx.pb>
# Set the peerOrg admin of an org and sign the config update
signConfigtxAsPeerOrg() {
ORG=$1
CONFIGTXFILE=$2
setGlobals $ORG
set -x
peer channel signconfigtx -f "${CONFIGTXFILE}"
{ set +x; } 2>/dev/null
}

增加新节点

  • addOrg3.sh为org3 生成证书

org3-scripts/joinChannel.sh

将org3加入通道。

加入通道

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
joinChannel() {
ORG=$1
local rc=1
local COUNTER=1
## Sometimes Join takes time, hence retry
while [ $rc -ne 0 -a $COUNTER -lt $MAX_RETRY ] ; do
sleep $DELAY
set -x
peer channel join -b $BLOCKFILE >&log.txt
res=$?
{ set +x; } 2>/dev/null
let rc=$res
COUNTER=$(expr $COUNTER + 1)
done
cat log.txt
verifyResult $res "After $MAX_RETRY attempts, peer0.org${ORG} has failed to join channel '$CHANNEL_NAME' "
}

更新锚节点

1
2
3
4
setAnchorPeer() {
ORG=$1
scripts/setAnchorPeer.sh $ORG $CHANNEL_NAME
}

整体流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
setGlobalsCLI 3
BLOCKFILE="${CHANNEL_NAME}.block"

echo "Fetching channel config block from orderer..."
set -x
peer channel fetch 0 $BLOCKFILE -o orderer.example.com:7050 --ordererTLSHostnameOverride orderer.example.com -c $CHANNEL_NAME --tls --cafile "$ORDERER_CA" >&log.txt
res=$?
{ set +x; } 2>/dev/null
cat log.txt
verifyResult $res "Fetching config block from orderer has failed"

infoln "Joining org3 peer to the channel..."
joinChannel 3

infoln "Setting anchor peer for org3..."
setAnchorPeer 3

successln "Channel '$CHANNEL_NAME' joined"
successln "Org3 peer successfully added to network"

org3-scripts/updateChannelConfig.sh

更新区块链配置信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# imports
. scripts/envVar.sh
. scripts/configUpdate.sh
. scripts/utils.sh

infoln "Creating config transaction to add org3 to network"

# Fetch the config for the channel, writing it to config.json
fetchChannelConfig 1 ${CHANNEL_NAME} config.json

# Modify the configuration to append the new org
set -x
jq -s '.[0] * {"channel_group":{"groups":{"Application":{"groups": {"Org3MSP":.[1]}}}}}' config.json ./organizations/peerOrganizations/org3.example.com/org3.json > modified_config.json
{ set +x; } 2>/dev/null

# Compute a config update, based on the differences between config.json and modified_config.json, write it as a transaction to org3_update_in_envelope.pb
createConfigUpdate ${CHANNEL_NAME} config.json modified_config.json org3_update_in_envelope.pb

infoln "Signing config transaction"
signConfigtxAsPeerOrg 1 org3_update_in_envelope.pb

infoln "Submitting transaction from a different peer (peer0.org2) which also signs it"
setGlobals 2
set -x
peer channel update -f org3_update_in_envelope.pb -c ${CHANNEL_NAME} -o orderer.example.com:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "$ORDERER_CA"
{ set +x; } 2>/dev/null

successln "Config transaction to add org3 to network submitted"

使用Fabric CA生成证书

使用Fabric CA生成组织节点证书。

以Fabric CA 生成 Org1证书为例:

  • enroll admin 生成org1 tls证书
  • 为org1 msp 创建 config.yaml
  • register org1admin,user1,peer0
  • 生成peer0 msp证书
  • 生成peer0-tls证书
  • 生成user1 org1admin msp证书
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
function createOrg1() {
infoln "Enrolling the CA admin"
mkdir -p organizations/peerOrganizations/org1.example.com/

export FABRIC_CA_CLIENT_HOME=${PWD}/organizations/peerOrganizations/org1.example.com/

set -x
fabric-ca-client enroll -u https://admin:adminpw@localhost:7054 --caname ca-org1 --tls.certfiles "${PWD}/organizations/fabric-ca/org1/tls-cert.pem"
{ set +x; } 2>/dev/null

echo 'NodeOUs:
Enable: true
ClientOUIdentifier:
Certificate: cacerts/localhost-7054-ca-org1.pem
OrganizationalUnitIdentifier: client
PeerOUIdentifier:
Certificate: cacerts/localhost-7054-ca-org1.pem
OrganizationalUnitIdentifier: peer
AdminOUIdentifier:
Certificate: cacerts/localhost-7054-ca-org1.pem
OrganizationalUnitIdentifier: admin
OrdererOUIdentifier:
Certificate: cacerts/localhost-7054-ca-org1.pem
OrganizationalUnitIdentifier: orderer' > "${PWD}/organizations/peerOrganizations/org1.example.com/msp/config.yaml"

infoln "Registering peer0"
set -x
fabric-ca-client register --caname ca-org1 --id.name peer0 --id.secret peer0pw --id.type peer --tls.certfiles "${PWD}/organizations/fabric-ca/org1/tls-cert.pem"
{ set +x; } 2>/dev/null

infoln "Registering user"
set -x
fabric-ca-client register --caname ca-org1 --id.name user1 --id.secret user1pw --id.type client --tls.certfiles "${PWD}/organizations/fabric-ca/org1/tls-cert.pem"
{ set +x; } 2>/dev/null

infoln "Registering the org admin"
set -x
fabric-ca-client register --caname ca-org1 --id.name org1admin --id.secret org1adminpw --id.type admin --tls.certfiles "${PWD}/organizations/fabric-ca/org1/tls-cert.pem"
{ set +x; } 2>/dev/null

infoln "Generating the peer0 msp"
set -x
fabric-ca-client enroll -u https://peer0:peer0pw@localhost:7054 --caname ca-org1 -M "${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/msp" --csr.hosts peer0.org1.example.com --tls.certfiles "${PWD}/organizations/fabric-ca/org1/tls-cert.pem"
{ set +x; } 2>/dev/null

cp "${PWD}/organizations/peerOrganizations/org1.example.com/msp/config.yaml" "${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/msp/config.yaml"

infoln "Generating the peer0-tls certificates"
set -x
fabric-ca-client enroll -u https://peer0:peer0pw@localhost:7054 --caname ca-org1 -M "${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls" --enrollment.profile tls --csr.hosts peer0.org1.example.com --csr.hosts localhost --tls.certfiles "${PWD}/organizations/fabric-ca/org1/tls-cert.pem"
{ set +x; } 2>/dev/null

cp "${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/tlscacerts/"* "${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt"
cp "${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/signcerts/"* "${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.crt"
cp "${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/keystore/"* "${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.key"

mkdir -p "${PWD}/organizations/peerOrganizations/org1.example.com/msp/tlscacerts"
cp "${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/tlscacerts/"* "${PWD}/organizations/peerOrganizations/org1.example.com/msp/tlscacerts/ca.crt"

mkdir -p "${PWD}/organizations/peerOrganizations/org1.example.com/tlsca"
cp "${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/tlscacerts/"* "${PWD}/organizations/peerOrganizations/org1.example.com/tlsca/tlsca.org1.example.com-cert.pem"

mkdir -p "${PWD}/organizations/peerOrganizations/org1.example.com/ca"
cp "${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/msp/cacerts/"* "${PWD}/organizations/peerOrganizations/org1.example.com/ca/ca.org1.example.com-cert.pem"

infoln "Generating the user msp"
set -x
fabric-ca-client enroll -u https://user1:user1pw@localhost:7054 --caname ca-org1 -M "${PWD}/organizations/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp" --tls.certfiles "${PWD}/organizations/fabric-ca/org1/tls-cert.pem"
{ set +x; } 2>/dev/null

cp "${PWD}/organizations/peerOrganizations/org1.example.com/msp/config.yaml" "${PWD}/organizations/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp/config.yaml"

infoln "Generating the org admin msp"
set -x
fabric-ca-client enroll -u https://org1admin:org1adminpw@localhost:7054 --caname ca-org1 -M "${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp" --tls.certfiles "${PWD}/organizations/fabric-ca/org1/tls-cert.pem"
{ set +x; } 2>/dev/null

cp "${PWD}/organizations/peerOrganizations/org1.example.com/msp/config.yaml" "${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/config.yaml"
}

参考:

https://github.com/hyperledger/fabric-samples