(续 1)Hyperledger Fabric---添加新的组织和 peer

383 阅读20分钟

简介

在前面的 11 个小节中,我们参考了官方提供的 byfn demo,使用非脚本的方式从 0 构建出了 fabric 网络。

如果你还有印象的话,我们中间举了一个渔民的例子,涉及三个角色,然后类比到 3 个组织,但 byfn 网络的组织中只有两个节点,所以我们在本篇文章补充两种添加组织和节点的方法:

  • 动态添加
  • 静态添加

添加一个新的组织

使用脚本动态添加组织

所谓动态添加组织指的是我们已经成功启动了 fabric 网络,在无需关闭现有网络的情况下,成功往网络中添加一个新的组织,并可以与其他组织通信。

我们需要在 fabric-samples/first-network 目录下进行工作。

  • 使用 byfn.sh 脚本附带的功能清理网络环境

down 命令会清除正在运行或终止状态的容器,并且移除之前生成的所有构件。

./byfn.sh down

docker volume prune

  • 使用脚本生成默认的 BYFN 构件

./byfn.sh generate

  • 启动网络,并执行 CLI 容器内的脚本

./byfn.sh up

ok,执行到这里,就说明拥有两个组织的 fabric 网络启动成功,我们下面尝试使用脚本增加以一个新的组织并将它加入通道中。

  • 使用脚本将 Org3 加入通道

我们使用 eyfn.sh ( “Extending Your First Network” )脚本以及和它相关的脚本来自动生成一个新的组织 Org3 并将它加入到应用通道 ( mychannel ) 中。

  • 执行 eyfn.sh 脚本

./eyfn.sh up

脚本执行过程中日志值得你一读,你可以看到向网络中添加了 Org3 的加密材料和证书Org3的配置更新被创建和签名,组织3的节点加入通道,然后在组织3的节点上安装链码,接着 Org3 就可以与账本交互了。

如果一切顺利,你会看到以下信息:

========= All GOOD, EYFN test execution completed =========== 

静态添加组织

前一部分我们通过官方提供的 eyfn 脚本实现了动态添加 Org3,我们可以感觉操作非常简单,但是我们仍然对一个组织如何添加到网络中的过程一知半解,所以放弃脚本,通过全手动方式实现动态添加组织,我们将执行每一个步骤并弄清每一步背后的逻辑。

前期准备

  • 将CORE_LOGGING_LEVEL 变量在 cli 和 Org3cli 容器中设置为 DEBUG

对于 cli 容器,你可以通过修改 first-network 目录下的 docker-compose-cli.yaml 文件来配置。

gedit docker-compose-cli.yaml

对于 Org3cli 容器,你可以通过修改 first-network 目录下的 docker-compose-org3.yaml 文件来配置。

gedit docker-compose-org3.yaml

  • 清理网络环境

我们上面使用了脚本 eyfn.sh 向网络中添加了Org3,现在我们需要关闭我们的网络并清理网络环境,防止与我们的测试冲突。

./eyfn.sh down

这会关闭网络、删除所有的容器并且撤销我们添加 Org3 的操作。

当网络停止后,再次将它启动起来。

./byfn.sh generate

./byfn.sh up

这会将你的网络恢复到你执行 eyfn.sh 脚本之前的状态,现在我们可以动态添加 Org3 了。

生成 Org3 的加密材料

在另外一个终端,切换到first-network的子目录org3-artifacts中

cd org3-artifacts

需要关注两个yaml文件:org3-crypto.yaml 和 configtx.yaml,我们指定 org3-crypto.yaml 为参数调用 cryptogen 工具为 org3 及与其绑定的两个节点生成加密材料(私钥、公钥、证书等)。

cryptogen generate --config=./org3-crypto.yaml

该命令读取我们新的加密配置的 yaml 文件 – org3-crypto.yaml – 然后调用 cryptogen 来为 Org3 CA 和其他两个绑定到这个新组织的节点生成秘钥和证书。

现在使用 configtxgen 工具以 JSON 格式打印出 Org3 对应的配置材料。

export FABRIC_CFG_PATH=$PWD && configtxgen -printOrg Org3MSP > ../channel-artifacts/org3.json

我们来到first-network/channel-artifacts/目录下可以看到org3.json文件已经生成

上面的命令会创建一个 JSON 文件 org3.json 并把文件输出到 first-network 的 channel-artifacts 子目录下。

这个文件包含了 Org3 的策略定义,还有三个 base64 格式的重要证书:管理员用户证书(之后作为 Org3 的管理员角色),一个根证书,一个 TLS 根证书。之后的步骤我们会将这个 JSON 文件追加到通道配置。

我们最后的工作是拷贝排序节点的 MSP 材料到 Org3 的 crypto-config 目录下。我们需要尤其关注排序节点的 TLS 根证书,它可以用于 Org3 的节点和网络的排序节点间的安全通信。

cd ../ && cp -r crypto-config/ordererOrganizations org3-artifacts/crypto-config/

在org3-artifacts/crypto-config/目录下,存储了排序节点 MSP 材料的ordererOrganizations文件夹已经存在

升级通道配置

更新的步骤需要用到配置转换工具 – configtxlator ,这个工具提供了独立于 SDK 的无状态 REST API,它还额外提供了 CLI,用于简化 Fabric 网络中的配置任务。

进入到 CLI 容器,这个容器挂载了 BYFN 的 crypto-config 目录,允许我们访问之前两个节点组织和排序组织的 MSP 材料。

进入容器所使用的默认身份是 Org1 的管理员用户,所以如果我们想以 Org2 的成员身份进行任何操作,就需要设置和 MSP 相关的四个环境变量完成身份切换。

我们先回到工作目录first-network/,然后进入到 CLI 容器中

docker exec -it cli bash

设置 ORDERER_CA 和 CHANNEL_NAME 变量

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
获取配置

现在我们有了一个设置了 ORDERER_CA 和 CHANNEL_NAME 环境变量的 CLI 容器,让我们 获取通道 mychannel 的最新的配置区块。

我们必须拉取最新版本配置的原因是通道配置元素是版本化的,版本管理由于一些原因显得很重要。它可以防止通道配置被重复更新或者被重放攻击,同时它也保证了并行性(例如,如果你想从你的通道中添加新的组织后,再删除一个组织,版本管理可以帮助你移除你期望移除的那个组织,并防止移除两个组织)。

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 )进行命名。

当你执行 peer channel fetch 命令后,在终端上会有相当数量的打印输出,日志的最后一 行比较有意思:

2021-07-27 07:10:53.160 UTC [channelCmd] fetch -> INFO 046 Retrieving last config block: 2

这是告诉我们最新的 mychannel 的配置区块实际上是区块 2, 并非是初始区块。 peer channel fetch config 命令默认返回目标通道最新的配置区块,在这个例子里是第三个区块(区块编号从0开始);这是因为 BYFN 脚本分别在两个不同通道更新交易中为两个组织 – Org1 和 Org2 – 定义了锚节点,分别对应包含了 channel.tx 通道配置的区块。

最终,我们有如下的配置块序列:

  • block 0: genesis block
  • block 1: Org1 anchor peer update
  • block 2: Org2 anchor peer update
将配置转换到 JSON 格式并裁剪

现在我们用 configtxlator 工具将这个通道配置解码为 JSON 格式(更方便的阅读和修改);我们也必须裁剪所有的头部、元数据、创建者签名和与修改无关的内容(不需要不涉及修改的那部分通道配置),我们通过 jq 这个工具来完成裁剪

请大家注意下,我用红线划起来的地方,

第一个..code::bash(代表命令需要在cli容器中执行)

关于bash,当你在进入v2.0版本以上cli容器时,它的前缀显示的就是 bash#

第二个划起来的地方,也就是官方命令出问题的地方,少了一个-号,应该是--input

configtxlator proto_decode --input config_block.pb --type common.Block | jq .data.data[0].payload.data.config > config.json

命令执行完毕后,我们得到一个裁剪后的 JSON 对象 config.json(包含的信息是关于org1、org2的) ,我们将它放置在 fabric-samples 下的 first-network 文件夹中 ---– first-network 是我们配置更新的基准工作目录。

我们退出容器,然后将容器内的config.json文件复制到本地first-network文件夹中,

exit #退出容器

docker cp cli:/opt/gopath/src/github.com/hyperledger/fabric/peer/config.json ./

然后你可以看到config.json已经被复制到first-network文件夹下了。

追加 Org3 加密材料

我们将再次使用 jq 工具去追加 Org3 的配置定义 org3.json 到通道的应用组字段,同时定义输出文件是 modified_config.json

先进入容器 CLI 中

docker exec -it cli bash

追加组织 3 的配置到通道应用组字段

jq -s '.[0] * {"channel_group":{"groups":{"Application":{"groups": {"Org3MSP":.[1]}}}}}' config.json ./channel-artifacts/org3.json > modified_config.json

现在,我们在 CLI 容器有两个重要的 JSON 文件,分别是 config.json 和 modified_config.json 。

初始的 json 文件包含 Org1 和 Org2 的配置,而 “modified” 文件包含了 3 个组织的配置;现在只需要将这 2 个 JSON 文件重新编码并计算出它们的差异部分。

  • 将裁剪后的对象 config.json 编码为 protobuf 格式并命名为 config.pb;

configtxlator proto_encode --input config.json --type common.Config --output config.pb

  • 将 modified_config.json 文件也编码为 protobuf 格式,命名为modified_config.pb:

configtxlator proto_encode --input modified_config.json --type common.Config --output modified_config.pb

现在使用 configtxlator 去计算两个 protobuf 配置的差异,这条命令会输出一个新的 protobuf 二进制文件并命名为 org3_update.pb。

configtxlator compute_update --channel_id mychannel --original config.pb --updated modified_config.pb --output org3_update.pb

这个新的 proto 文件(org3_update.pb)包含了 Org3 的定义和指向 Org1 和 Org2 材料的更高级别的指针。我们之所以抛弃 Org1 和 Org2 相关的 MSP 材料和修改策略信息,是因为这些数据已经存在于通道的初始区块中;因此,我们只需要保留两个配置的差异部分。

在我们提交通道更新前,我们执行最后几个步骤。

首先,我们将这个对象解码成可编辑的 JSON 格式并命名为 org3_update.json 。

configtxlator proto_decode --input org3_update.pb --type common.ConfigUpdate | jq . > org3_update.json

现在,我们有了一个解码后的更新文件 org3_update.json ,我们还对它的格式进行封装,这里使用信封消息来包装它。这个步骤要把之前裁剪掉的头部信息还原回来,我们将命名这个新文件为 org3_update_in_envelope.json。

  • 加入header信息(官方命令有误,以下面命令为准)

echo '{"payload":{"header":{"channel_header":{"channel_id":"mychannel", "type":2}},"data":{"config_update":'$(cat org3_update.json)'}}}' | jq . > org3_update_in_envelope.json

使用我们格式化好的 JSON 文件 (org3_update_in_envelope.json) 调用 configtxlator 工具转换为 Fabric 需要的完整独立的 protobuf 格式,我们将最后的更新对象命名为 org3_update_in_envelope.pb。

configtxlator proto_encode --input org3_update_in_envelope.json --type common.Envelope --output org3_update_in_envelope.pb

签名并提交配置更新

在我们的 CLI 容器内,已经有了一个 protobuf 二进制文件(org3_update_in_envelope.pb) 。但是,在配置写入到账本前,我们需要来自背书节点的 Admin 用户的签名。

我们通道应用组的修改策略(mod_policy)设置为默认值 “MAJORITY”,这意味着我们需要大多数已经存在的组织管理员去签名这个更新交易。因为我们只有两个组织 (Org1 和 Org2) ,即我们需要它们两个的签名。没有这两个签名,排序服务会因为不满足策略而拒绝这个交易。

首先,让我们以 Org1 管理员来签名这个更新 proto 。因为 CLI 容器是以 Org1 MSP 材料启动的,所以我们只需要简单地执行 peer channel signconfigtx 命令对配置更新交易签名:

peer channel signconfigtx -f org3_update_in_envelope.pb

最后一步,我们将 CLI 容器的身份切换为 Org2 管理员:

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

最后,我们执行 peer channel update 命令(Org2 管理员在执行这个命令过程中会附带签名,因此就没有必要对配置更新交易单独进行签名)。

对排序服务申请更新调用,会经历一系列的系统级签名和策略检查,在这个过程中,你会发现通过检视排序节点的日志流会非常有用。

在另外一个终端执行 docker logs -f orderer.example.com 命令就能展示它们了。

  • 发起更新调用:
peer channel update -f org3_update_in_envelope.pb -c $CHANNEL_NAME -o orderer.example.com:7050 --tls --cafile $ORDERER_CA

如果执行报错:--cafile 路径错误

只需要重新配置一下即可

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

成功的通道更新调用会返回一个新的区块给所有在这个通道上的节点。你是否还记得,区块 0-2 是初始的通道配置,而区块 3 和 4 是链码 mycc 的实例化和调用。所以,区块 5 就是带有 Org3 定义的最新的通道配置

  • 查看 peer0.org1.example.com 的日志(新开一个窗口)

docker logs -f peer0.org1.example.com

配置领导节点选举

引入这个章节作为通用参考,是为了理解在完成网络通道配置初始化之后,增加组织时,领导节点选举的设置。这个例子中,默认设置为动态领导选举,这是在 peer-base.yaml 文件中为网络中所有的节点设置的。

新加入的节点是根据初始区块启动的,初始区块是不包含通道配置更新中新加入的组织信息的。因此新的节点无法利用 gossip 协议,因为它们无法验证从自己组织里其他节点发送过 的区块,除非它们接收到将组织加入到通道的那个配置交易。新加入的节点必须有以下配置之一才能从排序服务接收区块:

1.采用静态领导者模式,将节点配置为组织的领导者

CORE_PEER_GOSSIP_USELEADERELECTION=false CORE_PEER_GOSSIP_ORGLEADER=true

这个配置对于新加入到通道中的所有节点必须一致。

2.采用动态领导者选举,配置节点采用领导选举的方式(推荐)

CORE_PEER_GOSSIP_USELEADERELECTION=true CORE_PEER_GOSSIP_ORGLEADER=false

因为新加入组织的节点,无法生成成员关系视图,这个选项和静态配置类似,每个节点启动时宣称自己是领导者。但是,一旦它们更新了将组织加入到通道的配置交易,组织中将只会有一个激活状态的领导者。如果你想最终组织的节点采用领导选举,那么建议你采用这个配置。

将Org3加入通道

此时,通道的配置已经更新并包含了我们新的组织(Org3),意味着这个组织下的节点可以加入 到 mychannel 。

首先,让我们部署 Org3 节点容器和 Org3-specific CLI 容器。

打开一个新的终端并从 first-network 目录启动 Org3 docker-compose

docker-compose -f docker-compose-org3.yaml up -d

这个新的 compose 文件配置为桥接我们的初始网络,因此两个节点容器和 CLI 容器可以连接到已经存在的节点和排序节点。当三个容器运行后,进入 Org3-specific CLI 容器:

docker exec -it Org3cli bash

和我们之前初始化 CLI 容器一样,导出两个关键环境变量: ORDERER_CA 和 CHANNEL_NAME

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

检查确保环境变量已经正确设置

echo ORDERER_CA && echo CHANNEL_NAME

现在,我们向排序服务发送一个获取 mychannel 初始区块的请求。如果通道更新成功执行,排序服务会成功校验这个请求中 Org3 的签名。如果 Org3 没有成功地添加到通 道配置中,排序服务会拒绝这个请求。

查看排序节点的签名和验签逻辑和策略检查的日志是很有用的

使用 peer channel fetch 命令来获取这个区块

peer channel fetch 0 mychannel.block -o orderer.example.com:7050 -c $CHANNEL_NAME --tls --cafile $ORDERER_CA

我们传递了 0 去索引我们在这个通道账本上想要的区块(例如,初始区块)。如果我们简单地执行 peer channel fetch config 命令,我们将会收到区块 5 (那个带有 Org3 定义的更新后的配置的最新区块)。然而,我们的账本不能从一个下游的区块开始(我们必须从区块 0 开始)。

执行 peer channel join 命令并指定初始区块(mychannel.block)。

peer channel join -b mychannel.block

如果你想将第二个节点加入到 Org3 中,导出 TLS 和 ADDRESS 变量,再重新执行 peer channel join command 。

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

升级和调用链码

升级链码的版本,并升级背书策略以加入 Org3 。

  • 先切换到 peer0.org3

export CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/tls/ca.crt && export CORE_PEER_ADDRESS=peer0.org3.example.com:11051

  • 安装 v2.0 版本的链码

peer chaincode install -n mycc -v 2.0 -p github.com/chaincode/chaincode_example02/go/

如果你要在 Org3 的第二个节点上安装链码,只需要修改环境变量切换身份并再次执行链码安装命令即可。注意第二次安装并不是强制的,因为你只需要在背书节点或者和账本有交互行为(比如,只做查询)节点上安装链码。即使没有运行链码容器,节点作为提交节点仍然会运行检验逻辑。

现在回到原始 CLI 容器,在 Org1 和 Org2 节点上安装新版本链码。

  • 切换到 peer0.org2 身份,执行以下命令

peer chaincode install -n mycc -v 2.0 -p github.com/chaincode/chaincode_example02/go/

  • 切回 peer0.org1 身份,执行以下命令

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

然后安装链码(版本号变更了)

peer chaincode install -n mycc -v 2.0 -p github.com/chaincode/chaincode_example02/go/

现在我们已经准备好升级链码,实际上底层的源代码没有任何变化,我们只是简单地在 mychannel 通道上的链码 (mycc) 的背书策略中增加了 Org3,并且更新了版本号 。

任何满足链码实例化策略的身份都可以执行升级调用,这些身份默认就是通道的管理者

  • 发送调用升级链码

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

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')"

链码升级和实例化一样需要用到 init 方法。,如果你的链码需要传递参数给 init 方法,那你需要在这里添加。

你可以看到上面的命令,我们用 -v 标志指定了新的版本号;你也能看到背书策略修改为 -P "OR ('Org1MSP.peer','Org2MSP.peer','Org3MSP.peer')" ,说明 Org3 被添加到策略中,最后一部分需要注意的是我们构造的请求参数(用 -c 标志指定)。

升级调用使得通道的账本添加一个新的区块(区块 6),这个区块中包含的更新配置允许 Org3 的节点在背书阶段执行交易,并为交易背书。

回到 Org3 CLI 容器,并执行对 a 的查询,这需要花费一点时间,因为需要为目标节点构建链码镜像并运行链码容器:

docker exec -it Org3cli bash

peer chaincode query -C mychannel -n mycc -c '{"Args":["query","a"]}'

现在执行调用,从 a 转移 10 到 b

peer chaincode invoke -o orderer.example.com:7050  --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA -C mychannel -n mycc -c '{"Args":["invoke","a","b","10"]}'

若成功,显示一下信息:

2021-07-27 09:09:07.910 UTC [chaincodeCmd] chaincodeInvokeOrQuery -> INFO 04c Chaincode invoke successful. result: status:200 

查询键a值是否变化

peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'

更新通道定义(为 Org3 设置锚节点)

由于 Org1 和 Org2 在通道配置中定义了锚点,因此 Org3 对等点能够与 Org1 和 Org2 对等点建立连接。同样,像 Org3 这样新添加的组织也应该在通道配置中定义它们的锚节点,以便来自其他组织的任何新对等点可以直接发现 Org3 中 peer 节点。

从 Org3 CLI 继续,我们将进行通道定义更新以为 Org3 设置锚节点,该过程将类似于之前的配置更新,因此我们这次会更快。

  • 在 Org3 的 CLI 容器内,使用命令获取通道的最新配置块
peer channel fetch config config_block.pb -o orderer.example.com:7050 -c $CHANNEL_NAME --tls --cafile $ORDERER_CA

获取配置块后,我们希望将其转换为 JSON 格式。为此,我们将使用 configtxlator 工具,就像之前将 Org3 添加到通道时所做的那样。转换时,我们需要使用 jq 工具删除与更新 Org3 无关的信息,还会删除更新锚节点不需要的所有标头、元数据和签名。

configtxlator proto_decode --input config_block.pb --type common.Block | jq .data.data[0].payload.data.config > config.json

该config.json是代表最新的通道配置,我们将更新修剪该配置文件。

  • 再次使用 jq 工具,将我们想要添加的 Org3 锚节点更新配置添加到配置文件中。

jq '.channel_group.groups.Application.groups.Org3MSP.values += {"AnchorPeers":{"mod_policy": "Admins","value":{"anchor_peers": [{"host": "peer0.org3.example.com","port": 11051}]},"version": "0"}}' config.json > modified_anchor_config.json

我们现在有两个 JSON 文件, config.json 用于当前通道配置,modified_anchor_config.json 用于所需的更新通道配置。接下来,我们将其中的每一个转换回 protobuf 格式并计算两者之间的增量。

  • 将config.json转换回 protobuf 格式,编码为 config.pb

configtxlator proto_encode --input config.json --type common.Config --output config.pb

  • 将 modified_anchor_config.json的protobuf 格式转换为modified_anchor_config.pb

configtxlator proto_encode --input modified_anchor_config.json --type common.Config --output modified_anchor_config.pb

  • 计算两个 protobuf 格式配置之间的增量

configtxlator compute_update --channel_id mychannel --original config.pb --updated modified_anchor_config.pb --output anchor_update.pb

现在我们已经对通道进行了所需的更新,我们必须将它包装在一个信封消息中,以便可以正确读取它。为此,我们必须首先将 protobuf 转换回可以包装的 JSON。我们将再次使用 configtxlator 命令转换anchor_update.pb为anchor_update.json

configtxlator proto_decode --input anchor_update.pb --type common.ConfigUpdate | jq . > anchor_update.json

  • 接下来我们将更新包装在一个信封消息中,恢复先前剥离的头,将其输出到 anchor_update_in_envelope.json

echo '{"payload":{"header":{"channel_header":{"channel_id":"mychannel", "type":2}},"data":{"config_update":'$(cat anchor_update.json)'}}}' | jq . > anchor_update_in_envelope.json

  • 现在我们已经重新合并了信封,我们需要将其转换为 protobuf,以便可以对其进行正确签名并提交给orderer以进行更新

configtxlator proto_encode --input anchor_update_in_envelope.json --type common.Envelope --output anchor_update_in_envelope.pb

现在更新已正确格式化,是时候签署并提交它了,由于这只是对 Org3 的更新,我们只需要让 Org3 签署更新即可。

我们当前位于 Org3 CLI 容器中,因此无需切换 CLI 容器身份,我们可以使用下面命令对配置更新进行签名,然后将签名后的配置更新提交给 orderer。

peer channel update -f anchor_update_in_envelope.pb -c mychannel -o orderer.example.com:7050 --tls --cafile $ORDERER_CA

orderer 收到配置更新请求并使用更新的配置切割一个块,当 peer 收到该块时,他们将处理配置更新。

检查 peer 之一的日志,在它处理来自新块的配置事务的相关日志中,您将看到 gossip 使用 Org3 的锚节点重新建立连接,这证明配置更新已成功应用!

docker logs -f peer0.org1.example.com

动态添加组织步骤总结

恭喜,您现在已经进行了两次配置更新 (一次将 Org3 添加到通道,第二次为 Org3 定义锚节点)。

到此为止,手动添加组织 3 已经完成,步骤很多,但是逻辑性很明确,可分为以下步骤:

1.为新组织产生加密材料和构件;

2.配置新组织的 peer 节点并启动;

3.获取最新的配置区块;

4.用 configtxlator 工具把配置区块转换成可编辑格式,增加新组织的配置信息, 使用 configtxlator 计算 proto 编码后的差值,再转换成原来的格式;

5.对更新的配置文件进行签名(按照配置的修改权限来确定需要签名的数量);

6.发送配置更新交易;

7.新增的peer节点加入通道(加入通道之后会自动同步账本和状态数据库);

8.新增的peer节点安装链码(v1.4之前),批准链码(v2.0之后);

9.新增的组织更新锚节点(按照3-4-5-6步骤来操作,5只需新增组织管理员的签名)。

静态添加一个 peer 节点(格式待排版)

1.4版本中,有两个组织,每个组织有两个peer,现在我们尝试往org2中再添加一个peer节点,并且在上面安装与其他节点不同的链码,并且能够与链码进行交互。

2.7.1.1 首先我们修改两个配置文件

在base/docker-compose-base.yaml末尾添加

  peer2.org2.example.com:
    container_name: peer2.org2.example.com
    extends:
      file: peer-base.yaml
      service: peer-base
    environment:
      - CORE_PEER_ID=peer2.org2.example.com
      - CORE_PEER_ADDRESS=peer2.org2.example.com:11051
      - CORE_PEER_LISTENADDRESS=0.0.0.0:11051
      - CORE_PEER_CHAINCODEADDRESS=peer2.org2.example.com:11052
      - CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:11052
      - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer2.org2.example.com:11051
      - CORE_PEER_GOSSIP_BOOTSTRAP=peer1.org2.example.com:10051
      - CORE_PEER_LOCALMSPID=Org2MSP
    volumes:
        - /var/run/:/host/var/run/
        - ../crypto-config/peerOrganizations/org2.example.com/peers/peer2.org2.example.com/msp:/etc/hyperledger/fabric/msp
        - ../crypto-config/peerOrganizations/org2.example.com/peers/peer2.org2.example.com/tls:/etc/hyperledger/fabric/tls
        - peer2.org2.example.com:/var/hyperledger/production
    ports:
      - 11051:11051

修改docker-compose-cli.yaml配置文件

# Copyright IBM Corp. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
#

version: '2'

volumes:
  orderer.example.com:
  peer0.org1.example.com:
  peer1.org1.example.com:
  peer0.org2.example.com:
  peer1.org2.example.com:
  peer2.org2.example.com:   #新增节点卷

networks:
  byfn:

services:

  orderer.example.com:
    extends:
      file:   base/docker-compose-base.yaml
      service: orderer.example.com
    container_name: orderer.example.com
    networks:
      - byfn

  peer0.org1.example.com:
    container_name: peer0.org1.example.com
    extends:
      file:  base/docker-compose-base.yaml
      service: peer0.org1.example.com
    networks:
      - byfn

  peer1.org1.example.com:
    container_name: peer1.org1.example.com
    extends:
      file:  base/docker-compose-base.yaml
      service: peer1.org1.example.com
    networks:
      - byfn

  peer0.org2.example.com:
    container_name: peer0.org2.example.com
    extends:
      file:  base/docker-compose-base.yaml
      service: peer0.org2.example.com
    networks:
      - byfn

  peer1.org2.example.com:
    container_name: peer1.org2.example.com
    extends:
      file:  base/docker-compose-base.yaml
      service: peer1.org2.example.com
    networks:
      - byfn

  peer2.org2.example.com: 				# 新增的节点服务
    container_name: peer2.org2.example.com
    extends:
      file:  base/docker-compose-base.yaml
      service: peer2.org2.example.com
    networks:
      - byfn

  cli:
    container_name: cli
    image: hyperledger/fabric-tools:$IMAGE_TAG
    tty: true
    stdin_open: true
    environment:
      - SYS_CHANNEL=$SYS_CHANNEL
      - GOPATH=/opt/gopath
      - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
      #- FABRIC_LOGGING_SPEC=DEBUG
      - FABRIC_LOGGING_SPEC=INFO
      - CORE_PEER_ID=cli
      - CORE_PEER_ADDRESS=peer0.org1.example.com:7051
      - CORE_PEER_LOCALMSPID=Org1MSP
      - CORE_PEER_TLS_ENABLED=true
      - CORE_PEER_TLS_CERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.crt
      - CORE_PEER_TLS_KEY_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.key
      - 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
      - CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
    working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
    command: /bin/bash
    volumes:
        - /var/run/:/host/var/run/
        - ./../chaincode/:/opt/gopath/src/github.com/chaincode
        - ./crypto-config:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/
        - ./scripts:/opt/gopath/src/github.com/hyperledger/fabric/peer/scripts/
        - ./channel-artifacts:/opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts
    depends_on:
      - orderer.example.com
      - peer0.org1.example.com
      - peer1.org1.example.com
      - peer0.org2.example.com
      - peer1.org2.example.com
      - peer2.org2.example.com # 新增 cli 对 peer2.org2.example.com 服务的依赖
    networks:
      - byfn

一共修改了三处,我在旁边进行了注释。

2.7.1.2 为新节点生成证书和加密材料

先将原有的加密材料证书以及通道组件删除

rm -rf channel-artifacts rm -rf crypto-config

mkdir channel-artifacts

重新生成加密材料和证书

cryptogen generate --config=./crypto-config.yaml --output ./crypto-config

2.7.1.3 生成创世区块、通道交易配置和锚节点更新文件

告诉 configtxgen 工具去哪儿寻找它需要的 configtx.yaml 文件

export FABRIC_CFG_PATH=$PWD

调用 configtxgen 工具去创建排序通道创世区块

configtxgen -profile TwoOrgsOrdererGenesis -channelID byfn-sys-channel -outputBlock ./channel-artifacts/genesis.block

创建通道配置交易

export CHANNEL_NAME=mychannel && configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID $CHANNEL_NAME

为通道上的 Org1 定义锚节点

configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP

为通道上的Org2定义锚节点

configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org2MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org2MSP

2.7.1.4 启动网络

如果之前网络启动还未关闭请执行以下命令:

./byfn down

docker volume prune (y)

然后使用指定docker-compose文件启动网络

docker-compose -f docker-compose-cli.yaml up -d

2.7.1.5 创建通道并将节点加入通道

docker exec -it cli bash

声明应用通道名称并创建通道

export CHANNEL_NAME=mychannel

peer channel create -o orderer.example.com:7050 -c mychannel -f ./channel-artifacts/channel.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

将所有节点都加入通道

  • peer0.org1.example.com加入通道

peer channel join -b mychannel.block

  • 将peer1.org1.example.com加入通道

CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp CORE_PEER_ADDRESS=peer1.org1.example.com:8051 CORE_PEER_LOCALMSPID="Org1MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls/ca.crt

peer channel join -b mychannel.block

  • 将peer0.org2.example.com加入通道

CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp CORE_PEER_ADDRESS=peer0.org2.example.com:9051 CORE_PEER_LOCALMSPID="Org2MSP" 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

peer channel join -b mychannel.block

  • 将peer1.org2.example.com加入通道

CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp CORE_PEER_ADDRESS=peer1.org2.example.com:10051 CORE_PEER_LOCALMSPID="Org2MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/tls/ca.crt

peer channel join -b mychannel.block

  • 将peer2.org2.example.com加入通道

CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp CORE_PEER_ADDRESS=peer2.org2.example.com:11051 CORE_PEER_LOCALMSPID="Org2MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer2.org2.example.com/tls/ca.crt

peer channel join -b mychannel.block

2.7.1.6 更新锚节点

将 Org1 的锚节点定义为 peer0.org1.example.com

CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp CORE_PEER_ADDRESS=peer0.org1.example.com:7051 CORE_PEER_LOCALMSPID="Org1MSP" 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

peer channel update -o orderer.example.com:7050 -c mychannel -f ./channel-artifacts/Org1MSPanchors.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

将 Org2 的锚节点定义为 peer0.org2.example.com

CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp CORE_PEER_ADDRESS=peer0.org2.example.com:9051 CORE_PEER_LOCALMSPID="Org2MSP" 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

peer channel update -o orderer.example.com:7050 -c mychannel -f ./channel-artifacts/Org2MSPanchors.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

2.7.1.7 安装并实例化链码

peer0.Org1安装链码mycc

CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp CORE_PEER_ADDRESS=peer0.org1.example.com:7051 CORE_PEER_LOCALMSPID="Org1MSP" 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

peer chaincode install -n mycc -v 1.0 -p github.com/chaincode/chaincode_example02/go/

peer0.Org2安装链码mycc

CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp CORE_PEER_ADDRESS=peer0.org2.example.com:9051 CORE_PEER_LOCALMSPID="Org2MSP" 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

peer chaincode install -n mycc -v 1.0 -p github.com/chaincode/chaincode_example02/go/

在通道上实例化链码mycc

peer chaincode instantiate -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n mycc -v 1.0 -c '{"Args":["init","a", "100", "b","200"]}' -P "AND ('Org1MSP.peer','Org2MSP.peer')"

2.7.1.8 与链码交互(查询、调用)

查询键为a的值

peer chaincode query -C mychannel -n mycc -c '{"Args":["query","a"]}'

a向b转账10

peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n mycc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"Args":["invoke","a","b","10"]}'

再次查询键为a的值

peer chaincode query -C mychannel -n mycc -c '{"Args":["query","a"]}'

在peer2.org2上安装链码marbles

CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp CORE_PEER_ADDRESS=peer2.org2.example.com:11051 CORE_PEER_LOCALMSPID="Org2MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer2.org2.example.com/tls/ca.crt

peer chaincode install -n marbles -v 1.0 -p github.com/chaincode/marbles02/go

在通道上实例化链码marbles

peer chaincode instantiate -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles -v 1.0 -c '{"Args":["init"]}' -P "OR ('Org1MSP.peer','Org2MSP.peer')"

创建一些 marbles例子

peer chaincode invoke -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles -c '{"Args":["initMarble","marble1","blue","35","tom"]}'

peer chaincode invoke -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles -c '{"Args":["initMarble","marble2","red","50","tim"]}'

peer chaincode invoke -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles -c '{"Args":["initMarble","marble3","blue","70","mother"]}'

peer chaincode invoke -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles -c '{"Args":["initMarble","marble4","yellow","90","father"]}'

peer chaincode invoke -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles -c '{"Args":["initMarble","marble5","white","110","hello"]}'

peer chaincode invoke -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles -c '{"Args":["delete","marble1"]}'

peer chaincode invoke -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles -c '{"Args":["initMarble","marble1","black","65","pim"]}'

链码marbles账本的常规查询(得到当前的世界状态)

peer chaincode query -C mychannel -n marbles -c '{"Args":["readMarble","marble2"]}'

链码marbles账本的特定查询(得到该marbles的交易日志)

peer chaincode query -C mychannel -n marbles -c '{"Args":["getHistoryForMarble","marble1"]}'

该命令显示所有marbles的修改历史(不包括当前状态)

2.7.2 动态添加peer节点

首先我们先准备好基础环境:

将静态添加节点启动的网络关闭

docker-compose -f docker-compose-cli.yaml down docker volume prune

使用脚本启动网络

./byfn up

2.7.2.1 生成新节点证书(extend)

此时组织1、组织2的前两个节点都已经加入通道并且都安装好了链码,通道上的链码mycc也已经成功实例化,我们现在要做的是,往网络中Org1中添加一个peer节点(peer2.org1.example.com),并且在上面也安装mycc链码,并使用peer2.org1的身份对链码进行查询和调用。

每个节点都有相应的证书才能连接网络,需要使用 cryptogen 工具生成新节点的证书。因为是新节点加入现有网络组织,因此,需要使用 cryptogen extend 命令首先对现有网络的证书文件进行扩展。在执行该命令之前需要首先修改 crypto-config.yaml 配置文件,将 org1 组织的节点数增加 1,其它的配置信息不要修改。

   Template:
      Count: 3

修改完成后即可使用 cryptogen extend 命令生成新节点证书,注意,这里需要使用 --config 选项指定刚才修改的配置文件

cryptogen extend --config=crypto-config.yaml

2.7.2.2 添加新节点配置信息并启动

证书文件生成之后,需要在 base/docker-compose-base.yaml 中添加新节点 peer2.org2.example.com 的配置信息,包括环境变量、映射的端口号、证书文件目录等信息。

与刚才静态添加peer节点相同,我们同样需要更改两个配置文件,一个是位于base文件夹下的docker-compose-base.yaml,另一个是工作目录下的docker-compose-cli.yaml.

修改模板如下: **docker-compose-base.yaml

# Copyright IBM Corp. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
#

version: '2'

services:

  orderer.example.com:
    container_name: orderer.example.com
    extends:
      file: peer-base.yaml
      service: orderer-base
    volumes:
        - ../channel-artifacts/genesis.block:/var/hyperledger/orderer/orderer.genesis.block
        - ../crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/msp:/var/hyperledger/orderer/msp
        - ../crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/tls/:/var/hyperledger/orderer/tls
        - orderer.example.com:/var/hyperledger/production/orderer
    ports:
      - 7050:7050

  peer0.org1.example.com:
    container_name: peer0.org1.example.com
    extends:
      file: peer-base.yaml
      service: peer-base
    environment:
      - CORE_PEER_ID=peer0.org1.example.com
      - CORE_PEER_ADDRESS=peer0.org1.example.com:7051
      - CORE_PEER_LISTENADDRESS=0.0.0.0:7051
      - CORE_PEER_CHAINCODEADDRESS=peer0.org1.example.com:7052
      - CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:7052
      - CORE_PEER_GOSSIP_BOOTSTRAP=peer1.org1.example.com:8051
      - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org1.example.com:7051
      - CORE_PEER_LOCALMSPID=Org1MSP
    volumes:
        - /var/run/:/host/var/run/
        - ../crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/msp:/etc/hyperledger/fabric/msp
        - ../crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls:/etc/hyperledger/fabric/tls
        - peer0.org1.example.com:/var/hyperledger/production
    ports:
      - 7051:7051

  peer1.org1.example.com:
    container_name: peer1.org1.example.com
    extends:
      file: peer-base.yaml
      service: peer-base
    environment:
      - CORE_PEER_ID=peer1.org1.example.com
      - CORE_PEER_ADDRESS=peer1.org1.example.com:8051
      - CORE_PEER_LISTENADDRESS=0.0.0.0:8051
      - CORE_PEER_CHAINCODEADDRESS=peer1.org1.example.com:8052
      - CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:8052
      - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer1.org1.example.com:8051
      - CORE_PEER_GOSSIP_BOOTSTRAP=peer0.org1.example.com:7051
      - CORE_PEER_LOCALMSPID=Org1MSP
    volumes:
        - /var/run/:/host/var/run/
        - ../crypto-config/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/msp:/etc/hyperledger/fabric/msp
        - ../crypto-config/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls:/etc/hyperledger/fabric/tls
        - peer1.org1.example.com:/var/hyperledger/production

    ports:
      - 8051:8051
#此处为新添加
  peer2.org1.example.com:
    container_name: peer2.org1.example.com
    extends:
      file: peer-base.yaml
      service: peer-base
    environment:
      - CORE_PEER_ID=peer2.org1.example.com
      - CORE_PEER_ADDRESS=peer2.org1.example.com:7151
      - CORE_PEER_LISTENADDRESS=0.0.0.0:7151
      - CORE_PEER_CHAINCODEADDRESS=peer2.org1.example.com:7152
      - CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:7152
      - CORE_PEER_GOSSIP_BOOTSTRAP=peer1.org1.example.com:8051
      - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer2.org1.example.com:7151
      - CORE_PEER_LOCALMSPID=Org1MSP
    volumes:
        - /var/run/:/host/var/run/
        - ../crypto-config/peerOrganizations/org1.example.com/peers/peer2.org1.example.com/msp:/etc/hyperledger/fabric/msp
        - ../crypto-config/peerOrganizations/org1.example.com/peers/peer2.org1.example.com/tls:/etc/hyperledger/fabric/tls
        - peer2.org1.example.com:/var/hyperledger/production
    ports:
      - 7151:7151


  peer0.org2.example.com:
    container_name: peer0.org2.example.com
    extends:
      file: peer-base.yaml
      service: peer-base
    environment:
      - CORE_PEER_ID=peer0.org2.example.com
      - CORE_PEER_ADDRESS=peer0.org2.example.com:9051
      - CORE_PEER_LISTENADDRESS=0.0.0.0:9051
      - CORE_PEER_CHAINCODEADDRESS=peer0.org2.example.com:9052
      - CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:9052
      - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org2.example.com:9051
      - CORE_PEER_GOSSIP_BOOTSTRAP=peer1.org2.example.com:10051
      - CORE_PEER_LOCALMSPID=Org2MSP
    volumes:
        - /var/run/:/host/var/run/
        - ../crypto-config/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/msp:/etc/hyperledger/fabric/msp
        - ../crypto-config/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls:/etc/hyperledger/fabric/tls
        - peer0.org2.example.com:/var/hyperledger/production
    ports:
      - 9051:9051

  peer1.org2.example.com:
    container_name: peer1.org2.example.com
    extends:
      file: peer-base.yaml
      service: peer-base
    environment:
      - CORE_PEER_ID=peer1.org2.example.com
      - CORE_PEER_ADDRESS=peer1.org2.example.com:10051
      - CORE_PEER_LISTENADDRESS=0.0.0.0:10051
      - CORE_PEER_CHAINCODEADDRESS=peer1.org2.example.com:10052
      - CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:10052
      - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer1.org2.example.com:10051
      - CORE_PEER_GOSSIP_BOOTSTRAP=peer0.org2.example.com:9051
      - CORE_PEER_LOCALMSPID=Org2MSP
    volumes:
        - /var/run/:/host/var/run/
        - ../crypto-config/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/msp:/etc/hyperledger/fabric/msp
        - ../crypto-config/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/tls:/etc/hyperledger/fabric/tls
        - peer1.org2.example.com:/var/hyperledger/production
    ports:
      - 10051:10051

  peer2.org2.example.com:
    container_name: peer2.org2.example.com
    extends:
      file: peer-base.yaml
      service: peer-base
    environment:
      - CORE_PEER_ID=peer2.org2.example.com
      - CORE_PEER_ADDRESS=peer2.org2.example.com:11051
      - CORE_PEER_LISTENADDRESS=0.0.0.0:11051
      - CORE_PEER_CHAINCODEADDRESS=peer2.org2.example.com:11052
      - CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:11052
      - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer2.org2.example.com:11051
      - CORE_PEER_GOSSIP_BOOTSTRAP=peer1.org2.example.com:10051
      - CORE_PEER_LOCALMSPID=Org2MSP
    volumes:
        - /var/run/:/host/var/run/
        - ../crypto-config/peerOrganizations/org2.example.com/peers/peer2.org2.example.com/msp:/etc/hyperledger/fabric/msp
        - ../crypto-config/peerOrganizations/org2.example.com/peers/peer2.org2.example.com/tls:/etc/hyperledger/fabric/tls
        - peer2.org2.example.com:/var/hyperledger/production
    ports:
      - 11051:11051

**docker-compose-cli.yaml

# Copyright IBM Corp. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
#

version: '2'

volumes:
  orderer.example.com:
  peer0.org1.example.com:
  peer1.org1.example.com:
  peer2.org1.example.com: #此处为新添加处
  peer0.org2.example.com:
  peer1.org2.example.com:
  peer2.org2.example.com:

networks:
  byfn:

services:

  orderer.example.com:
    extends:
      file:   base/docker-compose-base.yaml
      service: orderer.example.com
    container_name: orderer.example.com
    networks:
      - byfn

  peer0.org1.example.com:
    container_name: peer0.org1.example.com
    extends:
      file:  base/docker-compose-base.yaml
      service: peer0.org1.example.com
    networks:
      - byfn

  peer1.org1.example.com:
    container_name: peer1.org1.example.com
    extends:
      file:  base/docker-compose-base.yaml
      service: peer1.org1.example.com
    networks:
      - byfn

  peer2.org1.example.com:
    container_name: peer2.org1.example.com
    extends:
      file:  base/docker-compose-base.yaml
      service: peer2.org1.example.com
    networks:
      - byfn

        
  peer0.org2.example.com:
    container_name: peer0.org2.example.com
    extends:
      file:  base/docker-compose-base.yaml
      service: peer0.org2.example.com
    networks:
      - byfn
#此处为新添加处
  peer1.org2.example.com:
    container_name: peer1.org2.example.com
    extends:
      file:  base/docker-compose-base.yaml
      service: peer1.org2.example.com
    networks:
      - byfn

  peer2.org2.example.com:
    container_name: peer2.org2.example.com
    extends:
      file:  base/docker-compose-base.yaml
      service: peer2.org2.example.com
    networks:
      - byfn

  cli:
    container_name: cli
    image: hyperledger/fabric-tools:$IMAGE_TAG
    tty: true
    stdin_open: true
    environment:
      - SYS_CHANNEL=$SYS_CHANNEL
      - GOPATH=/opt/gopath
      - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
        #- FABRIC_LOGGING_SPEC=DEBUG
      - FABRIC_LOGGING_SPEC=INFO
      - CORE_PEER_ID=cli
      - CORE_PEER_ADDRESS=peer0.org1.example.com:7051
      - CORE_PEER_LOCALMSPID=Org1MSP
      - CORE_PEER_TLS_ENABLED=true
      - CORE_PEER_TLS_CERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.crt
      - CORE_PEER_TLS_KEY_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.key
      - 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
      - CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
    working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
    command: /bin/bash
    volumes:
        - /var/run/:/host/var/run/
        - ./../chaincode/:/opt/gopath/src/github.com/chaincode
        - ./crypto-config:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/
        - ./scripts:/opt/gopath/src/github.com/hyperledger/fabric/peer/scripts/
        - ./channel-artifacts:/opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts
    depends_on:
      - orderer.example.com
      - peer0.org1.example.com
      - peer1.org1.example.com
      - peer2.org1.example.com #此处为新添加处
      - peer0.org2.example.com
      - peer1.org2.example.com
      - peer2.org2.example.com
    networks:
      - byfn

之后使用 docker-compose 启动新节点容器并重新创建 cli 容器

docker-compose -f docker-compose-cli.yaml

至此,新节点 peer2.org1.example.com 已经启动。

2.7.2.3 新节点加入通道

此时该节点并没有加入到任何一个通道中,需要进入 cli 容器执行添加操作。

进入 cli 命令行,之后的所有操作均在容器内部进行

docker exec -it cli bash

设置环境变量,使 cli 切换到 peer2.org1.example.com 下

CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp CORE_PEER_ADDRESS=peer2.org1.example.com:7151 CORE_PEER_LOCALMSPID="Org1MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer2.org1.example.com/tls/ca.crt

从 orderer 上拉取通道的创世区块

peer channel fetch oldest mychannel.block -c mychannel -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

将peer2.org1加入通道

peer channel join -b mychannel.block -o orderer.example.com:7050

2.7.2.4 在组织1的peer2节点上安装链码

peer chaincode install -n mycc -v 1.0 -p github.com/chaincode/chaincode_example02/go

2.7.2.5 实例化链码

同一个通道内所有节点只需要对同样的链码实例化一次即可,该链码已经在之前的旧有节点初始化一次,所以新节点安装完链码后并不需要再次实例化,直接可以与链码交互。

2.7.2.6 与链码交互(查询、调用)

检验链码是否安装成功就需要简单查询链码内容

peer chaincode query -C mychannel -n mycc -c '{"Args":["query","a"]}'

a向b转账40

peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n mycc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"Args":["invoke","a","b","40"]}'

b向a转账100

peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n mycc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"Args":["invoke","b","a","100"]}'

a执行第一次转账时,余额为90,b余额210 第二次转账,a余额50,b余额250 第三次转账,a余额150,b余额150

执行最后一次查询,查看结果是否与上面分析相同

关闭网络并清理网络环境

docker-compose -f docker-compose-cli.yaml down docker volume prune

下面我们将之前讲解的各个对fabric网络修改的操作综合一下,实现一个比较复杂的网络。