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