1. 链码部署脚本思路
上章中分析了test-netwok的网络启动脚本,其中包含了链码部署部分,我们通过分析test-netwok的deployCC来梳理链码部署思路,进而编写freenet网络链码部署脚本。
pakeageChaincode()
: 将链码链码打包为既定的格式(tar.gz文件);installChaincode()
: 将打包好的链码安装在需要的peer节点上;queryInstalled()
: 查询链码是否已经安装至peer节点上;approveForMyOrg():
多个成员对链码进行批准,每个peer上的节点都应该是一致的;checkCommitReadiness()
:来审核一个链码是否符合通道的标准,也就是是否达到既定的多成员批准的设定;commitChaincodeDefinition()
:将链码安装至通道上;queryCommitted()
:通道的各组织查询是否已经被提交至通道上;
Fabric2.X版本对链码的生命周期做了优化,目前的链码逻辑较为清晰易懂,通道上各个组织按照一个代码逻辑去运行业务,每一步都蛮合理和易懂。
2. 链码部署脚本编写
链码部署脚本整体逻辑比较简单,test-network中脚本作为官方案例肯定是相当的完善,所以对shell脚本不熟悉看起来就会比较吃力,其中部分Shell脚本函数个人也仅将他作为一个整体去理解,以理解大体思路和调通链码部署脚本为最终目的,修改编写也非常容易出错。
2.1 链码打包与安装
链码脚本的公共参数配置,CC_COLL_CONFIG暂时还不需要。
CHANNEL_NAME="$1"
DELAY="$2"
MAX_RETRY="$3"
VERBOSE="$4"
CC_NAME="$5"
CC_VERSION="$6"
CC_END_POLICY="$9"
#CC_COLL_CONFIG="${10}"
CC_INIT_FCN="${11}"
CC_SRC_LANGUAGE="${12}"
: ${CHANNEL_NAME:="rentsign"}
: ${DELAY:="10"}
: ${MAX_RETRY:="3"}
: ${VERBOSE:="false"}
: ${CC_NAME:="freerent"}
: ${CC_VERSION:="v1"}
: ${CC_SEQUENCE:="1"}
# 结合cliup 工作目录
#: ${CC_COLL_CONFIG:="./network/artifacts/collections_config.json"}
: ${CC_INIT_FCN:="init"}
: ${CC_SRC_LANGUAGE:="golang"}
CC_RUNTIME_LANGUAGE=golang
# import utils
. scripts/env-var.sh
infoln "Deploy chaincode in $DOMAIN_OF_NETWORK network"
debugln " - CHANNEL_NAME: '$CHANNEL_NAME'"
debugln " - CC_NAME: '$CC_NAME'"
debugln " - CC_SRC_PATH: '$CC_SRC_PATH'"
debugln " - CC_SRC_LANGUAGE: '$CC_SRC_LANGUAGE'"
debugln " - CC_VERSION: '$CC_VERSION'"
#debugln " - CC_COLL_CONFIG: '$CC_COLL_CONFIG'"
## Install chaincode
infoln "Installing chaincode on supervisor"
for peer in 0 1 2 ; do
installChaincode supervisor $peer
done
infoln "Installing chaincode on rentalcrop"
for peer in 0 1; do
installChaincode rentalcrop $peer
done
infoln "Installing chaincode on agency"
for peer in 0 1; do
installChaincode agency $peer
done
## query whether the chaincode is installed
queryInstalled supervisor 0
queryInstalled rentalcrop 0
queryInstalled agency 0
## approve the definition for dispatcher
approveForMyOrg supervisor 0
## check whether the chaincode definition is ready to be committed
checkCommitReadiness supervisor "\"SupervisorMSP\": true" "\"RentalcropMSP\": false" "\"AgencyMSP\": false"
checkCommitReadiness rentalcrop "\"SupervisorMSP\": true" "\"RentalcropMSP\": false" "\"AgencyMSP\": false"
checkCommitReadiness agency "\"SupervisorMSP\": true" "\"RentalcropMSP\": false" "\"AgencyMSP\": false"
## now approve also for aggregator
approveForMyOrg rentalcrop 0
链码打包:--path
为链码的位置,这里和官方脚本有所不同,注意是docker容器中的映射位置,因为该脚本的运行在cliup容器中;--label
:用于指定链码标签,在安装后用来识别链码;--lang
标志用于指定链码语言。
packageChaincode() {
set -x
go env -w GOPROXY=https://goproxy.io,direct
go env -w GO111MODULE=on
peer lifecycle chaincode package ${CC_NAME}.tar.gz --path ${CC_SRC_PATH} --lang ${CC_SRC_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"
}
**链码安装:**这里一共对7个节点进行链码的安装,脚本函数有两个参数,分别为组织名称和节点名称。
# installChaincode ORG PEER
installChaincode() {
ORG=$1
PEER=$2
setGlobals $ORG $PEER
peer lifecycle chaincode install /opt/gopath/src/github.com/hyperledger/fabric/${CC_NAME}.tar.gz >&log.txt
res=$?
{ set +x; } 2>/dev/nulls
cat log.txt
verifyResult $res "Chaincode installation on peer$PEER.${ORG_NAME} has failed"
successln "Chaincode is installed on peer$PEER.${ORG_NAME}"
}
## Install chaincode
infoln "Installing chaincode on supervisor"
for peer in 0 1 2 ; do
installChaincode supervisor $peer
done
infoln "Installing chaincode on rentalcrop"
for peer in 0 1; do
installChaincode rentalcrop $peer
done
infoln "Installing chaincode on agency"
for peer in 0 1; do
installChaincode agency $peer
done
2.2 链码查询与批准
在进行链码的批准之前可以对链码安装的状态进行查询,批准链码主要是为了将链码的内容、版本、背书策略、数据集合等进行一个共识。一般情况通道上的大部分成员批准链码,链码就可以被提交至通道上。
approveForMyOrg()
函数中通过setGlobals
函数将环境变量设置为对应批准的节点;--sequence
参数是一个整数,用于跟踪链码已定义或更新的次数;
# queryInstalled ORG PEER
queryInstalled() {
ORG=$1
PEER=$2
peer lifecycle chaincode queryinstalled >&log.txt
res=$?
{ set +x; } 2>/dev/null
cat log.txt
}
# approveForMyOrg ORG PEER
approveForMyOrg() {
ORG=$1
PEER=$2
setOrdererGlobals 0
setGlobals $ORG $PEER
peer lifecycle chaincode approveformyorg \
-o "$CORE_ORDERER_ADDRESS" \
--tls true\
--cafile $CORE_ORDERER_TLS_ROOTCERT_FILE \
--channelID $CHANNEL_NAME \
--name ${CC_NAME} \
--version ${CC_VERSION} \
--package-id ${PACKAGE_ID} \
--sequence ${CC_SEQUENCE} \
res=$?
verifyResult $res "Chaincode definition approved on peer$PEER.${ORG_NAME} failed"
successln "Chaincode definition approved on peer$PEER.${ORG_NAME}"
}
## query whether the chaincode is installed
queryInstalled supervisor 0
queryInstalled rentalcrop 0
queryInstalled agency 0
## approve the definition for dispatcher
approveForMyOrg supervisor 0
在supervisor组织批准完毕后,我们可以对链码在通道上的状态进行查询,查询哪些组织对什么样的链码进行了批准,查询会返回一个json,用来告知通道中哪些组织已经批
准了。后续对Rentalcrop组织和AgencyMSP组织步骤都一样。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ovuvmsvr-1679127201138)(https://secure2.wostatic.cn/static/j7MEiTSBmeUA2RG5z3tbxH/image.png?auth_key=1679125851-npXxabAFgaCJ1t4sgtRfDq-0-0cdfc9cd4cc0908d0bad1f16dcab322c)]
checkCommitReadiness() {
ORG=$1
shift 1
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 -le $MAX_RETRY ]; do
if [ $COUNTER -gt 1 ]; then sleep $DELAY; fi
set -x
peer lifecycle chaincode checkcommitreadiness \
--channelID $CHANNEL_NAME \
--name ${CC_NAME} \
--version ${CC_VERSION} \
--sequence ${CC_SEQUENCE} \
--signature-policy "${CC_END_POLICY}" \
--output json >&log.txt
res=$?
{ set +x; } 2>/dev/null
let rc=0
for var in "$@"; do
grep "$var" log.txt &>/dev/null || let rc=1
done
COUNTER=$(expr $COUNTER + 1)
done
cat log.txt
if test $rc -eq 0; then
else
fi
}
# 当前仅supervisor组织批准了。
checkCommitReadiness supervisor "\"SupervisorMSP\": true" "\"RentalcropMSP\": false" "\"AgencyMSP\": false"
checkCommitReadiness rentalcrop "\"SupervisorMSP\": true" "\"RentalcropMSP\": false" "\"AgencyMSP\": false"
checkCommitReadiness agency "\"SupervisorMSP\": true" "\"RentalcropMSP\": false" "\"AgencyMSP\": false"
经过三个组织的查询与批准后:
2.3 链码提交与查看
在满足链码安装的策略条件下就可以对链码在通道上对链码的定义进行确认,对链码定义确认会生成一笔交易,交易就需要经过排序与落块,最后一步就是各个节点到账本中去查询链码定义交易是否成功。函数的逻辑和参数含义上面基本都已经涉及,
commitChaincodeDefinition() {
setOrdererGlobals 0
parsePeerConnectionParameters $@
res=$?
verifyResult $res "Invoke transaction failed due to uneven number of peer and org parameters "
set -x
peer lifecycle chaincode commit \
-o "$CORE_ORDERER_ADDRESS" \
--tls \
--cafile $CORE_ORDERER_TLS_ROOTCERT_FILE \
--channelID $CHANNEL_NAME \
--name $CC_NAME \
$PEER_CONN_PARMS \
--version ${CC_VERSION} \
--sequence ${CC_SEQUENCE} \
--signature-policy "${CC_END_POLICY}" \
--waitForEventTimeout 300s >&log.txt
res=$?
{ set +x; } 2>/dev/null
cat log.txt
verifyResult $res "Chaincode definition commit failed on peer0.${ORG_NAME} failed"
successln "Chaincode definition committed"
}
# queryCommitted ORG
queryCommitted() {
ORG=$1
setGlobals $ORG 0
EXPECTED_RESULT="Version: ${CC_VERSION}, Sequence: ${CC_SEQUENCE}, Endorsement Plugin: escc, Validation Plugin: vscc"
infoln "Querying chaincode definition on peer0.${ORG_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 -le $MAX_RETRY ]; do
if [ $COUNTER -gt 1 ]; then sleep $DELAY; fi
infoln "Attempting to Query committed status on peer0.${ORG_NAME}, 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 successfully on peer0.${ORG_NAME}"
else
fatalln "After $MAX_RETRY attempts, Query chaincode definition result on peer0.${ORG_NAME} is INVALID"
fi
}
## now that we know for sure both orgs have approved, commit the definition
commitChaincodeDefinition supervisor 0 rentalcrop 0 agency 0
## query on both orgs to see that the definition committed successfully
queryCommitted supervisor
queryCommitted rentalcrop
queryCommitted agency
3.当前应用完整目录
当前一个基础的多节点网络已经部署完成,当前项目的主要目录如下,这里主要梳理一下network文件下目录。
network中的目录结构布局和test-network有点不同,可能设计的不是很合理,但最核心的还是docker-compose中对于目录的映射,比较容易出错,docker不会出错,路径文件不存在那就是不存在,不管是路径错误还是映射路径错误。