github.com/leonlxy/hyperledger@v1.0.0-alpha.0.20170427033203-34922035d248/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  )
    35  
    36  func TestInit(t *testing.T) {
    37  	e := new(EndorserOneValidSignature)
    38  	stub := shim.NewMockStub("endorseronevalidsignature", e)
    39  
    40  	args := [][]byte{[]byte("DEFAULT"), []byte("PEER")}
    41  	if res := stub.MockInit("1", args); res.Status != shim.OK {
    42  		fmt.Println("Init failed", string(res.Message))
    43  		t.FailNow()
    44  	}
    45  }
    46  
    47  func TestInvoke(t *testing.T) {
    48  	e := new(EndorserOneValidSignature)
    49  	stub := shim.NewMockStub("endorseronevalidsignature", e)
    50  	successResponse := &pb.Response{Status: 200, Payload: []byte("payload")}
    51  	failResponse := &pb.Response{Status: 500, Message: "error"}
    52  	successRes, _ := putils.GetBytesResponse(successResponse)
    53  	failRes, _ := putils.GetBytesResponse(failResponse)
    54  	ccid := &pb.ChaincodeID{Name: "foo", Version: "v1"}
    55  	ccidBytes, err := proto.Marshal(ccid)
    56  	if err != nil {
    57  		t.Fail()
    58  		t.Fatalf("couldn't marshal ChaincodeID: err %s", err)
    59  		return
    60  	}
    61  
    62  	// Initialize ESCC supplying the identity of the signer
    63  	args := [][]byte{[]byte("DEFAULT"), []byte("PEER")}
    64  	if res := stub.MockInit("1", args); res.Status != shim.OK {
    65  		fmt.Println("Init failed", string(res.Message))
    66  		t.FailNow()
    67  	}
    68  
    69  	// Failed path: Not enough parameters
    70  	args = [][]byte{[]byte("test")}
    71  	if res := stub.MockInvoke("1", args); res.Status == shim.OK {
    72  		t.Fatalf("escc invoke should have failed with invalid number of args: %v", args)
    73  	}
    74  
    75  	// Failed path: Not enough parameters
    76  	args = [][]byte{[]byte("test"), []byte("test")}
    77  	if res := stub.MockInvoke("1", args); res.Status == shim.OK {
    78  		t.Fatalf("escc invoke should have failed with invalid number of args: %v", args)
    79  	}
    80  
    81  	// Failed path: Not enough parameters
    82  	args = [][]byte{[]byte("test"), []byte("test"), []byte("test")}
    83  	if res := stub.MockInvoke("1", args); res.Status == shim.OK {
    84  		t.Fatalf("escc invoke should have failed with invalid number of args: %v", args)
    85  	}
    86  
    87  	// Failed path: Not enough parameters
    88  	args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), []byte("test")}
    89  	if res := stub.MockInvoke("1", args); res.Status == shim.OK {
    90  		t.Fatalf("escc invoke should have failed with invalid number of args: %v", args)
    91  	}
    92  
    93  	// Failed path: Not enough parameters
    94  	args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), []byte("test"), []byte("test")}
    95  	if res := stub.MockInvoke("1", args); res.Status == shim.OK {
    96  		t.Fatalf("escc invoke should have failed with invalid number of args: %v", args)
    97  	}
    98  
    99  	// Failed path: header is null
   100  	args = [][]byte{[]byte("test"), nil, []byte("test"), ccidBytes, successRes, []byte("test")}
   101  	if res := stub.MockInvoke("1", args); res.Status == shim.OK {
   102  		fmt.Println("Invoke", args, "failed", string(res.Message))
   103  		t.Fatalf("escc invoke should have failed with a null header.  args: %v", args)
   104  	}
   105  
   106  	// Failed path: payload is null
   107  	args = [][]byte{[]byte("test"), []byte("test"), nil, ccidBytes, successRes, []byte("test")}
   108  	if res := stub.MockInvoke("1", args); res.Status == shim.OK {
   109  		fmt.Println("Invoke", args, "failed", string(res.Message))
   110  		t.Fatalf("escc invoke should have failed with a null payload.  args: %v", args)
   111  	}
   112  
   113  	// Failed path: response is null
   114  	args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), ccidBytes, nil, []byte("test")}
   115  	if res := stub.MockInvoke("1", args); res.Status == shim.OK {
   116  		fmt.Println("Invoke", args, "failed", string(res.Message))
   117  		t.Fatalf("escc invoke should have failed with a null response.  args: %v", args)
   118  	}
   119  
   120  	// Failed path: action struct is null
   121  	args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), ccidBytes, successRes, nil}
   122  	if res := stub.MockInvoke("1", args); res.Status == shim.OK {
   123  		fmt.Println("Invoke", args, "failed", string(res.Message))
   124  		t.Fatalf("escc invoke should have failed with a null action struct.  args: %v", args)
   125  	}
   126  
   127  	// Failed path: status code >=500
   128  	args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), ccidBytes, failRes, []byte("test")}
   129  	if res := stub.MockInvoke("1", args); res.Status == shim.OK {
   130  		fmt.Println("Invoke", args, "failed", string(res.Message))
   131  		t.Fatalf("escc invoke should have failed with a null response.  args: %v", args)
   132  	}
   133  
   134  	// Successful path - create a proposal
   135  	cs := &pb.ChaincodeSpec{
   136  		ChaincodeId: ccid,
   137  		Type:        pb.ChaincodeSpec_GOLANG,
   138  		Input:       &pb.ChaincodeInput{Args: [][]byte{[]byte("some"), []byte("args")}}}
   139  
   140  	cis := &pb.ChaincodeInvocationSpec{ChaincodeSpec: cs}
   141  
   142  	sId, err := mspmgmt.GetLocalMSP().GetDefaultSigningIdentity()
   143  	if err != nil {
   144  		t.Fail()
   145  		t.Fatalf("couldn't obtain identity: err %s", err)
   146  		return
   147  	}
   148  
   149  	sIdBytes, err := sId.Serialize()
   150  	if err != nil {
   151  		t.Fail()
   152  		t.Fatalf("couldn't serialize identity: err %s", err)
   153  		return
   154  	}
   155  
   156  	proposal, _, err := putils.CreateChaincodeProposal(common.HeaderType_ENDORSER_TRANSACTION, util.GetTestChainID(), cis, sIdBytes)
   157  	if err != nil {
   158  		t.Fail()
   159  		t.Fatalf("couldn't generate chaincode proposal: err %s", err)
   160  		return
   161  	}
   162  
   163  	// success test 1: invocation with mandatory args only
   164  	simRes := []byte("simulation_result")
   165  
   166  	args = [][]byte{[]byte(""), proposal.Header, proposal.Payload, ccidBytes, successRes, simRes}
   167  	res := stub.MockInvoke("1", args)
   168  	if res.Status != shim.OK {
   169  		t.Fail()
   170  		t.Fatalf("escc invoke failed with: %s", res.Message)
   171  		return
   172  	}
   173  
   174  	err = validateProposalResponse(res.Payload, proposal, cs.ChaincodeId, nil, successResponse, simRes, nil)
   175  	if err != nil {
   176  		t.Fail()
   177  		t.Fatalf("%s", err)
   178  		return
   179  	}
   180  
   181  	// success test 2: invocation with mandatory args + events
   182  	events := []byte("events")
   183  
   184  	args = [][]byte{[]byte(""), proposal.Header, proposal.Payload, ccidBytes, successRes, simRes, events}
   185  	res = stub.MockInvoke("1", args)
   186  	if res.Status != shim.OK {
   187  		t.Fail()
   188  		t.Fatalf("escc invoke failed with: %s", res.Message)
   189  		return
   190  	}
   191  
   192  	err = validateProposalResponse(res.Payload, proposal, cs.ChaincodeId, nil, successResponse, simRes, events)
   193  	if err != nil {
   194  		t.Fail()
   195  		t.Fatalf("%s", err)
   196  		return
   197  	}
   198  
   199  	// success test 3: invocation with mandatory args + events and visibility
   200  	args = [][]byte{[]byte(""), proposal.Header, proposal.Payload, ccidBytes, successRes, simRes, events, nil}
   201  	res = stub.MockInvoke("1", args)
   202  	if res.Status != shim.OK {
   203  		t.Fail()
   204  		t.Fatalf("escc invoke failed with: %s", res.Message)
   205  		return
   206  	}
   207  
   208  	err = validateProposalResponse(res.Payload, proposal, cs.ChaincodeId, []byte{}, successResponse, simRes, events)
   209  	if err != nil {
   210  		t.Fail()
   211  		t.Fatalf("%s", err)
   212  		return
   213  	}
   214  }
   215  
   216  func validateProposalResponse(prBytes []byte, proposal *pb.Proposal, ccid *pb.ChaincodeID, visibility []byte, response *pb.Response, simRes []byte, events []byte) error {
   217  	if visibility == nil {
   218  		// TODO: set visibility to the default visibility mode once modes are defined
   219  	}
   220  
   221  	pResp, err := putils.GetProposalResponse(prBytes)
   222  	if err != nil {
   223  		return err
   224  	}
   225  
   226  	// check the version
   227  	if pResp.Version != 1 {
   228  		return fmt.Errorf("invalid version: %d", pResp.Version)
   229  	}
   230  
   231  	// check the response status
   232  	if pResp.Response.Status != 200 {
   233  		return fmt.Errorf("invalid response status: %d", pResp.Response.Status)
   234  	}
   235  
   236  	// extract ProposalResponsePayload
   237  	prp, err := putils.GetProposalResponsePayload(pResp.Payload)
   238  	if err != nil {
   239  		return fmt.Errorf("could not unmarshal the proposal response structure: err %s", err)
   240  	}
   241  
   242  	// TODO: validate the epoch
   243  
   244  	hdr, err := putils.GetHeader(proposal.Header)
   245  	if err != nil {
   246  		return fmt.Errorf("could not unmarshal the proposal header structure: err %s", err)
   247  	}
   248  
   249  	// recompute proposal hash
   250  	pHash, err := putils.GetProposalHash1(hdr, proposal.Payload, visibility)
   251  	if err != nil {
   252  		return fmt.Errorf("could not obtain proposalHash: err %s", err)
   253  	}
   254  
   255  	// validate that proposal hash matches
   256  	if bytes.Compare(pHash, prp.ProposalHash) != 0 {
   257  		return errors.New("proposal hash does not match")
   258  	}
   259  
   260  	// extract the chaincode action
   261  	cact, err := putils.GetChaincodeAction(prp.Extension)
   262  	if err != nil {
   263  		return fmt.Errorf("could not unmarshal the chaincode action structure: err %s", err)
   264  	}
   265  
   266  	// validate that the response match
   267  	if cact.Response.Status != response.Status {
   268  		return errors.New("response status do not match")
   269  	}
   270  	if cact.Response.Message != response.Message {
   271  		return errors.New("response message do not match")
   272  	}
   273  	if bytes.Compare(cact.Response.Payload, response.Payload) != 0 {
   274  		return errors.New("response payload do not match")
   275  	}
   276  
   277  	// validate that the results match
   278  	if bytes.Compare(cact.Results, simRes) != 0 {
   279  		return errors.New("results do not match")
   280  	}
   281  
   282  	// validate that the events match
   283  	if bytes.Compare(cact.Events, events) != 0 {
   284  		return errors.New("events do not match")
   285  	}
   286  
   287  	// validate that the ChaincodeID match
   288  	if cact.ChaincodeId.Name != ccid.Name {
   289  		return errors.New("ChaincodeID name do not match")
   290  	}
   291  	if cact.ChaincodeId.Version != ccid.Version {
   292  		return errors.New("ChaincodeID version do not match")
   293  	}
   294  	if cact.ChaincodeId.Path != ccid.Path {
   295  		return errors.New("ChaincodeID path do not match")
   296  	}
   297  
   298  	// get the identity of the endorser
   299  	endorser, err := mspmgmt.GetManagerForChain(util.GetTestChainID()).DeserializeIdentity(pResp.Endorsement.Endorser)
   300  	if err != nil {
   301  		return fmt.Errorf("Failed to deserialize endorser identity, err %s", err)
   302  	}
   303  
   304  	// ensure that endorser has a valid certificate
   305  	err = endorser.Validate()
   306  	if err != nil {
   307  		return fmt.Errorf("The endorser certificate is not valid, err %s", err)
   308  	}
   309  
   310  	err = endorser.Verify(append(pResp.Payload, pResp.Endorsement.Endorser...), pResp.Endorsement.Signature)
   311  	if err != nil {
   312  		return fmt.Errorf("The endorser's signature over the proposal response is not valid, err %s", err)
   313  	}
   314  
   315  	// as extra, we assemble a transaction, sign it and then validate it
   316  
   317  	// obtain signer for the transaction
   318  	sId, err := mspmgmt.GetLocalMSP().GetDefaultSigningIdentity()
   319  	if err != nil {
   320  		return fmt.Errorf("couldn't obtain identity: err %s", err)
   321  	}
   322  
   323  	// generate a transaction
   324  	tx, err := putils.CreateSignedTx(proposal, sId, pResp)
   325  	if err != nil {
   326  		return err
   327  	}
   328  
   329  	// validate the transaction
   330  	_, txResult := validation.ValidateTransaction(tx)
   331  	if txResult != pb.TxValidationCode_VALID {
   332  		return err
   333  	}
   334  
   335  	return nil
   336  }
   337  
   338  func TestMain(m *testing.M) {
   339  	msptesttools.LoadMSPSetupForTesting()
   340  
   341  	os.Exit(m.Run())
   342  }