Fabric 2.0,不使用脚本的情况下启动test-network

之前使用过脚本的方式启动了fabric的测试网络,但是这个脚本封装的实在太好了,一行命令之后啥都看不出来,对于我这样的初学者来说感觉帮助不是特别大,所以我决定把这个脚本串行化的分析一遍,来真正看出整个网络的启动过程。

本文假设当前所在目录为test-network,使用fabric版本为2.3.0,并且和network.sh脚本默认情况下一样不使用预先定义的CA证书。

1.创建加密证书材料

创建org1、org2和org3所需的ca证书,这里会用到organizations/cryptogen下的yaml文件,以crypto-config-org1.yml为例:

# 定义组织管理的所有peer节点
PeerOrgs:
  # 节点名称
  - Name: Org1
    # 节点域名
    Domain: org1.example.com
    # 是否使用NodeOUs(更细粒度的MSP,可以理解为一个公司的不同部门)
    EnableNodeOUs: true
    # 这里省略了一段Spec配置,这些配置用于明确的指明每个peer节点的主机名等信息,也可以使用Template模板配置去自动给主机命名
	# 利用模板方式配置产生多少个peer节点,并按照某种规则进行命名
    Template:
      # 要产生的peer节点的数量
      Count: 1
      # 每个peer的地址
      SANS:
        - localhost
      # 起始序号,默认是0,也就是说对于节点的程序化命名中,第一个节点会是peer{start}
      # Start: 5
      # 主机名的默认规则,前缀加序号,前缀应该是Org1了。
      # Hostname: {{.Prefix}}{{.Index}} # default
	# 除了管理员之外的用户数量
    Users:
      Count: 1

然后利用如下的三条命令创建组织所需要的证书等

cryptogen generate --config=./organizations/cryptogen/crypto-config-org1.yaml --output="organizations" 
cryptogen generate --config=./organizations/cryptogen/crypto-config-org2.yaml --output="organizations" 
cryptogen generate --config=./organizations/cryptogen/crypto-config-orderer.yaml --output="organizations"

执行完成之后在organizations下会生成peerOrganizations和ordererOrganizations两个文件夹,里面分别存储了peer和order生成所需要的材料。

2.CCP文件创建

接下来用ccp-template.yaml和ccp-template.json这两个模板文件来创建CCP文件,这两个文件会分别被保存在organizations/peerOrganizations/org1.example.com和organizations/peerOrganizations/org2.example.com下。

从内容上来看这些文件描述的应该是各peer连接时可能会用到的端口、SSL证书等信息。

执行时只需要调用organizations下的ccp-generate.sh即可完成该步骤。

./organizations/ccp-generate.sh

3.启动网络

这里使用docker-compose的方式来启动用到的四个容器,容器的描述文件为docker/docker-compose-test-net.yaml。通过该文件可以看出网络包含四个服务,一个orderer,两个peer和一个cli,并让他们共同连接到名为test的网络。其中cli应该是用于和org1和org2的peer进行通讯的,它依赖于peer0.org1.example.com和peer0.org2.example.com镜像。使用如下命令启动网络:

export DOCKER_SOCK=/var/run/docker.sock # 不加这句可能会报错,我找不到在哪给这个环境变量赋值的,在docker-compose-test-net.yaml里面DOCKER_SOCK是作为一个环境变量使用的
docker-compose -f docker/docker-compose-test-net.yaml up -d

到这里组成网络的容器都会成功启动了。

4.创建与加入通道

4.1.创建创世块

首先设置FABRIC配置文件路径为configtx,其下有一个configtx.yaml文件,这个文件里定义了组织及其名称以及背书策略,应用调用权限,Order打包参数配置通道背书策略等,configtxgen命令可以通过该文件创建创世块。

export FABRIC_CFG_PATH=${PWD}/configtx	# 设置配置文件路径
export CHANNEL_NAME="mychannel"	# 设定通道名称
configtxgen \
-profile TwoOrgsApplicationGenesis \
-outputBlock ./channel-artifacts/${CHANNEL_NAME}.block \
-channelID $CHANNEL_NAME
# -profile configtx.yaml中的属性,用于生成创世块
# outputBlock 创世块输出位置
# -channelID 设定通道名称

执行之后,会在channel-artifacts出现一个mychannel.block,其中写入了对管道建立相关的背书、组织等,作为创世块。

4.2.创建通道

这里首先会将FABRIC配置文件路径设置为…/config,这里有三个文件,core.yaml、orderer.yaml和configtx.yaml,看起来应该是和通道内的选举等定义相关的配置文件。不知道为什么之前用的是configtx下的configtx.yaml,现在又要用…/config下的configtx.yaml,这里猜测应该是之后的步骤只用到了core.yaml和orderer.yaml,而把…/config下的configtx.yaml和那两个文件放到一起可能是因为…/config/configtx.yaml比configtx/configtx.yaml更加通用,因为里面存储的profile更多,能适用更多的情况

export FABRIC_CFG_PATH=$PWD/../config/

设置如下变量来指明目前已知的各组织的加密证书和密钥等信息。

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

然后通过设置环境变量指明目前设置的是组织1相关的信息,即说明由组织1来创建这个通道。

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/[email protected]/msp
export CORE_PEER_ADDRESS=localhost:7051

最后使用osadmin命令来实现对通道的创建。

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"

执行之后打印出如下内容,说明通道创建成功:

{
	"name": "mychannel",
	"url": "/participation/v1/channels/mychannel",
	"consensusRelation": "consenter",
	"status": "active",
	"height": 1
}

4.3.加入通道

需要让两个peer组织依次加入通道,具体可以通过设置环境变量来指明现在设置的是谁的配置,首先是组织1。

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/[email protected]/msp
export CORE_PEER_ADDRESS=localhost:7051

还要设定一下创世块的位置环境变量。

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

然后使用peer命令让peer节点加入通道中。

peer channel join -b $BLOCKFILE

然后打印出如下的内容说明成功加入了通道中。

2021-08-28 09:43:35.072 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2021-08-28 09:43:35.150 UTC [channelCmd] executeJoin -> INFO 002 Successfully submitted proposal to join channel

然后让组织2也进行一下如下的动作。

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/[email protected]/msp
export CORE_PEER_ADDRESS=localhost:9051
peer channel join -b $BLOCKFILE

然后可以看到组织2也成功加入了通道中。

4.4.设定通道中每个组织的锚点peer

这里类似加入通道的步骤,会让每个组织都执行一遍对应的命令来完成该操作,这部分必须在我们之前启动的CLI容器中去执行,个人猜想是因为容器的网络是和主机网络隔离的,而且我尝试了直接在主机上去运行这个脚本,会报网络解析错误,容器中执行的是script/setAnchorPeer.sh这个脚本,这个脚本在容器启动的时候已经挂载到容器的目录中了,这里我们只需要简单的执行下面两条米完成锚点的设定:

export ORG=1
docker exec cli ./scripts/setAnchorPeer.sh $ORG $CHANNEL_NAME 
export ORG=2
docker exec cli ./scripts/setAnchorPeer.sh $ORG $CHANNEL_NAME 

然后看到如下的输出说明锚点设置完成:

2021-08-28 10:11:26.402 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2021-08-28 10:11:26.431 UTC [channelCmd] update -> INFO 002 Successfully submitted channel update

当然这里也可以大概看一下setAnchorPeer.sh这个脚本,他里面首先会拉取最近的通道配置,然后通过jq命令对配置进行修改,写入锚点相关信息,最后提交新的通道配置,由此完成锚点peer的设置。

5.部署链码到通道中

这里我们使用的是go语言编写的链码,因此以下的变量设定和执行等都和GO语言相关。

首先设置相关环境变量,包括链码名称,链码所在路径和链码语言以及运行语言等。

export CC_NAME=basic
export CC_SRC_PATH=../asset-transfer-basic/chaincode-go
export CC_SRC_LANGUAGE=go
export CC_RUNTIME_LANGUAGE=golang
export CC_VERSION=1.0

然后进入链码所在路径进行依赖的构建

pushd $CC_SRC_PATH
GO111MODULE=on go mod vendor
popd

5.1.打包链码

使用peer命令来对链码进行打包。

peer lifecycle chaincode package ${CC_NAME}.tar.gz --path ${CC_SRC_PATH} --lang ${CC_RUNTIME_LANGUAGE} --label ${CC_NAME}_${CC_VERSION}

命令执行之后会在本地出现一个basic.tar.gz,这里存储的就是链码了。

5.2.安装链码

组织1和组织2分别给各自的peer上安装链码,通过设置环境变量来修改当前操作的组织。

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/[email protected]/msp
export CORE_PEER_ADDRESS=localhost:7051

然后执行如下命令给组织1的peer安装链码。

peer lifecycle chaincode install ${CC_NAME}.tar.gz

打印出如下内容,说明链码安装成功:

2021-08-28 11:16:51.166 UTC [cli.lifecycle.chaincode] submitInstallProposal -> INFO 001 Installed remotely: response: 
2021-08-28 11:16:51.167 UTC [cli.lifecycle.chaincode] submitInstallProposal -> INFO 002 Chaincode code package identifier: basic_1.0:0788b09dbb0681d835dad50a770a6f51aabdf53f0ad5da4135d776ad585ea48d

其中

basic_1.0:0788b09dbb0681d835dad50a770a6f51aabdf53f0ad5da4135d776ad585ea48d

为package id,之后批准链码时可能会用到。

切换到组织2再执行链码安装命令,这里不再赘述。

5.3.查询链码获取package id并进行环境变量设置

查询这步其实可以省略,因为我们安装好链码之后其实已经可以看到package id了,但如果忘了的话还是可以运行如下命令来查看package id的:

peer lifecycle chaincode queryinstalled

可以看到打印出的内容:

Installed chaincodes on peer:
Package ID: basic_1.0:0788b09dbb0681d835dad50a770a6f51aabdf53f0ad5da4135d776ad585ea48d, Label: basic_1.0

然后我们将package id设置为环境变量:

export PACKAGE_ID=basic_1.0:0788b09dbb0681d835dad50a770a6f51aabdf53f0ad5da4135d776ad585ea48d

5.4.组织批准链码

由此可以看出批准链码是一个手动的过程。。之前一直以为是自动的。

设定身份为组织1(设置组织环境变量的内容之后不再赘述

批准之前还需要设定一些环境变量用于批准的参数:

export CC_SEQUENCE=1	
export INIT_REQUIRED=--init-required	
# 链码序列
# 需要初始化账本

调用如下命令对链码进行批准:

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}

出现如下内容可以说明批准有效:

2021-08-28 12:01:45.568 UTC [chaincodeCmd] ClientWait -> INFO 001 txid [5e3c860acd3a28abc6bd84977d19737eccfe5d5b96cb638b83f5e5dc4872b767] committed with status (VALID) at localhost:7051

为了验证批准的效果,我们可以在各组织上查看链码的批准情况,首先设定身份为组织1,然后运行如下命令查看链码的批准情况:

peer lifecycle chaincode checkcommitreadiness --channelID $CHANNEL_NAME --name ${CC_NAME} --version ${CC_VERSION} --sequence ${CC_SEQUENCE} ${INIT_REQUIRED} ${CC_END_POLICY} ${CC_COLL_CONFIG} --output json

通过输出可以看出,对于该链码,组织1批准了,组织2没有批准:

{
	"approvals": {
		"Org1MSP": true,
		"Org2MSP": false
	}
}

在组织2的身份上去验证也会得到同样的输出。

之后我们切换到组织2来对链码进行批准以完成预先设定的策略,完成之后再查询批准情况,可以看到输出:

{
	"approvals": {
		"Org1MSP": true,
		"Org2MSP": true
	}
}

说明现在两个组织都已经批准了链码。

5.5.提交链码

脚本中写了一个拼接函数parsePeerConnectionParameters来在提交前产生提交链码时需要提交的参数,而这个函数执行时需要切换身份以拼接出各自部分的内容,这里我们可以直接跑出一个执行结果来,待会提交时传入即可。

--peerAddresses localhost:7051 --tlsRootCertFiles organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt

然后结合这些参数来对链码进行提交:

peer lifecycle chaincode commit -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "$ORDERER_CA" --channelID $CHANNEL_NAME --name ${CC_NAME} --peerAddresses localhost:7051 --tlsRootCertFiles organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt --version ${CC_VERSION} --sequence ${CC_SEQUENCE} ${INIT_REQUIRED} ${CC_END_POLICY} ${CC_COLL_CONFIG}

然后根据输出可以看出链码提交成功

2021-08-28 13:52:36.889 UTC [chaincodeCmd] ClientWait -> INFO 001 txid [b3dcab6ce45f5bddb150863655d67d46e234f370a61bb1d7ef20f8a3a7ec3209] committed with status (VALID) at localhost:7051
2021-08-28 13:52:36.916 UTC [chaincodeCmd] ClientWait -> INFO 002 txid [b3dcab6ce45f5bddb150863655d67d46e234f370a61bb1d7ef20f8a3a7ec3209] committed with status (VALID) at localhost:9051

可以通过以下的命令查看链码是否提交成功:

peer lifecycle chaincode querycommitted --channelID $CHANNEL_NAME --name ${CC_NAME}

如果有下面的输出,说明链码提交成功

Committed chaincode definition for chaincode 'basic' on channel 'mychannel':
Version: 1.0, Sequence: 1, Endorsement Plugin: escc, Validation Plugin: vscc, Approvals: [Org1MSP: true, Org2MSP: true]

到这里,链码也已经部署完毕了,接下来就可以正常调用了。

5.6.初始化链码

我们以初始化链码为例展示调用,事实上,通过对查询与修改的命令对比可以看出,调用查询命令时只需要指明通道即可,而对于修改命令则需要传入peer和orderer的相关信息,因为修改的操作需要这些节点背书。这里以初始化链码的操作为例,其他的操作命令可以参考我之前的博客Fabric2.0 demo,使用test-network或者fabric官网https://hyperledger-fabric.readthedocs.io/en/latest/test_network.html进行查看。

peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "$ORDERER_CA" -C $CHANNEL_NAME -n ${CC_NAME} --peerAddresses localhost:7051 --tlsRootCertFiles organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt --isInit -c '{"function":"InitLedger","Args":[]}'

通过输出可以看出初始化成功:

2021-08-28 14:10:54.121 UTC [chaincodeCmd] chaincodeInvokeOrQuery -> INFO 001 Chaincode invoke successful. result: status:200 payload:"Default initiator successful."

你可能感兴趣的:(fabric2.0,fabric)