github.com/anjalikarhana/fabric@v2.1.1+incompatible/internal/peer/chaincode/invoke_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package chaincode
     8  
     9  import (
    10  	"context"
    11  	"errors"
    12  	"fmt"
    13  	"testing"
    14  	"time"
    15  
    16  	cb "github.com/hyperledger/fabric-protos-go/common"
    17  	pb "github.com/hyperledger/fabric-protos-go/peer"
    18  	"github.com/hyperledger/fabric/bccsp"
    19  	"github.com/hyperledger/fabric/bccsp/sw"
    20  	"github.com/hyperledger/fabric/common/flogging/floggingtest"
    21  	"github.com/hyperledger/fabric/internal/peer/chaincode/mock"
    22  	"github.com/hyperledger/fabric/internal/peer/common"
    23  	"github.com/hyperledger/fabric/msp"
    24  	"github.com/hyperledger/fabric/protoutil"
    25  	"github.com/spf13/viper"
    26  	"github.com/stretchr/testify/assert"
    27  	"google.golang.org/grpc"
    28  )
    29  
    30  func TestInvokeCmd(t *testing.T) {
    31  	defer viper.Reset()
    32  	defer resetFlags()
    33  
    34  	resetFlags()
    35  	mockCF, err := getMockChaincodeCmdFactory()
    36  	assert.NoError(t, err, "Error getting mock chaincode command factory")
    37  
    38  	cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
    39  	assert.NoError(t, err)
    40  
    41  	// Error case 0: no channelID specified
    42  	cmd := invokeCmd(mockCF, cryptoProvider)
    43  	addFlags(cmd)
    44  	args := []string{"-n", "example02", "-c", "{\"Args\": [\"invoke\",\"a\",\"b\",\"10\"]}"}
    45  	cmd.SetArgs(args)
    46  	err = cmd.Execute()
    47  	assert.Error(t, err, "'peer chaincode invoke' command should have returned error when called without -C flag")
    48  
    49  	// Success case
    50  	cmd = invokeCmd(mockCF, cryptoProvider)
    51  	addFlags(cmd)
    52  	args = []string{"-n", "example02", "-c", "{\"Args\": [\"invoke\",\"a\",\"b\",\"10\"]}", "-C", "mychannel"}
    53  	cmd.SetArgs(args)
    54  	err = cmd.Execute()
    55  	assert.NoError(t, err, "Run chaincode invoke cmd error")
    56  
    57  	// set timeout for error cases
    58  	viper.Set("peer.client.connTimeout", 10*time.Millisecond)
    59  
    60  	// Error case 1: no orderer endpoints
    61  	t.Logf("Start error case 1: no orderer endpoints")
    62  	getEndorserClient := common.GetEndorserClientFnc
    63  	getOrdererEndpointOfChain := common.GetOrdererEndpointOfChainFnc
    64  	getBroadcastClient := common.GetBroadcastClientFnc
    65  	getDefaultSigner := common.GetDefaultSignerFnc
    66  	getDeliverClient := common.GetDeliverClientFnc
    67  	getPeerDeliverClient := common.GetPeerDeliverClientFnc
    68  	defer func() {
    69  		common.GetEndorserClientFnc = getEndorserClient
    70  		common.GetOrdererEndpointOfChainFnc = getOrdererEndpointOfChain
    71  		common.GetBroadcastClientFnc = getBroadcastClient
    72  		common.GetDefaultSignerFnc = getDefaultSigner
    73  		common.GetDeliverClientFnc = getDeliverClient
    74  		common.GetPeerDeliverClientFnc = getPeerDeliverClient
    75  	}()
    76  	common.GetEndorserClientFnc = func(string, string) (pb.EndorserClient, error) {
    77  		return mockCF.EndorserClients[0], nil
    78  	}
    79  	common.GetOrdererEndpointOfChainFnc = func(chainID string, signer common.Signer, endorserClient pb.EndorserClient, cryptoProvider bccsp.BCCSP) ([]string, error) {
    80  		return []string{}, nil
    81  	}
    82  	cmd = invokeCmd(nil, cryptoProvider)
    83  	addFlags(cmd)
    84  	args = []string{"-n", "example02", "-c", "{\"Args\": [\"invoke\",\"a\",\"b\",\"10\"]}", "-C", "mychannel"}
    85  	cmd.SetArgs(args)
    86  	err = cmd.Execute()
    87  	assert.Error(t, err)
    88  
    89  	// Error case 2: getEndorserClient returns error
    90  	t.Logf("Start error case 2: getEndorserClient returns error")
    91  	common.GetEndorserClientFnc = func(string, string) (pb.EndorserClient, error) {
    92  		return nil, errors.New("error")
    93  	}
    94  	err = cmd.Execute()
    95  	assert.Error(t, err)
    96  
    97  	// Error case 3: getDeliverClient returns error
    98  	t.Logf("Start error case 3: getDeliverClient returns error")
    99  	common.GetDeliverClientFnc = func(string, string) (pb.Deliver_DeliverClient, error) {
   100  		return nil, errors.New("error")
   101  	}
   102  	err = cmd.Execute()
   103  	assert.Error(t, err)
   104  
   105  	// Error case 4 : getPeerDeliverClient returns error
   106  	t.Logf("Start error case 4: getPeerDeliverClient returns error")
   107  	common.GetPeerDeliverClientFnc = func(string, string) (pb.DeliverClient, error) {
   108  		return nil, errors.New("error")
   109  	}
   110  	err = cmd.Execute()
   111  	assert.Error(t, err)
   112  
   113  	// Error case 5: getDefaultSignerFnc returns error
   114  	t.Logf("Start error case 5: getDefaultSignerFnc returns error")
   115  	common.GetEndorserClientFnc = func(string, string) (pb.EndorserClient, error) {
   116  		return mockCF.EndorserClients[0], nil
   117  	}
   118  	common.GetPeerDeliverClientFnc = func(string, string) (pb.DeliverClient, error) {
   119  		return mockCF.DeliverClients[0], nil
   120  	}
   121  	common.GetDefaultSignerFnc = func() (msp.SigningIdentity, error) {
   122  		return nil, errors.New("error")
   123  	}
   124  	err = cmd.Execute()
   125  	assert.Error(t, err)
   126  	common.GetDefaultSignerFnc = common.GetDefaultSigner
   127  
   128  	// Error case 6: getOrdererEndpointOfChainFnc returns error
   129  	t.Logf("Start error case 6: getOrdererEndpointOfChainFnc returns error")
   130  	common.GetEndorserClientFnc = func(string, string) (pb.EndorserClient, error) {
   131  		return mockCF.EndorserClients[0], nil
   132  	}
   133  	common.GetOrdererEndpointOfChainFnc = func(chainID string, signer common.Signer, endorserClient pb.EndorserClient, cryptoProvider bccsp.BCCSP) ([]string, error) {
   134  		return nil, errors.New("error")
   135  	}
   136  	err = cmd.Execute()
   137  	assert.Error(t, err)
   138  
   139  	// Error case 7: getBroadcastClient returns error
   140  	t.Logf("Start error case 7: getBroadcastClient returns error")
   141  	common.GetOrdererEndpointOfChainFnc = func(chainID string, signer common.Signer, endorserClient pb.EndorserClient, cryptoProvider bccsp.BCCSP) ([]string, error) {
   142  		return []string{"localhost:9999"}, nil
   143  	}
   144  	common.GetBroadcastClientFnc = func() (common.BroadcastClient, error) {
   145  		return nil, errors.New("error")
   146  	}
   147  	err = cmd.Execute()
   148  	assert.Error(t, err)
   149  
   150  	// Success case
   151  	t.Logf("Start success case")
   152  	common.GetBroadcastClientFnc = func() (common.BroadcastClient, error) {
   153  		return mockCF.BroadcastClient, nil
   154  	}
   155  	err = cmd.Execute()
   156  	assert.NoError(t, err)
   157  }
   158  
   159  func TestInvokeCmdSimulateESCCPluginResponse(t *testing.T) {
   160  	defer resetFlags()
   161  	mockCF, err := getMockChaincodeCmdFactory()
   162  	assert.NoError(t, err, "Error getting mock chaincode command factory")
   163  	cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
   164  	assert.NoError(t, err)
   165  
   166  	// success case - simulate an ESCC plugin that endorses a chaincode response
   167  	// with status greater than shim.ERRORTHRESHOLD or even shim.ERROR
   168  	mockResponse := &pb.ProposalResponse{
   169  		Response:    &pb.Response{Status: 504},
   170  		Endorsement: &pb.Endorsement{},
   171  	}
   172  	mockCF.EndorserClients = []pb.EndorserClient{
   173  		common.GetMockEndorserClient(mockResponse, nil),
   174  		common.GetMockEndorserClient(mockResponse, nil),
   175  	}
   176  
   177  	// set logger to logger with a backend that writes to a byte buffer
   178  	oldLogger := logger
   179  	defer func() { logger = oldLogger }()
   180  	l, recorder := floggingtest.NewTestLogger(t)
   181  	logger = l
   182  
   183  	cmd := invokeCmd(mockCF, cryptoProvider)
   184  	addFlags(cmd)
   185  	args := []string{"-n", "example02", "-c", "{\"Args\": [\"invoke\",\"a\",\"b\",\"10\"]}", "-C", "mychannel"}
   186  	cmd.SetArgs(args)
   187  
   188  	err = cmd.Execute()
   189  	assert.NoError(t, err, "Run chaincode invoke cmd error")
   190  
   191  	assert.NotEmpty(t, recorder.MessagesContaining("Chaincode invoke successful"), "missing invoke success log record")
   192  	assert.NotEmpty(t, recorder.MessagesContaining("result: <nil>"), "missing result log record")
   193  }
   194  
   195  func TestInvokeCmdEndorsementError(t *testing.T) {
   196  	defer resetFlags()
   197  	mockCF, err := getMockChaincodeCmdFactoryWithErr()
   198  	assert.NoError(t, err, "Error getting mock chaincode command factory")
   199  	cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
   200  	assert.NoError(t, err)
   201  
   202  	cmd := invokeCmd(mockCF, cryptoProvider)
   203  	addFlags(cmd)
   204  	args := []string{"-n", "example02", "-C", "mychannel", "-c", "{\"Args\": [\"invoke\",\"a\",\"b\",\"10\"]}"}
   205  	cmd.SetArgs(args)
   206  	err = cmd.Execute()
   207  	assert.Error(t, err, "Expected error executing invoke command")
   208  }
   209  
   210  func TestInvokeCmdEndorsementFailure(t *testing.T) {
   211  	defer resetFlags()
   212  	ccRespStatus := [2]int32{502, 400}
   213  	ccRespPayload := [][]byte{[]byte("Invalid function name"), []byte("Incorrect parameters")}
   214  	cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
   215  	assert.NoError(t, err)
   216  
   217  	for i := 0; i < 2; i++ {
   218  		mockCF, err := getMockChaincodeCmdFactoryEndorsementFailure(ccRespStatus[i], ccRespPayload[i])
   219  		assert.NoError(t, err, "Error getting mock chaincode command factory")
   220  
   221  		cmd := invokeCmd(mockCF, cryptoProvider)
   222  		addFlags(cmd)
   223  		args := []string{"-C", "mychannel", "-n", "example02", "-c", "{\"Args\": [\"invokeinvalid\",\"a\",\"b\",\"10\"]}"}
   224  		cmd.SetArgs(args)
   225  
   226  		err = cmd.Execute()
   227  		assert.Error(t, err)
   228  		assert.Contains(t, err.Error(), "endorsement failure during invoke")
   229  		assert.Contains(t, err.Error(), fmt.Sprintf("response: status:%d payload:\"%s\"", ccRespStatus[i], ccRespPayload[i]))
   230  	}
   231  }
   232  
   233  // Returns mock chaincode command factory with multiple endorser and deliver clients
   234  func getMockChaincodeCmdFactory() (*ChaincodeCmdFactory, error) {
   235  	signer, err := common.GetDefaultSigner()
   236  	if err != nil {
   237  		return nil, err
   238  	}
   239  	mockResponse := &pb.ProposalResponse{
   240  		Response:    &pb.Response{Status: 200},
   241  		Endorsement: &pb.Endorsement{},
   242  	}
   243  	mockEndorserClients := []pb.EndorserClient{common.GetMockEndorserClient(mockResponse, nil), common.GetMockEndorserClient(mockResponse, nil)}
   244  	mockBroadcastClient := common.GetMockBroadcastClient(nil)
   245  	mockDC := getMockDeliverClientResponseWithTxStatusAndID(pb.TxValidationCode_VALID, "txid0")
   246  	mockDeliverClients := []pb.DeliverClient{mockDC, mockDC}
   247  	mockCF := &ChaincodeCmdFactory{
   248  		EndorserClients: mockEndorserClients,
   249  		Signer:          signer,
   250  		BroadcastClient: mockBroadcastClient,
   251  		DeliverClients:  mockDeliverClients,
   252  	}
   253  	return mockCF, nil
   254  }
   255  
   256  // Returns mock chaincode command factory that is constructed with an endorser
   257  // client that returns an error for proposal request and a deliver client
   258  func getMockChaincodeCmdFactoryWithErr() (*ChaincodeCmdFactory, error) {
   259  	signer, err := common.GetDefaultSigner()
   260  	if err != nil {
   261  		return nil, err
   262  	}
   263  
   264  	errMsg := "invoke error"
   265  	mockEndorserClients := []pb.EndorserClient{common.GetMockEndorserClient(nil, errors.New(errMsg))}
   266  	mockBroadcastClient := common.GetMockBroadcastClient(nil)
   267  	mockDeliverClients := []pb.DeliverClient{getMockDeliverClientResponseWithTxStatusAndID(pb.TxValidationCode_INVALID_OTHER_REASON, "txid0")}
   268  	mockCF := &ChaincodeCmdFactory{
   269  		EndorserClients: mockEndorserClients,
   270  		Signer:          signer,
   271  		BroadcastClient: mockBroadcastClient,
   272  		DeliverClients:  mockDeliverClients,
   273  	}
   274  	return mockCF, nil
   275  }
   276  
   277  // Returns mock chaincode command factory with an endorser client (that fails) and
   278  // a deliver client
   279  func getMockChaincodeCmdFactoryEndorsementFailure(ccRespStatus int32, ccRespPayload []byte) (*ChaincodeCmdFactory, error) {
   280  	signer, err := common.GetDefaultSigner()
   281  	if err != nil {
   282  		return nil, err
   283  	}
   284  
   285  	// create a proposal from a ChaincodeInvocationSpec
   286  	prop, _, err := protoutil.CreateChaincodeProposal(cb.HeaderType_ENDORSER_TRANSACTION, "testchannelid", createCIS(), nil)
   287  	if err != nil {
   288  		return nil, fmt.Errorf("Could not create chaincode proposal, err %s\n", err)
   289  	}
   290  
   291  	response := &pb.Response{Status: ccRespStatus, Payload: ccRespPayload}
   292  	result := []byte("res")
   293  
   294  	mockRespFailure, err := protoutil.CreateProposalResponseFailure(prop.Header, prop.Payload, response, result, nil, "foo")
   295  	if err != nil {
   296  
   297  		return nil, fmt.Errorf("Could not create proposal response failure, err %s\n", err)
   298  	}
   299  
   300  	mockEndorserClients := []pb.EndorserClient{common.GetMockEndorserClient(mockRespFailure, nil)}
   301  	mockBroadcastClient := common.GetMockBroadcastClient(nil)
   302  	mockDeliverClients := []pb.DeliverClient{getMockDeliverClientResponseWithTxStatusAndID(pb.TxValidationCode(mockRespFailure.Response.Status), "txid0")}
   303  	mockCF := &ChaincodeCmdFactory{
   304  		EndorserClients: mockEndorserClients,
   305  		Signer:          signer,
   306  		BroadcastClient: mockBroadcastClient,
   307  		DeliverClients:  mockDeliverClients,
   308  	}
   309  	return mockCF, nil
   310  }
   311  
   312  func createCIS() *pb.ChaincodeInvocationSpec {
   313  	return &pb.ChaincodeInvocationSpec{
   314  		ChaincodeSpec: &pb.ChaincodeSpec{
   315  			Type:        pb.ChaincodeSpec_GOLANG,
   316  			ChaincodeId: &pb.ChaincodeID{Name: "chaincode_name"},
   317  			Input:       &pb.ChaincodeInput{Args: [][]byte{[]byte("arg1"), []byte("arg2")}}}}
   318  }
   319  
   320  func getMockDeliverClientResponseWithTxStatusAndID(txStatus pb.TxValidationCode, txID string) *mock.PeerDeliverClient {
   321  	mockDC := &mock.PeerDeliverClient{}
   322  	mockDC.DeliverFilteredStub = func(ctx context.Context, opts ...grpc.CallOption) (pb.Deliver_DeliverFilteredClient, error) {
   323  		return getMockDeliverConnectionResponseWithTxStatusAndID(txStatus, txID), nil
   324  	}
   325  	return mockDC
   326  }
   327  
   328  func getMockDeliverConnectionResponseWithTxStatusAndID(txStatus pb.TxValidationCode, txID string) *mock.Deliver {
   329  	mockDF := &mock.Deliver{}
   330  	resp := &pb.DeliverResponse{
   331  		Type: &pb.DeliverResponse_FilteredBlock{
   332  			FilteredBlock: createFilteredBlock(txStatus, txID),
   333  		},
   334  	}
   335  	mockDF.RecvReturns(resp, nil)
   336  	return mockDF
   337  }
   338  
   339  func getMockDeliverClientRespondsWithFilteredBlocks(fb []*pb.FilteredBlock) *mock.PeerDeliverClient {
   340  	mockDC := &mock.PeerDeliverClient{}
   341  	mockDC.DeliverFilteredStub = func(ctx context.Context, opts ...grpc.CallOption) (pb.Deliver_DeliverFilteredClient, error) {
   342  		mockDF := &mock.Deliver{}
   343  		for i, f := range fb {
   344  			resp := &pb.DeliverResponse{
   345  				Type: &pb.DeliverResponse_FilteredBlock{
   346  					FilteredBlock: f,
   347  				},
   348  			}
   349  			mockDF.RecvReturnsOnCall(i, resp, nil)
   350  		}
   351  		return mockDF, nil
   352  	}
   353  	return mockDC
   354  }
   355  
   356  func getMockDeliverClientRegisterAfterDelay(delayChan chan struct{}) *mock.PeerDeliverClient {
   357  	mockDC := &mock.PeerDeliverClient{}
   358  	mockDC.DeliverFilteredStub = func(ctx context.Context, opts ...grpc.CallOption) (pb.Deliver_DeliverFilteredClient, error) {
   359  		mockDF := &mock.Deliver{}
   360  		mockDF.SendStub = func(*cb.Envelope) error {
   361  			<-delayChan
   362  			return nil
   363  		}
   364  		return mockDF, nil
   365  	}
   366  	return mockDC
   367  }
   368  
   369  func getMockDeliverClientRespondAfterDelay(delayChan chan struct{}, txStatus pb.TxValidationCode, txID string) *mock.PeerDeliverClient {
   370  	mockDC := &mock.PeerDeliverClient{}
   371  	mockDC.DeliverFilteredStub = func(ctx context.Context, opts ...grpc.CallOption) (pb.Deliver_DeliverFilteredClient, error) {
   372  		mockDF := &mock.Deliver{}
   373  		mockDF.RecvStub = func() (*pb.DeliverResponse, error) {
   374  			<-delayChan
   375  			resp := &pb.DeliverResponse{
   376  				Type: &pb.DeliverResponse_FilteredBlock{
   377  					FilteredBlock: createFilteredBlock(txStatus, txID),
   378  				},
   379  			}
   380  			return resp, nil
   381  		}
   382  		return mockDF, nil
   383  	}
   384  	return mockDC
   385  }
   386  
   387  func getMockDeliverClientWithErr(errMsg string) *mock.PeerDeliverClient {
   388  	mockDC := &mock.PeerDeliverClient{}
   389  	mockDC.DeliverFilteredStub = func(ctx context.Context, opts ...grpc.CallOption) (pb.Deliver_DeliverFilteredClient, error) {
   390  		return nil, fmt.Errorf(errMsg)
   391  	}
   392  	return mockDC
   393  }
   394  
   395  func createFilteredBlock(txStatus pb.TxValidationCode, txIDs ...string) *pb.FilteredBlock {
   396  	var filteredTransactions []*pb.FilteredTransaction
   397  	for _, txID := range txIDs {
   398  		ft := &pb.FilteredTransaction{
   399  			Txid:             txID,
   400  			TxValidationCode: txStatus,
   401  		}
   402  		filteredTransactions = append(filteredTransactions, ft)
   403  	}
   404  	fb := &pb.FilteredBlock{
   405  		Number:               0,
   406  		ChannelId:            "testchannel",
   407  		FilteredTransactions: filteredTransactions,
   408  	}
   409  	return fb
   410  }