github.com/true-sqn/fabric@v2.1.1+incompatible/core/endorser/msgvalidation_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package endorser_test
     8  
     9  import (
    10  	"fmt"
    11  
    12  	. "github.com/onsi/ginkgo"
    13  	. "github.com/onsi/gomega"
    14  
    15  	cb "github.com/hyperledger/fabric-protos-go/common"
    16  	mspproto "github.com/hyperledger/fabric-protos-go/msp"
    17  	pb "github.com/hyperledger/fabric-protos-go/peer"
    18  	"github.com/hyperledger/fabric/core/endorser"
    19  	"github.com/hyperledger/fabric/core/endorser/fake"
    20  	"github.com/hyperledger/fabric/protoutil"
    21  
    22  	"github.com/golang/protobuf/proto"
    23  )
    24  
    25  var _ = Describe("UnpackProposal", func() {
    26  	var (
    27  		signedProposal           *pb.SignedProposal
    28  		proposal                 *pb.Proposal
    29  		header                   *cb.Header
    30  		channelHeader            *cb.ChannelHeader
    31  		signatureHeader          *cb.SignatureHeader
    32  		chaincodeHeaderExtension *pb.ChaincodeHeaderExtension
    33  		chaincodeProposalPayload *pb.ChaincodeProposalPayload
    34  		chaincodeInvocationSpec  *pb.ChaincodeInvocationSpec
    35  		chaincodeSpec            *pb.ChaincodeSpec
    36  		chaincodeInput           *pb.ChaincodeInput
    37  		chaincodeID              *pb.ChaincodeID
    38  
    39  		marshalProposal                 func() []byte
    40  		marshalHeader                   func() []byte
    41  		marshalSignatureHeader          func() []byte
    42  		marshalChannelHeader            func() []byte
    43  		marshalChaincodeHeaderExtension func() []byte
    44  		marshalChaincodeProposalPayload func() []byte
    45  		marshalChaincodeInvocationSpec  func() []byte
    46  	)
    47  
    48  	BeforeEach(func() {
    49  		/*
    50  			// This is the natural signed proposal structure, however, for test, we need
    51  			// to be able to control each of the elements, including the nested ones, so
    52  			// we build it awkwardly through function pointers
    53  
    54  			signedProposal = &pb.SignedProposal{
    55  				ProposalBytes: protoutil.MarshalOrPanic(&pb.Proposal{
    56  					Header: protoutil.MarshalOrPanic(&cb.Header{
    57  						ChannelHeader: protoutil.MarshalOrPanic(&cb.ChannelHeader{
    58  							Type: int32(cb.HeaderType_ENDORSER_TRANSACTION),
    59  							Extension: protoutil.MarshalOrPanic(&pb.ChaincodeHeaderExtension{
    60  								ChaincodeId: &pb.ChaincodeID{
    61  									Name: "chaincode-name",
    62  								},
    63  							}),
    64  						}),
    65  						SignatureHeader: protoutil.MarshalOrPanic(&cb.SignatureHeader{}),
    66  					}),
    67  					Payload: protoutil.MarshalOrPanic(&pb.ChaincodeProposalPayload{
    68  						Input: protoutil.MarshalOrPanic(&pb.ChaincodeInvocationSpec{
    69  							ChaincodeSpec: &pb.ChaincodeSpec{
    70  								Input: &pb.ChaincodeInput{
    71  									Args: [][]byte{[]byte("arg1"), []byte("arg2"), []byte("arg3")},
    72  								},
    73  							},
    74  						}),
    75  					}),
    76  				}),
    77  			}
    78  		*/
    79  		chaincodeID = &pb.ChaincodeID{
    80  			Name: "chaincode-name",
    81  		}
    82  
    83  		chaincodeHeaderExtension = &pb.ChaincodeHeaderExtension{
    84  			ChaincodeId: chaincodeID,
    85  		}
    86  
    87  		marshalChaincodeHeaderExtension = func() []byte {
    88  			return protoutil.MarshalOrPanic(chaincodeHeaderExtension)
    89  		}
    90  
    91  		channelHeader = &cb.ChannelHeader{
    92  			Type: int32(cb.HeaderType_ENDORSER_TRANSACTION),
    93  		}
    94  
    95  		marshalChannelHeader = func() []byte {
    96  			channelHeader.Extension = marshalChaincodeHeaderExtension()
    97  			return protoutil.MarshalOrPanic(channelHeader)
    98  		}
    99  
   100  		signatureHeader = &cb.SignatureHeader{
   101  			Creator: []byte("creator"),
   102  			Nonce:   []byte("nonce"),
   103  		}
   104  
   105  		marshalSignatureHeader = func() []byte {
   106  			return protoutil.MarshalOrPanic(signatureHeader)
   107  		}
   108  
   109  		header = &cb.Header{}
   110  
   111  		marshalHeader = func() []byte {
   112  			header.ChannelHeader = marshalChannelHeader()
   113  			header.SignatureHeader = marshalSignatureHeader()
   114  			return protoutil.MarshalOrPanic(header)
   115  		}
   116  
   117  		chaincodeInput = &pb.ChaincodeInput{
   118  			Args: [][]byte{[]byte("arg1"), []byte("arg2"), []byte("arg3")},
   119  		}
   120  
   121  		chaincodeSpec = &pb.ChaincodeSpec{
   122  			Input: chaincodeInput,
   123  		}
   124  
   125  		chaincodeInvocationSpec = &pb.ChaincodeInvocationSpec{
   126  			ChaincodeSpec: chaincodeSpec,
   127  		}
   128  
   129  		marshalChaincodeInvocationSpec = func() []byte {
   130  			return protoutil.MarshalOrPanic(chaincodeInvocationSpec)
   131  		}
   132  
   133  		chaincodeProposalPayload = &pb.ChaincodeProposalPayload{}
   134  
   135  		marshalChaincodeProposalPayload = func() []byte {
   136  			chaincodeProposalPayload.Input = marshalChaincodeInvocationSpec()
   137  			return protoutil.MarshalOrPanic(chaincodeProposalPayload)
   138  		}
   139  
   140  		proposal = &pb.Proposal{}
   141  
   142  		marshalProposal = func() []byte {
   143  			proposal.Header = marshalHeader()
   144  			proposal.Payload = marshalChaincodeProposalPayload()
   145  			return protoutil.MarshalOrPanic(proposal)
   146  		}
   147  
   148  	})
   149  
   150  	JustBeforeEach(func() {
   151  		signedProposal = &pb.SignedProposal{
   152  			ProposalBytes: marshalProposal(),
   153  		}
   154  	})
   155  
   156  	It("unmarshals the signed proposal into the interesting bits and returns them as a struct", func() {
   157  		up, err := endorser.UnpackProposal(signedProposal)
   158  		Expect(err).NotTo(HaveOccurred())
   159  		Expect(up.ChaincodeName).To(Equal("chaincode-name"))
   160  		Expect(up.SignedProposal).To(Equal(signedProposal))
   161  		Expect(proto.Equal(up.Proposal, proposal)).To(BeTrue())
   162  		Expect(proto.Equal(up.Input, chaincodeInput)).To(BeTrue())
   163  		Expect(proto.Equal(up.SignatureHeader, signatureHeader)).To(BeTrue())
   164  		Expect(proto.Equal(up.ChannelHeader, channelHeader)).To(BeTrue())
   165  	})
   166  
   167  	Context("when the proposal bytes are invalid", func() {
   168  		BeforeEach(func() {
   169  			marshalProposal = func() []byte {
   170  				return []byte("garbage")
   171  			}
   172  		})
   173  
   174  		It("wraps and returns an error", func() {
   175  			_, err := endorser.UnpackProposal(signedProposal)
   176  			Expect(err).To(MatchError("error unmarshaling Proposal: proto: can't skip unknown wire type 7"))
   177  		})
   178  	})
   179  
   180  	Context("when the header bytes are invalid", func() {
   181  		BeforeEach(func() {
   182  			marshalHeader = func() []byte {
   183  				return []byte("garbage")
   184  			}
   185  		})
   186  
   187  		It("wraps and returns the error", func() {
   188  			_, err := endorser.UnpackProposal(signedProposal)
   189  			Expect(err).To(MatchError("error unmarshaling Header: proto: can't skip unknown wire type 7"))
   190  		})
   191  	})
   192  
   193  	Context("when the channel header bytes are invalid", func() {
   194  		BeforeEach(func() {
   195  			marshalChannelHeader = func() []byte {
   196  				return []byte("garbage")
   197  			}
   198  		})
   199  
   200  		It("wraps and returns an error", func() {
   201  			_, err := endorser.UnpackProposal(signedProposal)
   202  			Expect(err).To(MatchError("error unmarshaling ChannelHeader: proto: can't skip unknown wire type 7"))
   203  		})
   204  	})
   205  
   206  	Context("when the signature header bytes are invalid", func() {
   207  		BeforeEach(func() {
   208  			marshalSignatureHeader = func() []byte {
   209  				return []byte("garbage")
   210  			}
   211  		})
   212  
   213  		It("wraps and returns an error", func() {
   214  			_, err := endorser.UnpackProposal(signedProposal)
   215  			Expect(err).To(MatchError("error unmarshaling SignatureHeader: proto: can't skip unknown wire type 7"))
   216  		})
   217  	})
   218  
   219  	Context("when the chaincode header extension bytes are invalid", func() {
   220  		BeforeEach(func() {
   221  			marshalChaincodeHeaderExtension = func() []byte {
   222  				return []byte("garbage")
   223  			}
   224  		})
   225  
   226  		It("wraps and returns an error", func() {
   227  			_, err := endorser.UnpackProposal(signedProposal)
   228  			Expect(err).To(MatchError("error unmarshaling ChaincodeHeaderExtension: proto: can't skip unknown wire type 7"))
   229  		})
   230  	})
   231  
   232  	Context("when the chaincode proposal payload is invalid", func() {
   233  		BeforeEach(func() {
   234  			marshalChaincodeProposalPayload = func() []byte {
   235  				return []byte("garbage")
   236  			}
   237  		})
   238  
   239  		It("wraps and returns an error", func() {
   240  			_, err := endorser.UnpackProposal(signedProposal)
   241  			Expect(err).To(MatchError("error unmarshaling ChaincodeProposalPayload: proto: can't skip unknown wire type 7"))
   242  		})
   243  	})
   244  
   245  	Context("when the chaincode id is empty", func() {
   246  		BeforeEach(func() {
   247  			chaincodeHeaderExtension.ChaincodeId = nil
   248  		})
   249  
   250  		It("wraps and returns an error", func() {
   251  			_, err := endorser.UnpackProposal(signedProposal)
   252  			Expect(err).To(MatchError("ChaincodeHeaderExtension.ChaincodeId is nil"))
   253  		})
   254  	})
   255  
   256  	Context("when the chaincode id name is empty", func() {
   257  		BeforeEach(func() {
   258  			chaincodeHeaderExtension.ChaincodeId.Name = ""
   259  		})
   260  
   261  		It("wraps and returns an error", func() {
   262  			_, err := endorser.UnpackProposal(signedProposal)
   263  			Expect(err).To(MatchError("ChaincodeHeaderExtension.ChaincodeId.Name is empty"))
   264  		})
   265  	})
   266  
   267  	Context("when the chaincode invocation spec is invalid", func() {
   268  		BeforeEach(func() {
   269  			marshalChaincodeInvocationSpec = func() []byte {
   270  				return []byte("garbage")
   271  			}
   272  		})
   273  
   274  		It("wraps and returns an error", func() {
   275  			_, err := endorser.UnpackProposal(signedProposal)
   276  			Expect(err).To(MatchError("error unmarshaling ChaincodeInvocationSpec: proto: can't skip unknown wire type 7"))
   277  		})
   278  	})
   279  
   280  	Context("when the chaincode invocation spec is has a nil chaincodespec", func() {
   281  		BeforeEach(func() {
   282  			chaincodeInvocationSpec.ChaincodeSpec = nil
   283  		})
   284  
   285  		It("wraps and returns an error", func() {
   286  			_, err := endorser.UnpackProposal(signedProposal)
   287  			Expect(err).To(MatchError("chaincode invocation spec did not contain chaincode spec"))
   288  		})
   289  	})
   290  
   291  	Context("when the input is missing", func() {
   292  		BeforeEach(func() {
   293  			chaincodeSpec.Input = nil
   294  		})
   295  
   296  		It("wraps and returns an error", func() {
   297  			_, err := endorser.UnpackProposal(signedProposal)
   298  			Expect(err).To(MatchError("chaincode input did not contain any input"))
   299  		})
   300  	})
   301  })
   302  
   303  var _ = Describe("Validate", func() {
   304  	var (
   305  		up *endorser.UnpackedProposal
   306  
   307  		fakeIdentity             *fake.Identity
   308  		fakeIdentityDeserializer *fake.IdentityDeserializer
   309  	)
   310  
   311  	BeforeEach(func() {
   312  		up = &endorser.UnpackedProposal{
   313  			SignedProposal: &pb.SignedProposal{
   314  				Signature:     []byte("signature"),
   315  				ProposalBytes: []byte("payload"),
   316  			},
   317  			ChannelHeader: &cb.ChannelHeader{
   318  				ChannelId: "channel-id",
   319  				Type:      int32(cb.HeaderType_ENDORSER_TRANSACTION),
   320  				TxId:      "876a1777b78e5e3a6d1aabf8b5a11b893c3838285b2f5eedca7d23e25365fcfd",
   321  			},
   322  			SignatureHeader: &cb.SignatureHeader{
   323  				Nonce: []byte("nonce"),
   324  				Creator: protoutil.MarshalOrPanic(&mspproto.SerializedIdentity{
   325  					Mspid:   "mspid",
   326  					IdBytes: []byte("identity"),
   327  				}),
   328  			},
   329  		}
   330  
   331  		fakeIdentity = &fake.Identity{}
   332  
   333  		fakeIdentityDeserializer = &fake.IdentityDeserializer{}
   334  		fakeIdentityDeserializer.DeserializeIdentityReturns(fakeIdentity, nil)
   335  	})
   336  
   337  	It("validates the proposal", func() {
   338  		err := up.Validate(fakeIdentityDeserializer)
   339  		Expect(err).NotTo(HaveOccurred())
   340  
   341  		Expect(fakeIdentityDeserializer.DeserializeIdentityCallCount()).To(Equal(1))
   342  		creator := fakeIdentityDeserializer.DeserializeIdentityArgsForCall(0)
   343  		Expect(creator).To(Equal(protoutil.MarshalOrPanic(&mspproto.SerializedIdentity{
   344  			Mspid:   "mspid",
   345  			IdBytes: []byte("identity"),
   346  		})))
   347  
   348  		Expect(fakeIdentity.ValidateCallCount()).To(Equal(1))
   349  		Expect(fakeIdentity.VerifyCallCount()).To(Equal(1))
   350  		payload, sig := fakeIdentity.VerifyArgsForCall(0)
   351  		Expect(payload).To(Equal([]byte("payload")))
   352  		Expect(sig).To(Equal([]byte("signature")))
   353  	})
   354  
   355  	Context("when the header type is config", func() {
   356  		BeforeEach(func() {
   357  			up.ChannelHeader = &cb.ChannelHeader{
   358  				Type: int32(cb.HeaderType_CONFIG),
   359  				TxId: "876a1777b78e5e3a6d1aabf8b5a11b893c3838285b2f5eedca7d23e25365fcfd",
   360  			}
   361  		})
   362  
   363  		It("preserves buggy behavior and does not error", func() {
   364  			err := up.Validate(fakeIdentityDeserializer)
   365  			Expect(err).NotTo(HaveOccurred())
   366  		})
   367  	})
   368  
   369  	Context("when the header type is bad", func() {
   370  		BeforeEach(func() {
   371  			up.ChannelHeader = &cb.ChannelHeader{
   372  				Type: int32(0),
   373  				TxId: "876a1777b78e5e3a6d1aabf8b5a11b893c3838285b2f5eedca7d23e25365fcfd",
   374  			}
   375  		})
   376  
   377  		It("returns an error", func() {
   378  			err := up.Validate(fakeIdentityDeserializer)
   379  			Expect(err).To(MatchError("invalid header type MESSAGE"))
   380  		})
   381  	})
   382  
   383  	Context("when the signature is missing", func() {
   384  		BeforeEach(func() {
   385  			up.SignedProposal.Signature = nil
   386  		})
   387  
   388  		It("returns an error", func() {
   389  			err := up.Validate(fakeIdentityDeserializer)
   390  			Expect(err).To(MatchError("empty signature bytes"))
   391  		})
   392  	})
   393  
   394  	Context("when the nonce is missing", func() {
   395  		BeforeEach(func() {
   396  			up.SignatureHeader.Nonce = nil
   397  		})
   398  
   399  		It("returns an error", func() {
   400  			err := up.Validate(fakeIdentityDeserializer)
   401  			Expect(err).To(MatchError("nonce is empty"))
   402  		})
   403  	})
   404  
   405  	Context("when the creator is missing", func() {
   406  		BeforeEach(func() {
   407  			up.SignatureHeader.Creator = nil
   408  		})
   409  
   410  		It("returns an error", func() {
   411  			err := up.Validate(fakeIdentityDeserializer)
   412  			Expect(err).To(MatchError("creator is empty"))
   413  		})
   414  	})
   415  
   416  	Context("when the epoch is nonzero", func() {
   417  		BeforeEach(func() {
   418  			up.ChannelHeader.Epoch = 7
   419  		})
   420  
   421  		It("returns an error", func() {
   422  			err := up.Validate(fakeIdentityDeserializer)
   423  			Expect(err).To(MatchError("epoch is non-zero"))
   424  		})
   425  	})
   426  
   427  	Context("when the txid is wrong", func() {
   428  		BeforeEach(func() {
   429  			up.ChannelHeader.TxId = "fake-txid"
   430  		})
   431  
   432  		It("returns an error", func() {
   433  			err := up.Validate(fakeIdentityDeserializer)
   434  			Expect(err).To(MatchError("incorrectly computed txid 'fake-txid' -- expected '876a1777b78e5e3a6d1aabf8b5a11b893c3838285b2f5eedca7d23e25365fcfd'"))
   435  		})
   436  	})
   437  
   438  	Context("when the proposal bytes are missing", func() {
   439  		BeforeEach(func() {
   440  			up.SignedProposal.ProposalBytes = nil
   441  		})
   442  
   443  		It("returns an error", func() {
   444  			err := up.Validate(fakeIdentityDeserializer)
   445  			Expect(err).To(MatchError("empty proposal bytes"))
   446  		})
   447  	})
   448  
   449  	Context("when the identity is actually not a validly serialized proto", func() {
   450  		BeforeEach(func() {
   451  			up.SignatureHeader.Creator = []byte("garbage")
   452  			up.ChannelHeader.TxId = "8f9de857052f103caee0fef35f66766562b4b4c2a14af34e9626351de52edfc4"
   453  		})
   454  
   455  		It("returns an auth error", func() {
   456  			err := up.Validate(fakeIdentityDeserializer)
   457  			Expect(err).To(MatchError("access denied: channel [channel-id] creator org unknown, creator is malformed"))
   458  		})
   459  	})
   460  
   461  	Context("when the identity cannot be deserialized", func() {
   462  		BeforeEach(func() {
   463  			fakeIdentityDeserializer.DeserializeIdentityReturns(nil, fmt.Errorf("fake-deserializing-error"))
   464  		})
   465  
   466  		It("returns a generic auth error", func() {
   467  			err := up.Validate(fakeIdentityDeserializer)
   468  			Expect(err).To(MatchError("access denied: channel [channel-id] creator org [mspid]"))
   469  		})
   470  	})
   471  
   472  	Context("when the identity is not valid", func() {
   473  		BeforeEach(func() {
   474  			fakeIdentity.ValidateReturns(fmt.Errorf("fake-validate-error"))
   475  		})
   476  
   477  		It("returns a generic auth error", func() {
   478  			err := up.Validate(fakeIdentityDeserializer)
   479  			Expect(err).To(MatchError("access denied: channel [channel-id] creator org [mspid]"))
   480  		})
   481  	})
   482  
   483  	Context("when the identity signature is not valid", func() {
   484  		BeforeEach(func() {
   485  			fakeIdentity.VerifyReturns(fmt.Errorf("fake-verify-error"))
   486  		})
   487  
   488  		It("returns a generic auth error", func() {
   489  			err := up.Validate(fakeIdentityDeserializer)
   490  			Expect(err).To(MatchError("access denied: channel [channel-id] creator org [mspid]"))
   491  		})
   492  	})
   493  })