github.com/yimialmonte/fabric@v2.1.1+incompatible/core/endorser/plugin_endorser.go (about)

     1  /*
     2  Copyright IBM Corp. 2018 All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package endorser
     8  
     9  import (
    10  	"fmt"
    11  	"sync"
    12  
    13  	pb "github.com/hyperledger/fabric-protos-go/peer"
    14  	endorsement "github.com/hyperledger/fabric/core/handlers/endorsement/api"
    15  	endorsement3 "github.com/hyperledger/fabric/core/handlers/endorsement/api/identities"
    16  	"github.com/hyperledger/fabric/core/transientstore"
    17  	"github.com/pkg/errors"
    18  )
    19  
    20  //go:generate mockery -dir . -name TransientStoreRetriever -case underscore -output mocks/
    21  
    22  // TransientStoreRetriever retrieves transient stores
    23  type TransientStoreRetriever interface {
    24  	// StoreForChannel returns the transient store for the given channel
    25  	StoreForChannel(channel string) *transientstore.Store
    26  }
    27  
    28  //go:generate mockery -dir . -name ChannelStateRetriever -case underscore -output mocks/
    29  
    30  // ChannelStateRetriever retrieves Channel state
    31  type ChannelStateRetriever interface {
    32  	// NewQueryCreator returns a QueryCreator for the given Channel
    33  	NewQueryCreator(channel string) (QueryCreator, error)
    34  }
    35  
    36  //go:generate mockery -dir . -name PluginMapper -case underscore -output mocks/
    37  
    38  // PluginMapper maps plugin names to their corresponding factories
    39  type PluginMapper interface {
    40  	PluginFactoryByName(name PluginName) endorsement.PluginFactory
    41  }
    42  
    43  // MapBasedPluginMapper maps plugin names to their corresponding factories
    44  type MapBasedPluginMapper map[string]endorsement.PluginFactory
    45  
    46  // PluginFactoryByName returns a plugin factory for the given plugin name, or nil if not found
    47  func (m MapBasedPluginMapper) PluginFactoryByName(name PluginName) endorsement.PluginFactory {
    48  	return m[string(name)]
    49  }
    50  
    51  // Context defines the data that is related to an in-flight endorsement
    52  type Context struct {
    53  	PluginName     string
    54  	Channel        string
    55  	TxID           string
    56  	Proposal       *pb.Proposal
    57  	SignedProposal *pb.SignedProposal
    58  	Visibility     []byte
    59  	Response       *pb.Response
    60  	Event          []byte
    61  	ChaincodeID    *pb.ChaincodeID
    62  	SimRes         []byte
    63  }
    64  
    65  // String returns a text representation of this context
    66  func (c Context) String() string {
    67  	return fmt.Sprintf("{plugin: %s, channel: %s, tx: %s, chaincode: %s}", c.PluginName, c.Channel, c.TxID, c.ChaincodeID.Name)
    68  }
    69  
    70  // PluginSupport aggregates the support interfaces
    71  // needed for the operation of the plugin endorser
    72  type PluginSupport struct {
    73  	ChannelStateRetriever
    74  	endorsement3.SigningIdentityFetcher
    75  	PluginMapper
    76  	TransientStoreRetriever
    77  }
    78  
    79  // NewPluginEndorser endorses with using a plugin
    80  func NewPluginEndorser(ps *PluginSupport) *PluginEndorser {
    81  	return &PluginEndorser{
    82  		SigningIdentityFetcher:  ps.SigningIdentityFetcher,
    83  		PluginMapper:            ps.PluginMapper,
    84  		pluginChannelMapping:    make(map[PluginName]*pluginsByChannel),
    85  		ChannelStateRetriever:   ps.ChannelStateRetriever,
    86  		TransientStoreRetriever: ps.TransientStoreRetriever,
    87  	}
    88  }
    89  
    90  // PluginName defines the name of the plugin as it appears in the configuration
    91  type PluginName string
    92  
    93  type pluginsByChannel struct {
    94  	sync.RWMutex
    95  	pluginFactory    endorsement.PluginFactory
    96  	channels2Plugins map[string]endorsement.Plugin
    97  	pe               *PluginEndorser
    98  }
    99  
   100  func (pbc *pluginsByChannel) createPluginIfAbsent(channel string) (endorsement.Plugin, error) {
   101  	pbc.RLock()
   102  	plugin, exists := pbc.channels2Plugins[channel]
   103  	pbc.RUnlock()
   104  	if exists {
   105  		return plugin, nil
   106  	}
   107  
   108  	pbc.Lock()
   109  	defer pbc.Unlock()
   110  	plugin, exists = pbc.channels2Plugins[channel]
   111  	if exists {
   112  		return plugin, nil
   113  	}
   114  
   115  	pluginInstance := pbc.pluginFactory.New()
   116  	plugin, err := pbc.initPlugin(pluginInstance, channel)
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  	pbc.channels2Plugins[channel] = plugin
   121  	return plugin, nil
   122  }
   123  
   124  func (pbc *pluginsByChannel) initPlugin(plugin endorsement.Plugin, channel string) (endorsement.Plugin, error) {
   125  	var dependencies []endorsement.Dependency
   126  	var err error
   127  	// If this is a channel endorsement, add the channel state as a dependency
   128  	if channel != "" {
   129  		query, err := pbc.pe.NewQueryCreator(channel)
   130  		if err != nil {
   131  			return nil, errors.Wrap(err, "failed obtaining channel state")
   132  		}
   133  		store := pbc.pe.TransientStoreRetriever.StoreForChannel(channel)
   134  		if store == nil {
   135  			return nil, errors.Errorf("transient store for channel %s was not initialized", channel)
   136  		}
   137  		dependencies = append(dependencies, &ChannelState{QueryCreator: query, Store: store})
   138  	}
   139  	// Add the SigningIdentityFetcher as a dependency
   140  	dependencies = append(dependencies, pbc.pe.SigningIdentityFetcher)
   141  	err = plugin.Init(dependencies...)
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  	return plugin, nil
   146  }
   147  
   148  // PluginEndorser endorsers proposal responses using plugins
   149  type PluginEndorser struct {
   150  	sync.Mutex
   151  	PluginMapper
   152  	pluginChannelMapping map[PluginName]*pluginsByChannel
   153  	ChannelStateRetriever
   154  	endorsement3.SigningIdentityFetcher
   155  	TransientStoreRetriever
   156  }
   157  
   158  // EndorseWithPlugin endorses the response with a plugin
   159  func (pe *PluginEndorser) EndorseWithPlugin(pluginName, channelID string, prpBytes []byte, signedProposal *pb.SignedProposal) (*pb.Endorsement, []byte, error) {
   160  	plugin, err := pe.getOrCreatePlugin(PluginName(pluginName), channelID)
   161  	if err != nil {
   162  		return nil, nil, errors.WithMessagef(err, "plugin with name %s could not be used", pluginName)
   163  	}
   164  
   165  	return plugin.Endorse(prpBytes, signedProposal)
   166  }
   167  
   168  // getOrCreatePlugin returns a plugin instance for the given plugin name and channel
   169  func (pe *PluginEndorser) getOrCreatePlugin(plugin PluginName, channel string) (endorsement.Plugin, error) {
   170  	pluginFactory := pe.PluginFactoryByName(plugin)
   171  	if pluginFactory == nil {
   172  		return nil, errors.Errorf("plugin with name %s wasn't found", plugin)
   173  	}
   174  
   175  	pluginsByChannel := pe.getOrCreatePluginChannelMapping(PluginName(plugin), pluginFactory)
   176  	return pluginsByChannel.createPluginIfAbsent(channel)
   177  }
   178  
   179  func (pe *PluginEndorser) getOrCreatePluginChannelMapping(plugin PluginName, pf endorsement.PluginFactory) *pluginsByChannel {
   180  	pe.Lock()
   181  	defer pe.Unlock()
   182  	endorserChannelMapping, exists := pe.pluginChannelMapping[PluginName(plugin)]
   183  	if !exists {
   184  		endorserChannelMapping = &pluginsByChannel{
   185  			pluginFactory:    pf,
   186  			channels2Plugins: make(map[string]endorsement.Plugin),
   187  			pe:               pe,
   188  		}
   189  		pe.pluginChannelMapping[PluginName(plugin)] = endorserChannelMapping
   190  	}
   191  	return endorserChannelMapping
   192  }