分享
  1. 首页
  2. 文章

fabric中的plugin机制

Pillar_Zhong · · 1244 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

fabric中的plugin机制

之前是研究1.0的实现,发现endorse的过程中去escc签名的流程变化很大。下面以escc为例,看下新版的fabric是怎么实现的。

首先怎么自定义一个escc的plugin

handlers:
 ...
 endorsers:
 escc:
 name: DefaultEndorsement
 custom:
 name: customEndorsement
 library: /etc/hyperledger/fabric/plugins/customEndorsement.so
 ...

core.yaml,可以看到/etc/hyperledger/fabric/plugins/customEndorsement.so,这里是使用了golang的一个plugin的特性,来实现endorse策略的热插拔,比1.0的实现灵活很多。配置好之后peer启动的时候会自动将这部分加载进来。在endorse的流程中会用到。

实现

定义

type Config struct {
 AuthFilters []*HandlerConfig `mapstructure:"authFilters" yaml:"authFilters"`
 Decorators []*HandlerConfig `mapstructure:"decorators" yaml:"decorators"`
 Endorsers PluginMapping `mapstructure:"endorsers" yaml:"endorsers"`
 Validators PluginMapping `mapstructure:"validators" yaml:"validators"`
}
type PluginMapping map[string]*HandlerConfig
type HandlerConfig struct {
 Name string `mapstructure:"name" yaml:"name"`
 Library string `mapstructure:"library" yaml:"library"`
}
type registry struct {
 filters []auth.Filter
 decorators []decoration.Decorator
 endorsers map[string]endorsement2.PluginFactory
 validators map[string]validation.PluginFactory
}

在解析配置的时候会将上面的配置映射到config里面,然后在加载完毕后会存在registry里面。

加载

func InitRegistry(c Config) Registry {
 once.Do(func() {
 reg = registry{
 endorsers: make(map[string]endorsement2.PluginFactory),
 validators: make(map[string]validation.PluginFactory),
 }
 reg.loadHandlers(c)
 })
 return &reg
}
func (r *registry) evaluateModeAndLoad(c *HandlerConfig, handlerType HandlerType, extraArgs ...string) {
 if c.Library != "" {
 r.loadPlugin(c.Library, handlerType, extraArgs...)
 } else {
 r.loadCompiled(c.Name, handlerType, extraArgs...)
 }
}

当然加载的时机就是peer节点启动的时候,会去初始化registry,接下来就是加载到运行时了。

这里escc的handler配置分两种,一种是系统内建的,另外一种就是热插拔的方式。

从配置的区别是有没有设置Library。

下面我们分别看下什么区别

loadCompiled

func (r *registry) loadCompiled(handlerFactory string, handlerType HandlerType, extraArgs ...string) {
 registryMD := reflect.ValueOf(&HandlerLibrary{})
 o := registryMD.MethodByName(handlerFactory)
 if !o.IsValid() {
 logger.Panicf(fmt.Sprintf("Method %s isn't a method of HandlerLibrary", handlerFactory))
 }
 inst := o.Call(nil)[0].Interface()
 if handlerType == Auth {
 ...
 } else if handlerType == Endorsement {
 if len(extraArgs) != 1 {
 logger.Panicf("expected 1 argument in extraArgs")
 }
 r.endorsers[extraArgs[0]] = inst.(endorsement2.PluginFactory)
 } 
 ...
}

意思是调用HandlerLibrary的DefaultEndorsement方法,用builtin.DefaultEndorsementFactory来作为escc的plugin

loadPlugin

func (r *registry) loadPlugin(pluginPath string, handlerType HandlerType, extraArgs ...string) {
 if _, err := os.Stat(pluginPath); err != nil {
 logger.Panicf(fmt.Sprintf("Could not find plugin at path %s: %s", pluginPath, err))
 }
 p, err := plugin.Open(pluginPath)
 if err != nil {
 logger.Panicf(fmt.Sprintf("Error opening plugin at path %s: %s", pluginPath, err))
 }
 ...
 } else if handlerType == Endorsement {
 r.initEndorsementPlugin(p, extraArgs...)
 } 
 ...
}
func (r *registry) initEndorsementPlugin(p *plugin.Plugin, extraArgs ...string) {
 if len(extraArgs) != 1 {
 logger.Panicf("expected 1 argument in extraArgs")
 }
 factorySymbol, err := p.Lookup(pluginFactory)
 if err != nil {
 panicWithLookupError(pluginFactory, err)
 }
 constructor, ok := factorySymbol.(func() endorsement2.PluginFactory)
 if !ok {
 panicWithDefinitionError(pluginFactory)
 }
 factory := constructor()
 if factory == nil {
 logger.Panicf("factory instance returned nil")
 }
 r.endorsers[extraArgs[0]] = factory
}

无非就是热加载golang的plugin并初始化

使用

...
endorsementPluginsByName := reg.Lookup(library.Endorsement).(map[string]endorsement2.PluginFactory)
...
pluginMapper := endorser.MapBasedPluginMapper(endorsementPluginsByName)
pluginEndorser := endorser.NewPluginEndorser(&endorser.PluginSupport{
 ChannelStateRetriever: channelStateRetriever,
 TransientStoreRetriever: peer.TransientStoreFactory,
 PluginMapper: pluginMapper,
 SigningIdentityFetcher: signingIdentityFetcher,
})
endorserSupport.PluginEndorser = pluginEndorser...
...

去已经初始化完成的registry里面查找endorsements, 并通过endorserSupport去对外提供服务。里面的关键是初始化了MapBasedPluginMapper

func (e *Endorser) endorseProposal(...) (*pb.ProposalResponse, error) {
 
 ...
 if isSysCC {
 escc = "escc"
 } else {
 escc = cd.Endorsement()
 }
 ...
 return e.s.EndorseWithPlugin(ctx)
}
func (pe *PluginEndorser) EndorseWithPlugin(ctx Context) (*pb.ProposalResponse, error) {
 ...
 plugin, err := pe.getOrCreatePlugin(PluginName(ctx.PluginName), ctx.Channel)
 ...
 endorsement, prpBytes, err := plugin.Endorse(prpBytes, ctx.SignedProposal)
 ...
}

可以看到在endorse的流程中会去查找escc的endorsehandler来切入进来做背书,在getOrCreatePlugin里面会去MapBasedPluginMapper拿到escc对应的plugin,并最终调用plugin.Endorse。

func (e *DefaultEndorsement) Endorse(prpBytes []byte, sp *peer.SignedProposal) (*peer.Endorsement, []byte, error) {
 signer, err := e.SigningIdentityForRequest(sp)
 if err != nil {
 return nil, nil, errors.New(fmt.Sprintf("failed fetching signing identity: %v", err))
 }
 // serialize the signing identity
 identityBytes, err := signer.Serialize()
 if err != nil {
 return nil, nil, errors.New(fmt.Sprintf("could not serialize the signing identity: %v", err))
 }
 // sign the concatenation of the proposal response and the serialized endorser identity with this endorser's key
 signature, err := signer.Sign(append(prpBytes, identityBytes...))
 if err != nil {
 return nil, nil, errors.New(fmt.Sprintf("could not sign the proposal response payload: %v", err))
 }
 endorsement := &peer.Endorsement{Signature: signature, Endorser: identityBytes}
 return endorsement, prpBytes, nil
}

这里引用了系统内建的Endorser,实现很清楚就是用本地签名人身份进行数字签名。


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:Pillar_Zhong

查看原文:fabric中的plugin机制

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
1244 次点击
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏