Fabric自定义插件的开发-Decorator插件开发

说明:链码开发语言是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节点传递给链码的附加数据可能不一致,但用户链码一定要保证读写集是一致的,否则会导致交易异常。我能想到的这个插件的使用场景比较少~~

你可能感兴趣的:(Hyperledger,Fabric)