github.com/yimialmonte/fabric@v2.1.1+incompatible/core/endorser/plugin_endorser_test.go (about) 1 /* 2 Copyright IBM Corp. 2016 All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package endorser_test 8 9 import ( 10 "io/ioutil" 11 "os" 12 "testing" 13 14 "github.com/golang/protobuf/proto" 15 "github.com/hyperledger/fabric-protos-go/ledger/rwset" 16 "github.com/hyperledger/fabric-protos-go/peer" 17 tspb "github.com/hyperledger/fabric-protos-go/transientstore" 18 "github.com/hyperledger/fabric/core/endorser" 19 "github.com/hyperledger/fabric/core/endorser/fake" 20 "github.com/hyperledger/fabric/core/endorser/mocks" 21 endorsement "github.com/hyperledger/fabric/core/handlers/endorsement/api" 22 . "github.com/hyperledger/fabric/core/handlers/endorsement/api/state" 23 "github.com/hyperledger/fabric/core/ledger" 24 "github.com/hyperledger/fabric/core/transientstore" 25 "github.com/hyperledger/fabric/gossip/privdata" 26 "github.com/pkg/errors" 27 "github.com/stretchr/testify/assert" 28 "github.com/stretchr/testify/mock" 29 ) 30 31 var ( 32 mockTransientStoreRetriever = transientStoreRetriever() 33 ) 34 35 //go:generate counterfeiter -o fake/query_executor.go -fake-name QueryExecutor . queryExecutor 36 type queryExecutor interface { 37 ledger.QueryExecutor 38 } 39 40 type testTransientStore struct { 41 storeProvider transientstore.StoreProvider 42 store *transientstore.Store 43 tempdir string 44 } 45 46 func newTransientStore(t *testing.T) *testTransientStore { 47 s := &testTransientStore{} 48 var err error 49 s.tempdir, err = ioutil.TempDir("", "ts") 50 if err != nil { 51 t.Fatalf("Failed to create test directory, got err %s", err) 52 return s 53 } 54 s.storeProvider, err = transientstore.NewStoreProvider(s.tempdir) 55 if err != nil { 56 t.Fatalf("Failed to open store, got err %s", err) 57 return s 58 } 59 s.store, err = s.storeProvider.OpenStore("test") 60 if err != nil { 61 t.Fatalf("Failed to open store, got err %s", err) 62 return s 63 } 64 return s 65 } 66 67 func (s *testTransientStore) tearDown() { 68 s.storeProvider.Close() 69 os.RemoveAll(s.tempdir) 70 } 71 72 func (s *testTransientStore) Persist(txid string, blockHeight uint64, 73 privateSimulationResultsWithConfig *tspb.TxPvtReadWriteSetWithConfigInfo) error { 74 return s.store.Persist(txid, blockHeight, privateSimulationResultsWithConfig) 75 } 76 77 func (s *testTransientStore) GetTxPvtRWSetByTxid(txid string, filter ledger.PvtNsCollFilter) (privdata.RWSetScanner, error) { 78 return s.store.GetTxPvtRWSetByTxid(txid, filter) 79 } 80 81 func TestPluginEndorserNotFound(t *testing.T) { 82 pluginMapper := &mocks.PluginMapper{} 83 pluginMapper.On("PluginFactoryByName", endorser.PluginName("notfound")).Return(nil) 84 pluginEndorser := endorser.NewPluginEndorser(&endorser.PluginSupport{ 85 PluginMapper: pluginMapper, 86 }) 87 endorsement, prpBytes, err := pluginEndorser.EndorseWithPlugin("notfound", "", nil, nil) 88 assert.Nil(t, endorsement) 89 assert.Nil(t, prpBytes) 90 assert.Contains(t, err.Error(), "plugin with name notfound wasn't found") 91 } 92 93 func TestPluginEndorserGreenPath(t *testing.T) { 94 expectedSignature := []byte{5, 4, 3, 2, 1} 95 expectedProposalResponsePayload := []byte{1, 2, 3} 96 pluginMapper := &mocks.PluginMapper{} 97 pluginFactory := &mocks.PluginFactory{} 98 plugin := &mocks.Plugin{} 99 plugin.On("Endorse", mock.Anything, mock.Anything).Return(&peer.Endorsement{Signature: expectedSignature}, expectedProposalResponsePayload, nil) 100 pluginMapper.On("PluginFactoryByName", endorser.PluginName("plugin")).Return(pluginFactory) 101 // Expect that the plugin would be instantiated only once in this test, because we call the endorser with the same arguments 102 plugin.On("Init", mock.Anything, mock.Anything).Return(nil).Once() 103 pluginFactory.On("New").Return(plugin).Once() 104 sif := &mocks.SigningIdentityFetcher{} 105 cs := &mocks.ChannelStateRetriever{} 106 queryCreator := &mocks.QueryCreator{} 107 cs.On("NewQueryCreator", "mychannel").Return(queryCreator, nil) 108 pluginEndorser := endorser.NewPluginEndorser(&endorser.PluginSupport{ 109 ChannelStateRetriever: cs, 110 SigningIdentityFetcher: sif, 111 PluginMapper: pluginMapper, 112 TransientStoreRetriever: mockTransientStoreRetriever, 113 }) 114 115 // Scenario I: Call the endorsement for the first time 116 endorsement, prpBytes, err := pluginEndorser.EndorseWithPlugin("plugin", "mychannel", nil, nil) 117 assert.NoError(t, err) 118 assert.Equal(t, expectedSignature, endorsement.Signature) 119 assert.Equal(t, expectedProposalResponsePayload, prpBytes) 120 // Ensure both state and SigningIdentityFetcher were passed to Init() 121 plugin.AssertCalled(t, "Init", &endorser.ChannelState{QueryCreator: queryCreator, Store: &transientstore.Store{}}, sif) 122 123 // Scenario II: Call the endorsement again a second time. 124 // Ensure the plugin wasn't instantiated again - which means the same instance 125 // was used to service the request. 126 // Also - check that the Init() wasn't called more than once on the plugin. 127 endorsement, prpBytes, err = pluginEndorser.EndorseWithPlugin("plugin", "mychannel", nil, nil) 128 assert.NoError(t, err) 129 assert.Equal(t, expectedSignature, endorsement.Signature) 130 assert.Equal(t, expectedProposalResponsePayload, prpBytes) 131 pluginFactory.AssertNumberOfCalls(t, "New", 1) 132 plugin.AssertNumberOfCalls(t, "Init", 1) 133 134 // Scenario III: Call the endorsement with a channel-less context. 135 // The init method should be called again, but this time - a channel state object 136 // should not be passed into the init. 137 pluginFactory.On("New").Return(plugin).Once() 138 plugin.On("Init", mock.Anything).Return(nil).Once() 139 endorsement, prpBytes, err = pluginEndorser.EndorseWithPlugin("plugin", "", nil, nil) 140 assert.NoError(t, err) 141 assert.Equal(t, expectedSignature, endorsement.Signature) 142 assert.Equal(t, expectedProposalResponsePayload, prpBytes) 143 plugin.AssertCalled(t, "Init", sif) 144 } 145 146 func TestPluginEndorserErrors(t *testing.T) { 147 pluginMapper := &mocks.PluginMapper{} 148 pluginFactory := &mocks.PluginFactory{} 149 plugin := &mocks.Plugin{} 150 plugin.On("Endorse", mock.Anything, mock.Anything) 151 pluginMapper.On("PluginFactoryByName", endorser.PluginName("plugin")).Return(pluginFactory) 152 pluginFactory.On("New").Return(plugin) 153 sif := &mocks.SigningIdentityFetcher{} 154 cs := &mocks.ChannelStateRetriever{} 155 queryCreator := &mocks.QueryCreator{} 156 cs.On("NewQueryCreator", "mychannel").Return(queryCreator, nil) 157 pluginEndorser := endorser.NewPluginEndorser(&endorser.PluginSupport{ 158 ChannelStateRetriever: cs, 159 SigningIdentityFetcher: sif, 160 PluginMapper: pluginMapper, 161 TransientStoreRetriever: mockTransientStoreRetriever, 162 }) 163 164 // Failed initializing plugin 165 t.Run("PluginInitializationFailure", func(t *testing.T) { 166 plugin.On("Init", mock.Anything, mock.Anything).Return(errors.New("plugin initialization failed")).Once() 167 endorsement, prpBytes, err := pluginEndorser.EndorseWithPlugin("plugin", "mychannel", nil, nil) 168 assert.Nil(t, endorsement) 169 assert.Nil(t, prpBytes) 170 assert.Contains(t, err.Error(), "plugin initialization failed") 171 }) 172 173 } 174 175 func transientStoreRetriever() *mocks.TransientStoreRetriever { 176 storeRetriever := &mocks.TransientStoreRetriever{} 177 storeRetriever.On("StoreForChannel", mock.Anything).Return(&transientstore.Store{}) 178 return storeRetriever 179 } 180 181 type fakeEndorsementPlugin struct { 182 StateFetcher 183 } 184 185 func (fep *fakeEndorsementPlugin) Endorse(payload []byte, sp *peer.SignedProposal) (*peer.Endorsement, []byte, error) { 186 state, _ := fep.StateFetcher.FetchState() 187 txrws, _ := state.GetTransientByTXID("tx") 188 b, _ := proto.Marshal(txrws[0]) 189 return nil, b, nil 190 } 191 192 func (fep *fakeEndorsementPlugin) Init(dependencies ...endorsement.Dependency) error { 193 for _, dep := range dependencies { 194 if state, isState := dep.(StateFetcher); isState { 195 fep.StateFetcher = state 196 return nil 197 } 198 } 199 panic("could not find State dependency") 200 } 201 202 type rwsetScanner struct { 203 mock.Mock 204 data []*rwset.TxPvtReadWriteSet 205 } 206 207 func (rws *rwsetScanner) Next() (*transientstore.EndorserPvtSimulationResults, error) { 208 if len(rws.data) == 0 { 209 return nil, nil 210 } 211 res := rws.data[0] 212 rws.data = rws.data[1:] 213 return &transientstore.EndorserPvtSimulationResults{ 214 PvtSimulationResultsWithConfig: &tspb.TxPvtReadWriteSetWithConfigInfo{ 215 PvtRwset: res, 216 }, 217 }, nil 218 } 219 220 func (rws *rwsetScanner) Close() { 221 rws.Called() 222 } 223 224 func TestTransientStore(t *testing.T) { 225 plugin := &fakeEndorsementPlugin{} 226 factory := &mocks.PluginFactory{} 227 factory.On("New").Return(plugin) 228 sif := &mocks.SigningIdentityFetcher{} 229 cs := &mocks.ChannelStateRetriever{} 230 queryCreator := &mocks.QueryCreator{} 231 queryCreator.On("NewQueryExecutor").Return(&fake.QueryExecutor{}, nil) 232 cs.On("NewQueryCreator", "mychannel").Return(queryCreator, nil) 233 234 transientStore := newTransientStore(t) 235 defer transientStore.tearDown() 236 rws := &rwset.TxPvtReadWriteSet{ 237 NsPvtRwset: []*rwset.NsPvtReadWriteSet{ 238 { 239 Namespace: "ns", 240 CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{ 241 { 242 CollectionName: "col", 243 }, 244 }, 245 }, 246 }, 247 } 248 249 transientStore.Persist("tx1", 1, &tspb.TxPvtReadWriteSetWithConfigInfo{ 250 PvtRwset: rws, 251 CollectionConfigs: make(map[string]*peer.CollectionConfigPackage), 252 }) 253 254 storeRetriever := &mocks.TransientStoreRetriever{} 255 storeRetriever.On("StoreForChannel", mock.Anything).Return(transientStore.store) 256 257 pluginEndorser := endorser.NewPluginEndorser(&endorser.PluginSupport{ 258 ChannelStateRetriever: cs, 259 SigningIdentityFetcher: sif, 260 PluginMapper: endorser.MapBasedPluginMapper{ 261 "plugin": factory, 262 }, 263 TransientStoreRetriever: storeRetriever, 264 }) 265 266 _, prpBytes, err := pluginEndorser.EndorseWithPlugin("plugin", "mychannel", nil, nil) 267 assert.NoError(t, err) 268 269 txrws := &rwset.TxPvtReadWriteSet{} 270 err = proto.Unmarshal(prpBytes, txrws) 271 assert.NoError(t, err) 272 assert.True(t, proto.Equal(rws, txrws)) 273 }