Orderer节点启动时,需要为其指定创世块,创世块其实就是我们在配置文件中设置的相关配置,将其组装为树型的ConfigGroup后,再封装为Enveloper,最后生成Block,保存到指定的文件中。
1.使用
从fabric源码中复制一份configtx,对其进行修改
cp $GOPATH/src/github.com/hyperledger/fabric/sampleconfig/configtx.yaml .
设置一个生成Genesis的配置,OrdererOrg/CoreOrg/SupplierOrg/BankOrg需要进行配置,在此省略
TestTwoOrgsOrdererGenesis:
Orderer:
<<: *OrdererDefaults
Organizations:
- *OrdererOrg
Consortiums:
SampleConsortium:
Organizations:
- *CoreOrg
- *SupplierOrg
- *BankOrg
最后,使用下面的命令就可以生成创世块了
configtxgen -profile TestTwoOrgsOrdererGenesis -outputBlock ./orderer.genesis.block
2.程序分析
configtxgen的入口在common/tools/configtxgen/main.go
中,在main方法中,
- 首先会 使用
factory.InitFactories(nil)
获取bccsp服务,bccsp用于后续的加密。 - 使用
genesisconfig.Load(profile)
加载指定的profile的配置,放在local.Profile中保存。 - 使用
genesisconfig.LoadTopLevel()
获取顶层的所有配置。 - 使用
doOutputBlock
生成创世块。
2.1 ChannelGroup
有了相关的配置之后,需要将配置保存在common.ChannelGroup
中,ChannelGroup是一个树形的结构,每个节点有若干子ChannelGroup,ChannelGroup的结构如下。所以我们首先要做的就是创建一个Root根ChannelGroup。
type ConfigGroup struct {
Version uint64
Groups map[string]*ConfigGroup
Values map[string]*ConfigValue
Policies map[string]*ConfigPolicy
ModPolicy string
}
其中,Version是配置的版本号;Groups是所有的子配置,也是ConfigGroup类型,Values是配置的值,ConfigPolicy是策略,ModPolicy是修改策略。
对于ConfigValue和ConfigPolicy来说,他们是字符串和Policy的包装,因为每个value和policy都需要有自己的版本号和ModPolicy。
type ConfigValue struct {
Version uint64
Value []byte
ModPolicy string
}
type ConfigPolicy struct {
Version uint64
Policy *Policy
ModPolicy string
}
Policy包含Type和一个Value,Type的目的是为了预留,后续可能需要多个策略引擎。
ChannelGroup创建后,有默认的一些配置,如Policies属性会先添加三个默认的Policy,Value分别为Admin
,Reader
和Writers
,这三个ConfigPolicy的ModPolicy均为Admins
。
除此之外,还有三个Values,分别是
HashingAlgorithm SHA256 // HASH算法,修改策略为`Admins`
BlockDataHashingStructure math.MaxUint32 //区块的最大大小,修改策略为`Admins`
OrdererAddresses conf.Orderer.Addresses // Orderer的地址,/Channel/Orderer/Admins,只有channel的orderer的admin可以修改
最后,会根据我们的配置文件,为ChannelGroup添加Orderer,Application和Consortiums的子ChannelGroup,即,这三个key下都有自己的配置,根据配置会生成各自的ChannelGroup子树。例如,对于
Orderer ->
key = {string} "Orderer"
Version = {uint64} 0
Groups =
0 = OrdererMSP -> 里面还是ChannelGroup
Values =
0 = ConsensusType ->
1 = BatchSize ->
2 = BatchTimeout ->
3 = ChannelRestrictions ->
Policies =
ModPolicy = {string} "Admins"
2.2 生成Block
有了 ChannelGroup之后,将其封装到Bootstrapper中,然后GenesisBlockForChannel生成Block,生成过程就是将ChannelGroup封装为Envelope,然后生成Block。
Block
Block就代表了一个区块,Block的结构定义在protos/common/common.pb.go
中
type Block struct {
Header *BlockHeader
Data *BlockData
Metadata *BlockMetadata
}
其中BlockHeader中包含了区块编号,上一个区块的Hash值,当前区块Data的Hash值。
type BlockHeader struct {
Number uint64
PreviousHash []byte
DataHash []byte
}
BlockData中包含了一个字符串数组,每个元素可以认为是一个交易,但是内部的数据需要根据不同场景进行反序列化。
type BlockData struct {
Data [][]byte
}
BlockMetadata也是一个字符串数组,保存区块的元数据
type BlockMetadata struct {
Metadata [][]byte
}
Envelope
Envelope的意思是将数据加密后,添加签名,因此其结构很简单,Payload是信封里的内容,Signature是发送者的签名
type Envelope struct {
Payload []byte
Signature []byte
}
Payload
Payload的意思是有效负载,就是我们需要放入的内容,包括Header和data
type Payload struct {
Header *Header `protobuf:"bytes,1,opt,name=header" json:"header,omitempty"`
Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
}
对header来说,由两个部分组成,分别是ChannelHeader和SignatureHeader
type Header struct {
ChannelHeader []byte
SignatureHeader []byte
}
其中,ChannelHeader用来保存Channel和版本,类型,此处的类型是Config
type ChannelHeader struct {
Type int32
Version int32
Timestamp *google_protobuf.Timestamp
ChannelId string
Epoch uint64
Extension []byte
TlsCertHash []byte
}
所有的HeaderType如下,用于反序列化Envelope中的内容,类型不同,反序列化后的结构也不相同。
const (
HeaderType_MESSAGE HeaderType = 0
HeaderType_CONFIG HeaderType = 1
HeaderType_CONFIG_UPDATE HeaderType = 2
HeaderType_ENDORSER_TRANSACTION HeaderType = 3
HeaderType_ORDERER_TRANSACTION HeaderType = 4
HeaderType_DELIVER_SEEK_INFO HeaderType = 5
HeaderType_CHAINCODE_PACKAGE HeaderType = 6
HeaderType_PEER_RESOURCE_UPDATE HeaderType = 7
)
SignatureHeader内部保存了创建者和nonce
type SignatureHeader struct {
Creator []byte
Nonce []byte
}
ConfigEnvelope
ConfigEnvelope是对config的包装,config内部包含了我们生成的ConfigGroup树
type ConfigEnvelope struct {
Config *Config
LastUpdate *Envelope
}
type Config struct {
Sequence uint64
ChannelGroup *ConfigGroup
Type int32
}