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 })