Fabric源码分析-生成Channel文件

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

【Fabric源码分析-生成Channel文件】最后,使用下面的命令就可以生成创世块了
configtxgen -profile TestTwoOrgsChannel -outputCreateChannelTx ./mychannel.tx -channelID mychannel

2.程序分析 configtxgen的程序在common/tools/configtxgen/main.go中,主要的功能是获取配置文件,生成ConfigUpdate,包装为一个ConfigUpdate的交易并写入文件。主流程是:
  1. factory.InitFactories(nil) 初始化BCCSP,用来为加密提供服务。
  2. 获取指定profile的配置,将其序列化为Profile类型。
  3. 我们设置了outputChannelCreateTx,因此会执行doOutputChannelCreateTx方法。
  4. 创建一个用来创建Channel的交易,将其写入文件。
2.1 创建Channel的交易文件 有了Profile配置,首先创建一个ConfigUpdate,将其包装到ConfigUpdateEnvelope中,之后序列化作为Data,添加Header后形成Payload,最后添加签名,封装为Envelope并返回。

Fabric源码分析-生成Channel文件
文章图片
channeltx的内容 因此,我们需要将主要逻辑聚焦于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的生成方式,我们可以查看最后的读写集合

Fabric源码分析-生成Channel文件
文章图片
ConfigUpdate 在上图中,ReadSet的Application的version为0,WriteSet中version为1,说明发生了变化,通过观察,我们看到ConfigGroup的Policies发生了变化,这与template的修改相符合。其实最终的ConfigUpdate就是要通知,Application的Policy发生了变化,如果设置了某些配置参数的话,Value也会发生变化。
计算出来ConfigUpdate,最后封装为Envelope后,写入文件中,整个流程就结束了。

    推荐阅读