说明:链码开发语言是golang,源码分析是基于v1.4.3版本
系列文章
1、Fabric自定义插件的开发-扩展插件的组织方式
2、Fabric自定义插件的开发-Validators插件开发
3、Fabric自定义插件的开发-Auth插件开发
有时候在用户链码侧需要感知peer的信息,或者需要规整传递给链码的参数,这就需要修改peer传递给链码的参数了,fabric支持用户自定义扩展插件来实现这些功能,本文梳理decorator插件的加载和使用方式,并分享一个样例。
读取插件,在自定义的插件中查找NewDecorator函数,且函数格式为:func() decoration.Decorator,即需要返回一个实现了接口core.handlers.decoration.Decorator的对象,并将该对象存储在插件库对象registry的成员decorators中。
函数:func (r *registry) initDecoratorPlugin(p *plugin.Plugin) {}
位置:core/handlers/library/registry.go:185
core.handlers.decoration.Decorator接口只有一个方法:
Decorate(proposal *peer.Proposal, input *peer.ChaincodeInput) *peer.ChaincodeInput
peer的插件库单例registry对象提供了Lookup方法给外部使用,以获取对应的插件。
而Decoration类型的插件,是在peer调用链码之前执行:
函数:func (s *SupportImpl) Execute(txParams *ccprovider.TransactionParams, cid, name, version, txid string, signedProp *pb.SignedProposal, prop *pb.Proposal, input *pb.ChaincodeInput) (*pb.Response, *pb.ChaincodeEvent, error) {}
位置:core/endorser/support.go:135
源码:
cccid := &ccprovider.CCContext{
Name: name,
Version: version,
}
// decorate the chaincode input
decorators := library.InitRegistry(library.Config{}).Lookup(library.Decoration).([]decoration.Decorator)
input.Decorations = make(map[string][]byte)
input = decoration.Apply(prop, input, decorators...)
txParams.ProposalDecorations = input.Decorations
return s.ChaincodeSupport.Execute(txParams, cccid, input)
可见Decoration类型插件的调用是顺序的,以配置文件中的顺序为准。因此只需要在core.yaml中配置我们开发的插件路径即可:
decorators:
-
name: DefaultDecorator
-
library: /opt/lib/myplugin.so
默认的DefaultDecorator并没有对input做任何修改,可以在配置文件中删除。
样例的功能是携带peer的mspid给链码:
package main
import (
"github.com/hyperledger/fabric/core/handlers/decoration"
"github.com/hyperledger/fabric/msp/mgmt"
"github.com/hyperledger/fabric/protos/peer"
)
// NewDecorator creates a new decorator
func NewDecorator() decoration.Decorator { // 函数签名不可修改,名称必须是NewDecorator,类型必须为func() decoration.Decorator
return &decorator{} // 返回一个实现了decoration.Decorator接口的对象
}
type decorator struct {
}
// Decorate decorates a chaincode input by changing it
func (d *decorator) Decorate(proposal *peer.Proposal, input *peer.ChaincodeInput) *peer.ChaincodeInput {
if input.Decorations == nil {
input.Decorations = make(map[string][]byte)
}
mspid, _ := mgmt.GetLocalMSP().GetIdentifier()
input.Decorations["msp"] = []byte(mspid)
return input
}
func main() {
}
插件的编译使用命令 go build -buildmod=plugin,生成so。
因为有依赖fabric源码,所以编译时,需要先clone一份fabric的代码,且在fabric的目录下进行编译,这样就可以直接用fabric项目的vendor了
这里有个限制,如果你的peer是1.4.0版本,那么在编译so时,也必须使用相同版本的fabric源码,否则会导致so加载失败!
多嘴一句,因为插件so和peer是同一个进程空间,所以,你懂得,peer有的数据,这里都可以获取到~
使用stub.GetDecorations()接口即可获取map结构的附加数据,只要和插件约定好key就可以啦,比如样例中的"msp",使用起来就是:
d := stub.GetDecorations()
if msp, ok := d["msp"]; ok {
// do something
}
注:这个修改是对所有链码调用都生效的,无论是哪个通道,哪个链码,切记切记。但是可以在Decorate方法中识别通道和链码,自行区分。
注:因为不同peer节点传递给链码的附加数据可能不一致,但用户链码一定要保证读写集是一致的,否则会导致交易异常。我能想到的这个插件的使用场景比较少~~