1. 切换路径并关闭已存在网络 1 cd /opt/gopath/src/github.com/hyperledger/fabric-samples/first-network/
执行通道配置更新并不是必需要关闭已有的网络。本次教程只是为了从初始状态开始才将原有网络关闭。
2.生成新的网络 1 2 ./byfn.sh generate ./byfn.sh up
3.添加新组织Org3 3.1 利用脚本添加新组织Org3
如果顺利运行,你可以看到以下内容:
1 2 3 4 5 6 7 8 ========= All GOOD, EYFN test execution completed =========== _____ _ _ ____ | ____| | \ | | | _ \ | _| | \| | | | | | | |___ | |\ | | |_| | |_____| |_| \_| |____/
3.2 手动添加新组织Org3 因为上面步骤已经使用了eyfn.sh脚本,因此需要关闭网络,删除所有容器,并撤销添加Org3的操作。
网络关闭之后,再次将其恢复
1 2 ./byfn.sh generate ./byfn.sh up
此时网络恢复到执行eyfn.sh脚本之前的状态
3.2.1 生成Org3的加密材料 进入org3-artifacts目录
1 2 3 [root@localhost first-network]# cd org3-artifacts/ [root@localhost org3-artifacts]# ls configtx.yaml org3-crypto.yaml
生成加密材料
1 cryptogen generate --config=./org3-crypto.yaml
1 2 [root@localhost org3-artifacts]# cryptogen generate --config=./org3-crypto.yaml org3.example.com
该命令读取加密材料生成所需要的yaml文件,并利用cryptogen工具为Org3的一个CA和两个与之绑定的peer节点生成密钥和证书。生成的材料放在当前目录的crypto-config文件夹中.
利用configtxgen来以JSON的格式打印Org3指定配置材料,配置让configtxgen在当前目录寻找configtx.yaml文件
1 export FABRIC_CFG_PATH=$PWD && configtxgen -printOrg Org3MSP > ../channel-artifacts/org3.json
该命令生成一个org3.json文件,存放在first-network目录下的channel-artifacts文件夹中。该文件包含Org3的策略定义和三个base64格式的证书:管理员用户证书,CA根证书和TLS根证书。该json文件会在后面的步骤中加入到通道配置中。
1 2 3 [root@localhost first-network]# cd channel-artifacts/ [root@localhost channel-artifacts]# ls channel.tx genesis.block Org1MSPanchors.tx Org2MSPanchors.tx org3.json
最后将orderer的MSP材料复制到Org3的crypto-config目录下。orderer的TLS根证书允许Org3和网络排序节点的安全通信。
1 cd ../ && cp -r crypto-config/ordererOrganizations org3-artifacts/crypto-config/
1 2 3 [root@localhost first-network]# cd org3-artifacts/crypto-config/ [root@localhost crypto-config]# ls ordererOrganizations peerOrganizations
可以看到已经将orderer的MSP材料复制到对应位置。
3.2.2 CLI环境准备 更新过程利用配置转换工具:configtxlator。该工具提供独立于SDK的无状态REST API。同时提供了CLI来简化Fabric网路中的配置工作。该工具作用是在不同等效数据表示形式/格式之间轻松转换(在本例子中,在protobufs和json格式之间转换)。另外,该工具可以基于两个通道配置之间的差异来计算配置更新事务。
进入容器并配置ORDERER_CA
和 CHANNEL_NAME
的值
1 2 docker exec -it cli bash export ORDERER_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem && export CHANNEL_NAME=mychannel
查看是否正确设置对应变量值
1 echo $ORDERER_CA && echo $CHANNEL_NAME
1 2 3 root@f7d186065b85:/opt/gopath/src/github.com/hyperledger/fabric/peer# echo $ORDERER_CA && echo $CHANNEL_NAME /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem mychannel
这样的变量设置方式是临时的,一旦退出cli容器,重新进入时就需要再次重新设置。
3.2.3 获取配置 获取通道mychannel
最新的配置区块。之所以要获取最新的配置区块是因为配置元素是版本化的。 可以避免重复的配置更改,同时有助于确保并发性(当需要从通道删除一个组织的时候,例如在一个新组织被添加到通道中之后,版本控制有助于防止同时将两个组织都删除了,而是保证仅仅删除想要删除的组织)
1 peer channel fetch config config_block.pb -o orderer.example.com:7050 -c $CHANNEL_NAME --tls --cafile $ORDERER_CA
该命令保存二进制protobuf
通道配置块到config_block.pb
。注意此处的名称和文件后缀选择是随意的,但是建议遵循一个约定,该约定既能表示对象类型也能体现对象的编码(protobuf 或者 json)格式。
1 2 3 4 5 root@f7d186065b85:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer channel fetch config config_block.pb -o orderer.example.com:7050 -c $CHANNEL_NAME --tls --cafile $ORDERER_CA 2019-10-22 11:17:44.424 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized 2019-10-22 11:17:44.436 UTC [cli.common] readBlock -> INFO 002 Received block: 4 2019-10-22 11:17:44.438 UTC [cli.common] readBlock -> INFO 003 Received block: 2 2019-10-22 11:17:44.438 UTC [channelCmd] fetch -> INFO 004 Retrieving last config block: 2
从日志最后一行可以看出通道mychannel最近的配置块实际上是区块2,并不是创世区块。默认情况下该命令返回目标通道的最新区块,在本次示例中最新区块为第三个区块。因为脚本为两个组织在两个独立的通道更新交易中定义了锚节点。所以配置序列如下:
区块0:创世区块
区块1:Org1锚节点更新
区块2:Org2锚节点更新
3.2.3 配置文件格式转换与修改 使用configtxlator工具将通道配置块解码为JSON格式(人类可读取和修改)。 同时必须将所有与我们要进行的更改无关的标题,元数据,创建者签名等剥离。 通过使用jq
工具完成此任务:
1 configtxlator proto_decode --input config_block.pb --type common.Block | jq .data.data[0].payload.data.config > config.json
执行完毕生成一个简洁的JSON对象config.json作为后续配置更新的基准文件。
1 2 root@f7d186065b85:/opt/gopath/src/github.com/hyperledger/fabric/peer# ls channel-artifacts config.json config_block.pb crypto log.txt mychannel.block scripts
3.2.4 添加Org3加密材料 再次使用jq
工具追加Org3的配置定义Org3.json
到通道的应用组字段中并将输出命名为modified_config.json
。
1 jq -s '.[0] * {"channel_group":{"groups":{"Application":{"groups": {"Org3MSP":.[1]}}}}}' config.json ./channel-artifacts/org3.json > modified_config.json
可以看到已经生成modified_config.json文件
1 2 root@f7d186065b85:/opt/gopath/src/github.com/hyperledger/fabric/peer# ls channel-artifacts config.json config_block.pb crypto log.txt modified_config.json mychannel.block scripts
此时。已经有两个JSON文件,分别是config.json
和modified_config.json
。初始文件只包含Org1和Org2的信息,而修改后的文件包含了三个组织的信息。此时,只需重新编码这两个JSON文件并计算增量即可。
将config.json
转回protobuf格式,命名为config.pb
1 configtxlator proto_encode --input config.json --type common.Config --output config.pb
将modified_config.json
转回protobuf格式,命名为modified_config.pb
1 configtxlator proto_encode --input modified_config.json --type common.Config --output modified_config.pb
查看对应文件是否生成
1 2 root@f7d186065b85:/opt/gopath/src/github.com/hyperledger/fabric/peer# ls channel-artifacts config.json config.pb config_block.pb crypto log.txt modified_config.json modified_config.pb mychannel.block scripts
利用configtxlator
计算两个配置protobufs的增量,以下命令会输出一个新的protobuf名为org3_update.pb
:
1 configtxlator compute_update --channel_id $CHANNEL_NAME --original config.pb --updated modified_config.pb --output org3_update.pb
可以看到所需要文件已经生成
1 2 root@f7d186065b85:/opt/gopath/src/github.com/hyperledger/fabric/peer# ls channel-artifacts config.json config.pb config_block.pb crypto log.txt modified_config.json modified_config.pb mychannel.block org3_update.pb scripts
新生成的org3_update.pb
包含Org3的定义和指向Org1和Org2的高级指针。
在提交通道更新之前,我们需要执行最后的几步。
首先将 org3_update.pb
解码为可编辑的JSON格式,命名为org3_update.json
1 configtxlator proto_decode --input org3_update.pb --type common.ConfigUpdate | jq . > org3_update.json
1 2 root@f7d186065b85:/opt/gopath/src/github.com/hyperledger/fabric/peer# ls channel-artifacts config.json config.pb config_block.pb crypto log.txt modified_config.json modified_config.pb mychannel.block org3_update.json org3_update.pb scripts
在有解码后的org3_update.json
文件之后,需要将其封装在信封消息之中。这一步会将我们先前删除的头部数据添加回来,封装完的文件命名为org3_update_in_envelope.json
1 echo '{"payload":{"header":{"channel_header":{"channel_id":"mychannel", "type":2}},"data":{"config_update":'$(cat org3_update.json)'}}}' | jq . > org3_update_in_envelope.json
1 2 3 root@f7d186065b85:/opt/gopath/src/github.com/hyperledger/fabric/peer# ls channel-artifacts config.pb crypto modified_config.json mychannel.block org3_update.pb scripts config.json config_block.pb log.txt modified_config.pb org3_update.json org3_update_in_envelope.json
最后再次利用configtxlator
工具将org3_update_in_envelope.json
转化为Fabric所需要的完整protobuf格式,命名为org3_update_in_envelope.pb
:
1 onfigtxlator proto_encode --input org3_update_in_envelope.json --type common.Envelope --output org3_update_in_envelope.pb
1 2 3 root@f7d186065b85:/opt/gopath/src/github.com/hyperledger/fabric/peer# ls channel-artifacts config.pb crypto modified_config.json mychannel.block org3_update.pb org3_update_in_envelope.pb config.json config_block.pb log.txt modified_config.pb org3_update.json org3_update_in_envelope.json scripts
3.2.5 签名并提交配置更新 现在在CLI容器中已经有了org3_update_in_envelope.pb
。但是在配置写入账本之前,必需要有来自Admin用户的签名,在已有通道应用组中修改策略设置为默认“MAJORITY”,即需要大多数现有的组织管理员的签名。因为目前只有Org1和Org2两个组织,所以需要这两个组织的签名。如果没有这两个签名,排序服务交易未能满足策略而拒绝交易。
Org1 Admin签名:
1 peer channel signconfigtx -f org3_update_in_envelope.pb
1 2 root@f7d186065b85:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer channel signconfigtx -f org3_update_in_envelope.pb 2019-10-22 14:03:32.763 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
切换到Org2环境变量
1 2 3 4 5 6 7 export CORE_PEER_LOCALMSPID="Org2MSP" export CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp export CORE_PEER_ADDRESS=peer0.org2.example.com:9051
发出更新命令,此时Org2 Admin的签名将会附加到此调用中。因此不需手动再次为protobuf签名。
1 peer channel update -f org3_update_in_envelope.pb -c $CHANNEL_NAME -o orderer.example.com:7050 --tls --cafile $ORDERER_CA
1 2 2019-10-22 14:06:55.118 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized 2019-10-22 14:06:55.164 UTC [channelCmd] update -> INFO 002 Successfully submitted channel update
3.2.6 选择配置 新加入的节点通过创世块进行引导,但是创世块中不包含通道配置更新中被新添加进去的组织。因此新节点无法利用gossip
,因为在新节点获得添加组织到通道的配置交易前,无法验证其它节点从自己组织中转发过来的区块。新添加的节点必须进行以下配置(二选一)使得新的节点能够从排序服务中接收区块。
利用静态选举模式,将节点配置为组织领导1 2 CORE_PEER_GOSSIP_USELEADERELECTION=false CORE_PEER_GOSSIP_ORGLEADER=true
所有添加到通道中的新节点配置必须相同
利用动态领导选举模式,配置节点使用领导选举1 2 CORE_PEER_GOSSIP_USELEADERELECTION=true CORE_PEER_GOSSIP_ORGLEADER=false
由于新添加组织中的节点无法形成成员视图,因此该配置是与静态模式相似的。因为每个节点在一开始的时候都会宣称自己是领导者。但是一旦它们获得添加组织到通道的配置交易更新,则组织只会有一个领导节点。所以如果最后希望组织节点利用领导选举则建议使用这种模式。
以上两种模式配置都是在first-network目录中的base文件夹下的peer-base.yaml文件中进行。以下截取可以配置部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 peer-base: image: hyperledger/fabric-peer:$IMAGE_TAG environment: - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock # the following setting starts chaincode containers on the same # bridge network as the peers # https://docs.docker.com/compose/networking/ - CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=${COMPOSE_PROJECT_NAME}_byfn - FABRIC_LOGGING_SPEC=INFO #- FABRIC_LOGGING_SPEC=DEBUG - CORE_PEER_TLS_ENABLED=true - CORE_PEER_GOSSIP_USELEADERELECTION=true - CORE_PEER_GOSSIP_ORGLEADER=false - CORE_PEER_PROFILE_ENABLED=true - CORE_PEER_TLS_CERT_FILE=/etc/hyperledger/fabric/tls/server.crt - CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/fabric/tls/server.key - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/tls/ca.crt working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer command: peer node start
3.2.7 将Org3加入通道 以上步骤只是将Org3添加到Fabric网络中,但是此时Org3还没有进入任何通道
首先,启动Org3 的节点容器和一个Org3对应的CLI容器 在first-network目录下:
1 docker-compose -f docker-compose-org3.yaml up -d
1 2 3 4 5 6 7 [root@localhost first-network]# docker-compose -f docker-compose-org3.yaml up -d Creating volume "net_peer0.org3.example.com" with default driver Creating volume "net_peer1.org3.example.com" with default driver WARNING: Found orphan containers (cli, peer0.org2.example.com, orderer.example.com, peer1.org2.example.com, peer1.org1.example.com, peer0.org1.example.com) for this project. If you removed or renamed this service in your compose file, you can run this command with the --remove-orphans flag to clean it up. Creating peer1.org3.example.com ... done Creating peer0.org3.example.com ... done Creating Org3cli ... done
compose文件已经配置了初始网络连接,所以两个peer节点和CLI容器能够与现有的peer和排序节点连接。进入Org3-CLI容器
1 docker exec -it Org3cli bash
设置两个关键环境变量ORDERER_CA
和 CHANNEL_NAME
1 export ORDERER_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem && export CHANNEL_NAME=mychannel
确认变量正确设置:
1 echo $ORDERER_CA && echo $CHANNEL_NAME
发送一个调用给排序服务请求mychannel
通道的创世区块。以为上述步骤中通道已经成功更新,因此排序服务可以验证调用中附带的Org3签名。如果Org3没有成功添加到通道配置,则该请求将会被排序服务拒绝。
使用peer channel fetch
命令检索区块:
1 peer channel fetch 0 mychannel.block -o orderer.example.com:7050 -c $CHANNEL_NAME --tls --cafile $ORDERER_CA
将0
作为参数表明希望得到的是通道账本中的第一个块(即创世块)。如果使用peer channel fetch config
命令,返回的是第五个区块——Org3定义的配置更新,但新加入通道必需从创世区块0开始。
1 2 2019-10-23 01:56:32.458 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized 2019-10-23 01:56:32.498 UTC [cli.common] readBlock -> INFO 002 Received block: 0
利用创世块mychannel.block
加入通道
1 peer channel join -b mychannel.block
1 2 3 root@e6a67979d990:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer channel join -b mychannel.block 2019-10-23 01:59:59.244 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized 2019-10-23 01:59:59.307 UTC [channelCmd] executeJoin -> INFO 002 Successfully submitted proposal to join channel
如果需要将Org3的第二个peer也加入通道,只需要设置TLS
和ADDRESS
的值后再次执行上述命令:
1 2 3 export CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.example.com/peers/peer1.org3.example.com/tls/ca.crt && export CORE_PEER_ADDRESS=peer1.org3.example.com:12051 peer channel join -b mychannel.block
3.2.8 升级并调用链码 升级链码版本同时更新背书策略来包含Org3。在Org3-CLI容器执行
1 peer chaincode install -n mycc -v 2.0 -p github.com/chaincode/chaincode_example02/go/
1 2 3 4 root@e6a67979d990:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer chaincode install -n mycc -v 2.0 -p github.com/chaincode/chaincode_example02/go/ 2019-10-23 02:14:13.535 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 001 Using default escc 2019-10-23 02:14:13.535 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default vscc 2019-10-23 02:14:13.918 UTC [chaincodeCmd] install -> INFO 003 Installed remotely response:<status:200 payload:"OK" >
回到原本的CLI容器,在Org1和Org2的peer上安装新版本的链码,以为上次执行通道更新调用使用Org2 管理员身份,所以当前容器代表身份仍然是peer0.org2
1 peer chaincode install -n mycc -v 2.0 -p github.com/chaincode/chaincode_example02/go/
1 2 3 4 root@f7d186065b85:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer chaincode install -n mycc -v 2.0 -p github.com/chaincode/chaincode_example02/go/ 2019-10-23 02:18:08.241 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 001 Using default escc 2019-10-23 02:18:08.241 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default vscc 2019-10-23 02:18:08.548 UTC [chaincodeCmd] install -> INFO 003 Installed remotely response:<status:200 payload:"OK" >
将容器环境切换为Org1
1 2 3 4 5 6 7 export CORE_PEER_LOCALMSPID="Org1MSP" export 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 export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp export CORE_PEER_ADDRESS=peer0.org1.example.com:7051
再次执行链码安装
1 peer chaincode install -n mycc -v 2.0 -p github.com/chaincode/chaincode_example02/go/
1 2 3 4 root@f7d186065b85:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer chaincode install -n mycc -v 2.0 -p github.com/chaincode/chaincode_example02/go/ 2019-10-23 02:19:40.763 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 001 Using default escc 2019-10-23 02:19:40.763 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default vscc 2019-10-23 02:19:40.920 UTC [chaincodeCmd] install -> INFO 003 Installed remotely response:<status:200 payload:"OK" >
直至目前,链码升级已经准备完毕,这次升级并没有修改底层源代码,只是为通道mychannel
中的mycc
链码简单添加Org3
到背书策略中。
1 peer chaincode upgrade -o orderer.example.com:7050 --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA -C $CHANNEL_NAME -n mycc -v 2.0 -c '{"Args":["init","a","90","b","210"]}' -P "OR ('Org1MSP.peer','Org2MSP.peer','Org3MSP.peer')"
-v标志指明新版本
-P可以看到已经将Org3添加到策略中去
与实例化调用一样,链码更新也需要调用init
方法,如果链码中init需要参数,则更新时同样需要。
更新调用添加了一个新的区块——区块6.同时允许Org3 peer在背书阶段执行交易。1 2 3 root@f7d186065b85:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer chaincode upgrade -o orderer.example.com:7050 --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA -C $CHANNEL_NAME -n mycc -v 2.0 -c '{"Args":["init","a","90","b","210"]}' -P "OR ('Org1MSP.peer','Org2MSP.peer','Org3MSP.peer')" 2019-10-23 02:25:18.999 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 001 Using default escc 2019-10-23 02:25:18.999 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default vscc
回到Org3-CLI容器并查询a
的值,这个过程需要耗费一些时间,因为对应peer需要构建并启动链码镜像:1 peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'
结果1 2 root@e6a67979d990:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}' 90
调用链码:1 peer chaincode invoke -o orderer.example.com:7050 --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA -C $CHANNEL_NAME -n mycc -c '{"Args":["invoke","a","b","10"]}'
1 2 root@e6a67979d990:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer chaincode invoke -o orderer.example.com:7050 --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA -C $CHANNEL_NAME -n mycc -c '{"Args":["invoke","a","b","10"]}' 2019-10-23 02:45:08.595 UTC [chaincodeCmd] chaincodeInvokeOrQuery -> INFO 001 Chaincode invoke successful. result: status:200
最后查询:1 peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'
1 2 root@e6a67979d990:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}' 80
3.2.9 结论 通道配置更新过程确实涉及很多,但是各个步骤都有一个逻辑方法。 最终的结果是形成一个以protobuf二进制格式表示的增量交易对象,然后获取必要数量的管理员签名,以便通道配置更新交易满足通道的修改政策。主要涉及各种文件的转换与获取,如下流程图所示:
附:文件转化过程: