go_study/fabric-main/internal/peer/chaincode/invoke_test.go

412 lines
15 KiB
Go

/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package chaincode
import (
"context"
"errors"
"fmt"
"testing"
"time"
cb "github.com/hyperledger/fabric-protos-go/common"
pb "github.com/hyperledger/fabric-protos-go/peer"
"github.com/hyperledger/fabric/bccsp"
"github.com/hyperledger/fabric/bccsp/sw"
"github.com/hyperledger/fabric/common/flogging/floggingtest"
"github.com/hyperledger/fabric/internal/peer/chaincode/mock"
"github.com/hyperledger/fabric/internal/peer/common"
"github.com/hyperledger/fabric/msp"
"github.com/hyperledger/fabric/protoutil"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
)
func TestInvokeCmd(t *testing.T) {
defer viper.Reset()
defer resetFlags()
resetFlags()
mockCF, err := getMockChaincodeCmdFactory()
require.NoError(t, err, "Error getting mock chaincode command factory")
cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
require.NoError(t, err)
// Error case 0: no channelID specified
cmd := invokeCmd(mockCF, cryptoProvider)
addFlags(cmd)
args := []string{"-n", "example02", "-c", "{\"Args\": [\"invoke\",\"a\",\"b\",\"10\"]}"}
cmd.SetArgs(args)
err = cmd.Execute()
require.Error(t, err, "'peer chaincode invoke' command should have returned error when called without -C flag")
// Success case
cmd = invokeCmd(mockCF, cryptoProvider)
addFlags(cmd)
args = []string{"-n", "example02", "-c", "{\"Args\": [\"invoke\",\"a\",\"b\",\"10\"]}", "-C", "mychannel"}
cmd.SetArgs(args)
err = cmd.Execute()
require.NoError(t, err, "Run chaincode invoke cmd error")
// set timeout for error cases
viper.Set("peer.client.connTimeout", 10*time.Millisecond)
// Error case 1: no orderer endpoints
t.Logf("Start error case 1: no orderer endpoints")
getEndorserClient := common.GetEndorserClientFnc
getOrdererEndpointOfChain := common.GetOrdererEndpointOfChainFnc
getBroadcastClient := common.GetBroadcastClientFnc
getDefaultSigner := common.GetDefaultSignerFnc
getDeliverClient := common.GetDeliverClientFnc
getPeerDeliverClient := common.GetPeerDeliverClientFnc
defer func() {
common.GetEndorserClientFnc = getEndorserClient
common.GetOrdererEndpointOfChainFnc = getOrdererEndpointOfChain
common.GetBroadcastClientFnc = getBroadcastClient
common.GetDefaultSignerFnc = getDefaultSigner
common.GetDeliverClientFnc = getDeliverClient
common.GetPeerDeliverClientFnc = getPeerDeliverClient
}()
common.GetEndorserClientFnc = func(string, string) (pb.EndorserClient, error) {
return mockCF.EndorserClients[0], nil
}
common.GetOrdererEndpointOfChainFnc = func(chainID string, signer common.Signer, endorserClient pb.EndorserClient, cryptoProvider bccsp.BCCSP) ([]string, error) {
return []string{}, nil
}
cmd = invokeCmd(nil, cryptoProvider)
addFlags(cmd)
args = []string{"-n", "example02", "-c", "{\"Args\": [\"invoke\",\"a\",\"b\",\"10\"]}", "-C", "mychannel"}
cmd.SetArgs(args)
err = cmd.Execute()
require.Error(t, err)
// Error case 2: getEndorserClient returns error
t.Logf("Start error case 2: getEndorserClient returns error")
common.GetEndorserClientFnc = func(string, string) (pb.EndorserClient, error) {
return nil, errors.New("error")
}
err = cmd.Execute()
require.Error(t, err)
// Error case 3: getDeliverClient returns error
t.Logf("Start error case 3: getDeliverClient returns error")
common.GetDeliverClientFnc = func(string, string) (pb.Deliver_DeliverClient, error) {
return nil, errors.New("error")
}
err = cmd.Execute()
require.Error(t, err)
// Error case 4 : getPeerDeliverClient returns error
t.Logf("Start error case 4: getPeerDeliverClient returns error")
common.GetPeerDeliverClientFnc = func(string, string) (pb.DeliverClient, error) {
return nil, errors.New("error")
}
err = cmd.Execute()
require.Error(t, err)
// Error case 5: getDefaultSignerFnc returns error
t.Logf("Start error case 5: getDefaultSignerFnc returns error")
common.GetEndorserClientFnc = func(string, string) (pb.EndorserClient, error) {
return mockCF.EndorserClients[0], nil
}
common.GetPeerDeliverClientFnc = func(string, string) (pb.DeliverClient, error) {
return mockCF.DeliverClients[0], nil
}
common.GetDefaultSignerFnc = func() (msp.SigningIdentity, error) {
return nil, errors.New("error")
}
err = cmd.Execute()
require.Error(t, err)
common.GetDefaultSignerFnc = common.GetDefaultSigner
// Error case 6: getOrdererEndpointOfChainFnc returns error
t.Logf("Start error case 6: getOrdererEndpointOfChainFnc returns error")
common.GetEndorserClientFnc = func(string, string) (pb.EndorserClient, error) {
return mockCF.EndorserClients[0], nil
}
common.GetOrdererEndpointOfChainFnc = func(chainID string, signer common.Signer, endorserClient pb.EndorserClient, cryptoProvider bccsp.BCCSP) ([]string, error) {
return nil, errors.New("error")
}
err = cmd.Execute()
require.Error(t, err)
// Error case 7: getBroadcastClient returns error
t.Logf("Start error case 7: getBroadcastClient returns error")
common.GetOrdererEndpointOfChainFnc = func(chainID string, signer common.Signer, endorserClient pb.EndorserClient, cryptoProvider bccsp.BCCSP) ([]string, error) {
return []string{"localhost:9999"}, nil
}
common.GetBroadcastClientFnc = func() (common.BroadcastClient, error) {
return nil, errors.New("error")
}
err = cmd.Execute()
require.Error(t, err)
// Success case
t.Logf("Start success case")
common.GetBroadcastClientFnc = func() (common.BroadcastClient, error) {
return mockCF.BroadcastClient, nil
}
err = cmd.Execute()
require.NoError(t, err)
}
func TestInvokeCmdSimulateESCCPluginResponse(t *testing.T) {
defer resetFlags()
mockCF, err := getMockChaincodeCmdFactory()
require.NoError(t, err, "Error getting mock chaincode command factory")
cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
require.NoError(t, err)
// success case - simulate an ESCC plugin that endorses a chaincode response
// with status greater than shim.ERRORTHRESHOLD or even shim.ERROR
mockResponse := &pb.ProposalResponse{
Response: &pb.Response{Status: 504},
Endorsement: &pb.Endorsement{},
}
mockCF.EndorserClients = []pb.EndorserClient{
common.GetMockEndorserClient(mockResponse, nil),
common.GetMockEndorserClient(mockResponse, nil),
}
// set logger to logger with a backend that writes to a byte buffer
oldLogger := logger
defer func() { logger = oldLogger }()
l, recorder := floggingtest.NewTestLogger(t)
logger = l
cmd := invokeCmd(mockCF, cryptoProvider)
addFlags(cmd)
args := []string{"-n", "example02", "-c", "{\"Args\": [\"invoke\",\"a\",\"b\",\"10\"]}", "-C", "mychannel"}
cmd.SetArgs(args)
err = cmd.Execute()
require.NoError(t, err, "Run chaincode invoke cmd error")
require.NotEmpty(t, recorder.MessagesContaining("Chaincode invoke successful"), "missing invoke success log record")
require.NotEmpty(t, recorder.MessagesContaining("result: <nil>"), "missing result log record")
}
func TestInvokeCmdEndorsementError(t *testing.T) {
defer resetFlags()
mockCF, err := getMockChaincodeCmdFactoryWithErr()
require.NoError(t, err, "Error getting mock chaincode command factory")
cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
require.NoError(t, err)
cmd := invokeCmd(mockCF, cryptoProvider)
addFlags(cmd)
args := []string{"-n", "example02", "-C", "mychannel", "-c", "{\"Args\": [\"invoke\",\"a\",\"b\",\"10\"]}"}
cmd.SetArgs(args)
err = cmd.Execute()
require.Error(t, err, "Expected error executing invoke command")
}
func TestInvokeCmdEndorsementFailure(t *testing.T) {
defer resetFlags()
ccRespStatus := [2]int32{502, 400}
ccRespPayload := [][]byte{[]byte("Invalid function name"), []byte("Incorrect parameters")}
cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
require.NoError(t, err)
for i := 0; i < 2; i++ {
mockCF, err := getMockChaincodeCmdFactoryEndorsementFailure(ccRespStatus[i], ccRespPayload[i])
require.NoError(t, err, "Error getting mock chaincode command factory")
cmd := invokeCmd(mockCF, cryptoProvider)
addFlags(cmd)
args := []string{"-C", "mychannel", "-n", "example02", "-c", "{\"Args\": [\"invokeinvalid\",\"a\",\"b\",\"10\"]}"}
cmd.SetArgs(args)
err = cmd.Execute()
require.Error(t, err)
require.Contains(t, err.Error(), "endorsement failure during invoke")
require.Contains(t, err.Error(), fmt.Sprintf("response: status:%d payload:\"%s\"", ccRespStatus[i], ccRespPayload[i]))
}
}
// Returns mock chaincode command factory with multiple endorser and deliver clients
func getMockChaincodeCmdFactory() (*ChaincodeCmdFactory, error) {
signer, err := common.GetDefaultSigner()
if err != nil {
return nil, err
}
mockResponse := &pb.ProposalResponse{
Response: &pb.Response{Status: 200},
Endorsement: &pb.Endorsement{},
}
mockEndorserClients := []pb.EndorserClient{common.GetMockEndorserClient(mockResponse, nil), common.GetMockEndorserClient(mockResponse, nil)}
mockBroadcastClient := common.GetMockBroadcastClient(nil)
mockDC := getMockDeliverClientResponseWithTxStatusAndID(pb.TxValidationCode_VALID, "txid0")
mockDeliverClients := []pb.DeliverClient{mockDC, mockDC}
mockCF := &ChaincodeCmdFactory{
EndorserClients: mockEndorserClients,
Signer: signer,
BroadcastClient: mockBroadcastClient,
DeliverClients: mockDeliverClients,
}
return mockCF, nil
}
// Returns mock chaincode command factory that is constructed with an endorser
// client that returns an error for proposal request and a deliver client
func getMockChaincodeCmdFactoryWithErr() (*ChaincodeCmdFactory, error) {
signer, err := common.GetDefaultSigner()
if err != nil {
return nil, err
}
errMsg := "invoke error"
mockEndorserClients := []pb.EndorserClient{common.GetMockEndorserClient(nil, errors.New(errMsg))}
mockBroadcastClient := common.GetMockBroadcastClient(nil)
mockDeliverClients := []pb.DeliverClient{getMockDeliverClientResponseWithTxStatusAndID(pb.TxValidationCode_INVALID_OTHER_REASON, "txid0")}
mockCF := &ChaincodeCmdFactory{
EndorserClients: mockEndorserClients,
Signer: signer,
BroadcastClient: mockBroadcastClient,
DeliverClients: mockDeliverClients,
}
return mockCF, nil
}
// Returns mock chaincode command factory with an endorser client (that fails) and
// a deliver client
func getMockChaincodeCmdFactoryEndorsementFailure(ccRespStatus int32, ccRespPayload []byte) (*ChaincodeCmdFactory, error) {
signer, err := common.GetDefaultSigner()
if err != nil {
return nil, err
}
// create a proposal from a ChaincodeInvocationSpec
prop, _, err := protoutil.CreateChaincodeProposal(cb.HeaderType_ENDORSER_TRANSACTION, "testchannelid", createCIS(), nil)
if err != nil {
return nil, fmt.Errorf("Could not create chaincode proposal, err %s\n", err)
}
response := &pb.Response{Status: ccRespStatus, Payload: ccRespPayload}
result := []byte("res")
mockRespFailure, err := protoutil.CreateProposalResponseFailure(prop.Header, prop.Payload, response, result, nil, "foo")
if err != nil {
return nil, fmt.Errorf("Could not create proposal response failure, err %s\n", err)
}
mockEndorserClients := []pb.EndorserClient{common.GetMockEndorserClient(mockRespFailure, nil)}
mockBroadcastClient := common.GetMockBroadcastClient(nil)
mockDeliverClients := []pb.DeliverClient{getMockDeliverClientResponseWithTxStatusAndID(pb.TxValidationCode(mockRespFailure.Response.Status), "txid0")}
mockCF := &ChaincodeCmdFactory{
EndorserClients: mockEndorserClients,
Signer: signer,
BroadcastClient: mockBroadcastClient,
DeliverClients: mockDeliverClients,
}
return mockCF, nil
}
func createCIS() *pb.ChaincodeInvocationSpec {
return &pb.ChaincodeInvocationSpec{
ChaincodeSpec: &pb.ChaincodeSpec{
Type: pb.ChaincodeSpec_GOLANG,
ChaincodeId: &pb.ChaincodeID{Name: "chaincode_name"},
Input: &pb.ChaincodeInput{Args: [][]byte{[]byte("arg1"), []byte("arg2")}},
},
}
}
func getMockDeliverClientResponseWithTxStatusAndID(txStatus pb.TxValidationCode, txID string) *mock.PeerDeliverClient {
mockDC := &mock.PeerDeliverClient{}
mockDC.DeliverFilteredStub = func(ctx context.Context, opts ...grpc.CallOption) (pb.Deliver_DeliverFilteredClient, error) {
return getMockDeliverConnectionResponseWithTxStatusAndID(txStatus, txID), nil
}
return mockDC
}
func getMockDeliverConnectionResponseWithTxStatusAndID(txStatus pb.TxValidationCode, txID string) *mock.Deliver {
mockDF := &mock.Deliver{}
resp := &pb.DeliverResponse{
Type: &pb.DeliverResponse_FilteredBlock{
FilteredBlock: createFilteredBlock(txStatus, txID),
},
}
mockDF.RecvReturns(resp, nil)
return mockDF
}
func getMockDeliverClientRespondsWithFilteredBlocks(fb []*pb.FilteredBlock) *mock.PeerDeliverClient {
mockDC := &mock.PeerDeliverClient{}
mockDC.DeliverFilteredStub = func(ctx context.Context, opts ...grpc.CallOption) (pb.Deliver_DeliverFilteredClient, error) {
mockDF := &mock.Deliver{}
for i, f := range fb {
resp := &pb.DeliverResponse{
Type: &pb.DeliverResponse_FilteredBlock{
FilteredBlock: f,
},
}
mockDF.RecvReturnsOnCall(i, resp, nil)
}
return mockDF, nil
}
return mockDC
}
func getMockDeliverClientRegisterAfterDelay(delayChan chan struct{}) *mock.PeerDeliverClient {
mockDC := &mock.PeerDeliverClient{}
mockDC.DeliverFilteredStub = func(ctx context.Context, opts ...grpc.CallOption) (pb.Deliver_DeliverFilteredClient, error) {
mockDF := &mock.Deliver{}
mockDF.SendStub = func(*cb.Envelope) error {
<-delayChan
return nil
}
return mockDF, nil
}
return mockDC
}
func getMockDeliverClientRespondAfterDelay(delayChan chan struct{}, txStatus pb.TxValidationCode, txID string) *mock.PeerDeliverClient {
mockDC := &mock.PeerDeliverClient{}
mockDC.DeliverFilteredStub = func(ctx context.Context, opts ...grpc.CallOption) (pb.Deliver_DeliverFilteredClient, error) {
mockDF := &mock.Deliver{}
mockDF.RecvStub = func() (*pb.DeliverResponse, error) {
<-delayChan
resp := &pb.DeliverResponse{
Type: &pb.DeliverResponse_FilteredBlock{
FilteredBlock: createFilteredBlock(txStatus, txID),
},
}
return resp, nil
}
return mockDF, nil
}
return mockDC
}
func getMockDeliverClientWithErr(errMsg string) *mock.PeerDeliverClient {
mockDC := &mock.PeerDeliverClient{}
mockDC.DeliverFilteredStub = func(ctx context.Context, opts ...grpc.CallOption) (pb.Deliver_DeliverFilteredClient, error) {
return nil, fmt.Errorf(errMsg)
}
return mockDC
}
func createFilteredBlock(txStatus pb.TxValidationCode, txIDs ...string) *pb.FilteredBlock {
var filteredTransactions []*pb.FilteredTransaction
for _, txID := range txIDs {
ft := &pb.FilteredTransaction{
Txid: txID,
TxValidationCode: txStatus,
}
filteredTransactions = append(filteredTransactions, ft)
}
fb := &pb.FilteredBlock{
Number: 0,
ChannelId: "testchannel",
FilteredTransactions: filteredTransactions,
}
return fb
}