-
应用程序角度
-
身份管理
-
账本管理
-
交易管理
-
只能合约
-
-
底层角度
-
成员管理
MSP
-
共识服务
同一条链上不同结点的区块的一致性,同时确保区块里面的交易有效和有序
-
链码服务
-
安全和密码服务
-
2、网络结点架构
-
客户端、Peer结点、排序服务结点、CA结点
-
结点描述
-
客户端结点
由用户操作,并且与背书结点和排序服务结点通信。向背书结点提交交易提案,让背书结点背书,当收集到足够的背书以后向排序服务结点广播交易。
-
Peer结点
-
所有的Peer结点都是记账结点,他们验证区块中的交易,并将区块添加到账本
-
部分Peer结点同时是交易的背书结点,一个交易的背书结点由该交易对应的链码指定
-
当多个Peer结点成为一个组织,那么这个组织中会有一个Peer结点成为主结点,他与排序服务结点通信并且将新区块分发给组织内部
-
-
排序服务结点
接收包含足够背书的交易,并按顺序将他们打包成区块,然后将区块分发给各个组织的主结点
-
CA结点
CA结点是证书颁发机构,由客户端和服务器组成。
-
3、典型交易流程
-
创建交易提案并发送给背书结点
SignedProposal:{ ProposalBytes(Proposal):{ #提案消息头部 Header:{ #通道头部 ChannelHeader:{ Type:"HeaderType_ENDORSER_TRANSACTION", #客户端本地生成的交易号,跟客户端的身份证书相关,并且背书结点和记账结点利用这个校验是否存在重复交易 TxId:TxId, TimeStamp:TimeStamp, ChannelId:ChannelId, Extension(ChaincodeHeaderExtension):{ PayloadVisibility:PayloadVisibility, ChaincodeId:{ Path:Path, Name:Name, Version:Version } }, Epoch:Epoch }, #签名头部,用于消息有效性校验 SignatureHeader:{ #客户端的身份证书 Creator:Creator, #随机数 Nonce:Nonce } }, Payload:{ ChaincodeProposalPayload:{ Input(ChaincodeInvocationSpec):{ ChaincodeSpec:{ Type:Type, ChaincodeId:{ Name:Name }, Input(ChaincodeInput):{ Args:[] } } }, TransientMap:TransientMap } } }, # 调用者的签名信息,客户端将这个提案发送给背书结点,这个就是客户端的签名信息 Signature:Signature }
正常情况下,客户端将这个交易提案发给不同的背书结点背书,不同的背书结点模拟执行交易的结果应该是相同的,只有背书结点对结果的签名信息不一样
-
背书结点模拟执行交易并生成背书签名
背书结点收到来自客户端的交易提案以后进行下列验证:
-
交易提案的格式是否正确
-
交易是否曾经被提交过
-
交易签名是否有效
-
交易提案的客户端在当前channel上是否有write权限
上面验证通过以后,背书结点根据当前账本上的数据模拟执行提案中的交易,并生成一个读写集,但是此时账本数据不会发生更新,然后背书结点将生成的读写集签名,就是提案响应,并将响应返回给客户端
ProposalResponse:{ Version:Version, TimeStamp:Timestamp, Response:{ Status:Status, Message:Message, Payload:Payload }, Payload(ProposalResponPayload):{ ProposalHash:ProposalHash, Extension(ChaincodeAction):{ #返回的结果包含了交易模拟执行的读写集 Results(TxRwSet):{ NsRwSets(NsRwSet):[ NameSpace:NameSpace, KvRwSet:{ Reads(KVRead):[ Key:Key, Version:{ BlockNum:BlockNum, TxNum:TxNum } ], RangeQueriesInfo(RangeQueryInfo):[ StartKey:StartKey, EndKey:EndKey, ItrExhausted:ItrExhausted, ReadsInfo:ReadsInfo ], Writes(KVWrite):[ Key:Key, IsDelete:IsDelete, Value:Value ] } ] }, Events(ChaincodeEvent):{ ChaincodeId:ChaincodeId, TxId:TxId, EventName:EventName, Payload:Payload } Response:{ Status:Status, Message:Message, Payload:Payload }, ChaincodeId:ChaincodeId } }, #背书结点签名 Endorsement:{ Endorser:Endorser, Signature:Signature } }
-
-
客户端收集交易的背书
客户端首先对提案相应的背书结点签名进行验证
如果交易只是进行查询操作,那么这个提案响应不会发送给排序服务结点
-
客户端构造交易请求并发送给排序服务结点
客户端收到足够多的提案响应后就生成交易,将他发送给排序服务结点。
此时的交易中包含了交易提案,提案响应,背书签名
Envelope:{ Payload:{ #Envelope.Payload.Header==SignedProposal.Proposal.Header,都是交易提案的头部 Header:{ ChannelHeader:{ Type:"HeaderType_ENDORSER_TRANSACTION", TxId:TxId, Timestamp:Timestamp, Extension(ChaincodeHeaderExtension):{ PayloadVisibility:PayloadVisibility, ChaincodeId:{ Path:Path, Name:Name, Version:Version } }, Epoch:Epoch }, SignatureHeader:{ Creator:Creator, Nonce:Nonce } }, Data(Transaction):{ TransactionAction:[ # Envelope.Payload.Data.Transaction.Header是交易提交者的身份信息 # 与SignedProposal.Proposal.Header.SignatureHeader # 和Envelope.Payload.Header.SignatureHeader是冗余的 Header(SignatureHeader):{ Creator:Creator, Nonce:Nonce }, Payload(ChaincodeActionPayload):{ # Envelope.Payload.Data.TransactionAction.Payload.ChaincodeProposalPayload # 与交易提案的SignedProposal.Proposal.Payload.ChaincodeProposalPayload相同,不同的地方是 # TransientMap字段被强行设置为nil--保证了隐私性 ChaincodeProposalPayload:{ Input(ChaincodeInvocationSpec):{ ChaincodeSpec:{ Type:Type, ChaincodeId:{ Name:Name }, Input(ChaincodeInput):{ Args:[] } } }, TransientMap:nil }, Action(ChaincodeEndorseAction):{ # Envelope.Payload.Data.TransactionAction.Payload.Action.Payload # 与ProposalResponse.Payload结构完全一样 Payload(ProposalResponsePayload):{ ProposalHash:ProposalHash, Extension(ChaincodeAction):{ Results(TxRwSet):{ NsRwSets(NsRwSet):[ NameSpace:NameSpace, KvRwSet:{ Reads(KVRead):[ Key:Key, Version:{ BlockNum:BlockNum, TxNum:TxNum } ], RangeQueriesInfo(RangeQueryInfo):[ StartKey:StartKey, EndKey:EndKey, ItrExhausted:ItrExhausted, ReadsInfo:ReadsInfo ], Writes(KVWrite):[ Key:Key, IsDelete:IsDelete, Value:Value ] } ] }, Events(ChaincodeEvent):{ ChaincodeId:ChaincodeId, TxId:TxId, EventName:EventName, Payload:Payload } Response:{ Status:Status, Message:Message, Payload:Payload }, ChaincodeId:ChaincodeId } }, # Envelope.Payload.Data.TransactionAction.Payload.Action.Endorsement变成了数组, # 表示多个背书结点的签名 Endorsement:[ Endorser:Endorser, Signature:Signature ] } } ] } }, # 这是整个信封的签名,是交易提交者(客户端)对整个Envelope.Payload的签名 Signature:Signature }
客户端可以将上面的签名信封(生成的交易)发送给任意几个排序服务结点
-
排序服务结点对交易进行排序并生成区块
排序服务结点不会读取交易的内容,只是通过Envelope.Payload.Header.ChannelHeader.ChannelId获取通道名称,并把的交易生成的区块发送往这个通道
-
排序服务结点分发区块
排序服务结点向通道中的各个组织的主结点分发区块
-
记账结点验证区块内容并写入区块
-
所有的Peer结点都是记账结点,它首先验证接收的区块中交易的有效性,然后将区块提交到本地账本,并且产生一个生成区块的事件,监听区块事件的客户端就可以根据这个事件做后续的处理。
-
如果记账结点接收到的是配置区块,就会根据区块的内容跟新本地的配置信息。
-
交易数据的验证
区块数据的验证是以交易为单位
-
是否为合法的交易:交易格式是否正确,签名是否合法,内容是否被篡改
-
记账结点是否加入了这个通道
-
-
记账结点与VSCC
链码的交易是隔离的,每个交易的模拟执行结果读写集TxRwSet都包含了交易所属的链码,所以在将交易提交给系统链码VSCC验证之前,还会对应用链码进行校验,以下交易是非法的:
-
链码名称或版本为空
-
交易消息头部的链码名称Envelope.Payload.Header.ChannelHeader.Extension.ChaincodeId.Name和交易数据里的链码名称Envelope.Payload.Data.TransactionAction.Payload.ChaincodeProposalPayload.Input.ChaincodeSpec.ChaincodeSpec.Chaincode.Name不一样
-
链码更新当前链码数据时,产生的读写集的链码版本不是LSCC记录的最新版本
-
应用程序链码更新了LSCC链码的数据
-
应用程序链码更新了不可被外部调用的系统链码的数据
-
应用程序链码更新了不可被其他链码调用的的系统链码的数据
-
应用程序链码调用了不可被外部调用的系统链码
-
-
基于状态数据的验证和MVCC检查
交易通过了VSCC校验以后就进入了记账流程,kvledger会对读写集TxRwSet进行MVCC(Multi-Version Concuruency Control,版本并发控制)检查
-
kvledger是基于键值对的状态数据模型
-
读,写,删除状态数据
-
基于单一的键读取
-
基于键范围查询
-
-
MVCC对读数据(包括读集合和范围查询集合)进行校验,逻辑是对模拟执行时状态数据的版本和提交时的状态数据版本进行比较,如果版本不匹配,说明了在这段时间别的交易改变了状态数据,所以当前基于之前状态数据的交易是存在问题的
-
交易提交时并行的,所以在生成区块之前并不能确定交易执行的顺序,如果多个交易之间存在着执行依赖(几个交易必须串行化),MVCC还是检查这种依赖关系,如果依赖发生改变,也认定为交易是不合法的,(依赖关系发生变化实际上是状态数据发生了改变)
-
写集合包含了写入和删除数据两种操作
-
整个区块的交易的状态是同时提交的,保证了整个区块的状态数据要么都成功提交,要么都失败,所以只可能是账本数据和状态数据不一致,不会出现状态数据与区块数据不一致
-
-
无效交易的处理
伪造的交易是无效的交易,正常的交易也可能是无效的交易,MVCC检查的时候是检查的背书结点在模拟执行交易时的环境是否与记账结点提交交易时的环境(key,value,version)一样。
无效交易也会保存在区块中。但是无效交易的的读写集不会提交到状态数据库中,不会导致状态数据库发生改变
-
-
在组织内部同步最新的区块
4、消息协议结构
-
信封消息结构
信封消息是认证内容中的基本单元,包括一个消息负载(Payload)和一个签名(Signature)
// 信封包含一个带有签名的负载,以便认证该消息 // Peer接收这个信封的条件是: // 1.消息中指定的时期信息是当前窗口期 // 2.该负载在该周期内只看到一次(没有重放) message Envelope{ // 负载 payload bytes // 签名 signature bytes }
// 负载 payload Payload{ // 负载头部,包含类型,负载的性质,如何解组负载的数据字段,创建者的信息和随机数,标识时间逻辑窗口的时期信息 header Header // 负载数据 data bytes }
// 负载的头部 header Header{ // 负载头部中的通道头部信息 channel_header bytes // 负载头部中的签名头部信息 signature_header bytes }
// 通道头部信息,用于预防重放和身份标识 channel_header ChannelHeader{ // 消息类型,由HeaderType定义 type int32 // 消息协议的版本 version int32 // 这个时间戳是发件人创建消息的本地时间 timestamp google.protobuf.Timestamp // 该消息绑定的通道的标识符 channel_id string // 端到端使用的唯一的标识符,由较高层(客户端)设置,传递给背书结点(用于检查消息的唯一性),当消息正确传递是,它被记账结点检索,存储与账本中 tx_id string // 这个时期信息基于区块高度定义,这个字段用于标识时间的逻辑窗口 // 只有在如下两个条件都成立的情况下,对方才接受提案响应 // 1.消息中指定的时期信息是当前时期 // 2.该消息在该时期内只看到一次 epoch uint64 // 根据消息头类型附加的扩展 extension bytes }
// 消息类型 enum HeaderTyoe{ // 非透明消息 MESSAGE // 通道配置消息 CONFIG // 通道配置更新消息 CONFIG_UPDATE // 客户端提交的背书提案消息 ENDORSER_TRANSACTION // 排序管理服务内部使用 ORDERER_TRANSACTION // 指示deliver API查找信息 DELIVER_SEEK_INFO // 链码打包安装消息 CHAINCODE_PACKAGE }
// 签名头部信息 signature_header SignatureHeader{ // 消息的创建者,链的证书 creator bytes // 一个随机数,用于检测重放攻击 nonce bytes }
-
配置管理结构
-
配置设置在创世区块中,在后续也可以更改配置
-
配置信息在CONFIGURATION_TRANSACTION的信封消息中,这个信封消息就是基于前文描述的信封消息
-
配置信息是一个单数的交易,配置信息没有任何的依赖,包含了全量数据而不是增量数据
CONFIGURATION_TRANSACTION类型的信封消息的负载部分除了编组的负载,签名还有ConfigurationEnvelope类型的负载数据
// 配置信息的消息信封 message ConfigurationEnvelope{ // 负载 payload bytes // 签名 signature bytes // 主要是配置信息的序列号和ID,repeated的意思是这个数据成员可以包含多个 repeated Item SignedConfigurationItem }
// 这个结构主要用来关联与配置信息相关的序列号和ID,序列号递增,可以防止重放攻击 Item SignedConfigurationItem{ // 这个结构就是用来保存序列号和ID之类的信息 ConfigurationItem bytes // 多个签名消息 repeated Signatures Envelope }
// 配置条目的数据信息保存在这个结构中 configurationItem ConfigurationItem{ enum ConfigurationType{ Policy Chain Order Fabric } // 链的id ChainID bytes // 上一次配置信息被修改时配置消息信封中的序列号,如果需要修改配置,那么就将这个字段修改为递增后的序列号 LastModified uint64 // 配置消息的类型 Type ConfigurationType // 指向一个已经命名的策略,用来对将来的签名进行评估,以确定修改是否被授权 ModificationPolicy string // Key和Value分别是配置项及其内容 Key string Value bytes }
修改配置包含以下内容
-
检索现有配置
-
递增配置信息中的序列号
-
修改所需要的配置项,将每个配置项的LastModified字段设置为递增后的序列号
-
更新SignedConfigurationItem中的签名信息
-
将签名后的信封信息提交给排序服务结点
配置管理员将验证
-
所有的配置项和信封都指向了正确的链(通过ChainID这个字段)
-
添加或者修改了哪些配置项
-
是否有现有的配置项被忽略
-
所有配置更改的LastModification都等于信封消息中的序列号
-
所有的更改配置都符合修改策略
注意:修改配置项将会产生新的创世区块。
-
-
背书流程结构
-
客户端向所有相关的背书结点发送提案消息
-
每个背书结点向客户端发送提案响应消息
-
客户端将背书结果写入交易中,签名发送到排序服务
-
交易提案结构
头部(一些元数据,如类型,调用者的身份,时间,链的ID,加密的随机数)
不透明负载
// 包含签名的提案消息 signedProposal SignedProposal{ // 真正的提案,bytes序列化 proposal_bytes bytes(Proposal) // 消息创建者的签名 signature bytes }
// 真正的提案,包含了头部,负载和可选的扩展三个部分 proposal Proposal{ // 提案头部 header bytes // 提案负载,具体类型由头部的类型决定 payload bytes // 可选的扩展,对于CHAINCODE类型的消息,其内容可能是ChaincodeAction消息 extension bytes }
背书结点验证从客户端发送过来的提案消息的签名
-
预验证用户生成签名证书的有效性
-
验证证书是否可信(证书是否由受信任的CA签名),并允许交易(能否通过ACL检查,访问控制)
-
验证SignedProposal.proposal_bytes上的签名是否有效
-
检测重放攻击
当ChainHeader的类型为ENDORSER_TRANSACTION时。Proposal中的扩展消息如下:
// 可选的扩展 extension ChaincodeHeaderExtension{ // 控制提案的负载在交易和账本中的可见程度 // 负载所有字节可见 // 只有负载的hash值可见 // 任何东西都不可见 // 可见性也可以由系统链码ESCC设置,此时本字段的值会被覆盖掉 payload_visibility bytes // 要定位的链码ID chaincode_id ChaincodeID }
chaincodeProposalPayload ChaincodeProposalPayload{ // 包含调用的应用链码传入的参数 input bytes // 实现应用程序级的加密数据,这个字段的值不会记录到账本中 TransientMap map[string]bytes }
// 本字段包含链码执行所产生的动作和事件 chaincodeAction ChaincodeAction{ // 调用链码产生的读写集 results bytes // 调用链码产生的事件 events bytes // 调用链码的结果 response bytes // 链ID chaincode_id ChaincodeID }
-
-
提案响应结构
提案响应从背书结点返回给客户端,响应结果可能是提案成功,也可能是失败,如果足够多的背书结点都同意这个提案,那么这个提案就可以生成负载消息并且发送给排序服务结点
// 包含背书结点背书的相关信息的提案响应消息 proposalResponse ProposalResponse{ // 消息协议的版本 version int32 // 消息创建的时间,由消息发送者(背书结点)定义 timestamp google.protobuf.Timestamp // 某个提案的背书是否成功 response Response // 负载,ProposalResponsePayload的字节序列 payload bytes // 提案的背书内容,基本就是背书结点的签名 endorsement Endorsement }
// 提案响应的真正负载 payload ProposalResponsePayload{ // 触发此应答交易提案的哈希值 proposal_hash bytes // 扩展内容,被解组为特定类型的消息 extension bytes }
proposal_hash字段将交易提案和提案响应对应起来,为异步系统的记账功能实现了追责和防抵赖
// 背书结点的背书内容 endorsement Endorsement{ // 背书结点的身份(如证书信息) endorser bytes // 签名(对提案响应和背书结点证书这两个内容签名) signature bytes }
-
背书交易结构
客户端收到足够多的背书后,将这些背书组合成一个交易信息
// 一个交易信息 transaction Transaction{ // 负载是一个TransactionAction数组,每个交易需要一个数组来适应多个动作 repeated actions TransactionAction }
transactionAction TransactionAction{ // 交易信息头部 header bytes // 负载,是ChaincodeActionPayload字节序列 payload bytes }
chaincodeActionPayload ChaincodeActionPayload{ // ChaincodeProposalPayload的字节序列,内容来自链码原始调用的参数 // full--整个ChaincodeProposalPayload消息包含在这里 // hash--仅包含ChaincodeProposalPayload的hash值 // nothing chaincode_proposal_payload bytes // 应用于账本的动作列表 action ChaincodeEndorsedAction }
// 本结构承载有关具体提案的背书信息 chaincodeProposalPayload ChaincodeProposalPayload{ // 背书结点签名的ProposalResponsePayload的字节序列 proposal_response_payload bytes // 提案背书,基本是背书结点自己的签名,这个字段包含提案已经收到的所有的背书信息 repeated endorsements Endorsement }
5、策略管理和访问控制
策略管理主要包括交易背书策略,链码的实例化策略,通道管理策略等。
-
策略定义及其类型
策略定义了一些规则,验证签名数据是否符合定义的条件,结果为TRUE或者FALSE
type Policy struct{ // 策略的类型,有如下两种 // SignaturePolicy:在验证签名策略的基础上,支持AND,OR,NOutOf(NOutOf(n,m)指的是m个条件中满足n个就返回true) // ImplicitMetaPolicy:只适用于通道管理策略,在SignaturePolicy之上的策略 Type int32 // 策略的内容 Value []byte }
SignaturePolicy实际只有两种,SignedBy和NOutOf,其他的比如AND和OR都会转化成NOutOf
type SignaturePolicy struct{ // 支持的类型有: // *SignaturePolicy_SignedBy 验证单个签名是否正确 // *SignaturePolicy_NOutOf_ 验证是否有n个签名正确 Type isSignaturePolicy_Type `protobuf_oneof:"Type"` }
ImplicitMetaPolicy是递归定义的方法,Implict(隐含的)说明规则是由子策略生成的,Meta说明策略依赖其他策略的验证结果
type ImplicitMetaPolicy struct{ // 子策略的名称 SubPolicy string // 策略的规则,有如下三种形式 // ImplicitMetaPolicy_ANY:任意一个子规则成立就满足策略 // ImplicitMetaPolicy_ALL:所有子规则成立才满足策略 // ImplicitMetaPolicy_MAJORITY:大多数子规则成立就满足策略,计数方法len(subPolicies)/2+1 Rule ImplicitMetaPolicy_Rule }
-
交易背书策略
交易背书策略是对交易进行背书的规则,在链码实例化的时候指定
-
交易背书策略的验证
背书是由一组签名组成,每个Peer结点接收到区块时,都能根据交易的内容本地校验证书本书是否满足背书策略,不需要和其他结点交互
验证交易背书的基本原则:
-
所有背书有效
-
满足背书策略的有效背书数量(转化为NOutOf)
-
背书是期望的背书结点签名的,背书策略中指定哪些组织和角色是有效的背书结点
NOutOf的实现:
// 背书策略定义 type SignaturePolicyEnvelope struct{ // 背书策略的版本,默认是0 Version int32 // 背书策略规则,签名策略 Rule *SignaturePolicy // 背书策略主体,MSP主体签名,MSP主体是基于MSP身份标识的,有如下几种类型 // MSPPrincipal_ROLE:基于MSP角色验证,目前只有admin和member两种 // MSPPrincipal_ORGANIZATION_UNIT:基于部门的验证方法,同一个MSP中不同的部门 // MSPPrincipal_IDENTITY:基于某个具体的身份证书的验证方法,验证签名是否有效 Identities []*common1.MSPPrincipal }
MSPPrincipal的定义:
type MSPPrincipal struct{ // MSP的类型 PrincipalClassifiication MSPPrincipal_Classification // 根据MSP的类型不同,实体有不同的内容 Principal []byte }
-
基于MSP角色的验证
// 当PrincipalClassificition是MSPPrincipal_ROLE时,主体存储的内容如下 type MSPRole struct{ // MSP标识符 MspIdentifier string // MSP角色:可选值是MSPRole_MEMBER和MSPRole_ADMIN // MSPRole_MEMBER验证是否为同一个MSP的有效签名 // MSPRole_ADMIN验证签名者是否是MSP设置好的admin成员 Role MSPRole_MSPRoleType }
-
-
基于部门的验证
// 当PrincipalClassificition是MSPPrincipal_ORGANIZATION_UNIT时,主体存储的内容如下 // 验证过程为: // 验证是否为相同的MSP // 验证是否是有效的证书 // 验证组织部门信息是否匹配 type OrganizationUnit struct{ // MSP标识符 MspIdentifier string // 组织部门标识符 OrganizationalUnitIdentifier string // 证书标识符:信任证书链和组织部门信息的哈希 CertifiersIdentifier []byte }
-
基于身份证书的验证
// 当PrincipalClassificition是MSPPrincipal_IDENTITY时,主体存储的内容如下 type identity struct{ // 身份标识符,包含MSP标识符和身份编号 id *IdentityIdentifier // 身份的数字证书,包含了对公钥的签名 cert *x59.Certtificate // 身份的公钥 pk bccsp.Key // 身份的msp信息 msp *bccspmsp }
-
-
链码的实例化策略
链码的实例化策略是用来验证是否有权限进行链码实例化和链码升级的,实例化策略是咋idui链码打包和签名的时候指定的,如果没有指定,默认是只有通道管理员才能实例化
// 链码的实例化策略是直接从链码打包中获取的,实例化完成后会将策略存放在链上 type SignedChaincodeDeploymentSpec struct{ // 链码的部署规范 ChaincodeDeploymentSpec []byte // 链码的实例化策略,结构同背书策略,在实例化时验证 InstantiationPolicy []byte // 链码所有者的签名背书列表 OwnerEndorsements []*Endorsement }
存放在链上的链码信息结构如下:
type ChaincodeData struct{ // 链码名称 Name string // 链码版本 Version string // 链码的ESCC Escc string // 链码的VSCC Vscc string // 链码的背书策略 Policy []byte // 链码的内容:包含链码的名称,版本,源码哈希 // 不包含链码的源码 Data []byte // 链码的ID(指纹)标识,未使用 Id []byte // 链码实例化策略 InstantiationPolicy []byte }
-
通道管理策略
通道配置是递归定义的
type ConfigGroup struct{ // 配置版本 Version uint64 // 子配置 Groups map[string]*ConfigGroup // 配置值 Values map[string]*ConfigValue // 配置策略定义 Policies map[string]*ConfigPolicy // 配置修改策略的名称 ModPolicy string }
配置值ConfigValue定义的是一些配置数据:
type ConfigValue struct{ // 配置版本 Version uint64 // 配置数据,可以是json结构 Value []byte // 配置修改策略的名称 ModPolicy string }
配置策略的定义:
// 基于SignaturePolicy和ImplicitMetaPolicy,ModPolicy代表的是修改统计策略用到的策略名称 type ConfigPolicy struct{ // 配置策略的版本 Version uint64 // 配置策略的内容,前面有 Policy *Policy // 配置策略中修改策略的定义 ModPolicy string }
通道设置了三种配置策略
策略名称 策略含义 策略说明 Readers 通道读权限 任何读取通道交易的权限控制策略 Writers 通道写权限 任何向通道提交交易的权限控制策略 Admins 通道管理权限 任何修改通道配置的权限管理策略
-