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  }