github.com/darrenli6/fabric-sdk-example@v0.0.0-20220109053535-94b13b56df8c/core/scc/escc/endorser_onevalidsignature_test.go (about)

     1  /*
     2  Copyright IBM Corp. 2016 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  		 http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  package escc
    17  
    18  import (
    19  	"bytes"
    20  	"errors"
    21  	"fmt"
    22  	"os"
    23  	"testing"
    24  
    25  	"github.com/golang/protobuf/proto"
    26  	"github.com/hyperledger/fabric/common/util"
    27  	"github.com/hyperledger/fabric/core/chaincode/shim"
    28  	"github.com/hyperledger/fabric/core/common/validation"
    29  	mspmgmt "github.com/hyperledger/fabric/msp/mgmt"
    30  	"github.com/hyperledger/fabric/msp/mgmt/testtools"
    31  	"github.com/hyperledger/fabric/protos/common"
    32  	pb "github.com/hyperledger/fabric/protos/peer"
    33  	putils "github.com/hyperledger/fabric/protos/utils"
    34  	"github.com/stretchr/testify/assert"
    35  )
    36  
    37  func TestInit(t *testing.T) {
    38  	e := new(EndorserOneValidSignature)
    39  	stub := shim.NewMockStub("endorseronevalidsignature", e)
    40  
    41  	args := [][]byte{[]byte("DEFAULT"), []byte("PEER")}
    42  	if res := stub.MockInit("1", args); res.Status != shim.OK {
    43  		fmt.Println("Init failed", string(res.Message))
    44  		t.FailNow()
    45  	}
    46  }
    47  
    48  func TestInvoke(t *testing.T) {
    49  	e := new(EndorserOneValidSignature)
    50  	stub := shim.NewMockStub("endorseronevalidsignature", e)
    51  	successResponse := &pb.Response{Status: 200, Payload: []byte("payload")}
    52  	failResponse := &pb.Response{Status: 500, Message: "error"}
    53  	ccFailResponse := &pb.Response{Status: 400, Message: "chaincode error"}
    54  	successRes, _ := putils.GetBytesResponse(successResponse)
    55  	failRes, _ := putils.GetBytesResponse(failResponse)
    56  	ccFailRes, _ := putils.GetBytesResponse(ccFailResponse)
    57  	ccid := &pb.ChaincodeID{Name: "foo", Version: "v1"}
    58  	ccidBytes, err := proto.Marshal(ccid)
    59  	if err != nil {
    60  		t.Fail()
    61  		t.Fatalf("couldn't marshal ChaincodeID: err %s", err)
    62  		return
    63  	}
    64  
    65  	// Initialize ESCC supplying the identity of the signer
    66  	args := [][]byte{[]byte("DEFAULT"), []byte("PEER")}
    67  	if res := stub.MockInit("1", args); res.Status != shim.OK {
    68  		fmt.Println("Init failed", string(res.Message))
    69  		t.FailNow()
    70  	}
    71  
    72  	// Failed path: Not enough parameters
    73  	args = [][]byte{[]byte("test")}
    74  	if res := stub.MockInvoke("1", args); res.Status == shim.OK {
    75  		t.Fatalf("escc invoke should have failed with invalid number of args: %v", args)
    76  	}
    77  
    78  	// Failed path: Not enough parameters
    79  	args = [][]byte{[]byte("test"), []byte("test")}
    80  	if res := stub.MockInvoke("1", args); res.Status == shim.OK {
    81  		t.Fatalf("escc invoke should have failed with invalid number of args: %v", args)
    82  	}
    83  
    84  	// Failed path: Not enough parameters
    85  	args = [][]byte{[]byte("test"), []byte("test"), []byte("test")}
    86  	if res := stub.MockInvoke("1", args); res.Status == shim.OK {
    87  		t.Fatalf("escc invoke should have failed with invalid number of args: %v", args)
    88  	}
    89  
    90  	// Failed path: Not enough parameters
    91  	args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), []byte("test")}
    92  	if res := stub.MockInvoke("1", args); res.Status == shim.OK {
    93  		t.Fatalf("escc invoke should have failed with invalid number of args: %v", args)
    94  	}
    95  
    96  	// Failed path: Not enough parameters
    97  	args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), []byte("test"), []byte("test")}
    98  	if res := stub.MockInvoke("1", args); res.Status == shim.OK {
    99  		t.Fatalf("escc invoke should have failed with invalid number of args: %v", args)
   100  	}
   101  
   102  	// Too many parameters
   103  	a := []byte("test")
   104  	args = [][]byte{a, a, a, a, a, a, a, a, a}
   105  	if res := stub.MockInvoke("1", args); res.Status == shim.OK {
   106  		t.Fatalf("escc invoke should have failed with invalid number of args: %v", args)
   107  	}
   108  
   109  	// Failed path: ccid is null
   110  	args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), nil, successRes, []byte("test")}
   111  	if res := stub.MockInvoke("1", args); res.Status == shim.OK {
   112  		fmt.Println("Invoke", args, "failed", string(res.Message))
   113  		t.Fatalf("escc invoke should have failed with a null header.  args: %v", args)
   114  	}
   115  
   116  	// Failed path: ccid is bogus
   117  	args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), []byte("barf"), successRes, []byte("test")}
   118  	if res := stub.MockInvoke("1", args); res.Status == shim.OK {
   119  		fmt.Println("Invoke", args, "failed", string(res.Message))
   120  		t.Fatalf("escc invoke should have failed with a null header.  args: %v", args)
   121  	}
   122  
   123  	// Failed path: response is bogus
   124  	args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), ccidBytes, []byte("barf"), []byte("test")}
   125  	if res := stub.MockInvoke("1", args); res.Status == shim.OK {
   126  		fmt.Println("Invoke", args, "failed", string(res.Message))
   127  		t.Fatalf("escc invoke should have failed with a null header.  args: %v", args)
   128  	}
   129  
   130  	// Failed path: header is null
   131  	args = [][]byte{[]byte("test"), nil, []byte("test"), ccidBytes, successRes, []byte("test")}
   132  	if res := stub.MockInvoke("1", args); res.Status == shim.OK {
   133  		fmt.Println("Invoke", args, "failed", string(res.Message))
   134  		t.Fatalf("escc invoke should have failed with a null header.  args: %v", args)
   135  	}
   136  
   137  	// Failed path: payload is null
   138  	args = [][]byte{[]byte("test"), []byte("test"), nil, ccidBytes, successRes, []byte("test")}
   139  	if res := stub.MockInvoke("1", args); res.Status == shim.OK {
   140  		fmt.Println("Invoke", args, "failed", string(res.Message))
   141  		t.Fatalf("escc invoke should have failed with a null payload.  args: %v", args)
   142  	}
   143  
   144  	// Failed path: response is null
   145  	args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), ccidBytes, nil, []byte("test")}
   146  	if res := stub.MockInvoke("1", args); res.Status == shim.OK {
   147  		fmt.Println("Invoke", args, "failed", string(res.Message))
   148  		t.Fatalf("escc invoke should have failed with a null response.  args: %v", args)
   149  	}
   150  
   151  	// Failed path: action struct is null
   152  	args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), ccidBytes, successRes, nil}
   153  	if res := stub.MockInvoke("1", args); res.Status == shim.OK {
   154  		fmt.Println("Invoke", args, "failed", string(res.Message))
   155  		t.Fatalf("escc invoke should have failed with a null action struct.  args: %v", args)
   156  	}
   157  
   158  	// Failed path: status code = 500
   159  	args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), ccidBytes, failRes, []byte("test")}
   160  	res := stub.MockInvoke("1", args)
   161  	assert.NotEqual(t, res.Status, shim.OK, "Invoke should have failed with status code: %d", ccFailResponse.Status)
   162  	assert.Contains(t, res.Message, fmt.Sprintf("Status code less than %d will be endorsed", shim.ERRORTHRESHOLD))
   163  
   164  	// Failed path: status code = 400
   165  	args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), ccidBytes, ccFailRes, []byte("test")}
   166  	res = stub.MockInvoke("1", args)
   167  	assert.NotEqual(t, res.Status, shim.OK, "Invoke should have failed with status code: %d", ccFailResponse.Status)
   168  	assert.Contains(t, res.Message, fmt.Sprintf("Status code less than %d will be endorsed", shim.ERRORTHRESHOLD))
   169  
   170  	// Successful path - create a proposal
   171  	cs := &pb.ChaincodeSpec{
   172  		ChaincodeId: ccid,
   173  		Type:        pb.ChaincodeSpec_GOLANG,
   174  		Input:       &pb.ChaincodeInput{Args: [][]byte{[]byte("some"), []byte("args")}}}
   175  
   176  	cis := &pb.ChaincodeInvocationSpec{ChaincodeSpec: cs}
   177  
   178  	sId, err := mspmgmt.GetLocalMSP().GetDefaultSigningIdentity()
   179  	if err != nil {
   180  		t.Fail()
   181  		t.Fatalf("couldn't obtain identity: err %s", err)
   182  		return
   183  	}
   184  
   185  	sIdBytes, err := sId.Serialize()
   186  	if err != nil {
   187  		t.Fail()
   188  		t.Fatalf("couldn't serialize identity: err %s", err)
   189  		return
   190  	}
   191  
   192  	proposal, _, err := putils.CreateChaincodeProposal(common.HeaderType_ENDORSER_TRANSACTION, util.GetTestChainID(), cis, sIdBytes)
   193  	if err != nil {
   194  		t.Fail()
   195  		t.Fatalf("couldn't generate chaincode proposal: err %s", err)
   196  		return
   197  	}
   198  
   199  	simRes := []byte("simulation_result")
   200  
   201  	// bogus header
   202  	args = [][]byte{[]byte(""), []byte("barf"), proposal.Payload, ccidBytes, successRes, simRes}
   203  	if res := stub.MockInvoke("1", args); res.Status == shim.OK {
   204  		fmt.Println("Invoke", args, "failed", string(res.Message))
   205  		t.Fatalf("escc invoke should have failed with a null response.  args: %v", args)
   206  	}
   207  
   208  	// success test 1: invocation with mandatory args only
   209  	args = [][]byte{[]byte(""), proposal.Header, proposal.Payload, ccidBytes, successRes, simRes}
   210  	res = stub.MockInvoke("1", args)
   211  	if res.Status != shim.OK {
   212  		t.Fail()
   213  		t.Fatalf("escc invoke failed with: %s", res.Message)
   214  		return
   215  	}
   216  
   217  	err = validateProposalResponse(res.Payload, proposal, cs.ChaincodeId, nil, successResponse, simRes, nil)
   218  	if err != nil {
   219  		t.Fail()
   220  		t.Fatalf("%s", err)
   221  		return
   222  	}
   223  
   224  	// success test 2: invocation with mandatory args + events
   225  	events := []byte("events")
   226  
   227  	args = [][]byte{[]byte(""), proposal.Header, proposal.Payload, ccidBytes, successRes, simRes, events}
   228  	res = stub.MockInvoke("1", args)
   229  	if res.Status != shim.OK {
   230  		t.Fail()
   231  		t.Fatalf("escc invoke failed with: %s", res.Message)
   232  		return
   233  	}
   234  
   235  	err = validateProposalResponse(res.Payload, proposal, cs.ChaincodeId, nil, successResponse, simRes, events)
   236  	if err != nil {
   237  		t.Fail()
   238  		t.Fatalf("%s", err)
   239  		return
   240  	}
   241  
   242  	// success test 3: invocation with mandatory args + events and visibility
   243  	args = [][]byte{[]byte(""), proposal.Header, proposal.Payload, ccidBytes, successRes, simRes, events, nil}
   244  	res = stub.MockInvoke("1", args)
   245  	if res.Status != shim.OK {
   246  		t.Fail()
   247  		t.Fatalf("escc invoke failed with: %s", res.Message)
   248  		return
   249  	}
   250  
   251  	err = validateProposalResponse(res.Payload, proposal, cs.ChaincodeId, []byte{}, successResponse, simRes, events)
   252  	if err != nil {
   253  		t.Fail()
   254  		t.Fatalf("%s", err)
   255  		return
   256  	}
   257  }
   258  
   259  func validateProposalResponse(prBytes []byte, proposal *pb.Proposal, ccid *pb.ChaincodeID, visibility []byte, response *pb.Response, simRes []byte, events []byte) error {
   260  	if visibility == nil {
   261  		// TODO: set visibility to the default visibility mode once modes are defined
   262  	}
   263  
   264  	pResp, err := putils.GetProposalResponse(prBytes)
   265  	if err != nil {
   266  		return err
   267  	}
   268  
   269  	// check the version
   270  	if pResp.Version != 1 {
   271  		return fmt.Errorf("invalid version: %d", pResp.Version)
   272  	}
   273  
   274  	// check the response status
   275  	if pResp.Response.Status != 200 {
   276  		return fmt.Errorf("invalid response status: %d", pResp.Response.Status)
   277  	}
   278  
   279  	// extract ProposalResponsePayload
   280  	prp, err := putils.GetProposalResponsePayload(pResp.Payload)
   281  	if err != nil {
   282  		return fmt.Errorf("could not unmarshal the proposal response structure: err %s", err)
   283  	}
   284  
   285  	// TODO: validate the epoch
   286  
   287  	hdr, err := putils.GetHeader(proposal.Header)
   288  	if err != nil {
   289  		return fmt.Errorf("could not unmarshal the proposal header structure: err %s", err)
   290  	}
   291  
   292  	// recompute proposal hash
   293  	pHash, err := putils.GetProposalHash1(hdr, proposal.Payload, visibility)
   294  	if err != nil {
   295  		return fmt.Errorf("could not obtain proposalHash: err %s", err)
   296  	}
   297  
   298  	// validate that proposal hash matches
   299  	if bytes.Compare(pHash, prp.ProposalHash) != 0 {
   300  		return errors.New("proposal hash does not match")
   301  	}
   302  
   303  	// extract the chaincode action
   304  	cact, err := putils.GetChaincodeAction(prp.Extension)
   305  	if err != nil {
   306  		return fmt.Errorf("could not unmarshal the chaincode action structure: err %s", err)
   307  	}
   308  
   309  	// validate that the response match
   310  	if cact.Response.Status != response.Status {
   311  		return errors.New("response status do not match")
   312  	}
   313  	if cact.Response.Message != response.Message {
   314  		return errors.New("response message do not match")
   315  	}
   316  	if bytes.Compare(cact.Response.Payload, response.Payload) != 0 {
   317  		return errors.New("response payload do not match")
   318  	}
   319  
   320  	// validate that the results match
   321  	if bytes.Compare(cact.Results, simRes) != 0 {
   322  		return errors.New("results do not match")
   323  	}
   324  
   325  	// validate that the events match
   326  	if bytes.Compare(cact.Events, events) != 0 {
   327  		return errors.New("events do not match")
   328  	}
   329  
   330  	// validate that the ChaincodeID match
   331  	if cact.ChaincodeId.Name != ccid.Name {
   332  		return errors.New("ChaincodeID name do not match")
   333  	}
   334  	if cact.ChaincodeId.Version != ccid.Version {
   335  		return errors.New("ChaincodeID version do not match")
   336  	}
   337  	if cact.ChaincodeId.Path != ccid.Path {
   338  		return errors.New("ChaincodeID path do not match")
   339  	}
   340  
   341  	// get the identity of the endorser
   342  	endorser, err := mspmgmt.GetManagerForChain(util.GetTestChainID()).DeserializeIdentity(pResp.Endorsement.Endorser)
   343  	if err != nil {
   344  		return fmt.Errorf("Failed to deserialize endorser identity, err %s", err)
   345  	}
   346  
   347  	// ensure that endorser has a valid certificate
   348  	err = endorser.Validate()
   349  	if err != nil {
   350  		return fmt.Errorf("The endorser certificate is not valid, err %s", err)
   351  	}
   352  
   353  	err = endorser.Verify(append(pResp.Payload, pResp.Endorsement.Endorser...), pResp.Endorsement.Signature)
   354  	if err != nil {
   355  		return fmt.Errorf("The endorser's signature over the proposal response is not valid, err %s", err)
   356  	}
   357  
   358  	// as extra, we assemble a transaction, sign it and then validate it
   359  
   360  	// obtain signer for the transaction
   361  	sId, err := mspmgmt.GetLocalMSP().GetDefaultSigningIdentity()
   362  	if err != nil {
   363  		return fmt.Errorf("couldn't obtain identity: err %s", err)
   364  	}
   365  
   366  	// generate a transaction
   367  	tx, err := putils.CreateSignedTx(proposal, sId, pResp)
   368  	if err != nil {
   369  		return err
   370  	}
   371  
   372  	// validate the transaction
   373  	_, txResult := validation.ValidateTransaction(tx)
   374  	if txResult != pb.TxValidationCode_VALID {
   375  		return err
   376  	}
   377  
   378  	return nil
   379  }
   380  
   381  func TestMain(m *testing.M) {
   382  	msptesttools.LoadMSPSetupForTesting()
   383  
   384  	os.Exit(m.Run())
   385  }