fabric源码解析28——官网案例部署

fabric源码解析28——官网案例部署

概述

从这篇文章开始,系列文章进入实际操作阶段。根据实际的操作,也可以细细的体会操作与代码实现间的相互呼应。官网案例的部署多是一键操作,但为了说明问题,文中尽量手工一步步的实现官网案例的部署。

下载部署环境

查看官方文档的Getting Started部分,Install Prerequisites部分讲的是安装案例部署时系统所必须有的软件和环境,就是安装一些软件而已,这里不做说明。其余需要下载的有:1.fabric-samples,包含要部署的chaincode例子。2.运行的docker镜像、原始程序(peer/orderer/configtxgen等)。

官方文档给出了下载第1点内容(即fabric-samples)的一键式傻瓜操作:git clone -b master https://github.com/hyperledger/fabric-samples.git,如果安装了Prerequisites部分的git,则直接执行此命令即可,若没有,也可手动的用浏览器打开 https://github.com/hyperledger/fabric-samples.git 这个github网址,手工下载即可。下载后这里形成一个fabric-samples文件夹。

官方文档给出了下载第2点内容(即docker镜像、预编译程序)的一键式的傻瓜式操作:curl -sSL https://goo.gl/6wtTN5 | bash -s 1.1.0-preview,这个命令其实就是定位到 https://goo.gl/6wtTN5 ,这个网址指向的是一个脚本,然后使用-s指定版本号为1.1.0-preview,并使用bash执行这个脚本。因为墙的存在, https://goo.gl/6wtTN5 ,这个网址一般打不开,且因版本间的官方文档的变化,每个版本的网址的6wtTN5部分可能都不一样。其实,而这个脚本本身是存在于源码目录中的,我们可以手工下载。另外,下文在部署的时候若要顺利自动执行脚本,对某些文件所在路径是有要求的,因此这里的下载的第2点内容均要下载到fabric-samples文件夹中(主要是bin目录要放到这个文件夹中)。

在github(github没有被墙)上直接搜索fabric,然后找hyperledger/fabric,点击进去,自行选择版本,然后找script目录下的bootstrap-x.x.x.sh,这个文件其实就是上述网址指向的脚本,x代表版本数字,这里以1.0.4版本为例。该脚本文件各版本间框架基本保持不变,只是有若干变量值不同而已,如版本号VERSION。打开,可以尝试直接执行,但是笔者在试的时候,总会出错,比如下载docker镜像说找不到。因此可以根据这个脚本自己手工的下载。该脚本里面有两个比较重要的变量,一个是版本号VERSION/CA_VERSION,一个是适用的系统ARCH/MARCH。VERSION是手工指定的,如脚本里的VERSION=${1:-1.0.4},即版本号为1.0.4,自己可以根据自己的版本进行指定,如改为1.0.0(但官网上相应版本的资源是否还存在则不确定),也即下文命令中的凡${VERSION}处都直接替换成1.0.4。CA_VERSION的值同VERSION。ARCH的值的确定方法脚本里也直接给出了,ARCH=$(echo "$(uname...)),自行将echo "$(uname...)放入自己的系统的命令行指向一下就能得到具体的值,比如在笔者这里输出的为linux-amd64,即下文命令中所有${ARCH}处都直接替换成linux-amd64即可。MARCH同ARCH一样的操作方法,笔者系统的MARCH为x86_64。如此,变量值确定了,就可以手工执行脚本中的命令了。

脚本主要执行了三项任务,均可手工执行:

  • curl https://nexus.hyperledger.org/....tar.gz | tar xz下载与系统适用的二进制程序压缩包并解压,如peer,orderer,configtxgen,configtxlator和脚本。这里可以替换掉命令中的变量值之后,将https://nexus.hyperledger.org/....tar.gz这个网址(其实已经是一个指向tar.gz压缩包文件的下载地址了)直接粘贴到下载工具中自行下载,然后自己解压即可。这里形成了一个bin目录,把这个bin目录放入fabric-samples文件夹下即可。这些都是编译好的现成的程序,而关于如何手工编译这些程序,则在下文详述。
  • dockerFabricPull ${FABRIC_TAG},下载所有与hyperledger/fabric有关的docker镜像。在这个函数中,循环执行了docker pull hyperledger/fabric-$IMAGES:$FABRIC_TAGdocker tag hyperledger/fabric...,即先下载镜像,后将镜像改为指定的标签,改标签这一步可做可不做。$IMAGES变量分别是for IMAGES in peer orderer couchdb ccenv javaenv kafka zookeeper tools;处指定的这些值,也即要循环下载peer、orderer、couchdb…这些镜像,$FABRIC_TAG则是$MARCH-$VERSION的组合,在笔者的系统中即为x86_64-1.0.4,也即手动逐条执行docker pull hyperledger/fabric-peer:x86_64-1.0.4docker pull hyperledger/fabric-orderer:x86_64-1.0.4…即可。这里需要说一句的是笔者遇到的情况,同样的docker pull命令,有时有执行失败的说没有指定的镜像的错误,各位若发生类似错误,可以多试几次。
  • dockerCaPull ${CA_TAG},下载CA镜像,原理与上一步相同。

如此,即可手工下载官网案例部署所需的程序、脚本、镜像。需要强调的是,因为fabric项目正在开发过程中,因此版本号也在不断的变,有些版本对应的资源可能在调整中无法下载,可能已经删除等等,因此版本号的值尽量选择比较新的,或fabric在github中有所呈现的版本号。

编译peer、orderer、configtxgen等程序

fabric源码涉及的技术较多 + 编译期间会存在墙的问题 + 编译的环节很多 + 每个人的系统具体情况都一样,基于此,不讲具体的编译步骤,而是直接讲项目的Makefile构成,至于具体的编译过程,若读懂Makefile,则一切都不再是问题。这里假设各位对Makefile有最简单的了解,比如知道Makefile的基本书写规则以及make依据判定是否重新编译的规则(推荐《跟我一起写Makefile》这本书)。

表28-1 Makefile相关目录文件

文件 说明
Makefile 主编译文件,make执行的主体。
docker-env.mk Makefile直接包含进的一个子makefile文件,用于定义编译期间使用到的关于docker一系列数据。
scripts/ 目录,包含Makefile编译时调用到的一些shell脚本。
images/ 目录,包含Makefile编译时使用到的一些用于生成Dockerfile文件的Dockerfile.in文件,Dockerfile文件则是用于生成各个docker镜像的模板文件。
gotools/ 目录,包含一个子Makefile文件,该子Makefile文件用于安装Makefile编译时使用到的各种第三方的go工具,如ginkgo,gocov,govendor等等。

首先说明的一点,不要被fabric冗长的看似复杂的Makefile糊弄了,其实一切都是万变不离其宗,依然遵循target ... : prerequisites ...这个基本规则。Makefile文件开头的注释中,罗列了所有的target,下文以编译peer结点为例,即make peer命令,其余target的形式与peer如出一辙。在具体编译peer的过程中,target的依赖项可以是另一个子target,而子target又可以有子target,因此整个过程是一个树形发散的过程,因此为了标记清楚编译执行的顺序路径,这里用xxx.xxx.xxx的形式标记每个target,如1表示顶层的target,1.1就表示1下面的第一个子target,1.2就表示1下面的第二个子target,1.1.1则表示1.1下面的第一个子target,如此类推。

先整体说一下Makefile,以include docker-env.mk为线,可将Makefile分为上下两部分:(1)上面部分,无论是=,?=,+=,:=,均是对一个下文会使用到的变量的赋值而已,其中个中差别,其自行搜索。include docker-env.mk即在Makefile中包含了docker-env.mk文件,从名字上即可看出该文件定义了一系列运行docker命令时的环境变量数据,其中对.dummy-xxx文件的设计初衷有大段解释,大概意思就是基于docker容器编译输出的文件并不算是标准的系统文件(主要在文件时间上会和实际系统中参与编译的文件不符),因此需要对容器输出的文件创建对应的.dummy-xxx文件,让.dummy-xxx文件真正参与make的编译。另外,在Makefile中到处使用的$DUMMY变量也在docker-env.mk中定义。(2)下面部分,遵循target ... : prerequisites ...,是makefile的执行主体。下面make peer的执行路径进行图示:

fabric源码解析28——官网案例部署

Makefile真正执行的一条条命令基本上均集中在末端项(即图中带※符号的框所代表的项),具体所做的事情请自行查看学习,不再详述。这些命令根据依赖的前后顺序,因此执行也会有先后顺序,如1.1肯定比1.2先执行,1.1.5也肯定比1.2.1先执行,以此类推。

部署

切换到fabric-samples/first-network目录下,傻瓜式脚本自动的操作(由于可能存在系统权限问题,最好以sudo执行所有的脚本、命令):

  1. 执行byfn.sh脚本,生成启动运行所需的必要配置或数据,如channel.tx、gensisblock等。sudo ./first-network/byfn.sh -m generate,这里可以加入一些flag:(1)-c channelDIY,指定channel的ID为channelDIY,若不指定默认为mychannel。(2)-t 10000,指定CLI容器自动退出的时间,若不指定默认为20000秒。(3)-s couchdb,指定使用的数据库为couchdb,若不指定默认为goleveldb。其他的参数可参看byfn.sh中的printHelp()函数中的打印(即执行./first-network/byfn.sh --help显示的内容)。
  2. 执行byfn.sh脚本,启动运行环境。sudo ./byfn.sh -m up,第一次执行这一步的时候,需要确保自己的电脑是联网状态,因为启动过程中仍然会下载一些东西,如fabric-ca、fabric-baseos镜像。但是之后再次执行时,由于该下载的都已经下载过了,因此电脑是断网状态也无所谓。

首先关注byfn这个名字,是Build your first network的首字母缩写,寓意很清晰。byfn.sh脚本具体都做了哪些工作,分析这个脚本即可,然后我们也可以顺着这个脚本手工执行。byfn.sh开篇处有两个export,指定了脚本执行过程中寻找程序文件的路径(./和../bin),也指定了fabric的配置路径FABRIC_CFG_PATH的值(./)。文件分为3部分。首先,第1部分是定义各种操作的函数体,如networkUp、networkDown、generateCerts等。第2部分从while getopts "h?m:c:t:d:f:s:" opt; do循环开始,分析执行该脚本时携带的各种参数,如上文第1点分析的那些参数。第3部分处于脚本最底部,从if [ "${MODE}" == "up" ]; then开始,根据第2步所确定的MODE值,在每个if分支中执行up、down还是generate操作,这里只看generate操作对应的generateCertsreplacePrivateKeygenerateChannelArtifacts三个函数和up操作对应的networkUp函数。

Crypto Generator

generateCerts函数主要使用了cryptogen工具。在检查cryptogen工具和crypto-config是否存在后(在脚本开始时export指定的路径中寻找),执行了cryptogen generate --config=./crypto-config.yaml,即使用bin/cryptogen工具用于使用first-network/crypto-config.yaml配置文件生成fabric网络拓扑结构和各个组织的根证书。这个网络拓扑结构,指的是你这个区域链初始化时有哪些组织,组织的域名是什么,组织下有多少实体成员。并相应的生成每个组织及组织下实体所使用的身份证书、tls证书。最终在first-network下生成了一个crypto-config文件夹。

crypto-config.yaml

以组织为单位,如OrdererOrgs、PeerOrgs。在每个组织下可配置一个个实体,这个实体的意思同网络中的一个入口,或者说一个结点的意思,如OrdererOrgs下配置多个orderer结点:

  • Name - 组织名
  • Domain - 域名
  • Specs - 详细说明,更细致的对每个实体名字的配置
    • Hostname - 主机名,必填(以Hostname.Domain格式形成实体名)
    • CommonName - 主机通用名,可选,若定义,将覆盖上面Hostname.Domain格式的实体名
  • Template - 模板,算是批量的对多个实体的名字进行配置
    • Count - 实体的数量,如5
    • Start - 从哪个数开始,若不定义,则默认从0开始
    • Hostname - 主机名,若这里定义了,Count和Start定义将无效而只配置此名
  • User - 普通用户,该配置区域只针对PeerOrgs组织
    • Count - 普通用户的数量,即Specs配置实体数+Template配置实体数-Admin个数

Specs下可定义多个实体,每个实体都可以包含Hostname、CommonName一对儿字符串,CommonName是可选的。若未定义CommonName,则Hostname.Domain将该实体证书的CN值,若定义了CommonName,则CommonName将是该实体证书的CN值。Template中会从Start开始,定义Count个ordererN.Domain格式名字的实体,Start <= N < Count+Start。Template与Specs不互斥,可同时定义,所定义的实体均会被配置,但是注意两个区域所配置形成的实体名不要重复,否则会形成覆盖。User中,Admin实体数不允许配置,默认为1个,普通用户的数量也可不遵循上述公式。

crypto-config文件夹

crypto-config文件夹就是对之后运行起来的整个区域链的网络拓扑结构很好的描述:crypto-config有两个文件夹,表示有两类组织,orderer组织ordererOrganizations和peer组织peerOrganizations。以peerOrganizations为例,又包含两个文件夹,表示该类组织包含两个组织,org1.example.com和org2.example.com。以org1.example.com为例,包含组织根证书文件夹ca、组织中所有的MSP身份文件夹msp、组织中所有的TLS网络证书文件夹tlsca、组织中所有成员角色的MSP身份文件夹users、组织包含的实体结点文件夹peers。在peers中,包含两个文件夹,代表两个实体结点,peer0.org1.example.com和peer1.org1.example.com。以peer0.org1.example.com为例,包含结点MSP身份文件夹、结点网络证书文件夹tls。如此,一级一级的形成管理、对应关系,比如peer组织中的org1中,实体成员peer0与peer1的MSP身份文件夹中的管理员证书应该一样,且应该等于org1的msp/admincerts下的证书。再比如peer0的根证书是msp/cacerts/ca.org1.example.com-cert.pem,从名字上即可看出peer0使用的是org1组织的证书,如此的话,与peer0同级的peer1使用的也应该是所属组织org1的证书作为自己的根证书,而实际情况确实如此。再比如,peer0属于org1组织,则peer0的自有的签名证书msp/signcerts/peer0.org1.example.com-cert.pem理应是由org1这个组织所认证的证书,即由org1.example.com/ca/ca.org1.example.com-cert.pem所认证签发,对于此点我们可以自己写一个小程序验证一下,即分别读取两个证书,然后调用peer0证书的Verify()函数验证,源码参看verify_cert.go。或者你若是在Ubuntu环境下,可以找到该证书,直接点开peer0的证书,Ubuntu中由解析证书的软件,在Issuer Name(发行者)栏中可以清楚的看到组织O为org1.example.com,CN(通用名称)为ca.org1.example.com,即org1的证书。事实证明peer0的证书确实是org1的证书签发的。另外,组织、实体结点等的命名均是xxx.xxx.xxx格式,也形成了前面的在后面的范围之内的意思。

replacePrivateKey函数“人如其名”,所做的主要是在generateCerts生成各个实体的证书和私匙后,替换部署的fabric-ca0、fabric-ca1容器所使用的配置文件中的指定的容器所使用的证书对应的私匙文件名。这一点也很好理解:fabric-ca容器是用于给新加入某一组织的peer结点签发认证的证书的,因此ca容器首先得自己持有组织的根证书和根证书的私匙(才能进行签发新证书的操作)。根证书的文件名根据cryptogen程序可预先得知,但是对应生成的私匙(*_sk)文件名则事先是不知道的,因此在docker-compose-e2e-template.yaml(带有template字样,表明是一个样板文件)中预先是用CA1_PRIVATE_KEY、CA2_PRIVATE_KEY这样的字段临时代替了私匙文件名,也因此需要在生成org1和org2两个组织的证书的私匙后用私匙文件名替换掉这两个字段。具体的操作是:先复制了一份docker-compose-e2e-template.yaml模板文件为docker-compose-e2e.yaml,在相应目录中执行PRIV_KEY=$(ls *_sk)获取私匙文件名,然后使用sed $OPTS "s/...命令将docker-compose-e2e.yaml中的CA1_PRIVATE_KEY、CA2_PRIVATE_KEY字段分别替换成org1、org2组织的根证书对应的私匙文件名。

Configuration Transaction Generator

generateChannelArtifacts函数主要使用了configtxgen工具。函数所做的事情和configtxgen工具的介绍可以参看脚本中该函数上面的注释。在此简单说一下,configtxgen使用configtx.yaml配置生成部署的网络实体所需的“手工艺品”,这些生成的文件均放入了channel-artifacts文件夹内:

  • genesis.block - 不必多说,orderer服务启动必备的。特别说一下,每个组织的根证书都包含在gensis.block中。
  • channel.tx - fabric的channel的配置。该配置在orderer服务启动创建channel时配置channel。这里就已经形成了channel的读写策略(即哪些实体可以读,哪些实体可以写,下同)。
  • Org1MSPanchors.tx和Org2MSPanchors.tx - 用于指定在channel中,当前peer组织中都有哪些peer结点。这里就已经形成了组织的读写策略。

configtx.yaml包含部署网络实体的定义,主要分为三部分:Profiles,直接供configtxgen使用的配置部分,可以通过-profile命令行参数指定配置这里的哪一项;Organizations,定义了三个成员(组织),orderer组织OrdererOrg、两个peer组织Org1和Org2(每个组织管理维护两个peer结点),供Profiles使用;其余的Orderer和Application,具体的定义,供Profiles和Organizations两部分使用。这里可以留意的是每个组织都配置了MSPDir项,即每个组织的MSP所在的路径,这正是为了configtxgen能够找到每个组织的根证书,找到组织的根证书,就可以对实体身份或实体交易的数字签名进行验证。

configtx.yaml配置文件(的写法)与configtxgen的实现紧密相关,参看源码common/configtx/tool/configtxgen/main.go:(1)configtxgen默认就是读取configtx.yaml作为配置文件的,在main中,config := genesisconfig.Load(profile)默认就读取了当前文件夹或FABRIC_CFG_PATH或fabric/sampleconfig/(参看byfn.sh开始处的两个export,三个路径依次搜查指定的配置文件,先搜查到即停止)下的名为configtx的文件(也就是configtx.yaml)的profile配置项(profile由-profile参数指定)。(2)在main中,所有的flag.StringVar(...)即为configtxgen可以解析的命令行参数,其中就有outputBlock、outputCreateChannelTx、outputAnchorPeersUpdate,即是用于生成上面所说的“手工艺品”。比如,执行configtxgen时给的了-outputBlock参数,则在main的中会进入if outputBlock != ""分支,从而执行doOutputBlock(config, channelID, outputBlock)来生成gensisblock。另外还有两个用于检查参数,inspectBlock、inspectChannelCreateTx,分别用于检查生成的gensis.block和channel.tx文件是否符合要求。(3)对于生成的gensisblock、channel.tx都包含什么数据,建议执行同目录下的main_test.go中的TestBlockFlagsTestConfigTxFlags两个测试函数,在测试过程中读取的是sampleconfig下的configtx.yaml中的配置,并会将生成的gensis.block和channel.tx以JSON格式打印出来。

重看generateChannelArtifacts函数,在检查configtxgen工具是否存在后,开始执行任务:(1)configtxgen -profile TwoOrgsOrdererGenesis -outputBlock ./channel-artifacts/genesis.block,生成gensisblock。-profile指定使用configtx.yaml中的TwoOrgsOrdererGenesis项配置去生成gensisblock,-outputBlock则指定了生成的gensisblock放到哪里和名字。(2)configtxgen -profile TwoOrgsChannel -outputCreateChannelTx...,使用configtx.yaml中的TwoOrgsChannel生成channel.tx。(3)执行了两个configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate...命令,使用configtx.yaml中的TwoOrgsChannel配置生成org1、org2的配置文件Org1MSPanchors.tx和Org2MSPanchors.tx。

networkUp

networkUp函数真正启动了部署网络。函数所做的很简单(前提是熟悉docker相关操作),if [ ! -d "crypto-config" ]; then检查crypto-config文件夹是否存在,也即变相的检查上文讲述过的generateCertsreplacePrivateKeygenerateChannelArtifacts三个函数的准备工作是否已经做完。然后根据是否使用couchdb数据库,执行对应if分支中的... docker-compose -f ...命令,其实就是除了-f $COMPOSE_FILE外是否多添加一个-f $COMPOSE_FILE_COUCH参数,即多指定一个docker-compose-couch.yaml文件以启动couchdb容器。-f $COMPOSE_FILE默认指定的是docker-compose-cli.yaml,也可在byfn.sh的命令行参数中以-f指定。docker-compose -f命令所做的就是根据指定的docker-compose-cli.yaml文件自动部署文件中定义的容器。

运行容器+区域链操作

关于docker-compose-cli.yaml,共在byfn网络中在services下设置了6个服务:一个orderer容器,四个peer容器,一个cli容器。其中orderer和peer容器都具体定义在base/docker-compose-base.yaml中,cli则在当前文件中具体定义。容器的每个字段的定义没有冗余的地方,每个字段都很重要,具体在用到的时候会回过头来提及。docker-compose-couch.yaml中,除了为每个peer结点容器都对应启动一个couchdb容器外,还对每个peer容器增加了一些环境变量。一旦networkUp函数执行成功,所有的容器,容器的设置,都会被应用和启动。

在启动各个容器时,docker-compose-cli.yaml中每个服务下的command字段都给定了默认执行的命令。orderer容器默认执行的是orderer(也即orderer start命令),4个peer容器各自执行的是peer node start,最后启动的cli容器,在working_dir目录下默认执行了/bin/bash -c './scripts/script.sh ${CHANNEL_NAME} ${DELAY}; sleep $TIMEOUT',即scripts下的script.sh脚本,但是当script.sh脚本运行结束之后,cli容器即会自动停止并退出。script.sh的执行就是各位在终端上看到的以大大的START开始,并以大大的END结束的过程。这个过程主要执行了几种区域链操作,如创建channel、加入channel、升级组织配置、安装部署chaincode、调用chaincode,算是cli变相的先测试一下这个部署的区域链网络的基本操作,同时也把peer1.org1和peer1.org2两个结点加入各自组织(回忆一下,生成gensis.block、channel.tx所使用的configtx.yaml中,org1,org2组织下均只包含了一个peer0结点)的状态留在了还在运行的网络里面(一个orderer结点容器,4个peer结点容器仍在运行)。由此我们可以知道,当你在终端上看到大大的START时,示例中的6个容器已经启动,整个实例的网络实际上已经部署完毕,再执行script.sh脚本只是为了在这个区域链网络中测试各种操作,如果想手工自己操作,在docker-compose-cli.yaml中将cli服务中的command字段注释掉即可,甚至不启动cli容器也可以,转而在各个结点的容器中执行操作。这里需要注意的是,script.sh脚本是在cli容器内运行的,也就是说脚本所使用的环境变量、路径等元素的值,均为cli容器中的值。而命令中script.sh后边的三个变量${CHANNEL_NAME}${DELAY}$TIMEOUT则来自于byfn.sh中networkUp中执行的... docker-compose -f ...命令。由于script.sh脚本进行了区域链网络的基本操作,因此我们可以细细分析,从中学习,若产生问题,可以适当的修改脚本,验证猜想从而解除疑问。

再回头分析一下docker-compose-cli.yaml中对cli容器的设置:container_name指定了容器名字为cli,image指定了cli以下载的hyperledger/fabric-tools为镜像,tty也设置为true(即开启了控制台),environment下设置了cli启动时所设置的环境变量,working_dir指定了cli的基准工作目录,command指定了cli启动时运行的命令,volumes指定了宿主主机与cli之间挂接的目录,depends_on指定了cli所依赖的容器(即必须这些容器正确启动之后cli才能启动,也即cli是最后启动的),networks指定了cli所使用的网络。

在script.sh中,从echo "Creating channel..."处开始执行命令,均为调用脚本上面已定义好的函数,这些函数有:

  • setGlobals() - 公用函数,根据所给的参数来指定配置针对某一peer结点的环境变量以执行其他操作。这里预设的是给一个参数,0对应peer0.org1,1对应peer1.org1,2对应peer0.org2,3对应peer1.org2。比如当调用setGlobals 0,表示针对peer0.org1设置cli容器的CORE_PEER_LOCALMSPID(peer结点所在组织的MSPID)、CORE_PEER_TLS_ROOTCERT_FILE(peer结点的TLS证书所在路径)、CORE_PEER_MSPCONFIGPATH(peer结点的MSP配置所在路径)、CORE_PEER_ADDRESS(peer结点的容器通信地址)等环境变量。调用者需要这些环境变量的值继续执行其他操作。
  • createChannel() - setGlobals 0设置了关于peer0.org1的环境变量,然后因为CORE_PEER_TLS_ENABLED的值默认是true(启用TLS连接),因此执行了peer channel create...-o...-c...-f...--tls...命令,从而建立了一个与orderer结点相连的Application Channel。channel的名为$CHANNEL_NAME指定,该变量来自于cli容器执行script.sh时所给的第1个参数,这个第1个参数又来自执行byfn.sh时所给的-c参数值。若不指定,默认值是mychannel,这里假设使用的是默认值。创建mychannel后,会在cli容器内的working_dir下生成一个mychannel.block,供之后要加入mychannel的结点使用。mychannel.block中包含了channel.tx中的配置信息。
  • joinChannel() - 循环0-3四个值,分别代表4个peer结点,在循环中依次调用setGlobals $chjoinWithRetry $ch将每个peer结点加入上一步所创建的mychannel中。joinWithRetry中具体执行的命令为peer channel join -b $CHANNEL_NAME.block,其中$CHANNEL_NAME.block即是createChannel生成的mychannel.block。
  • updateAnchorPeers() - 使用之前生成的Org1MSPanchors.tx、Org2MSPanchors.tx升级mychannel中现有组织的信息(参看configtx.yaml中的生成升级包所使用的TwoOrgsChannel下包含的组织Org1、Org2的信息,如MSPDir等)。使用的命令为peer channel update -o ... -c ... -f...。Org1MSPanchors.tx、Org2MSPanchors.tx就算是针对组织的升级包,可以升级组织的配置信息,又由于生成的Org1MSPanchors.tx、Org2MSPanchors.tx与创建mychannel的channel.tx所使用的组织信息一致,所以这里的升级实际上不会对现有部署的网络产生任何影响,这里只是为了测试peer channel update这项功能而已(也即在script.sh中将updateAnchorPeers 0updateAnchorPeers 2注释掉,也没有关系)。
  • installChaincode() - 使用指定的结点安装一个chaincode,经过cli的路径映射,安装的chaincode实际上是下载fabric-sample/chaincode/chaincode_example02。具体执行的命令为peer chaincode install -n mycc -v 1.0 ...,分别在peer0.org1和peer0.org2两个结点上安装。
  • instantiateChaincode() - 从指定结点部署安装的chaincode_example02,这里是从peer0.org2结点部署。具体执行的命令是peer chaincode instantiate -o ... --tls true --cafile...-C...-n mycc -v 1.0 -c '{"Args":["init","a","100","b","200"]}' -P "OR ('Org1MSP.member','Org2MSP.member')",-c指定了初始化的参数,初始化了账户a的余额为100,账户b的余额为200。-P指定了chaincode的背书策略,因为是OR,所以Org1或Org2下的任一成员背书交易即可,这个意思就是Org1或Org2组织下的所有peer结点都能执行这个部署的chaincode。
  • chaincodeQuery() - 以第一个参数$1指定结点身份执行查询,对比查询的结果是否a账户的余额是否等于第二个参数$2。查询过程会在$TIMEOUT内每隔$DELAY秒尝试查询一次,每次执行的命令是peer chaincode query -C ... -n mycc -c '{"Args":["query","a"]}'
  • chaincodeInvoke() - 从指定结点执行chaincode_example02。具体执行的命令是peer chaincode invoke -o ... --tls true --cafile ... -C ... -n mycc -c '{"Args":["invoke","a","b","10"]}',所做的就是将a账户的10元转账给b账户(chaincode_example02源码中所实现的)。