- 区块链
- 密码学
【免费下载链接】fabric
Hyperledger Fabric is an enterprise-grade permissioned distributed ledger framework for developing solutions and applications. Its modular and versatile design satisfies a broad range of industry use cases. It offers a unique approach to consensus that enables performance at scale while preserving privacy.
Hyperledger Fabric 作为一个企业级许可制(permissioned)分布式账本框架,其整个区块链网络的配置均由"策略"(Policy)管理。本文以仓库文档 docs/source/policies.rst 为骨架,系统讲解 Fabric 策略的核心概念、两种策略类型(SignaturePolicy 与 ImplicitMetaPolicy)的构造原理、通道配置中的层级结构与默认策略,并结合仓库源码(common/policies、common/cauthdsl)与sampleconfig/configtx.yaml中的真实配置,提供从原理到实战的完整参考。读者读完后将能够读懂任意 channel config 中的策略定义、自行设计签名策略,并理解策略在通道读写、链码背书、系统管理中的实际作用。
什么是策略(Policy)
在 Fabric 中,策略本质上是一个函数:它接收一组"签名数据"(signed data)作为输入,当签名数据满足策略要求时评估成功(返回 nil),否则返回错误,指出签名数据中某些方面未满足策略。
更具体地说,策略用来判定"某数据的签名者或签名者集合"是否满足某个条件,从而确认正确的参与方已经同意了一笔交易或一项变更。例如,一个策略可以定义:
- 5 个不同组织中的任意 2 个组织的管理员必须签名;
- 任意组织的任意成员必须签名;
- 两个特定证书必须都签名。
这些只是最简单的例子,借助下面的策略类型,可以构造出功能强大得多的规则。
策略的运行时接口
在仓库源码 common/policies/policy.go 中,策略被抽象为Policy接口,包含两个核心评估方法:
type Policy interface { // EvaluateSignedData 接收一组 SignedData,评估: // 1) 签名相对于相关消息是否有效 // 2) 签名身份是否满足策略 EvaluateSignedData(signatureSet []*protoutil.SignedData) error // EvaluateIdentities 接收一组身份,评估它们是否满足策略 EvaluateIdentities(identities []mspi.Identity) error }EvaluateSignedData首先通过policies.SignatureSetToValidIdentities(同样位于 common/policies/policy.go)对签名集进行合法性校验与去重,再委托给EvaluateIdentities做策略判定。这套接口是所有策略实现(包括下面的两种类型)的统一契约。
策略的两种类型
Fabric 目前实现了两种策略类型:
- SignaturePolicy(签名策略):功能最强的一种。它以 MSP Principal(MSP 主体)的求值规则组合来表达策略,支持任意组合的AND、OR、NOutOf,从而可以构造诸如"组织 A 的管理员加上另外 2 个管理员,或者 20 个组织管理员中的 11 个"这样极其强大的规则。
- ImplicitMetaPolicy(隐式元策略):灵活性不如 SignaturePolicy,仅在配置上下文中有效。它聚合配置层级中更深处策略的求值结果——这些底层策略最终仍由 SignaturePolicy 定义。它擅长表达"组织管理员策略的大多数"这类好的默认规则。
策略的 Protobuf 编码
两种策略都被编码在common.Policy消息中,其定义位于fabric-protos/common/policies.proto:
message Policy { enum PolicyType { UNKNOWN = 0; // 保留用于检查是否正确初始化 SIGNATURE = 1; MSP = 2; IMPLICIT_META = 3; } int32 type = 1; // 对外部实现者:前 1000 个类型编号保留,否则使用 PolicyType 之一 bytes policy = 2; }编码时,只需选择SIGNATURE或IMPLICIT_META作为type字段的值,再把对应策略实现 proto 的 marshal 结果填入policy字段即可。注意UNKNOWN(0)类型仅用于初始化检查,而MSP(2)类型在当前实现中不实际使用。
在 common/policies/policy.go 的ManagerImpl.NewManagerImpl中可以看到,配置管理器在解析策略时依据policy.GetType()分派:IMPLICIT_META类型走NewImplicitMetaPolicy,其余类型则从providers注册表中查找对应的Provider(如 cauthdsl 提供的 SignaturePolicy Provider)进行编译;未知类型会直接报错,保证配置的严格性。
通道配置与策略的层级结构
通道配置表现为配置组(ConfigGroup)的层级结构,每个配置组都关联一组 Values 和 Policies。对于一个配置合理的、包含两个应用组织和排序组织的应用通道,其配置最小形态如下:
Channel: Policies: Readers Writers Admins Groups: Orderer: Policies: Readers Writers Admins Groups: OrderingOrganization1: Policies: Readers Writers Admins Application: Policies: Readers -----------> Writers Admins Groups: ApplicationOrganization1: Policies: Readers Writers Admins ApplicationOrganization2: Policies: Readers Writers Admins上图中用------->标记的Writers策略,可以用简写路径/Channel/Application/Writers引用。注意:形似目录路径的各段是组名,而最后形似文件名的部分是策略名。仓库源码 common/policies/policy.go 中定义了这些标准策略路径的常量,例如:
ChannelReaders=/Channel/Readers,涵盖排序与应用的读者策略;ChannelApplicationReaders=/Channel/Application/Readers;ChannelApplicationAdmins=/Channel/Application/Admins;BlockValidation=/Channel/Orderer/BlockValidation;ChannelOrdererWriters=/Channel/Orderer/Writers。
系统不同组件会引用这些策略名来完成访问控制。例如:在 orderer 上调用Deliver服务,请求签名必须满足/Channel/Readers策略;而要将区块通过 gossip 发给 peer,则要求满足/Channel/Application/Readers策略。通过设置不同的策略,系统即可获得丰富的访问控制能力。这在sampleconfig/configtx.yaml的 ACL 映射中体现得非常直观——例如_lifecycle/CommitChaincodeDefinition映射到/Channel/Application/Writers,qscc/GetBlockByNumber映射到/Channel/Application/Readers。
构造 SignaturePolicy(签名策略)
与所有策略一样,SignaturePolicy 以 protobuf 表达:
message SignaturePolicyEnvelope { int32 version = 1; SignaturePolicy policy = 2; repeated MSPPrincipal identities = 3; } message SignaturePolicy { message NOutOf { int32 N = 1; repeated SignaturePolicy policies = 2; } oneof Type { int32 signed_by = 1; NOutOf n_out_of = 2; } }外层SignaturePolicyEnvelope定义了一个版本(当前仅支持0,common/cauthdsl/policy.go 中NewPolicy会校验版本必须为 0)、一组以MSPPrincipal表达的身份,以及一个通过索引引用这些identities的策略规则。SignaturePolicy是一个递归数据结构:它要么表示来自特定MSPPrincipal的单一签名要求,要么表示一组SignaturePolicy的集合,要求其中N个得到满足。
基本示例:双签名策略
SignaturePolicyEnvelope{ version: 0, policy: SignaturePolicy{ n_out_of: NOutOf{ N: 2, policies: [ SignaturePolicy{ signed_by: 0 }, SignaturePolicy{ signed_by: 1 }, ], }, }, identities: [mspP1, mspP2], }该策略作用于 MSP PrincipalmspP1和mspP2,要求签名集中同时存在一个满足mspP1的签名和一个满足mspP2的签名。
复杂示例:嵌套 NOutOf
SignaturePolicyEnvelope{ version: 0, policy: SignaturePolicy{ n_out_of: NOutOf{ N: 2, policies: [ SignaturePolicy{ signed_by: 0 }, SignaturePolicy{ n_out_of: NOutOf{ N: 1, policies: [ SignaturePolicy{ signed_by: 1 }, SignaturePolicy{ signed_by: 2 }, ], }, }, ], }, }, identities: [mspP1, mspP2, mspP3], }该策略要求:一个满足mspP1的签名,加上一个满足mspP2或mspP3的签名。可以看到,借助递归的NOutOf,几乎任意复杂的逻辑都可以用 SignaturePolicy 表达。
源码层面的编译与求值
从源码结构看,common/cauthdsl/cauthdsl.go 中的compile函数会把上述 protobuf 结构递归编译成 Go 可执行的闭包:
SignaturePolicy_SignedBy分支:校验索引t.SignedBy是否在identities范围内,然后对签名集中每个"未使用"(used[i] == false)的身份调用sd.SatisfiesPrincipal(signedByID)进行匹配,成功后把该身份标记为已使用并返回 true;SignaturePolicy_NOutOf_分支:递归编译所有子策略,统计满足的子策略个数verified,当verified >= N时该门(gate)求值成功。
这正是"AND / OR / NOutOf 任意组合"能力的来源。而 common/cauthdsl/policy.go 的provider.NewPolicy负责将策略字节反序列化、校验版本,并调用compile生成可评估的policy实例,使其实现policies.Policy接口。
已知限制:签名"消耗"顺序
限制:在针对签名集评估签名策略时,签名会按照它们出现的顺序被"消耗"(consumed),无论它们是否满足多个策略主体。
例如,考虑一个需要如下签名的策略:
2 of [org1.Member, org1.Admin]该策略的朴素意图是要求一个管理员和一个成员都签名。对于签名集:
[org1.MemberSignature, org1.AdminSignature]策略求值为 true,正如预期。然而,对于签名集:
[org1.AdminSignature, org1.MemberSignature]这个签名集不满足该策略。失败原因是:当org1.AdminSignature满足了org1.Member角色时,它就被org1.Member需求"消耗"掉了;而org1.MemberSignature无法满足org1.Admin主体,因此策略求值为 false。这一行为与 common/cauthdsl/cauthdsl.go 中compile函数里used数组的标记逻辑完全一致——每个身份在签名集遍历中一旦被某条signed_by规则使用,后续规则便会跳过它。
规避方法:在策略 identities 规范中,身份应按"从最高权限到最低权限"排序;在签名集中,签名应按"从最低权限到最高权限"排序。
MSP Principals(MSP 主体)
MSP Principal 是"密码学身份"的广义概念。虽然 MSP 框架设计上可与 X.509 以外的密码学类型协同工作,但本文档默认底层 MSP 实现为基于 X.509 的默认 MSP 类型。
MSPPrincipal 定义于fabric-protos/msp_principal.proto:
message MSPPrincipal { enum Classification { ROLE = 0; ORGANIZATION_UNIT = 1; IDENTITY = 2; } Classification principal_classification = 1; bytes principal = 2; }principal_classification必须设为ROLE或IDENTITY。ORGANIZATIONAL_UNIT在本文档写作时尚未实现(仓库代码中同样没有对应实现)。
- IDENTITY类型:
principal字段设置为证书字面量的字节; - ROLE类型:更常用,因为它允许主体匹配由该 MSP 证书颁发机构签发的多个不同证书。
对于ROLE类型,principal是一个被 marshal 的MSPRole消息:
message MSPRole { string msp_identifier = 1; enum MSPRoleType { MEMBER = 0; // 表示 MSP 成员 ADMIN = 1; // 表示 MSP 管理员 CLIENT = 2; // 表示 MSP 客户端 PEER = 3; // 表示 MSP Peer } MSPRoleType role = 2; }msp_identifier设为将评估该签名的 MSP 的 ID(由通道配置中该组织的MSPConfigproto 定义),role设为MEMBER、ADMIN、CLIENT或PEER之一。特别地:
MEMBER:匹配该 MSP 签发的任何证书;ADMIN:匹配在 MSP 定义中被枚举为管理员(admin)的证书;CLIENT(PEER):匹配携带客户端(peer)组织单元(OU)的证书。
构造 ImplicitMetaPolicy(隐式元策略)
ImplicitMetaPolicy只能在通道配置上下文中正确定义。它之所以"Implicit(隐式)",是因为它是基于当前配置隐式构造的;之所以"Meta(元)",是因为它的求值对象不是 MSP 主体,而是其他策略。它定义于fabric-protos/common/policies.proto:
message ImplicitMetaPolicy { enum Rule { ANY = 0; // 要求任一子策略被满足;若不存在子策略,恒返回 true ALL = 1; // 要求所有子策略被满足 MAJORITY = 2; // 要求严格多数(超过一半)子策略被满足 } string sub_policy = 1; Rule rule = 2; }求值语义示例
示例一:考虑定义在/Channel/Readers的策略:
ImplicitMetaPolicy{ rule: ANY, sub_policy: "foo", }该策略会隐式选择/Channel的子组(此例中为Application和Orderer),取出名为foo的策略,得到/Channel/Application/foo和/Channel/Orderer/foo。求值时,检查这两个策略中任意一个(ANY)是否能无错通过;若规则是ALL,则要求两者都通过。
示例二:考虑定义在/Channel/Application/Writers的策略,且配置了 3 个应用组织OrgA、OrgB、OrgC:
ImplicitMetaPolicy{ rule: MAJORITY, sub_policy: "bar", }此时收集到的策略为/Channel/Application/OrgA/bar、/Channel/Application/OrgB/bar、/Channel/Application/OrgC/bar。由于规则要求MAJORITY,该策略要求 3 个组织的bar策略中至少 2 个被满足。
源码实现细节
common/policies/implicitmeta.go 中的NewImplicitMetaPolicy精确实现了上述语义:它反序列化ImplicitMetaPolicy定义后,遍历当前组的所有子管理器(managers),为每个子组取出同名子策略;随后按规则计算阈值(threshold):
switch definition.GetRule() { case cb.ImplicitMetaPolicy_ANY: threshold = 1 case cb.ImplicitMetaPolicy_ALL: threshold = len(subPolicies) case cb.ImplicitMetaPolicy_MAJORITY: threshold = len(subPolicies)/2 + 1 } // 特例:无子策略时,将 0 视为 majority 或 any if len(subPolicies) == 0 { threshold = 0 }EvaluateSignedData则逐个对子策略求值,每成功一个就将remaining减一,直到满足阈值为止——这与文档描述"对更深处策略的求值结果进行聚合"完全吻合。
策略默认值(Policy Defaults)与 configtx.yaml 实战
configtxgen工具使用的策略必须在 configtx.yaml 中显式指定。需要特别注意的是:层级较高处的策略一律定义为ImplicitMetaPolicy,而叶子节点必然定义为SignaturePolicy。这组默认值设计得很巧妙:当组织数量变化时,ImplicitMetaPolicy无需重新定义;而每个组织可以自行选择自己的 Reader、Writer、Admin 规则和阈值。
仓库示例:sampleconfig/configtx.yaml
样本配置 完整展示了这套策略体系(该文件同时定义了SampleOrg的"读者/写者/管理员/背书"四类策略):
Policies: &SampleOrgPolicies Readers: Type: Signature Rule: "OR('SampleOrg.member')" # 若 MSP 配置了新的 NodeOUs,可使用更精确的规则: # Rule: "OR('SampleOrg.admin', 'SampleOrg.peer', 'SampleOrg.client')" Writers: Type: Signature Rule: "OR('SampleOrg.member')" Admins: Type: Signature Rule: "OR('SampleOrg.admin')" Endorsement: Type: Signature Rule: "OR('SampleOrg.member')"注意SampleOrg组织级策略的规范路径是/Channel/<Application|Orderer>/<OrgName>/<PolicyName>——这与文档中介绍的层级引用方式一一对应。
在 Application 层级(规范路径/Channel/Application/<PolicyName>),默认策略全部是 ImplicitMeta,聚合各组织同名策略:
Policies: &ApplicationDefaultPolicies LifecycleEndorsement: Type: ImplicitMeta Rule: "MAJORITY Endorsement" Endorsement: Type: ImplicitMeta Rule: "MAJORITY Endorsement" Readers: Type: ImplicitMeta Rule: "ANY Readers" Writers: Type: ImplicitMeta Rule: "ANY Writers" Admins: Type: ImplicitMeta Rule: "MAJORITY Admins"Orderer 层级(规范路径/Channel/Orderer/<PolicyName>)同样遵循"高层 ImplicitMeta、叶子 Signature"的约定,并额外定义了区块校验策略:
Policies: Readers: Type: ImplicitMeta Rule: "ANY Readers" Writers: Type: ImplicitMeta Rule: "ANY Writers" Admins: Type: ImplicitMeta Rule: "MAJORITY Admins" # BlockValidation 指定区块中必须包含 orderer 的哪些签名,peer 才会校验通过 BlockValidation: Type: ImplicitMeta Rule: "ANY Writers"Channel 根层级(规范路径/Channel/<PolicyName>)则定义了系统级访问边界——注释中明确说明:Readers控制谁可调用DeliverAPI,Writers控制谁可调用BroadcastAPI,Admins控制谁可修改本层配置:
Channel: &ChannelDefaults Policies: Readers: Type: ImplicitMeta Rule: "ANY Readers" Writers: Type: ImplicitMeta Rule: "ANY Writers" Admins: Type: ImplicitMeta Rule: "MAJORITY Admins"策略在链码生命周期与 ACL 中的应用
除了通道配置,策略还渗透到链码与系统资源访问控制中。从 sampleconfig/configtx.yaml 的 ACLs 默认段可以看到大量资源到策略路径的映射,例如:
- 链码生命周期
_lifecycle的CheckCommitReadiness、CommitChaincodeDefinition、QueryChaincodeDefinition均映射到/Channel/Application/Writers; - 查询类系统链码
qscc、lscc、cscc的各类查询接口映射到/Channel/Application/Readers; - 事件订阅
event/Block、event/FilteredBlock映射到/Channel/Application/Readers; peer/Propose(调用链码)与peer/ChaincodeToChaincode(链码间调用)映射到/Channel/Application/Writers。
这意味着:通道的 Writers 策略一旦被修改,所有依赖该策略的链码提交、提案与配置更新操作的访问控制会同步生效——这正是"通过设置不同策略获得丰富访问控制"在真实网络中的落地形态。
总结与设计建议
Fabric 的策略体系可归纳为三层心智模型:
- 叶子层 = SignaturePolicy:由各组织自行定义,直接表达"谁(哪个 MSP 的哪种角色、哪个证书)签名才算数",支持 AND / OR / NOutOf 任意嵌套组合;
- 聚合层 = ImplicitMetaPolicy:位于配置层级较高处,隐式收集所有子组的同名策略,再按 ANY / ALL / MAJORITY 规则聚合出结果,从而对组织数量的增减天然免疫;
- 消费层 = 系统组件引用:orderer 的 Deliver/Broadcast、peer 的 gossip 区块分发、链码生命周期与各类系统链码的 ACL,均通过
/Channel/...这样的规范路径引用策略完成鉴权。
在设计自己的通道策略时,请务必记住两条关键原则:
- 遵循层级约定:高层用 ImplicitMeta 聚合、叶子用 Signature 落地,这样组织扩展时无需重写聚合策略;
- 警惕签名消耗顺序:策略 identities 按"高权限 → 低权限"排序,签名集按"低权限 → 高权限"排序,避免出现"2 of [org1.Member, org1.Admin]"在签名顺序变化时求值失败的陷阱。
- 区块链
- 密码学
【免费下载链接】fabric
Hyperledger Fabric is an enterprise-grade permissioned distributed ledger framework for developing solutions and applications. Its modular and versatile design satisfies a broad range of industry use cases. It offers a unique approach to consensus that enables performance at scale while preserving privacy.
相关推荐
Hyperledger Fabric 策略(Policies)完全指南:从 Signature 与 ImplicitMeta 语法到通道配置实战
Hyperledger Fabric 策略(Policies)完全指南:从 Signature 与 ImplicitMeta 语法到通道配置实战 导读 :本指南
区块链密码学Hyperledger Fabric 通道策略完全指南:Signature 策略、ImplicitMeta 策略与 ACL 实战解析
Hyperledger Fabric 通道策略完全指南:Signature 策略、ImplicitMeta 策略与 ACL 实战解析 通道是组织之间进行私有通信
区块链密码学Hyperledger Fabric 背书策略(Endorsement Policy)完全指南:链码级、集合级与键级配置与验证
Hyperledger Fabric 背书策略(Endorsement Policy)完全指南:链码级、集合级与键级配置与验证 导读 背书策略是 Hyperle
区块链密码学
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考