Hyperledger Fabric官方中文教程-改进笔记(十四)-向通道中添加组织
本Fabric中文文档专栏的阅读前言:前言
文章目录
- 设置环境
- 使用脚本将 Org3 加入通道
- 手动将 Org3 加入通道
- 生成 Org3 加密材料
- 启动Org3组件
- 获取配置
- 将配置转换为 JSON 并精简
- 添加 Org3 加密材料
- 签署并提交配置更新
- 让 Org3 加入通道
- 通过创世区块加入
- 通过快照加入
- 配置 Leader 选举
- 安装、定义并调用链码
- 打包链码
- 批准链码定义
- 调用链码
- 总结
- 更新通道配置以包含 Org3 锚节点(可选)
- ==笔者注:==
- 手动添加组织和手动添加节点对比
- 一、两者的共性
- 二、差异点对比
- 三、这套步骤还能做什么
- 总结:
- 出现的问题
- docker.sock文件路径问题
注意
确保你已经按照 安装 Fabric 和 Fabric 示例 和 先决条件 中的说明下载了与本文档版本相符的镜像和二进制文件(你可以在左侧目录的底部找到文档版本号)。
本教程将通过向应用通道添加一个新组织 —— Org3 —— 来扩展 Fabric 测试网络。
虽然我们将重点放在向通道添加新组织,但你也可以使用类似的过程来进行其他通道配置更新(例如更新修改策略或更改批处理大小)。想了解更多关于通道配置更新的流程和可能性,请参考 更新通道配置。
还值得注意的是,像本文演示的通道配置更新通常由组织管理员负责,而不是链码或应用开发者。
设置环境
我们将在本地克隆的 fabric-samples
中的 test-network
子目录下进行操作。现在切换到该目录:
cd fabric-samples/test-network
首先,使用 network.sh
脚本清理环境。此命令会终止任何活跃或遗留的 Docker 容器,并删除先前生成的构件。执行通道配置更新任务时并 不需要 必须关闭 Fabric 网络。然而,在本教程中,我们希望从一个已知的初始状态开始。因此,运行以下命令清理旧环境:
./network.sh down
现在可以使用脚本启动一个包含名为 channel1
的通道的测试网络:
./network.sh up createChannel -c channel1
如果命令执行成功,你将在日志中看到如下消息:
Channel 'channel1' joined
现在你已经在本地运行了一个干净的测试网络,我们可以开始将新组织添加到我们创建的通道。首先,我们会使用脚本把 Org3 添加到通道中,以确认流程可行。随后,我们会逐步讲解如何通过手动更新通道配置来完成添加 Org3 的过程。
使用脚本将 Org3 加入通道
确保你当前位于 test-network
目录。要使用脚本,只需执行以下命令:
cd addOrg3 ./addOrg3.sh up -c channel1
这里的输出值得仔细阅读。你将看到 Org3 的加密材料被生成,Org3 的组织定义被创建,然后通道配置被更新、签名并提交到通道。
如果一切顺利,你会得到如下消息:
Org3 peer successfully added to network
现在我们已经确认可以将 Org3 添加到通道,接下来我们会逐步走一遍脚本在幕后完成的通道配置更新过程。
手动将 Org3 加入通道
如果你刚刚使用了 addOrg3.sh
脚本,你需要关闭网络。以下命令会关闭所有运行的组件并删除所有组织的加密材料:
cd .. ./network.sh down
网络关闭后,再次启动:
./network.sh up createChannel -c channel1
这会把网络恢复到执行 addOrg3.sh
脚本之前的状态。
现在我们准备手动将 Org3 添加到通道。第一步,我们需要生成 Org3 的加密材料。
生成 Org3 加密材料
在另一个终端,切换到 test-network
的子目录 addOrg3
中。
cd addOrg3
首先,我们将为Org3的peer节点以及一个应用程序和管理员用户创建证书和密钥。因为我们在更新一个示例通道,所以我们将使用 cryptogen
工具代替CA。下面的命令使用 cryptogen
读取 org3-crypto.yaml
文件并在 org3.example.com
文件夹中生成Org3的加密材料。
../../bin/cryptogen generate --config=org3-crypto.yaml --output="../organizations"
在 test-network/organizations/peerOrganizations
目录中,你能在Org1和Org2证书和秘钥旁边找到已生成的Org3加密材料。
一旦我们生成了Org3的加密材料,我们就能使用 configtxgen
工具打印出Org3的组织定义。我们将在执行命令前告诉这个工具在当前目录去获取 configtx.yaml
文件。
export FABRIC_CFG_PATH=$PWD
../../bin/configtxgen -printOrg Org3MSP > ../organizations/peerOrganizations/org3.example.com/org3.json
注:PWD为addOrg3目录。这里的configtx.yaml文件与==《使用测试网络创建通道》==文章里提到的
test-network/configtx
下的configtx.yaml有些许不同。而后者是创建通道时对org1,ogr2,排序组织和通道配置的定义。这里的yaml是单独为org3写的,即扩展用途。
上面的命令会创建一个 JSON 文件 – org3.json
– 并将其写入到 test-network/organizations/peerOrganizations/org3.example.com
文件夹下。这个组织定义文件包含了Org3 的策略定义,还有三个 base 64 格式的重要的证书:
- 一个CA 根证书t, 用于建立组织的根信任
- 一个TLS根证书, 用于在gossip协议中识别Org3的块传播和服务发现
- 管理员用户证书 (以后作为Org3的管理员会用到它)
我们将通过用 configtxgen 从 configtx.yaml 提取 Org3MSP 的组织配置,生成 JSON 文件,供后续将 Org3 加入到应用通道使用。
启动Org3组件
在创建了Org3证书材料之后,现在可以启动Org3 peer节点。在addOrg3目录中执行以下命令:
docker-compose -f compose/compose-org3.yaml -f compose/docker/docker-compose-org3.yaml up -d
注:docker compose命令
-f
可以指定多个 Compose 文件,但后面的文件会 覆盖 前面文件里的同名服务或配置。前者是定义 Org3 服务的逻辑结构和环境变量,后者是补充或覆盖 Docker 相关的细节配置。感兴趣的可以去vim对比一下区别,两个文件都比较简单。
如果命令成功执行,你将看到Org3 peer节点的创建和一个命名为Org3CLI的Fabric tools容器:
Creating peer0.org3.example.com ... done
Creating Org3cli ... done
这个 Docker Compose 文件已经被配置为与我们初始网络建立桥接,使得 Org3 的 peer 能够与测试网络中现有的 peer 和排序节点进行解析。
./addOrg3.sh up
命令使用了jq
命令行工具来执行通道配置更新过程。建议在本地机器上直接按照下面的流程操作。
获取配置
让我们获取通道 – mychannel
的最新的配置区块。
我们必须拉取最新版本配置的原因是通道配置元素是版本化的。版本管理由于一些原因显得很重要。它可以防止通道配置更新被重复或者重放攻击(例如,回退到带有旧的 CRLs的通道配置将会产生安全风险)。同时它保证了并行性(例如,如果你想从你的通道中添加新的组织后,再删除一个组织,版本管理可以帮助你移除想移除的那个组织,并防止移除两个组织)。
因为Org3还不是通道的成员,所以我们需要作为另一个组织的管理员来操作以获取通道配置。因为Org1是通道的成员,所以Org1管理员有权从ordering服务中获取通道配置。作为Org1管理员进行操作,执行以下命令。
# you can issue all of these commands at onceexport PATH=${PWD}/../bin:$PATH
export FABRIC_CFG_PATH=${PWD}/../config/
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID=Org1MSP
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=localhost:7051
我们现在执行命令获取最新的配置块:
peer channel fetch config channel-artifacts/config_block.pb -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com -c channel1 --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem"
这个命令将通道配置区块以二进制protobuf形式保存在 config_block.pb
。注意文件的名字和扩展名可以任意指定。但是,推荐遵循标识要表示的对象类型及其编码(protobuf或JSON)的约定。
当你执行 peer channel fetch
命令后,下面的输出将出现在你的日志中:
2017-11-07 17:17:57.383 UTC [channelCmd] readBlock -> DEBU 011 Received block: 2
这是告诉我们最新的 mychannel
的配置区块实际上是区块 2, **并非**初始区块。 peer channel fetch config
命令默认返回目标通道最新的配置区块,在这个例子里是第三个区块。这是因为测试网络脚本 network.sh
分别在两个通道更新交易中为两个组织 – Org1
和 Org2
定义了锚节点。最终,我们有如下的配置块序列:
- block 0: genesis block
- block 1: Org1 anchor peer update
- block 2: Org2 anchor peer update
将配置转换为 JSON 并精简
通道配置区块被存储在 channel-artifacts
文件夹中,以便将更新过程与其他制品分开。进入 channel-artifacts
文件夹来完成接下来的步骤:
cd channel-artifacts
现在我们将使用 configtxlator
工具把该通道配置区块解码为 JSON 格式(人类可读并可修改)。我们还必须去除所有与我们要进行的更改无关的头部、元数据、创建者签名等。我们通过 jq
工具来完成这一操作(你需要在本地机器上安装 jq 工具):
configtxlator proto_decode --input config_block.pb --type common.Block --output config_block.json
jq ".data.data[0].payload.data.config" config_block.json > config.json
该命令会生成一个精简后的 JSON 对象 —— config.json
,它将作为我们配置更新的基准。
花点时间用你喜欢的文本编辑器(或者浏览器)打开这个文件。即使在完成本教程后,也值得研究它,因为它揭示了底层的配置结构以及可以进行的其他类型的通道更新。我们在 更新通道配置 中对它们进行了更详细的讨论。
添加 Org3 加密材料
注意
到目前为止你所执行的步骤几乎对任何类型的配置更新都相同。我们选择在本教程中添加一个组织,因为这是你能够尝试的最复杂的通道配置更新之一。
我们将再次使用 jq
工具,把 Org3 配置定义 —— org3.json
—— 追加到通道的 application groups 字段中,并将输出命名为 —— modified_config.json
:
jq -s '.[0] * {"channel_group":{"groups":{"Application":{"groups": {"Org3MSP":.[1]}}}}}' config.json ../organizations/peerOrganizations/org3.example.com/org3.json > modified_config.json
注:jp命令详解
jq -s
--s
(slurp)表示把输入的多个 JSON 文件读进来,合并成一个 JSON 数组。
-.[0]
= 第一个输入文件 →config.json
(当前通道配置快照)
-.[1]
= 第二个输入文件 →org3.json
(Org3 的 MSP 定义).[0] * {...}
-*
在 jq 里表示合并(update operator),右边的内容会覆盖或新增到左边的 JSON。
- 也就是说:把通道原有配置.[0]
和你手动写的结构{...}
进行合并。{"channel_group":{"groups":{"Application":{"groups": {"Org3MSP":.[1]}}}}}
- 这里写出了通道配置的嵌套结构:
-channel_group.groups.Application.groups
→ 这是应用组织的配置位置
-"Org3MSP": .[1]
→ 把第二个文件里的 JSON (org3.json
) 塞到 Org3MSP 下面> modified_config.json
- 把合并后的结果输出到modified_config.json
,作为后续通道更新的基础。
现在我们有两个需要关注的 JSON 文件 —— config.json
和 modified_config.json
。初始文件仅包含 Org1 和 Org2 的数据,而“修改后的”文件包含了三个组织。此时,只需重新编码这两个 JSON 文件并计算它们之间的差异即可。
首先,把 config.json
转换回一个名为 config.pb
的 protobuf:
configtxlator proto_encode --input config.json --type common.Config --output config.pb
接下来,把 modified_config.json
编码为 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 channel1 --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 --output org3_update.json
现在,我们得到了一个解码后的更新文件 —— org3_update.json
—— 需要将其包装在一个 envelope (信件格式)消息中。此步骤会重新生成我们之前去掉的 header 字段。我们将该文件命名为 org3_update_in_envelope.json
:
echo '{"payload":{"header":{"channel_header":{"channel_id":"channel1", "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
签署并提交配置更新
快完成了!
我们现在已经有一个 protobuf 二进制文件 —— org3_update_in_envelope.pb
。但是,在配置写入账本之前,我们还需要必要的管理员用户签名。我们的通道 Application group 的修改策略(mod_policy)默认为 “MAJORITY”,这意味着我们需要大多数现有组织管理员的签名。由于目前只有两个组织 —— Org1 和 Org2 —— 而两个的多数就是两个,所以我们需要两者都签名。如果缺少任何一个签名,排序服务就会拒绝该交易,因为未满足策略要求。
首先,我们作为 Org1 来签署该更新 proto。切换回 test-network
目录:
cd ..
请记住,我们之前已经导出了必要的环境变量,以 Org1 管理员身份运行。因此,下面的命令会以 Org1 的身份签署该更新:
peer channel signconfigtx -f channel-artifacts/org3_update_in_envelope.pb
最后一步是将容器身份切换为 Org2 管理员用户。我们通过导出 Org2 MSP 的四个环境变量来完成。
注意
在组织之间切换以签署配置交易(或做其他操作)并不符合真实世界的 Fabric 运作。一个容器不会挂载整个网络的所有加密材料。实际上,配置更新需要通过安全的 out-of-band 方式传递给 Org2 管理员进行检查和批准。
导出 Org2 环境变量:
# 你可以一次性执行这些命令
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID=Org2MSP
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
export CORE_PEER_ADDRESS=localhost:9051
最后,我们将执行 peer channel update
命令。Org2 管理员的签名会附加在此调用中,因此无需再次手动签署 protobuf:
注意
即将发送到排序服务的更新调用将经历一系列系统化的签名和策略检查。因此,你可能会发现实时查看排序节点的日志非常有用。你可以执行以下命令查看:docker logs -f orderer.example.com
发送更新调用:
peer channel update -f channel-artifacts/org3_update_in_envelope.pb -c channel1 -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem"
如果提交成功,你会看到类似如下的消息:
2021-01-07 18:51:48.015 UTC [channelCmd] update -> INFO 002 Successfully submitted channel update
成功的通道更新调用会向通道上的所有 peer 返回一个新的区块 —— 区块 3。还记得吗,区块 0-2 是初始通道配置。区块 3 是最新的通道配置,其中已经定义了 Org3。
你可以通过以下命令查看 peer0.org1.example.com
的日志:
docker logs -f peer0.org1.example.com
让 Org3 加入通道
此时,通道配置已经更新,包含了我们的新组织 —— Org3 —— 这意味着连接到 Org3 的 peer 现在可以加入 channel1
。
导出以下环境变量,以 Org3 管理员身份操作:
# 你可以一次性执行这些命令
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID=Org3MSP
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org3.example.com/users/Admin@org3.example.com/msp
export CORE_PEER_ADDRESS=localhost:11051
Org3 的 peer 可以通过创世区块或 Org3 加入通道后创建的快照来加入 channel1
。
通过创世区块加入
向排序服务发送请求,获取 channel1
的创世区块。由于通道更新成功,排序服务会验证 Org3 是否可以拉取创世区块并加入通道。如果 Org3 没有被成功追加到通道配置,排序服务会拒绝此请求。
注意
你同样可以实时查看排序节点日志,以了解签名/验证逻辑和策略检查。
使用 peer channel fetch
命令获取该区块:
peer channel fetch 0 channel-artifacts/channel1.block -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com -c channel1 --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem"
注意,我们传递了 0
来表示我们需要通道账本上的第一个区块 —— 创世区块。如果我们仅仅执行 peer channel fetch config
命令,就会得到区块 3 —— 即包含 Org3 的更新配置。但我们不能用下游区块作为账本的开始 —— 必须从区块 0 开始。
注:这里并没有同步所有区块,真正的账本同步,是在 Peer 成功入网后,由 Gossip/Leader Peer 自动完成的。
如果成功,该命令会返回创世区块到一个名为 channel1.block
的文件中。我们现在可以用这个区块让 peer 加入通道。执行以下命令:
peer channel join -b channel-artifacts/channel1.block
通过快照加入
你也可以参考 Taking a snapshot 在现有 peer 上创建快照。快照必须在 Org3 加入 channel1
之后创建,以确保其中包含了包含 Org3 的更新通道配置。找到快照目录,将其复制到新 Org3 peer 的文件系统中,然后使用以下命令加入:
peer channel joinbysnapshot --snapshotpath <path to snapshot>
配置 Leader 选举
注意
本节作为通用参考,用于理解在初始通道配置完成后添加组织时的 leader 选举设置。
新加入的 peer 使用创世区块启动,而创世区块中并没有包含在后续通道配置更新中添加的组织信息。因此,新 peer 无法利用 gossip,因为它们无法验证来自自己组织其他 peer 转发的区块,直到它们获得包含该组织的配置交易。因此,新加入的 peer 必须具备以下两种配置之一,以便从排序服务接收区块:
-
静态领导者模式:确保 peer 始终直接从排序服务接收区块
CORE_PEER_GOSSIP_USELEADERELECTION=false CORE_PEER_GOSSIP_ORGLEADER=true
注意
从 Fabric v2.2 开始,这是默认配置,且必须在所有新加入的 peer 上保持一致。 -
动态领导者选举模式:配置 peer 使用 leader 选举
CORE_PEER_GOSSIP_USELEADERELECTION=true CORE_PEER_GOSSIP_ORGLEADER=false
注意
由于新组织的 peer 初期无法形成成员视图,此选项与静态配置相似,每个 peer 都会自称为 leader。但在获得添加该组织的配置交易后,组织中最终只会有一个活跃的 leader。如果你希望组织的 peer 最终利用 leader 选举,推荐使用此选项。
安装、定义并调用链码
我们可以通过在通道上安装并调用链码来确认 Org3 是否已经成为 channel1
的成员。如果现有通道成员已经将某个链码定义提交到通道,新组织只需批准该链码定义即可开始使用。
在我们作为 Org3 安装链码之前,可以先使用 ./network.sh
脚本在通道上部署 Basic 链码。在新终端中进入 test-network
目录:
cd fabric-samples/test-network
./network.sh deployCC -ccn basic -ccp ../asset-transfer-basic/chaincode-go/ -ccl go -c channel1
该脚本会在 Org1 和 Org2 的 peer 上安装 Basic 链码,为 Org1 和 Org2 批准链码定义,并将链码定义提交到通道。一旦链码定义提交完成,Basic 链码就会被初始化,并在账本上写入初始数据。
接下来我们以 Org3 管理员身份调用 Basic 链码。在终端中复制粘贴以下环境变量:
export PATH=${PWD}/../bin:$PATH
export FABRIC_CFG_PATH=$PWD/../config/
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID=Org3MSP
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org3.example.com/users/Admin@org3.example.com/msp
export CORE_PEER_ADDRESS=localhost:11051
打包链码
peer lifecycle chaincode package basic.tar.gz --path ../asset-transfer-basic/chaincode-go/ --lang golang --label basic_1.0
该命令会创建一个名为 basic.tar.gz
的链码包,我们可以在 peer0.org3.example.com
上安装。执行以下命令:
peer lifecycle chaincode install basic.tar.gz
批准链码定义
Org3 需要批准与 Org1、Org2 相同的链码定义。首先查询安装的链码以获取包 ID:
peer lifecycle chaincode queryinstalled
示例输出:
Get installed chaincodes on peer:
Package ID: basic_1.0:c6a45e2d5563c883869149c3dbd941c22fbe27daa21f0552834f5a53fbb8058a, Label: basic_1.0
将查询结果中的 package ID 保存为环境变量(请替换为你自己终端输出的值):
export CC_PACKAGE_ID=basic_1.0:c6a45e2d5563c883869149c3dbd941c22fbe27daa21f0552834f5a53fbb8058a
批准链码定义:
# 使用 --package-id 提供包标识符
# 使用 --init-required 要求在调用其他链码函数前执行初始化函数
peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" --channelID channel1 --name basic --version 1.0 --package-id $CC_PACKAGE_ID --sequence 1
检查定义是否已提交:
peer lifecycle chaincode querycommitted --channelID channel1 --name basic
示例输出:
Committed chaincode definition for chaincode 'basic' on channel 'channel1':
Version: 1.0, Sequence: 1, Endorsement Plugin: escc, Validation Plugin: vscc, Approvals: [Org1MSP: true, Org2MSP: true, Org3MSP: true]
此时 Org3 已经可以使用链码。
调用链码
写入一些测试数据,同时从 Org2 peer 和 Org3 peer 获取背书以满足策略:
peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" -C channel1 -n basic --peerAddresses localhost:9051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt" --peerAddresses localhost:11051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/tls/ca.crt" -c '{"function":"InitLedger","Args":[]}'
查询链码以确认数据已写入:
peer chaincode query -C channel1 -n basic -c '{"Args":["GetAllAssets"]}'
你应该能看到账本中的初始资产列表。
总结
通道配置更新过程确实较为复杂,但其步骤逻辑清晰。最终目标是形成一个以 protobuf 二进制格式表示的差异交易对象,并获得足够数量的管理员签名,从而使通道配置更新交易满足修改策略。
configtxlator
和 jq
工具,以及 peer channel
命令,为我们完成此任务提供了必要的功能。
更新通道配置以包含 Org3 锚节点(可选)
Org1 和 Org2 已经在通道配置中定义了锚节点,因此 Org3 peer 能够通过 gossip 与它们建立连接。同样,新添加的 Org3 也应该定义锚节点,以便其他组织的新 peer 能够直接发现 Org3 的 peer。本节我们将进行一次通道配置更新,为 Org3 定义一个锚节点。步骤与之前类似,这里会更快一些。
首先获取最新通道配置:
peer channel fetch config channel-artifacts/config_block.pb -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com -c channel1 --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem"
进入 channel-artifacts
文件夹:
cd channel-artifacts
转换为 JSON 并去掉无关信息:
configtxlator proto_decode --input config_block.pb --type common.Block --output config_block.json
jq ".data.data[0].payload.data.config" config_block.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 转换为 protobuf 并计算差异:
configtxlator proto_encode --input config.json --type common.Config --output config.pb
configtxlator proto_encode --input modified_anchor_config.json --type common.Config --output modified_anchor_config.pb
configtxlator compute_update --channel_id channel1 --original config.pb --updated modified_anchor_config.pb --output anchor_update.pb
解码为 JSON 并包装到 envelope 中:
configtxlator proto_decode --input anchor_update.pb --type common.ConfigUpdate --output anchor_update.json
echo '{"payload":{"header":{"channel_header":{"channel_id":"channel1", "type":2}},"data":{"config_update":'$(cat anchor_update.json)'}}}' | jq . > anchor_update_in_envelope.json
重新编码为 protobuf:
configtxlator proto_encode --input anchor_update_in_envelope.json --type common.Envelope --output anchor_update_in_envelope.pb
回到 test-network
目录:
cd ..
以 Org3 管理员身份签署并提交更新:
export CORE_PEER_LOCALMSPID=Org3MSP
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org3.example.com/users/Admin@org3.example.com/msp
export CORE_PEER_ADDRESS=localhost:11051peer channel update -f channel-artifacts/anchor_update_in_envelope.pb -c channel1 -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem"
排序服务接收到更新请求后会生成一个包含新配置的区块。当 peer 收到该区块时,会处理配置更新。你可以检查 peer 日志:
docker logs -f peer0.org1.example.com
示例输出:
2021-01-07 19:07:01.244 UTC [gossip.gossip] learnAnchorPeers -> INFO 05a Learning about the configured anchor peers of Org1MSP for channel channel1: [{peer0.org1.example.com 7051}]
2021-01-07 19:07:01.243 UTC [gossip.gossip] learnAnchorPeers -> INFO 05b Learning about the configured anchor peers of Org2MSP for channel channel1: [{peer0.org2.example.com 9051}]
2021-01-07 19:07:01.244 UTC [gossip.gossip] learnAnchorPeers -> INFO 05c Learning about the configured anchor peers of Org3MSP for channel channel1: [{peer0.org3.example.com 11051}]
恭喜🎉,你已经完成了两次配置更新 —— 一次将 Org3 添加到通道,另一次为 Org3 定义锚节点。
笔者注:
手动添加组织和手动添加节点对比
学习到这里,笔者觉得添加组织和添加节点的步骤十分之相似,所以在此将二者进行对比分析。
一、两者的共性
-
核心都是通道配置更新
-
无论是加组织,还是加节点,最终都需要修改通道的配置(
config.json → modified_config.json → org_update.pb → org_update_in_envelope.pb
)。 -
修改方式一样:获取区块配置 → decode → 修改 JSON → encode → 生成 delta → 提交 update。
-
-
用到的工具相同
-
peer channel fetch config
拉取配置块。 -
configtxlator
做 decode/encode/计算差异。 -
jq
修改 JSON 结构。
-
-
流程模板相似
- 拉配置 → 解码 → 修改 → 重新编码 → 计算 delta → 包装成 Envelope → 提交到 orderer。
换句话说,Fabric 的 一切配置变更 都走的这一套流程,只是修改 JSON 的部分不一样。
二、差异点对比
步骤/对象 | 添加组织到通道 | 添加节点到组织/通道 |
---|---|---|
修改位置 | 在 Application.groups 下新增一个 OrgXMSP | 在已有组织的 AnchorPeers (或 Org MSP 下的 Endpoints)中添加节点信息 |
涉及材料 | Org MSP 定义文件(证书、CA 根证书、策略) | Peer 节点的 host、端口信息(锚节点/Endpoint) |
复杂度 | 高:需要导入完整 MSP 定义、策略 | 低:仅修改组织下的一个小字段 |
生效范围 | 影响整个通道(新增一整个组织) | 影响组织内部(只新增或修改节点配置) |
所以:
-
加组织 = 组织整体加入,需要完整 MSP 材料。
-
加节点 = 节点层级的扩展或更新,主要是 Anchor Peer 或 Endpoint 配置。
三、这套步骤还能做什么
实际上,这套 通道配置更新流程 是一个通用模版,可以用来:
-
修改锚节点
- 常见场景:新增/修改组织的锚节点。
-
修改策略
- 例如:修改背书策略(Endorsement Policy)、修改组织写权限策略。
-
添加/移除组织
- 新增组织,或让某组织退出通道。
-
更新 Orderer 相关配置
- 比如更换共识节点、修改 Orderer 地址。
-
更新 MSP 材料
- 当证书过期,需要替换组织 MSP 的证书/CA 根证书时,必须通过通道配置更新。
-
扩展其他应用参数
- 例如修改 Batch Size、Batch Timeout(Orderer 通道参数)。
总结:
-
添加组织和添加节点本质上都是 “通道配置更新” 的不同应用场景。
-
加组织要动到 Application.groups,加节点则只改组织内部的 AnchorPeers 或 Endpoint。
-
这套流程不仅能加组织/节点,还能做证书更新、策略调整、Orderer 扩展等几乎所有链上治理动作。
出现的问题
docker.sock文件路径问题
WARN[0000] The "DOCKER_SOCK" variable is not set. Defaulting to a blank string.
invalid spec: :/host/var/run/docker.sock: empty section between colons
原因
YAML 里有一行使用了环境变量 ${DOCKER_SOCK},但你系统里没有定义这个变量。Docker Compose 默认会把未定义变量替换为空字符串 → 就出现下一条错误。所以在Docker 解析 volume 映射时,左边为空 → 报错。
解决
export DOCKER_SOCK=/var/run/docker.sock
注:如果是snap安装的docker,用类似路径
/var/snap/docker/current/run/docker.sock
替换
peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" -C channel1 -n basic --peerAddresses localhost:11051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/tls/ca.crt" -c '{"function":"InitLedger","Args":[]}'