一個超級賬本區塊鏈網絡里每個channel的共享配置都是存儲在一個配置交易里。每個配置交易通常被簡稱為configtx。
Channel 配置有以下重要屬性:
1、有版本標識:配置里的所有元素都有一個關聯版本。每次更新配置,版本號都維持最新。另外,每個提交的配置會獲得一個序列號。
2、有權限:配置里的每個元素都有一個關聯策略,用來管理是否允許對這個元素進行修改。每一個保存有當前配置副本的實例,都會按照關聯策略的內容對新收到的配置改動進行驗證權限。
3、層次性:一個配置根組包含多個子組,並且層次中的每個組都有關聯的值和策略。較低層次可以獲取上層的策略。
配置分析
配置作為一個HeaderType_CONFIG
類型的交易
存儲在一個不包含其他交易的區塊中。這些區塊被稱為Configuration Blocks,所有區塊中的第一個區塊稱為genesis block。
配置的樣例存儲在fabric/protos/common/configtx.proto
。
HeaderType_CONFIG
類型的
Env
elope
編碼
ConfigEnvelop
信息作為
Payload data
的值。
ConfigEnvelop
定義如下:
message ConfigEnvelope {
Config config = 1;
Envelope last_update = 2;
}
last_update
字段在下面的更新配置中有定義,該字段僅用於驗證配置。當前提交的配置存在
config
字段,包含一個
Config
消息。
message Config {
uint64 sequence = 1;
ConfigGroup channel_group = 2;
}
seq
uence
的值在每次提交配置后加
1,channel_group
字段是包含該配置的根組。
ConfigGroup
是一個遞歸定義的結構,生成一個
gruops
樹,每個組都包含值和策略。
ConfigGroup
的定義如下:
message ConfigGroup {
uint64 version = 1;
map<string,ConfigGroup> groups = 2;
map<string,ConfigValue> values = 3;
map<string,ConfigPolicy> policies = 4;
string mod_policy = 5;
}
因為
ConfigGroup
是一個遞歸結構,有層次概念。接下來我們用
go
語言例子來表示。
// Assume the following groups are defined
var root, child1, child2, grandChild1, grandChild2, grandChild3 *ConfigGroup
// Set the following values
root.Groups["child1"] = child1
root.Groups["child2"] = child2
child1.Groups["grandChild1"] = grandChild1
child2.Groups["grandChild2"] = grandChild2
child2.Groups["grandChild3"] = grandChild3
// The resulting config structure of groups looks like:
// root:
// child1:
// grandChild1
// child2:
// grandChild2
// grandChild3
在config層次結構中,每個組定義了一個層次,每個組有一系列關聯的值和策略。值的定義如下:
message ConfigValue {
uint64 version = 1;
bytes value = 2;
string mod_policy = 3;
}
策略的定義如下:
message ConfigPolicy {
uint64 version = 1;
Policy policy = 2;
string mod_policy = 3;
}
注意值、策略和組都有一個version和mod_policy字段。一個元素的version在每次更改后遞增。mod_policy用來管理修改元素的簽名。對於組,更改就是添加或者移除Values、Policies、或者組地圖(或者修改mod_policy)。對於Values和Policies,修改就是改變修改Value或者Policy字段(或者修改mod_policy)。每個元素的mod_policy在當前當前元素中有效
下面是一個定義在Channel.Groups["Application"]
中的策略的示例(這里用的是golang語法,因此Channel.Groups["Application"].Policies["policy1"]
表示根組Channel
的子組Application
的Policies
里的policy1
對應的策略)
-
policy1
對應Channel.Groups["Application"].Policies["policy1"]
-
Org1/policy2
對應Channel.Groups["Application"].Groups["Org1"].Policies["policy2"]
-
/Channel/policy3
對應Channel.Policies["policy3"]
注意,如果mod_policy
引用了一個不存在的策略,那么該元素不可修改。
Configuration updates / 更新配置
更新配置是提交一個HeaderType_CONFIG_UPDATE
類型的Envelope
消息,交易的Payload.data
字段是序列化的ConfigUpdateEnvelope
,其定義如下:
message ConfigUpdateEnvelope {
bytes config_update = 1; // A marshaled ConfigUpdate structure
repeated ConfigSignature signatures = 2; // Signatures over the config_update
}
其中signatures
字段包含了授權更新配置的簽名集,定義如下:
message ConfigSignature {
bytes signature_header = 1; // A marshaled SignatureHeader
bytes signature = 2; // Signature over the concatenation signatureHeader bytes and config bytes
}
signature_header
如標准交易所定義,而簽名則是signature_header
字節和ConfigUpdateEnvelope
中的config_update
字節的拼接。
ConfigUpdateEnvelope
中的config_update
字段是序列化的ConfigUpdate
,其定義為:
message ConfigUpdate {
string channel_id = 1; // Which channel this config update is for
ConfigGroup read_set = 2; // ReadSet explicitly lists the portion of the config which was read, this should be sparse with only Version set
ConfigGroup write_set = 3; // WriteSet lists the portion of the config which was written, this should included updated Versions
}
其中channel_id
是配置更新所對應的channel ID,該字段是必要,因為它界定了支持本次配置更新的所需的簽名范圍。
read_set
是現有配置的一個子集,其中僅含version
字段,ConfigValue.value
和ConfigPolicy.policy
等其他字段不包含在read_set
中。ConfigGroup
會map字段組成的子集,以便引用配置樹的深層元素。例如,為使Application
group包含到read_set
,它的上層(Channel
group)也必須包含到read_set
中,但不必將Channel
group中所有的key都包括進去,比如Orderer``group
或者任何values
或policies
。
write_set
指定了要被修改的那部分配置。由於配置的分層特性,修改深層元素就必須在write_set
中包含其上層元素。write_set
中的任意元素都會在read_set
中指定相同版本的該元素。
例如,給出如下配置:
Channel: (version 0)
Orderer (version 0)
Appplication (version 3)
Org1 (version 2)
修改Org1
提交的read_set
應為:
Channel: (version 0)
Application: (version 3)
對應的write_set
應是:
Channel: (version 0)
Application: (version 3)
Org1 (version 3)
When the CONFIG_UPDATE
is received, the orderer computes the resulting CONFIG
by doing the following:
接收到CONFIG_UPDATE
后,orderer會通過以下步驟計算CONFIG
結果:
-
校驗
channel_id
和read_set
,read_set
中所有元素必須存在對應的版本。 -
收集
read_set
與write_set
中版本不一致的元素,計算更新集。 -
校驗更新集中的元素的版本號是否遞增1
-
校驗更新集中每個元素,
ConfigUpdateEnvelope
的簽名滿足mod_policy
。 -
通過將更新集應用於當前配置,計算該配置的完整新版本
-
將新配置寫成
ConfigEnvelope
作為CONFIG_UPDATE
賦給last_update
字段,新的配置賦給config
字段,sequence
字段自增。 -
將
ConfigEnvelope
寫成CONFIG
類型的Envelope
,最終將此作為唯一交易寫入配置區塊。
當peer(或者任意其他接收Deliver
者)接收到這個配置區塊后,就會通過將last_update
信息應用到當前配置並校驗orderer計算的config
字段是否包含正確的新配置,來驗證該配置是否被正確校驗。
Permitted configuration groups and values / 組和值得配置許可
有效的配置都是下面配置的子集。在此,用peer.<MSG>
表示一個ConfigValue
,其value
字段是稱為<MSG>
的序列化后的信息,定義在fabric/protos/peer/configuration.proto
。common.<MSG>
,msp.<MSG>
和orderer.<MSG>
分別定義在fabric/protos/common/configuration.proto
,fabric/protos/msp/mspconfig.proto
和fabric/protos/orderer/configuration.proto
。
Note, that the keys {{org_name}}
and {{consortium_name}}
represent arbitrary names, and indicate an element which may be repeated with different names.
注意,下面的{{org_name}}
和 {{consortium_name}}
是任意的名字,表示可以重復使用不同名稱的元素。
&ConfigGroup{
Groups: map<string, *ConfigGroup> {
"Application":&ConfigGroup{
Groups:map<String, *ConfigGroup> {
{{org_name}}:&ConfigGroup{
Values:map<string, *ConfigValue>{
"MSP":msp.MSPConfig,
"AnchorPeers":peer.AnchorPeers,
},
},
},
},
"Orderer":&ConfigGroup{
Groups:map<String, *ConfigGroup> {
{{org_name}}:&ConfigGroup{
Values:map<string, *ConfigValue>{
"MSP":msp.MSPConfig,
},
},
},
Values:map<string, *ConfigValue> {
"ConsensusType":orderer.ConsensusType,
"BatchSize":orderer.BatchSize,
"BatchTimeout":orderer.BatchTimeout,
"KafkaBrokers":orderer.KafkaBrokers,
},
},
"Consortiums":&ConfigGroup{
Groups:map<String, *ConfigGroup> {
{{consortium_name}}:&ConfigGroup{
Groups:map<string, *ConfigGroup> {
{{org_name}}:&ConfigGroup{
Values:map<string, *ConfigValue>{
"MSP":msp.MSPConfig,
},
},
},
Values:map<string, *ConfigValue> {
"ChannelCreationPolicy":common.Policy,
}
},
},
},
},
Values: map<string, *ConfigValue> {
"HashingAlgorithm":common.HashingAlgorithm,
"BlockHashingDataStructure":common.BlockDataHashingStructure,
"Consortium":common.Consortium,
"OrdererAddresses":common.OrdererAddresses,
},
}
Orderer system channel configuration / Order channel 配置
ordering系統channel定義了創建channel的ordering參數和consortiums。ordering service必須有一個ordering系統channel,這是被創建的第一個channel。建議不要在ordering系統channel初始配置中定義application部分,但是測試是可以這么做。注意,任何對ordering系統channel有讀權限的成員都可以查看所有channel創建,因此channel的訪問應受限制。
The ordering parameters are defined as the following subset of config:
ordering參數定義如下:
&ConfigGroup{
Groups: map<string, *ConfigGroup> {
"Orderer":&ConfigGroup{
Groups:map<String, *ConfigGroup> {
{{org_name}}:&ConfigGroup{
Values:map<string, *ConfigValue>{
"MSP":msp.MSPConfig,
},
},
},
Values:map<string, *ConfigValue> {
"ConsensusType":orderer.ConsensusType,
"BatchSize":orderer.BatchSize,
"BatchTimeout":orderer.BatchTimeout,
"KafkaBrokers":orderer.KafkaBrokers,
},
},
},
ordering中的每個組織都在Orderer
組下有一個組元素,這個組定義了一個MSP
參數,這個參數包含該組織的加密身份信息。Orderer
組中的Values
決定了ordering節點的功能。他們存在於每個channel中,所以像orderer.BatchTimeout
就可在不同channel中指定不同值。
啟動時,orderer面對含有很多channel信息的文件系統,orderer通過識別帶有consortiums組定義的channel來標識系統channel。consortiums組結構如下。
&ConfigGroup{
Groups: map<string, *ConfigGroup> {
"Consortiums":&ConfigGroup{
Groups:map<String, *ConfigGroup> {
{{consortium_name}}:&ConfigGroup{
Groups:map<string, *ConfigGroup> {
{{org_name}}:&ConfigGroup{
Values:map<string, *ConfigValue>{
"MSP":msp.MSPConfig,
},
},
},
Values:map<string, *ConfigValue> {
"ChannelCreationPolicy":common.Policy,
}
},
},
},
},
},
注意,每個consortium定義一組成員,就行ordering組織的組織成員一樣。每個consortium也都定義了一個ChannelCreationPolicy
,它是一種應用於授權channel創建請求的策略,通常這個值設為ImplicitMetaPolicy
,要求channel的新成員簽名授權channel創建。有關channel創建更信息的內容,請參閱文檔后面的內容。
Application channel configuration / APP channel 配置
應用程序配置用於為應用類型交易設計的channel。其定義如下:
&ConfigGroup{
Groups: map<string, *ConfigGroup> {
"Application":&ConfigGroup{
Groups:map<String, *ConfigGroup> {
{{org_name}}:&ConfigGroup{
Values:map<string, *ConfigValue>{
"MSP":msp.MSPConfig,
"AnchorPeers":peer.AnchorPeers,
},
},
},
},
},
}
就像Orderer
部分,每個組織被編碼為一個組。然而,app channel不僅有MSP
身份信息,每個組織都附加了一個AnchorPeers
列表。這個列表允許不同組織的節點彼此聯系。
應用程序channel通過對orderer組織和共識選項的編碼,以允許對這些參數進行確定性更新,因此包含了orderer系統channel配置的相同Orderer
部分。但從應用角度看,這會在很大程度上被忽略。
Channel creation / 創建channel
當Orderer 接收到對一個不存在的channel的CONFIG_UPDATE
信息時,orderer就會假設這是個創建channel的請求並執行以下操作:
-
通過查看高層組中的
Consortium
值,orderer標識所要執行創建channel請求的consortium(譯注:這個詞暫時不知翻譯成什么好)。 -
orderer驗證Application組中的組織是對應的consortium中組織的一部分,並驗證
ApplicationGroup
的版本是1。 -
orderer驗證consortium是否有成員,新的channel也會有application成員(創建沒有成員的consortiums和channel只用於測試)。
-
orderer從ordering系統channel取得
Orderer
組,並創建一個包含新指定成員的Application
組,並將其mod_policy
指定為在consortium config中指定的ChannelCreationPolicy
,從而創建一個模板配置。注意,這個策略(mod_policy)是基於新配置的上下文的,因此需要所有成員的策略就是要需要新channel中所有成員的簽名,而不是consortium中的所有成員。 -
orderer用
CONFIG_UPDATE
更新這個模板配置。因為CONFIG_UPDATE
用於Application
組(其版本是1)的修改,所以配置碼根據ChannelCreationPolicy
驗證這些更新。如果channel創建包含任何其它修改,例如修改單個組織的錨節點,則調用該元素的相應mod策略。 -
帶有新channel配置的
CONFIG
交易被包裝並通過order系統channel發送到ordering,ordering之后channel就創建完成。