github.com/true-sqn/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 }