参考资料:
1. 什么是通道配置
通道配置包含关于通道管理的所有信息。
最重要的一点是,通道配置指定了哪些组织是通道的成员。但同时它也包含了其他通道范围的配置信息,例如,通道访问策略和区块的批处理大小。
这个配置被存储在账本的一个区块里,因此被称为配置(config)区块。每个配置区块包含一个配置。这些区块中的第一个就是创世区块,包含启动通道所需要的初始配置信息。
对通道的每次配置修改都是通过新配置一个区块完成的。也就是说,想要修改一个通道的配置,那么必须新增加一个配置区块去完成这个操作。最新的配置区块表示当前的通道配置。
我在Hyperledger Fabric从源码分析区块一文中提到,区块底层结构有个metadata元数据字段,里面存储了一个LAST_CONFIG信息。其实这个字段就是存着最新配置的区块的区块号。比如当没有发生过任何配置更新的时候,这个区块号就是0也就是创世区块,当区块n是新增的一个配置区块时,那么这个时候往后的区块的区块号就都是n了。
orderer节点和peer节点在内存中保存当前通道配置以便于所有通道操作,例如分割新区块、验证区块交易等等。
配置交易
因为配置保存在区块中,更新配置是通过一个称为配置交易的过程进行的(这个过程和普通的交易有点不同)。
更新配置可以理解话下面的过程
- 拉取配置
- 转换为人可读的格式(JSON)
- 修改并提交审核
2. 编辑配置
通道是高度可配置的,但并非无限制。不同的配置元素有不同的修改策略(指定了签名配置更新所需要的身份组)
挑几个常见可以更改的字段出来解析一下,原始的JSON格式文件特别长就不放了,有兴趣的可以自行去官网看。
有很多通道的特性是可以通过配置更新进行修改的。
-
策略定义:定义谁可以在通道级别做某些事情,以及谁有权限更改,谁可以更改配置
-
将一个组织加入通道:将一个新的组织加入现有的通道(这应该算是最复杂的一个更新了,至少官方文档是这么说的)
-
批处理大小:这些参数决定了一个区块中交易的数量和大小。
- 没有区块会大于
absolute_max_bytes
的大小 - 没有区块会有比
max_message_count
更多的交易 - 如果有可能在
preferred_max_bytes
之下构造一个区块,那么区块将被提早分隔,而大于此大小的交易将出现在它们自己的区块中
{ "absolute_max_bytes": 102760448, "max_message_count": 10, "preferred_max_bytes": 524288 }
- 没有区块会大于
-
批处理超时:自一个交易达到后,在分隔区块前,等待另外交易的时间量。降低这个值会改善延迟,但降低太多将因为不允许区块填充到其最大容量而减少吞吐量。
{ "timeout": "2s" }
-
通道限制:排序节点愿意分配的通道总数量被定义为
max_count
,这主要用于具有弱联盟ChannelCreation
策略的预生产环境{ "max_count":1000 }
-
通道创建策略: 定义策略值,该值将被用来设置联盟中定义的新通道的 Application 组的 mod_policy。附加到通道创建请求中的签名集将根据策略在新通道中的实例化进行检查,以确保通道 创建是经过授权的。注意这个配置值仅在排序系统通道中设置。
{ "type": 3, "value": { "rule": "ANY", "sub_policy": "Admins" } }
-
Kafka代理:当
ConsensusType
被设置为kafka
时,brokers
列表将遍历Kafka代理的某些子集(最好是全部),供排序节点在启动时进行初始连接。注意,共识类型一旦建立(在创世区块的启动过程中),就不可能更改。{ "brokers": [ "kafka0:9092", "kafka1:9092", "kafka2:9092", "kafka3:9092" ] }
-
锚节点定义:为每个组织定义锚节点位置
{ "host": "peer0.org2.example.com", "port": 9051 }
-
哈希结构: 区块数据是字节数组的数组。区块数据的哈希值用默克尔树进行计算。这个值定义了默克尔树的宽度。目前,此值固定为
4294967295
,它对应于区块字节数据的串联的简单直接的哈希。{ "width": 4294967295 }
-
哈希算法:这个算法被用来计算将要被编码进区块链中的区块的哈希值。 尤其,这会影响dataHash和previous Hash。注意,这个字段当前仅有一个合法的值(
SHA256
),而且不应被改变。{ "name": "SHA256" }
-
区块验证:这个策略制定了一个区块被视为有效的签名需求。默认情况下,它需要一个来自排序组织中的一些成员的签名。
{ "type": 3, "value": { "rule": "ANY", "sub_policy": "Writers" } }
-
排序节点地址:一个地址列表,客户端可以用来调用排序节点
Broadcast
和Deliver
功能。Peer节点在这些地址中随机选择,并在它们之间应用故障转移来获取区块{ "addresses": [ "orderer.example.com:7050" ] }
3. 获取必需的签名
一旦你成功地生成了原型文件,就可以签名了。为完成这个,你需要知道将要修改的东西的相关的策略。
默认情况下,编辑如下配置:
- 特定组织 (例如,改变锚节点) 仅需要该组织的管理员签名。
- 应用 (例如,谁是组织成员)需要应用组织里的多数管理员的签名。
- 排序节点 需要排序节点组织里多数管理员的签名(默认为1)。
- 顶级
channel
组 同时需要应用组织、及排序节点组织里多数管理员的同意。
如果你已经修改了通道的默认策略,你需要相应地计算签名要求。
注意:你可能会编写收集签名的脚本,这取决于你的应用程序。一般来说,你可能总收集比需求的多的签名。
收集签名的真实过程取决于你如何设置你的系统,但有两种主要的实现。当前,Fabric 命令行默认 使用“传递”系统。就是说,提出配置更新的组织的管理员将更新发送给需要签名的其他人(如另一个管理员)。这个管理员对之签名(或不签名)并把它传递给下一个管理员,以此类推,直到有足够可以提交配置的签名。
这有一个简单的优点——当有了足够的签名时,最后一个管理员可以简单地提交配置交易(在 Fabric 里,peer channel update
命令默认地包含签名)。但是,这个过程只适应于较小的通道,因为 “传递”方法可能会很耗时。
另一个选项是将更新提交给通道中每个管理员并等待返回足够的签名。这些签名可以被集中在一起提交。 这使得创建配置更新的管理员的工作更加困难(强制他们处理每个签名者的一个文件),但对于正在开发 Fabric 管理应用程序的用户来说,这是推荐的工作流程。
一旦配置被加入账本,最好将之拉取并转换为 JSON 以确认所有内容添加正确。这也将作为最新配置的有用的副本。
4. 将通道加入组织
通过这一章来看一下通道配置更新的大致过程。
二进制文件及一些测试环境的搭建我这边我就不重复说明了,如果有需要阅读的可以去看下官方文档——向通道内添加组织。
这里对测试环境做一个简单说明。执行
./byfn.sh up
以后,fabric网络中有两个组织,Org1和Org2,它们被加入了通道mychannel。下面命令执行的目录是first-nework
-
生成新组织
Org3
的加密材料cd org3-artifacts cryptogen generate --config=./org3-crypto.yaml
-
将
Org3
对应的配置材料以JSON文件的格式输出到channel-artifacts
下,该文件包含了Org3
的策略定义,还有管理员证书,根证书,TLS证书export FABRIC_CFG_PATH=$PWD && ../../bin/configtxgen -printOrg Org3MSP > ../channel-artifacts/org3.json
-
拷贝排序节点的MSP材料到
Org3
的crypto-config
目录下cd ../ && cp -r crypto-config/ordererOrganizations org3-artifacts/crypto-config/
-
进入已经启动的cli容器
docker exec -it cli bash
-
设置环境变量,设置排序的节点的ca路径和通道名
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 channel fetch config config_block.pb -o orderer.example.com:7050 -c $CHANNEL_NAME --tls --cafile $ORDERER_CA
-
将配置块解码为JSON文件
configtxlator proto_decode --input config_block.pb --type common.Block | jq .data.data[0].payload.data.config > config.json
-
添加
Org3
的配置信息jq -s '.[0] * {"channel_group":{"groups":{"Application":{"groups": {"Org3MSP":.[1]}}}}}' config.json ./channel-artifacts/org3.json > modified_config.json
-
分别将原来的
config.json
和增加了Org3
的modified_config.json
编码成区块configtxlator proto_encode --input config.json --type common.Config --output config.pb configtxlator proto_encode --input modified_config.json --type common.Config --output modified_config.pb
-
计算出添加了
Org3
后文件的增量configtxlator compute_update --channel_id $CHANNEL_NAME --original config.pb --updated modified_config.pb --output org3_update.pb
-
把通过差别计算出来的块解码成JSON文件
configtxlator proto_decode --input org3_update.pb --type common.ConfigUpdate | jq . > org3_update.json
-
将上一步生成的JSON文件封装成
envelope
格式,这是源码中交易的格式echo '{"payload":{"header":{"channel_header":{"channel_id":"mychannel","type":2}},"data":{"config_update":'$(cat org3_update.json)'}}}' | jq . > org3_update_in_envelope.json
-
将上一步的文件编码成
fabric
的proto
格式configtxlator proto_encode --input org3_update_in_envelope.json --type common.Envelope --output org3_update_in_envelope.pb
-
对上一步的pb文件签名
peer channel signconfigtx -f org3_update_in_envelope.pb 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 signconfigtx -f org3_update_in_envelope.pb
-
发送
update
请求peer channel update -f org3_update_in_envelope.pb -c $CHANNEL_NAME -o orderer.example.com:7050 --tls --cafile $ORDERER_CA
-
完成以上步骤后,重新打开一个终端启动
Org3
docker-compose -f docker-compose-org3.yaml up -d
-
进入
Org3
的容器,设置相关环境变量,将Org3
加入通道# 进入容器 docker exec -it Org3cli 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 # 获取区块,需要获取区块0 peer channel fetch 0 mychannel.block -o orderer.example.com:7050 -c $CHANNEL_NAME --tls --cafile $ORDERER_CA # Org3的第一个节点加入通道 peer channel join -b mychannel.block # 切换Org3内的节点 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 # Org3的第二个节点加入通道 peer channel join -b mychannel.block
再用形象一点简单地几步来过一下在通知中加入组织的步骤
- 生成新组织的加密材料
- 获取通道的最新配置,拉取最新配置块,得到的是一个pb文件,这个文件中存储的就是原始配置,config_block.pb
- 将配置块解码为JSON文件,解码为人为可读的形式,config.json
- 在解码出的json文件中加入Org3的配置信息,modify_config.json
- 将原始的配置文件和新的配置文件编码为pb文件,config.pb和modify_config.pb
- 计算出两个文件的增量,org3_update.pb
- 将通过差别计算出来的块解码为JSON文件,org3_update.json
- 将解码出的JSON文件封装成envelope格式,org3_update_in_envelope.json
- 再将org3_update_in_envelope.json文件编码为pb格式org3_update_in_envelope.pb,最后发送update请求
5. 大白话解析配置更新过程
拉取原始的配置块,解码以后加入的新的配置信息,将两个配置文件重新编码成块后计算一个差值得到一个差值块文件,然后继续解码,并转成envelope格式,然后继续编码成块文件后提交更新