说明:链码开发语言是golang,源码分析是基于v1.4.3版本
系列文章
1、Fabric自定义插件的开发-扩展插件的组织方式
2、Fabric自定义插件的开发-Decorator插件开发
3、Fabric自定义插件的开发-Auth插件开发
validators插件,其实就是系统链码里的vscc链码,用于校验区块中交易的背书是否满足背书策略。如果需要定制校验逻辑,或者在默认校验逻辑上增加新的校验,就需要开发validdators插件了。本文梳理validators插件的加载和使用方式,以及分享一个样例。
读取插件,在自定义的插件中查找NewPluginFactory函数,且函数格式为:func() validation.PluginFactory,即需要返回一个实现了接口core.handlers.validation.PluginFactory的对象,并将该对象存储在插件库对象registry的成员validators中,注意这里的validators是map结构,其key来自配置文件core.yaml。
这里相比Decorator插件,多了一层,因为Decorator插件是单例,而Validators插件是多实例,每个通道都会有一个实例。
core.handlers.validation.PluginFactory接口定义如下:
type PluginFactory interface {
New() Plugin
}
New方法返回的是一个实现Plugin接口的对象,Plugin接口定义如下:
type Plugin interface {
Validate(block *common.Block, namespace string, txPosition int, actionPosition int, contextData ...ContextDatum) error
Init(dependencies ...Dependency) error
}
这个实现了Plugin接口的对象,才是我们自定义validators插件中的核心对象!
同样是通过peer的插件库单例registry对象提供的Lookup方法。peer在创建chain对象时,就把所有validators插件定义和chain对象进行关联。
txvaliddator.PluginMapper接口对象
函数:func createChain(cid string, ledger ledger.PeerLedger, cb *common.Block, ccp ccprovider.ChaincodeProvider, sccp sysccprovider.SystemChaincodeProvider, pm txvalidator.PluginMapper) error {}
位置:core/peer/peer.go:328
peer在收到新的区块之后,解析区块中的交易,获取交易对应的channelId、chaincodeId,从db中读取chaincodeid对应的信息,如背书策略、vscc名称、escc名称等信息;如果是系统链码,背书策略为通道内任意组织背书,vscc和escc都是默认系统链码。
函数:func (v *VsccValidatorImpl) VSCCValidateTx(seq int, payload *common.Payload, envBytes []byte, block *common.Block) (error, peer.TxValidationCode) {}
位置:core/committer/txvalidator/vscc_validator.go:49
// 从db中获取链码对应的信息
函数:func (v *VsccValidatorImpl) GetInfoForValidate(chdr *common.ChannelHeader, ccID string) (*sysccprovider.ChaincodeInstance, *sysccprovider.ChaincodeInstance, []byte, error) {}
位置:core/committer/txvalidator/vscc_validator.go:325
在获取链码信息之后,根据vscc的名称获取指定的validators插件对象(实现了Plugin接口的对象),缓存并调用Validate方法。
函数:func (pv *PluginValidator) ValidateWithPlugin(ctx *Context) error {}
位置:core/committer/txvalidator/plugin_validator.go:92
函数:func (pv *PluginValidator) getOrCreatePlugin(ctx *Context) (validation.Plugin, error) {}
位置:core/committer/txvalidator/plugin_validator.go:108
// 新创建的插件对象,会调用Init方法
函数:func (pbc *pluginsByChannel) createPluginIfAbsent(channel string) (validation.Plugin, error) {}
位置:core/committer/txvalidator/plugin_validator.go:144
vscc对应的默认实现是:
对象:core.handlers.validation.builtin.DefaultValidation
位置:core/handlers/validation/builtin/default_validation.go:35
1、修改core.yaml文件,增加自定义validators的信息:
validators:
vscc:
name: DefaultValidation
library:
customvscc:
library: /opt/lib/xxx.so
2、在链码实例化或升级的时候,通过-V参数指定链码使用的vscc名称,如:
peer chaincode instantiate -o orderer.example.com:7050 -n yourchaincode -v 1.0 -V customvscc -C yourchannel -c ‘{“Args”:[“init”,“a”,“1”]}’ -P “OR (‘Org1MSP.member’,‘Org2MSP.member’,‘Org3MSP.member’)”
样例没有特殊功能,只是复用了vscc的默认实现。
package main
import (
validation "github.com/hyperledger/fabric/core/handlers/validation/api"
"github.com/hyperledger/fabric/core/handlers/validation/builtin"
"github.com/hyperledger/fabric/protos/common"
)
// 函数签名不可修改,名称必须是NewPluginFactory,类型必须是func() validation.PluginFactory
func NewPluginFactory() validation.PluginFactory {
return &ValidateFactory{}
}
type ValidateFactory struct {
}
func (*ValidateFactory) New() validation.Plugin {
return &ipfsValidation{
defValidate: &builtin.DefaultValidation{},
}
}
type ipfsValidation struct {
defValidate *builtin.DefaultValidation
}
func (v *ipfsValidation) Validate(block *common.Block, namespace string, txPosition int, actionPosition int, contextData ...validation.ContextDatum) error {
// 修改这个函数的实现即可
resp := v.defValidate.Validate(block, namespace, txPosition, actionPosition, contextData...)
if resp != nil {
return resp
}
return nil
}
func (v *ipfsValidation) Init(dependencies ...validation.Dependency) error {
return v.defValidate.Init(dependencies...)
}
func main() {
}
插件的编译使用命令 go build -buildmod=plugin,生成so。
因为有依赖fabric源码,所以编译时,需要先clone一份fabric的代码,且在fabric的目录下进行编译,这样就可以直接用fabric项目的vendor了
这里有个限制,如果你的peer是1.4.0版本,那么在编译so时,也必须使用相同版本的fabric源码,否则会导致so加载失败!