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  }