原文档地址:https://hyperledger-fabric.readthedocs.io/en/release-2.0/pluggable_endorsement_and_validation.html
一个交易提交到记账节点后,记账节点在更改世界状态之前对交易作以下验证:
-
验证交易签名
-
验证背书签名
-
验证背书签名集是否符合背书策略
但是在有些实际使用中需要自定义验证规则,比如下面两个例子: -
需要考虑这个交易的输入是否被使用过,比特币的UTXO机制;
-
匿名交易:即背书不包含身份,只有公钥和签名,即只能验证它是持有这个公钥的人,不知道这个公钥对应的是谁。
Fabric允许将自定义背书和验证逻辑的实现和部署到对等方,并以可插拔的方式与链码处理相关联。该逻辑既可以作为内置的可选逻辑编译到peer节点中,也可以作为Golang插件与peer节点一起编译和部署
默认情况下,链码将使用内置的背书和验证逻辑。但是,用户可以选择自定义背书和验证插件作为链码定义的一部分。管理员可以通过自定义peer节点的本地配置来扩展peer节点可用的背书/验证逻辑。
怎么配置及部署
每个peer节点都有一个本地配置文件(core.yaml),其中有用于设置认可/验证逻辑名称与要运行的实现代码之间的映射。
默认逻辑ESCC和 VSCC,可以在core.yaml中的handlers部分找到它们:
将背书或验证实现编译到peer节点时,该 name属性表示要运行的初始化函数,以便获得创建认可/验证逻辑实例的工厂。该函数是core/handlers/library/library.go中HandlerLibrary类的实例的方法,为了添加自定义背书或验证逻辑,需要在该类中添加其他方法,扩展该类。该类的方法包括:
// HandlerLibrary is used to assert
// how to create the various handlers
type HandlerLibrary struct {
}
// DefaultAuth creates a default auth.Filter
// that doesn't do any access control checks - simply
// forwards the request further.
// It needs to be initialized via a call to Init()
// and be passed a peer.EndorserServer
func (r *HandlerLibrary) DefaultAuth() auth.Filter {
return filter.NewFilter()
}
// ExpirationCheck is an auth filter which blocks requests
// from identities with expired x509 certificates
func (r *HandlerLibrary) ExpirationCheck() auth.Filter {
return filter.NewExpirationCheckFilter()
}
// DefaultDecorator creates a default decorator
// that doesn't do anything with the input, simply
// returns the input as output.
func (r *HandlerLibrary) DefaultDecorator() decoration.Decorator {
return decorator.NewDecorator()
}
func (r *HandlerLibrary) DefaultEndorsement() endorsement.PluginFactory {
return &builtin.DefaultEndorsementFactory{}
}
func (r *HandlerLibrary) DefaultValidation() validation.PluginFactory {
return &DefaultValidationFactory{}
}
由于直接在类中添加方法很麻烦并且给部署带来了挑战,因此另一种添加自定义背书验证逻辑的方式就是,通过在name的下方添加另一个属性library,将自定义背书和验证逻辑部署为Golang插件。
配置如下所示,.so文件是实现了自定义背书验证逻辑的插件,library的值是插件在本地的绝对路径。
然后需要在链码定义中指定使用的插件名字,例如通过CLI方式初始化链码时通过flag传参的方式peer chaincode instantiate --escc customEndorsement --vscc customValidation指定使用的验证插件的名字(可否指定两个?不指定的话就是默认的那个)。SDK等其他方式的设置请参考原网页链接。
背书插件的实现
- 实现背书插件工厂接口
- 实现背书插件接口
Endorse方法的输入payload是交易响应
- 实现背书插件中需要注入的依赖,目前为背书插件提供两个可注入依赖(peer节点在创建插件后,调用插件的Init方法注入依赖)
SigningIdentity对象是用于对payload签名及序列化签名证书;State对象是用于与世界状态交互。(背书过程中为什么需要与世界状态交互啊?有时间看看默认的背书插件对象的实现)
验证插件的实现 - 实现插件工厂
这是不同包中的Plugin,和上面的Plugin不是同类型 - 实现插件
Validate方法:当给定区块的给定位置的交易中给定位置的操作action有效时返回nil,否则返回err.方法的变长参数ContextDatum之一是链码的背书策略 - 实现验证插件中需要注入的依赖,目前为验证插件提供三个可注入依赖
IdentityDeserializer接口: 将身份证书反序列化为证书对象,以便验证签名,看对应的MSP,验证是否符合MSP的规则。
PolicyEvaluator:评估给定的政策是否得到满足
StateFetcher:获取与世界状态交互的State对象
重要笔记
对等节点的验证插件一致性:在将来的版本中,Fabric通道基础结构将确保通道中的所有对等点在任何给定的区块链高度上都对给定的链码使用相同的验证逻辑,以消除配置错误的可能性可能会导致运行不同插件实现的peer之间的世界状态差异(节点A的验证插件认为一笔交易有效,另一个节点却判定为无效)。但是,目前,确保不会发生这种情况是系统操作员和管理员的唯一责任。
**验证插件错误处理:**每当验证插件无法确定给定事务是否有效时,由于某些短暂的执行问题(例如无法访问数据库),它应返回定义为ExecutionFailureError类型的错误core/handlers/validation/api/validation.go。返回的任何其他错误均被视为背书策略错误,并由验证逻辑将交易标记为无效。但是,如果ExecutionFailureError返回an ,则链处理将停止而不是将事务标记为无效。这是为了防止不同对等体之间的状态分歧。
**私有元数据检索的错误处理:**如果插件通过使用StateFetcher接口检索私有数据的元数据,则必须按以下方式处理错误,这一点很重要。CollConfigNotDefinedError’’ andInvalidCollNameError'', signalling that the specified collection does not exist, should be handled as deterministic errors and should not lead the plugin to return an
ExecutionFailureError
**将Fabric代码导入到插件中:**强烈建议不要将属于protobufs以外的Fabric的代码导入作为插件的一部分,这可能会导致因Fabric版本的不同或更新而引起问题,或者在运行混合peer版本时可能导致无法操作的问题。理想情况下,插件代码应仅使用依赖项注入,并且应导入除protobufs之外的最低要求。