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 indextype 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 ConfigSchematype 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 channeltype 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 configtype 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 datatype 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 nowtype 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", "" ] }}