id:BSN_2021 公众号:BSN 研习社 作者:红枣科技高晨光
背景:BSN 公网 Fabric 联盟链的呈现升高了应用区块链的难度,但在局部特定环境中,仍须要本人搭建 Fabric 环境时,理解 Fabric 中的配置信息可能帮忙搭建或在运行中调整本人的链环境配置。
指标:理解账本数据结构中的配置信息,更好的保护本人的 Fabric 环境。
对象:应用 BSN 联盟链 Fabric 的开发人员、运维人员。
什么是配置块
配置块是 Fabric Channel 的重要数据,外面蕴含了以后 Channel 的配置信息,配置块蕴含初始配置块,以及在链运行过程中的批改配置信息而提交的交易生成的配置区块信息。
它蕴含了以后 channel 的证书信息、策略信息、以及其余要害链配置信息。
如何查问配置块
在前一篇中咱们理解到在区块的 Metadata
中蕴含了 5 组数据,其中第一组为 Ordere 签名信息,第二组即为以后 channel 的最初一个配置块的块号。其中 Orderer 签名信息中也蕴含最初一个配置块信息,
是 common.LastConfig
序列化后的 Bytes,咱们能够查问最新的区块,失去最初一个配置块号,而后间接查问该块即可。
// OrdererBlockMetadata defines metadata that is set by the ordering service.
type OrdererBlockMetadata struct {
LastConfig *LastConfig `protobuf:"bytes,1,opt,name=last_config,json=lastConfig,proto3" json:"last_config,omitempty"`
ConsenterMetadata []byte `protobuf:"bytes,2,opt,name=consenter_metadata,json=consenterMetadata,proto3" json:"consenter_metadata,omitempty"`}
// LastConfig is the encoded value for the Metadata message which is encoded in the LAST_CONFIGURATION block metadata index
type LastConfig struct {Index uint64 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"`}
在查到配置块之后,咱们解析区块内的交易信息,依据交易的 ChannelHeader
中的 Type 来判断该交易是否是配置信息。HeaderType
蕴含以下类型:
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_CONFIG
和HeaderType_CONFIG_UPDATE
是配置块交易类型。
配置块蕴含哪些数据
当交易配型为 HeaderType_CONFIG
或者 HeaderType_CONFIG_UPDATE
时,Envelope
的 data 即为 ConfigEnvelope
序列化之后的 Bytes,
以下为 proto 中的 ConfigEnvelope
构造
// ConfigEnvelope is designed to contain _all_ configuration for a chain with no dependency
// on previous configuration transactions.
//
// It is generated with the following scheme:
// 1. Retrieve the existing configuration
// 2. Note the config properties (ConfigValue, ConfigPolicy, ConfigGroup) to be modified
// 3. Add any intermediate ConfigGroups to the ConfigUpdate.read_set (sparsely)
// 4. Add any additional desired dependencies to ConfigUpdate.read_set (sparsely)
// 5. Modify the config properties, incrementing each version by 1, set them in the ConfigUpdate.write_set
// Note: any element not modified but specified should already be in the read_set, so may be specified sparsely
// 6. Create ConfigUpdate message and marshal it into ConfigUpdateEnvelope.update and encode the required signatures
// a) Each signature is of type ConfigSignature
// b) The ConfigSignature signature is over the concatenation of signature_header and the ConfigUpdate bytes (which includes a ChainHeader)
// 5. Submit new Config for ordering in Envelope signed by submitter
// a) The Envelope Payload has data set to the marshaled ConfigEnvelope
// b) The Envelope Payload has a header of type Header.Type.CONFIG_UPDATE
//
// The configuration manager will verify:
// 1. All items in the read_set exist at the read versions
// 2. All items in the write_set at a different version than, or not in, the read_set have been appropriately signed according to their mod_policy
// 3. The new configuration satisfies the ConfigSchema
type ConfigEnvelope struct {
Config *Config `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"`
LastUpdate *Envelope `protobuf:"bytes,2,opt,name=last_update,json=lastUpdate,proto3" json:"last_update,omitempty"`
}
// Config represents the config for a particular channel
type Config struct {
Sequence uint64 `protobuf:"varint,1,opt,name=sequence,proto3" json:"sequence,omitempty"`
ChannelGroup *ConfigGroup `protobuf:"bytes,2,opt,name=channel_group,json=channelGroup,proto3" json:"channel_group,omitempty"`
}
// ConfigGroup is the hierarchical data structure for holding config
type ConfigGroup struct {
Version uint64 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"`
Groups map[string]*ConfigGroup `protobuf:"bytes,2,rep,name=groups,proto3" json:"groups,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
Values map[string]*ConfigValue `protobuf:"bytes,3,rep,name=values,proto3" json:"values,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
Policies map[string]*ConfigPolicy `protobuf:"bytes,4,rep,name=policies,proto3" json:"policies,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
ModPolicy string `protobuf:"bytes,5,opt,name=mod_policy,json=modPolicy,proto3" json:"mod_policy,omitempty"`
}
其中 ConfigEnvelope.Config
对象为以后块的配置信息,LastUpdate
为最初一个的配置更新在上面的配置更新中会讲到它。
首先来看一下 Config
对象,其中 ConfigGroup
中存储的即为以后 Channel 的次要配置,它与通过 cryptogen 命令初始化网络时的 configtx.yaml
配置文件中的项绝对应,次要蕴含以下几项:
Version
配置的版本号,每次更新配置,Version 将加 1
Groups
Groups 中为上级配置项,仍然是 ConfigGroup
对象,通常蕴含以下几种:
Consortiums: 联盟配置,蕴含整个网络的组织配置信息,个别呈现在 genesischannel
中,蕴含整个网络的联盟配置以及各个联盟下的组织信息配置
Orderer: Orderer 组织的配置信息,个别呈现在 genesischannel
中,它蕴含整个链中的各个 Orderer 组织配置信息
Application: 利用 channel
的配置信息,个别呈现在利用 channel
中,他蕴含了利用 channel
中的各个组织的配置信息。
Values
// ConfigValue represents an individual piece of config data
type ConfigValue struct {
Version uint64 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"`
Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
ModPolicy string `protobuf:"bytes,3,opt,name=mod_policy,json=modPolicy,proto3" json:"mod_policy,omitempty"`
}
Values 中寄存的是链的一些根本配置信息,为 ConfigValue
对象存储,它通常蕴含链的配置信息等。
例如链的证书信息,节点信息,区块配置参数等
以BatchSize
举例,在 configtx.yaml
中,咱们通常这么配置,
# 写入区块内的交易大小
BatchSize:
#音讯的最大个数
MaxMessageCount: 10000
#交易的最大字节数,任何时候均不能超过
AbsoluteMaxBytes: 98 MB
#批量交易的倡议字节数
PreferredMaxBytes: 10 MB
在配置块中为
"values": {
"BatchSize": {
"mod_policy": "Admins",
"value": {
"absolute_max_bytes": 102760448,
"max_message_count": 10000,
"preferred_max_bytes": 10485760
},
"version": "0"
}
}
Policies
type ConfigPolicy struct {
Version uint64 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"`
Policy *Policy `protobuf:"bytes,2,opt,name=policy,proto3" json:"policy,omitempty"`
ModPolicy string `protobuf:"bytes,3,opt,name=mod_policy,json=modPolicy,proto3" json:"mod_policy,omitempty"`
}
// Policy expresses a policy which the orderer can evaluate, because there has been some desire expressed to support
// multiple policy engines, this is typed as a oneof for now
type Policy struct {
Type int32 `protobuf:"varint,1,opt,name=type,proto3" json:"type,omitempty"`
Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`}
Policies 中链的读写策略,个别蕴含 Readers
、Writers
、Admins
等,应用 ConfigPolicy
对象存储。
它在链的配置中示例如下:
Policies: &OrgbNodeOrgPolicies
Readers:
Type: Signature
Rule: "OR('OrgbNodeMSP.admin','OrgbNodeMSP.peer','OrgbNodeMSP.client')"
Writers:
Type: Signature
Rule: "OR('OrgbNodeMSP.admin','OrgbNodeMSP.client')"
Admins:
Type: Signature
Rule: "OR('OrgbNodeMSP.admin')"
如何批改配置块
当咱们须要批改一个 channel 的配置时,例如减少一个组织,批改读写其策略,或者须要在 channel 中撤消某个用户的证书时,须要依据以后的配置信息,以及批改后的配置信息生成批改配置的Envelope
,签名之后,提交给 Orderer 实现配置的批改。
在 Fabric 的源码中为咱们提供了生成 ConfigUpdate
的办法,它在 github.com/hyperledger/fabric/internal/configtxlator/update
包中,能够依据以后的配置和批改后的配置生成提交批改的配置更新项。它蕴含了咱们对配置批改的读写汇合。
func Compute(original, updated *cb.Config) (*cb.ConfigUpdate, error) {
if original.ChannelGroup == nil {return nil, fmt.Errorf("no channel group included for original config")
}
if updated.ChannelGroup == nil {return nil, fmt.Errorf("no channel group included for updated config")
}
readSet, writeSet, groupUpdated := computeGroupUpdate(original.ChannelGroup, updated.ChannelGroup)
if !groupUpdated {return nil, fmt.Errorf("no differences detected between original and updated config")
}
return &cb.ConfigUpdate{
ReadSet: readSet,
WriteSet: writeSet,
}, nil
}
依据生成的 ConfigUpdate
,能够参考上面的代码生成Envelope
对象,提交至 Orderer 后即可实现对配置的批改。
updt, err := update.Compute(&cb.Config{ChannelGroup: original}, &cb.Config{ChannelGroup: updated})
if err != nil {return errors.WithMessage(err, "could not compute update")
}
updt.ChannelId = channelID
newConfigUpdateEnv := &cb.ConfigUpdateEnvelope{ConfigUpdate: protoutil.MarshalOrPanic(updt),
}
updateTx, err := protoutil.CreateSignedEnvelope(cb.HeaderType_CONFIG_UPDATE, channelID, nil, newConfigUpdateEnv, 0, 0)
配置块示例
本次的示例是一个转换为 json
格局的配置块信息,删除了过长的证书信息和反复的组织信息,保留了根本的配置字段,以供参考。
{
"data": {
"data": [
{
"payload": {
"data": {
"config": {
"channel_group": {
"groups": {
"Consortiums": {
"groups": {
"SampleConsortium": {
"groups": {
"OrgaNodeMSP": {"groups": {},
"mod_policy": "Admins",
"policies": {
"Admins": {
"mod_policy": "Admins",
"policy": {
"type": 1,
"value": {
"identities": [
{
"principal": {
"msp_identifier": "OrgaNodeMSP",
"role": "ADMIN"
},
"principal_classification": "ROLE"
}
],
"rule": {
"n_out_of": {
"n": 1,
"rules": [
{"signed_by": 0}
]
}
},
"version": 0
}
},
"version": "0"
},
"Readers": {},
"Writers": {}},
"values": {
"MSP": {
"mod_policy": "Admins",
"value": {
"config": {
"admins": ["LS0 ... 0tLS0K==="],
"crypto_config": {
"identity_identifier_hash_function": "SHA256",
"signature_hash_family": "SHA2"
},
"fabric_node_ous": {
"admin_ou_identifier": null,
"client_ou_identifier": {
"certificate": "LS0 ... 0tLS0K=",
"organizational_unit_identifier": "client"
},
"enable": true,
"orderer_ou_identifier": null,
"peer_ou_identifier": {
"certificate": "LS0 ... 0tLS0K=",
"organizational_unit_identifier": "peer"
}
},
"intermediate_certs": [
"LS0 ... 0tLS0K=",
"LS0 ... 0tLS0K="
],
"name": "OrgaNodeMSP",
"organizational_unit_identifiers": [],
"revocation_list": [],
"root_certs": ["LS0 ... 0tLS0K=="],
"signing_identity": null,
"tls_intermediate_certs": [],
"tls_root_certs": []},
"type": 0
},
"version": "0"
}
},
"version": "0"
}
},
"mod_policy": "/Channel/Orderer/Admins",
"policies": {},
"values": {
"ChannelCreationPolicy": {
"mod_policy": "/Channel/Orderer/Admins",
"value": {
"type": 3,
"value": {
"rule": "ANY",
"sub_policy": "Admins"
}
},
"version": "0"
}
},
"version": "2"
}
},
"mod_policy": "/Channel/Orderer/Admins",
"policies": {
"Admins": {
"mod_policy": "/Channel/Orderer/Admins",
"policy": {
"type": 1,
"value": {"identities": [],
"rule": {
"n_out_of": {
"n": 0,
"rules": []}
},
"version": 0
}
},
"version": "0"
}
},
"values": {},
"version": "0"
},
"Orderer": {
"groups": {
"OrdererMSP": {"groups": {},
"mod_policy": "Admins",
"policies": {
"Admins": {
"mod_policy": "Admins",
"policy": {
"type": 1,
"value": {
"identities": [
{
"principal": {
"msp_identifier": "OrdererMSP",
"role": "ADMIN"
},
"principal_classification": "ROLE"
}
],
"rule": {
"n_out_of": {
"n": 1,
"rules": [
{"signed_by": 0}
]
}
},
"version": 0
}
},
"version": "0"
},
"Readers": { },
"Writers": {}},
"values": {
"MSP": {
"mod_policy": "Admins",
"value": {
"config": {
"admins": ["LS0 ... 0tLS0K"],
"crypto_config": {
"identity_identifier_hash_function": "SHA256",
"signature_hash_family": "SHA2"
},
"fabric_node_ous": null,
"intermediate_certs": [
"LS0 ... 0tLS0K",
"LS0 ... 0tLS0K"
],
"name": "OrdererMSP",
"organizational_unit_identifiers": [],
"revocation_list": [],
"root_certs": ["LS0t ... S0tLQo="],
"signing_identity": null,
"tls_intermediate_certs": [
"LS0 ... 0tLS0K",
"LS0 ... 0tLS0K"
],
"tls_root_certs": ["LS0 ... 0tLS0K"]
},
"type": 0
},
"version": "0"
}
},
"version": "0"
}
},
"mod_policy": "Admins",
"policies": {
"Admins": {
"mod_policy": "Admins",
"policy": {
"type": 3,
"value": {
"rule": "ANY",
"sub_policy": "Admins"
}
},
"version": "0"
},
"BlockValidation": {
"mod_policy": "Admins",
"policy": {
"type": 3,
"value": {
"rule": "ANY",
"sub_policy": "Writers"
}
},
"version": "0"
},
"Readers": {
"mod_policy": "Admins",
"policy": {
"type": 3,
"value": {
"rule": "ANY",
"sub_policy": "Readers"
}
},
"version": "0"
},
"Writers": {
"mod_policy": "Admins",
"policy": {
"type": 3,
"value": {
"rule": "ANY",
"sub_policy": "Writers"
}
},
"version": "0"
}
},
"values": {
"BatchSize": {
"mod_policy": "Admins",
"value": {
"absolute_max_bytes": 102760448,
"max_message_count": 10000,
"preferred_max_bytes": 10485760
},
"version": "0"
},
"BatchTimeout": {
"mod_policy": "Admins",
"value": {"timeout": "1s"},
"version": "0"
},
"Capabilities": {
"mod_policy": "Admins",
"value": {
"capabilities": {"V1_4_2": {}
}
},
"version": "0"
},
"ChannelRestrictions": {
"mod_policy": "Admins",
"value": null,
"version": "0"
},
"ConsensusType": {
"mod_policy": "Admins",
"value": {
"metadata": {
"consenters": [
{
"client_tls_cert": "LS0 ... 0tLS0K=",
"host": "order1.ordernode.bsnbase.com",
"port": 17051,
"server_tls_cert": "LS0 ... 0tLS0K="
},
{
"client_tls_cert": "LS0 ... 0tLS0K=",
"host": "order2.ordernode.bsnbase.com",
"port": 17052,
"server_tls_cert": "LS0 ... 0tLS0K="
},
{
"client_tls_cert": "LS0 ... 0tLS0K=",
"host": "order3.ordernode.bsnbase.com",
"port": 17053,
"server_tls_cert": "LS0 ... 0tLS0K="
}
],
"options": {
"election_tick": 10,
"heartbeat_tick": 1,
"max_inflight_blocks": 5,
"snapshot_interval_size": 200,
"tick_interval": "500ms"
}
},
"state": "STATE_NORMAL",
"type": "etcdraft"
},
"version": "0"
}
},
"version": "0"
}
},
"mod_policy": "Admins",
"policies": {
"Admins": {
"mod_policy": "Admins",
"policy": {
"type": 3,
"value": {
"rule": "ANY",
"sub_policy": "Admins"
}
},
"version": "0"
},
"Readers": {
"mod_policy": "Admins",
"policy": {
"type": 3,
"value": {
"rule": "ANY",
"sub_policy": "Readers"
}
},
"version": "0"
},
"Writers": {
"mod_policy": "Admins",
"policy": {
"type": 3,
"value": {
"rule": "ANY",
"sub_policy": "Writers"
}
},
"version": "0"
}
},
"values": {
"BlockDataHashingStructure": {
"mod_policy": "Admins",
"value": {"width": 4294967295},
"version": "0"
},
"Capabilities": {
"mod_policy": "Admins",
"value": {
"capabilities": {"V1_4_2": {}
}
},
"version": "0"
},
"HashingAlgorithm": {
"mod_policy": "Admins",
"value": {"name": "SHA256"},
"version": "0"
},
"OrdererAddresses": {
"mod_policy": "/Channel/Orderer/Admins",
"value": {
"addresses": ["order1.ordernode.bsnbase.com:17051"]
},
"version": "0"
}
},
"version": "0"
},
"sequence": "2"
},
"last_update": {
"payload": {
"data": {
"config_update": {
"channel_id": "genesischannel",
"isolated_data": {},
"read_set": {"groups": {},
"mod_policy": "","policies": {},"values": {},"version":"0"},"write_set": {"groups": {},"mod_policy":"",
"policies": {},
"values": {},
"version": "0"
}
},
"signatures": [
{
"signature": "MEUCIQChKw0GLbCI3Mg3oN14hw25ETgzZOMGOycuPQYwbAEulgIgIxihwa1x/5/mRhEqSUatBPafbyYzlPNRRdA0syYYALA=",
"signature_header": {
"creator": {
"id_bytes": "LS0 ... 0tLS0K",
"mspid": "OrdererMSP"
},
"nonce": "4UQFkmPE3ajSubYgeSp8TwBHqQQ7Jq3+"
}
}
]
},
"header": {
"channel_header": {
"channel_id": "genesischannel",
"epoch": "0",
"extension": null,
"timestamp": "2021-11-11T05:42:43Z",
"tls_cert_hash": null,
"tx_id": "","type": 2,"version": 0
},
"signature_header": {
"creator": {
"id_bytes": "LS0 ... 0tLS0K",
"mspid": "OrdererMSP"
},
"nonce": "XKHFVcxjbdp/Jp+TwPpKvtPw7wogD27H"
}
}
},
"signature": "MEUCIQDn3u47EfAjbqJjqkwks+bB4gCuyDcmLEhUurVeyJqcjQIgIW7/28nFgAyNBXYEtMC/qZjfriL7xCEijLuRlHqRJuo="
}
},
"header": {
"channel_header": {
"channel_id": "genesischannel",
"epoch": "0",
"extension": null,
"timestamp": "2021-11-11T05:42:43Z",
"tls_cert_hash": null,
"tx_id": "","type": 1,"version": 0
},
"signature_header": {
"creator": {
"id_bytes": "LS0 ... 0tLS0K=",
"mspid": "OrdererMSP"
},
"nonce": "B/92hIoLno1ewPDddf9EmDfYMGdSO668"
}
}
},
"signature": "MEUCIQCpSlOWFD+fBCqFk8DVw4m+qkLwspqe0vGqmqh6tv/OlQIgatIGPHJzhmdPzbwU7zPy9e+KtpghTdGiFpyi4UqpqBo="
}
]
},
"header": {
"data_hash": "mgHIsx3RqeeybaKDYmetYvypDqVxKSB5O0YzqwnG/IM=",
"number": "34",
"previous_hash": "I5IMydQTa5njnHojXNnsDdHLlDkF/RlPIsO48iUI4cc="
},
"metadata": {
"metadata": [
"ChIK ... kJZ+j9Pr",
"CgIIIg==",
"","CgoKAwECAxAEGIwb",""
]
}
}