Channel提供了多账本的功能,我们创建Channel时首先就是要配置联盟和Channel中包含的组织,之后使用configtxgen生成Channel文件,这个文件内部其实就是创建账本所需要的配置更新。
1.使用
从fabric源码中复制一份configtx,对其进行修改
cp $GOPATH/src/github.com/hyperledger/fabric/sampleconfig/configtx.yaml .
设置一个生成Channe的配置,OrdererOrg/CoreOrg/SupplierOrg/BankOrg需要进行配置,在此省略
TestTwoOrgsChannel:
Consortium: SampleConsortium
Application:
Organizations:
- *CoreOrg
- *SupplierOrg
- *BankOrg
最后,使用下面的命令就可以生成创世块了
configtxgen -profile TestTwoOrgsChannel -outputCreateChannelTx ./mychannel.tx -channelID mychannel
2.程序分析
configtxgen的程序在common/tools/configtxgen/main.go
中,主要的功能是获取配置文件,生成ConfigUpdate,包装为一个ConfigUpdate的交易并写入文件。主流程是:
- factory.InitFactories(nil) 初始化BCCSP,用来为加密提供服务。
- 获取指定profile的配置,将其序列化为Profile类型。
- 我们设置了outputChannelCreateTx,因此会执行doOutputChannelCreateTx方法。
- 创建一个用来创建Channel的交易,将其写入文件。
2.1 创建Channel的交易文件
有了Profile配置,首先创建一个ConfigUpdate,将其包装到ConfigUpdateEnvelope中,之后序列化作为Data,添加Header后形成Payload,最后添加签名,封装为Envelope并返回。
因此,我们需要将主要逻辑聚焦于ConfigUpdate的内容。
2.2 ConfigGroup
NewChannelCreateConfigUpdate
方法会生成一个ConfigUpdate,ConfigUpdate会被发送到orderer来创建一个新的Channel,该方法根据指定的profile的配置中的Application,创建Application的ConfigGroup,ConfigGroup可以理解为一个配置树型,Application中包含了Organizations,Organizations中是三个Org,因此,最终会形成一个Application的配置树。ConfigGroup的结构可以参考ConfigGroup,Application的Groups中是组织的配置,在NewApplicationOrgGroup
中,会对Org的配置组装,读取并验证MSP的相关证书。
2.3 ConfigUpdate
上面只是将配置转化为ConfigGroup组成的配置树,接下来就是计算ConfigUpdate了,也就是计算配置有那些更新。计算的程序如下,首先会创建一个新的ConfigGroup,其Groups子配置有一个Application
,对应这我们之前生成的Application的Config,这是因为在TestTwoOrgsChannel
中,Application是作为一个子级,需要将其放在一个Root配置下。
newChannelGroup = &cb.ConfigGroup{
Groups: map[string]*cb.ConfigGroup{
channelconfig.ApplicationGroupKey: ag,
},
}
template = proto.Clone(newChannelGroup).(*cb.ConfigGroup)
template.Groups[channelconfig.ApplicationGroupKey].Values = nil
template.Groups[channelconfig.ApplicationGroupKey].Policies = nil
之后,克隆了一份新创建的ConfigGroup,将其Application
的子ConfigGroup的Value和Policy设置为nil,之前我们在2.2节构造ConfigGroup时,ConfigGroup会设置默认的一些Ploicy,如果配置了Capabilities,已经设置Value
addImplicitMetaPolicyDefaults(applicationGroup)
if len(conf.Capabilities) > 0 {
addValue(applicationGroup, channelconfig.CapabilitiesValue(conf.Capabilities), channelconfig.AdminsPolicyKey)
}
template的修改势必会造成配置发生变化,因此下一步就是计算两个ConfigGroup的更新了,我们以template作为原来的配置,newChannelGroup最为更新之后的配置,计算哪些配置发生了更新。虽然这里我们还容易看出来发生了那些变化,但是为了统一处理,还是进行了计算(如果设置了orderingSystemChannelGroup的话,template的方式就不是这么简单的克隆后修改了)。
计算方法
主要是遍历原ConfigGroup的Policy,Value和ConfigGroup,将未发生变化的部分保存到sameSet,发送变化后的配置保存到writeSet中且版本号加1。最后,如果未发生变化,返回的ConfigUpdate的ReadSet和WriteSet均为nil,如果发生了变化,将sameSet分别赋值给ReadSet和WriteSet,保证未发生变化的部分在两个Set中都存在。这样我们就知道了发生变化前后的配置。
根据之前template的生成方式,我们可以查看最后的读写集合
在上图中,ReadSet的Application的version为0,WriteSet中version为1,说明发生了变化,通过观察,我们看到ConfigGroup的Policies发生了变化,这与template的修改相符合。其实最终的ConfigUpdate就是要通知,Application的Policy发生了变化,如果设置了某些配置参数的话,Value也会发生变化。
计算出来ConfigUpdate,最后封装为Envelope后,写入文件中,整个流程就结束了。