github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/endorser/endorser_test.go (about) 1 /* 2 Copyright hechain. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package endorser_test 8 9 import ( 10 "context" 11 "fmt" 12 "sort" 13 14 "github.com/golang/protobuf/proto" 15 "github.com/hechain20/hechain/common/metrics/metricsfakes" 16 "github.com/hechain20/hechain/core/chaincode/lifecycle" 17 "github.com/hechain20/hechain/core/endorser" 18 "github.com/hechain20/hechain/core/endorser/fake" 19 "github.com/hechain20/hechain/core/ledger" 20 ledgermock "github.com/hechain20/hechain/core/ledger/mock" 21 "github.com/hechain20/hechain/protoutil" 22 cb "github.com/hyperledger/fabric-protos-go/common" 23 "github.com/hyperledger/fabric-protos-go/ledger/rwset" 24 "github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset" 25 mspproto "github.com/hyperledger/fabric-protos-go/msp" 26 pb "github.com/hyperledger/fabric-protos-go/peer" 27 . "github.com/onsi/ginkgo" 28 . "github.com/onsi/gomega" 29 "github.com/pkg/errors" 30 ) 31 32 type CcInterest pb.ChaincodeInterest 33 34 func (a CcInterest) Len() int { return len(a.Chaincodes) } 35 func (a CcInterest) Swap(i, j int) { 36 a.Chaincodes[i], a.Chaincodes[j] = a.Chaincodes[j], a.Chaincodes[i] 37 } 38 39 func (a CcInterest) Less(i, j int) bool { 40 ai := a.Chaincodes[i] 41 aj := a.Chaincodes[j] 42 43 if ai.Name != aj.Name { 44 return ai.Name < aj.Name 45 } 46 47 if len(ai.CollectionNames) != len(aj.CollectionNames) { 48 return len(ai.CollectionNames) < len(aj.CollectionNames) 49 } 50 51 for ii := range ai.CollectionNames { 52 if ai.CollectionNames[ii] != aj.CollectionNames[ii] { 53 return ai.CollectionNames[ii] < aj.CollectionNames[ii] 54 } 55 } 56 57 return false 58 } 59 60 var _ = Describe("Endorser", func() { 61 var ( 62 fakeProposalDuration *metricsfakes.Histogram 63 fakeProposalsReceived *metricsfakes.Counter 64 fakeSuccessfulProposals *metricsfakes.Counter 65 fakeProposalValidationFailed *metricsfakes.Counter 66 fakeProposalACLCheckFailed *metricsfakes.Counter 67 fakeInitFailed *metricsfakes.Counter 68 fakeEndorsementsFailed *metricsfakes.Counter 69 fakeDuplicateTxsFailure *metricsfakes.Counter 70 fakeSimulateFailure *metricsfakes.Counter 71 72 fakeLocalIdentity *fake.Identity 73 fakeLocalMSPIdentityDeserializer *fake.IdentityDeserializer 74 75 fakeChannelIdentity *fake.Identity 76 fakeChannelMSPIdentityDeserializer *fake.IdentityDeserializer 77 78 fakeChannelFetcher *fake.ChannelFetcher 79 80 fakePrivateDataDistributor *fake.PrivateDataDistributor 81 82 fakeSupport *fake.Support 83 fakeTxSimulator *fake.TxSimulator 84 fakeHistoryQueryExecutor *fake.HistoryQueryExecutor 85 86 signedProposal *pb.SignedProposal 87 channelID string 88 chaincodeName string 89 90 chaincodeResponse *pb.Response 91 chaincodeEvent *pb.ChaincodeEvent 92 chaincodeInput *pb.ChaincodeInput 93 94 e *endorser.Endorser 95 ) 96 97 BeforeEach(func() { 98 fakeProposalDuration = &metricsfakes.Histogram{} 99 fakeProposalDuration.WithReturns(fakeProposalDuration) 100 101 fakeProposalACLCheckFailed = &metricsfakes.Counter{} 102 fakeProposalACLCheckFailed.WithReturns(fakeProposalACLCheckFailed) 103 104 fakeInitFailed = &metricsfakes.Counter{} 105 fakeInitFailed.WithReturns(fakeInitFailed) 106 107 fakeEndorsementsFailed = &metricsfakes.Counter{} 108 fakeEndorsementsFailed.WithReturns(fakeEndorsementsFailed) 109 110 fakeDuplicateTxsFailure = &metricsfakes.Counter{} 111 fakeDuplicateTxsFailure.WithReturns(fakeDuplicateTxsFailure) 112 113 fakeProposalsReceived = &metricsfakes.Counter{} 114 fakeSuccessfulProposals = &metricsfakes.Counter{} 115 fakeProposalValidationFailed = &metricsfakes.Counter{} 116 117 fakeSimulateFailure = &metricsfakes.Counter{} 118 fakeSimulateFailure.WithReturns(fakeSimulateFailure) 119 120 fakeLocalIdentity = &fake.Identity{} 121 fakeLocalMSPIdentityDeserializer = &fake.IdentityDeserializer{} 122 fakeLocalMSPIdentityDeserializer.DeserializeIdentityReturns(fakeLocalIdentity, nil) 123 124 fakeChannelIdentity = &fake.Identity{} 125 fakeChannelMSPIdentityDeserializer = &fake.IdentityDeserializer{} 126 fakeChannelMSPIdentityDeserializer.DeserializeIdentityReturns(fakeChannelIdentity, nil) 127 128 fakeChannelFetcher = &fake.ChannelFetcher{} 129 fakeChannelFetcher.ChannelReturns(&endorser.Channel{ 130 IdentityDeserializer: fakeChannelMSPIdentityDeserializer, 131 }) 132 133 fakePrivateDataDistributor = &fake.PrivateDataDistributor{} 134 135 channelID = "channel-id" 136 chaincodeName = "chaincode-name" 137 chaincodeInput = &pb.ChaincodeInput{ 138 Args: [][]byte{[]byte("arg1"), []byte("arg2"), []byte("arg3")}, 139 } 140 141 chaincodeResponse = &pb.Response{ 142 Status: 200, 143 Payload: []byte("response-payload"), 144 } 145 chaincodeEvent = &pb.ChaincodeEvent{ 146 ChaincodeId: "chaincode-id", 147 TxId: "event-txid", 148 EventName: "event-name", 149 Payload: []byte("event-payload"), 150 } 151 152 fakeSupport = &fake.Support{} 153 fakeSupport.ExecuteReturns( 154 chaincodeResponse, 155 chaincodeEvent, 156 nil, 157 ) 158 159 fakeSupport.ChaincodeEndorsementInfoReturns(&lifecycle.ChaincodeEndorsementInfo{ 160 Version: "chaincode-definition-version", 161 EndorsementPlugin: "plugin-name", 162 }, nil) 163 164 fakeSupport.GetLedgerHeightReturns(7, nil) 165 166 fakeSupport.EndorseWithPluginReturns( 167 &pb.Endorsement{ 168 Endorser: []byte("endorser-identity"), 169 Signature: []byte("endorser-signature"), 170 }, 171 []byte("endorser-modified-payload"), 172 nil, 173 ) 174 175 fakeTxSimulator = &fake.TxSimulator{} 176 fakeTxSimulator.GetTxSimulationResultsReturns( 177 &ledger.TxSimulationResults{ 178 PubSimulationResults: &rwset.TxReadWriteSet{}, 179 PvtSimulationResults: &rwset.TxPvtReadWriteSet{}, 180 }, 181 nil, 182 ) 183 184 fakeHistoryQueryExecutor = &fake.HistoryQueryExecutor{} 185 fakeSupport.GetHistoryQueryExecutorReturns(fakeHistoryQueryExecutor, nil) 186 187 fakeSupport.GetTxSimulatorReturns(fakeTxSimulator, nil) 188 189 fakeSupport.GetTransactionByIDReturns(nil, fmt.Errorf("txid-error")) 190 191 e = &endorser.Endorser{ 192 LocalMSP: fakeLocalMSPIdentityDeserializer, 193 PrivateDataDistributor: fakePrivateDataDistributor, 194 Metrics: &endorser.Metrics{ 195 ProposalDuration: fakeProposalDuration, 196 ProposalsReceived: fakeProposalsReceived, 197 SuccessfulProposals: fakeSuccessfulProposals, 198 ProposalValidationFailed: fakeProposalValidationFailed, 199 ProposalACLCheckFailed: fakeProposalACLCheckFailed, 200 InitFailed: fakeInitFailed, 201 EndorsementsFailed: fakeEndorsementsFailed, 202 DuplicateTxsFailure: fakeDuplicateTxsFailure, 203 SimulationFailure: fakeSimulateFailure, 204 }, 205 Support: fakeSupport, 206 ChannelFetcher: fakeChannelFetcher, 207 } 208 }) 209 210 JustBeforeEach(func() { 211 signedProposal = &pb.SignedProposal{ 212 ProposalBytes: protoutil.MarshalOrPanic(&pb.Proposal{ 213 Header: protoutil.MarshalOrPanic(&cb.Header{ 214 ChannelHeader: protoutil.MarshalOrPanic(&cb.ChannelHeader{ 215 Type: int32(cb.HeaderType_ENDORSER_TRANSACTION), 216 ChannelId: channelID, 217 Extension: protoutil.MarshalOrPanic(&pb.ChaincodeHeaderExtension{ 218 ChaincodeId: &pb.ChaincodeID{ 219 Name: chaincodeName, 220 }, 221 }), 222 TxId: "6f142589e4ef6a1e62c9c816e2074f70baa9f7cf67c2f0c287d4ef907d6d2015", 223 }), 224 SignatureHeader: protoutil.MarshalOrPanic(&cb.SignatureHeader{ 225 Creator: protoutil.MarshalOrPanic(&mspproto.SerializedIdentity{ 226 Mspid: "msp-id", 227 }), 228 Nonce: []byte("nonce"), 229 }), 230 }), 231 Payload: protoutil.MarshalOrPanic(&pb.ChaincodeProposalPayload{ 232 Input: protoutil.MarshalOrPanic(&pb.ChaincodeInvocationSpec{ 233 ChaincodeSpec: &pb.ChaincodeSpec{ 234 Input: chaincodeInput, 235 }, 236 }), 237 }), 238 }), 239 Signature: []byte("signature"), 240 } 241 }) 242 243 It("successfully endorses the proposal", func() { 244 proposalResponse, err := e.ProcessProposal(context.Background(), signedProposal) 245 Expect(err).NotTo(HaveOccurred()) 246 Expect(proposalResponse.Endorsement).To(Equal(&pb.Endorsement{ 247 Endorser: []byte("endorser-identity"), 248 Signature: []byte("endorser-signature"), 249 })) 250 Expect(proposalResponse.Timestamp).To(BeNil()) 251 Expect(proposalResponse.Version).To(Equal(int32(1))) 252 Expect(proposalResponse.Payload).To(Equal([]byte("endorser-modified-payload"))) 253 Expect(proto.Equal(proposalResponse.Response, &pb.Response{ 254 Status: 200, 255 Payload: []byte("response-payload"), 256 })).To(BeTrue()) 257 258 Expect(fakeSupport.EndorseWithPluginCallCount()).To(Equal(1)) 259 pluginName, cid, propRespPayloadBytes, sp := fakeSupport.EndorseWithPluginArgsForCall(0) 260 Expect(sp).To(Equal(signedProposal)) 261 Expect(pluginName).To(Equal("plugin-name")) 262 Expect(cid).To(Equal("channel-id")) 263 264 prp := &pb.ProposalResponsePayload{} 265 err = proto.Unmarshal(propRespPayloadBytes, prp) 266 Expect(err).NotTo(HaveOccurred()) 267 Expect(fmt.Sprintf("%x", prp.ProposalHash)).To(Equal("6fa450b00ebef6c7de9f3479148f6d6ff2c645762e17fcaae989ff7b668be001")) 268 269 ccAct := &pb.ChaincodeAction{} 270 err = proto.Unmarshal(prp.Extension, ccAct) 271 Expect(err).NotTo(HaveOccurred()) 272 Expect(ccAct.Events).To(Equal(protoutil.MarshalOrPanic(chaincodeEvent))) 273 Expect(proto.Equal(ccAct.Response, &pb.Response{ 274 Status: 200, 275 Payload: []byte("response-payload"), 276 })).To(BeTrue()) 277 Expect(fakeSupport.GetHistoryQueryExecutorCallCount()).To(Equal(1)) 278 ledgerName := fakeSupport.GetHistoryQueryExecutorArgsForCall(0) 279 Expect(ledgerName).To(Equal("channel-id")) 280 }) 281 282 Context("when the chaincode endorsement fails", func() { 283 BeforeEach(func() { 284 fakeSupport.EndorseWithPluginReturns(nil, nil, fmt.Errorf("fake-endorserment-error")) 285 }) 286 287 It("returns the error, but with no payload encoded", func() { 288 proposalResponse, err := e.ProcessProposal(context.Background(), signedProposal) 289 Expect(err).NotTo(HaveOccurred()) 290 Expect(proposalResponse.Payload).To(BeNil()) 291 Expect(proposalResponse.Response).To(Equal(&pb.Response{ 292 Status: 500, 293 Message: "endorsing with plugin failed: fake-endorserment-error", 294 })) 295 }) 296 }) 297 298 It("checks for duplicate transactions", func() { 299 _, err := e.ProcessProposal(context.Background(), signedProposal) 300 Expect(err).NotTo(HaveOccurred()) 301 Expect(fakeSupport.GetTransactionByIDCallCount()).To(Equal(1)) 302 channelID, txid := fakeSupport.GetTransactionByIDArgsForCall(0) 303 Expect(channelID).To(Equal("channel-id")) 304 Expect(txid).To(Equal("6f142589e4ef6a1e62c9c816e2074f70baa9f7cf67c2f0c287d4ef907d6d2015")) 305 }) 306 307 Context("when the txid is duplicated", func() { 308 BeforeEach(func() { 309 fakeSupport.GetTransactionByIDReturns(nil, nil) 310 }) 311 312 It("wraps and returns an error and responds to the client", func() { 313 proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal) 314 Expect(err).To(MatchError("duplicate transaction found [6f142589e4ef6a1e62c9c816e2074f70baa9f7cf67c2f0c287d4ef907d6d2015]. Creator [0a066d73702d6964]")) 315 Expect(proposalResponse).To(Equal(&pb.ProposalResponse{ 316 Response: &pb.Response{ 317 Status: 500, 318 Message: "duplicate transaction found [6f142589e4ef6a1e62c9c816e2074f70baa9f7cf67c2f0c287d4ef907d6d2015]. Creator [0a066d73702d6964]", 319 }, 320 })) 321 }) 322 }) 323 324 It("gets a transaction simulator", func() { 325 _, err := e.ProcessProposal(context.Background(), signedProposal) 326 Expect(err).NotTo(HaveOccurred()) 327 Expect(fakeSupport.GetTxSimulatorCallCount()).To(Equal(1)) 328 ledgerName, txid := fakeSupport.GetTxSimulatorArgsForCall(0) 329 Expect(ledgerName).To(Equal("channel-id")) 330 Expect(txid).To(Equal("6f142589e4ef6a1e62c9c816e2074f70baa9f7cf67c2f0c287d4ef907d6d2015")) 331 }) 332 333 Context("when getting the tx simulator fails", func() { 334 BeforeEach(func() { 335 fakeSupport.GetTxSimulatorReturns(nil, fmt.Errorf("fake-simulator-error")) 336 }) 337 338 It("returns a response with the error", func() { 339 proposalResponse, err := e.ProcessProposal(context.Background(), signedProposal) 340 Expect(err).NotTo(HaveOccurred()) 341 Expect(proposalResponse.Payload).To(BeNil()) 342 Expect(proposalResponse.Response).To(Equal(&pb.Response{ 343 Status: 500, 344 Message: "fake-simulator-error", 345 })) 346 }) 347 }) 348 349 It("gets a history query executor", func() { 350 _, err := e.ProcessProposal(context.Background(), signedProposal) 351 Expect(err).NotTo(HaveOccurred()) 352 Expect(fakeSupport.GetHistoryQueryExecutorCallCount()).To(Equal(1)) 353 ledgerName := fakeSupport.GetHistoryQueryExecutorArgsForCall(0) 354 Expect(ledgerName).To(Equal("channel-id")) 355 }) 356 357 Context("when getting the history query executor fails", func() { 358 BeforeEach(func() { 359 fakeSupport.GetHistoryQueryExecutorReturns(nil, fmt.Errorf("fake-history-error")) 360 }) 361 362 It("returns a response with the error", func() { 363 proposalResponse, err := e.ProcessProposal(context.Background(), signedProposal) 364 Expect(err).NotTo(HaveOccurred()) 365 Expect(proposalResponse.Payload).To(BeNil()) 366 Expect(proposalResponse.Response).To(Equal(&pb.Response{ 367 Status: 500, 368 Message: "fake-history-error", 369 })) 370 }) 371 }) 372 373 It("gets the channel context", func() { 374 _, err := e.ProcessProposal(context.Background(), signedProposal) 375 Expect(err).NotTo(HaveOccurred()) 376 Expect(fakeChannelFetcher.ChannelCallCount()).To(Equal(1)) 377 channelID := fakeChannelFetcher.ChannelArgsForCall(0) 378 Expect(channelID).To(Equal("channel-id")) 379 }) 380 381 Context("when the channel context cannot be retrieved", func() { 382 BeforeEach(func() { 383 fakeChannelFetcher.ChannelReturns(nil) 384 }) 385 386 It("returns a response with the error", func() { 387 proposalResponse, err := e.ProcessProposal(context.Background(), signedProposal) 388 Expect(err).NotTo(HaveOccurred()) 389 Expect(proposalResponse.Payload).To(BeNil()) 390 Expect(proposalResponse.Response).To(Equal(&pb.Response{ 391 Status: 500, 392 Message: "channel 'channel-id' not found", 393 })) 394 }) 395 }) 396 397 It("checks the submitter's identity", func() { 398 _, err := e.ProcessProposal(context.Background(), signedProposal) 399 Expect(err).NotTo(HaveOccurred()) 400 Expect(fakeChannelMSPIdentityDeserializer.DeserializeIdentityCallCount()).To(Equal(1)) 401 identity := fakeChannelMSPIdentityDeserializer.DeserializeIdentityArgsForCall(0) 402 Expect(identity).To(Equal(protoutil.MarshalOrPanic(&mspproto.SerializedIdentity{ 403 Mspid: "msp-id", 404 }))) 405 406 Expect(fakeLocalMSPIdentityDeserializer.DeserializeIdentityCallCount()).To(Equal(0)) 407 }) 408 409 Context("when the proposal is not validly signed", func() { 410 BeforeEach(func() { 411 fakeChannelMSPIdentityDeserializer.DeserializeIdentityReturns(nil, fmt.Errorf("fake-deserialize-error")) 412 }) 413 414 It("wraps and returns an error and responds to the client", func() { 415 proposalResponse, err := e.ProcessProposal(context.Background(), signedProposal) 416 Expect(err).To(MatchError("error validating proposal: access denied: channel [channel-id] creator org unknown, creator is malformed")) 417 Expect(proposalResponse).To(Equal(&pb.ProposalResponse{ 418 Response: &pb.Response{ 419 Status: 500, 420 Message: "error validating proposal: access denied: channel [channel-id] creator org unknown, creator is malformed", 421 }, 422 })) 423 }) 424 }) 425 426 It("checks the ACLs for the identity", func() { 427 _, err := e.ProcessProposal(context.Background(), signedProposal) 428 Expect(err).NotTo(HaveOccurred()) 429 Expect(fakeProposalACLCheckFailed.WithCallCount()).To(Equal(0)) 430 Expect(fakeInitFailed.WithCallCount()).To(Equal(0)) 431 Expect(fakeEndorsementsFailed.WithCallCount()).To(Equal(0)) 432 Expect(fakeDuplicateTxsFailure.WithCallCount()).To(Equal(0)) 433 }) 434 435 Context("when the acl check fails", func() { 436 BeforeEach(func() { 437 fakeSupport.CheckACLReturns(fmt.Errorf("fake-acl-error")) 438 }) 439 440 It("wraps and returns an error and responds to the client", func() { 441 proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal) 442 Expect(err).To(MatchError("fake-acl-error")) 443 Expect(proposalResponse).To(Equal(&pb.ProposalResponse{ 444 Response: &pb.Response{ 445 Status: 500, 446 Message: "fake-acl-error", 447 }, 448 })) 449 }) 450 451 Context("when it's for a system chaincode", func() { 452 BeforeEach(func() { 453 fakeSupport.IsSysCCReturns(true) 454 }) 455 456 It("skips the acl check", func() { 457 proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal) 458 Expect(err).NotTo(HaveOccurred()) 459 Expect(proposalResponse.Response.Status).To(Equal(int32(200))) 460 }) 461 }) 462 }) 463 464 It("gets the chaincode definition", func() { 465 _, err := e.ProcessProposal(context.Background(), signedProposal) 466 Expect(err).NotTo(HaveOccurred()) 467 Expect(fakeSupport.ChaincodeEndorsementInfoCallCount()).To(Equal(1)) 468 channelID, chaincodeName, txSim := fakeSupport.ChaincodeEndorsementInfoArgsForCall(0) 469 Expect(channelID).To(Equal("channel-id")) 470 Expect(chaincodeName).To(Equal("chaincode-name")) 471 Expect(txSim).To(Equal(fakeTxSimulator)) 472 }) 473 474 Context("when the chaincode definition is not found", func() { 475 BeforeEach(func() { 476 fakeSupport.ChaincodeEndorsementInfoReturns(nil, fmt.Errorf("fake-definition-error")) 477 }) 478 479 It("returns an error in the response", func() { 480 proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal) 481 Expect(err).NotTo(HaveOccurred()) 482 Expect(proposalResponse.Response).To(Equal(&pb.Response{ 483 Status: 500, 484 Message: "make sure the chaincode chaincode-name has been successfully defined on channel channel-id and try again: fake-definition-error", 485 })) 486 }) 487 }) 488 489 It("calls the chaincode", func() { 490 _, err := e.ProcessProposal(context.Background(), signedProposal) 491 Expect(err).NotTo(HaveOccurred()) 492 Expect(fakeSupport.ExecuteCallCount()).To(Equal(1)) 493 txParams, chaincodeName, input := fakeSupport.ExecuteArgsForCall(0) 494 Expect(txParams.ChannelID).To(Equal("channel-id")) 495 Expect(txParams.SignedProp).To(Equal(signedProposal)) 496 Expect(txParams.TXSimulator).To(Equal(fakeTxSimulator)) 497 Expect(txParams.HistoryQueryExecutor).To(Equal(fakeHistoryQueryExecutor)) 498 Expect(chaincodeName).To(Equal("chaincode-name")) 499 Expect(proto.Equal(input, &pb.ChaincodeInput{ 500 Args: [][]byte{[]byte("arg1"), []byte("arg2"), []byte("arg3")}, 501 })).To(BeTrue()) 502 }) 503 504 Context("when calling the chaincode returns an error", func() { 505 BeforeEach(func() { 506 fakeSupport.ExecuteReturns(nil, nil, fmt.Errorf("fake-chaincode-execution-error")) 507 }) 508 509 It("returns a response with the error and no payload", func() { 510 proposalResponse, err := e.ProcessProposal(context.Background(), signedProposal) 511 Expect(err).NotTo(HaveOccurred()) 512 Expect(proposalResponse.Payload).To(BeNil()) 513 Expect(proposalResponse.Response).To(Equal(&pb.Response{ 514 Status: 500, 515 Message: "error in simulation: fake-chaincode-execution-error", 516 })) 517 Expect(fakeSimulateFailure.AddCallCount()).To(Equal(1)) 518 }) 519 }) 520 521 It("distributes private data", func() { 522 _, err := e.ProcessProposal(context.Background(), signedProposal) 523 Expect(err).NotTo(HaveOccurred()) 524 Expect(fakePrivateDataDistributor.DistributePrivateDataCallCount()).To(Equal(1)) 525 cid, txid, privateData, blkHt := fakePrivateDataDistributor.DistributePrivateDataArgsForCall(0) 526 Expect(cid).To(Equal("channel-id")) 527 Expect(txid).To(Equal("6f142589e4ef6a1e62c9c816e2074f70baa9f7cf67c2f0c287d4ef907d6d2015")) 528 Expect(blkHt).To(Equal(uint64(7))) 529 530 // TODO, this deserves a better test, but there was none before and this logic, 531 // really seems far too jumbled to be in the endorser package. There are separate 532 // tests of the private data assembly functions in their test file. 533 Expect(privateData).NotTo(BeNil()) 534 Expect(privateData.EndorsedAt).To(Equal(uint64(7))) 535 }) 536 537 Context("when the private data cannot be distributed", func() { 538 BeforeEach(func() { 539 fakePrivateDataDistributor.DistributePrivateDataReturns(fmt.Errorf("fake-private-data-error")) 540 }) 541 542 It("returns a response with the error and no payload", func() { 543 proposalResponse, err := e.ProcessProposal(context.Background(), signedProposal) 544 Expect(err).NotTo(HaveOccurred()) 545 Expect(proposalResponse.Payload).To(BeNil()) 546 Expect(proposalResponse.Response).To(Equal(&pb.Response{ 547 Status: 500, 548 Message: "error in simulation: fake-private-data-error", 549 })) 550 Expect(fakeSimulateFailure.AddCallCount()).To(Equal(1)) 551 }) 552 }) 553 554 It("checks the block height", func() { 555 _, err := e.ProcessProposal(context.Background(), signedProposal) 556 Expect(err).NotTo(HaveOccurred()) 557 Expect(fakeSupport.GetLedgerHeightCallCount()).To(Equal(1)) 558 }) 559 560 Context("when the block height cannot be determined", func() { 561 BeforeEach(func() { 562 fakeSupport.GetLedgerHeightReturns(0, fmt.Errorf("fake-block-height-error")) 563 }) 564 565 It("returns a response with the error and no payload", func() { 566 proposalResponse, err := e.ProcessProposal(context.Background(), signedProposal) 567 Expect(err).NotTo(HaveOccurred()) 568 Expect(proposalResponse.Payload).To(BeNil()) 569 Expect(proposalResponse.Response).To(Equal(&pb.Response{ 570 Status: 500, 571 Message: "error in simulation: failed to obtain ledger height for channel 'channel-id': fake-block-height-error", 572 })) 573 574 Expect(fakeSimulateFailure.AddCallCount()).To(Equal(1)) 575 }) 576 }) 577 578 It("records metrics about the proposal processing", func() { 579 _, err := e.ProcessProposal(context.Background(), signedProposal) 580 Expect(err).NotTo(HaveOccurred()) 581 582 Expect(fakeProposalsReceived.AddCallCount()).To(Equal(1)) 583 Expect(fakeSuccessfulProposals.AddCallCount()).To(Equal(1)) 584 Expect(fakeProposalValidationFailed.AddCallCount()).To(Equal(0)) 585 586 Expect(fakeProposalDuration.WithCallCount()).To(Equal(1)) 587 Expect(fakeProposalDuration.WithArgsForCall(0)).To(Equal([]string{ 588 "channel", "channel-id", 589 "chaincode", "chaincode-name", 590 "success", "true", 591 })) 592 }) 593 594 Context("when the channel id is empty", func() { 595 BeforeEach(func() { 596 channelID = "" 597 }) 598 599 It("returns a successful proposal response with no endorsement", func() { 600 proposalResponse, err := e.ProcessProposal(context.Background(), signedProposal) 601 Expect(err).NotTo(HaveOccurred()) 602 Expect(proposalResponse.Endorsement).To(BeNil()) 603 Expect(proposalResponse.Timestamp).To(BeNil()) 604 Expect(proposalResponse.Version).To(Equal(int32(0))) 605 Expect(proposalResponse.Payload).To(BeNil()) 606 Expect(proto.Equal(proposalResponse.Response, &pb.Response{ 607 Status: 200, 608 Payload: []byte("response-payload"), 609 })).To(BeTrue()) 610 }) 611 612 It("does not attempt to get a history query executor", func() { 613 _, err := e.ProcessProposal(context.Background(), signedProposal) 614 Expect(err).NotTo(HaveOccurred()) 615 Expect(fakeSupport.GetHistoryQueryExecutorCallCount()).To(Equal(0)) 616 }) 617 618 It("does not attempt to deduplicate the txid", func() { 619 _, err := e.ProcessProposal(context.Background(), signedProposal) 620 Expect(err).NotTo(HaveOccurred()) 621 Expect(fakeSupport.GetTransactionByIDCallCount()).To(Equal(0)) 622 }) 623 624 It("does not attempt to get a tx simulator", func() { 625 _, err := e.ProcessProposal(context.Background(), signedProposal) 626 Expect(err).NotTo(HaveOccurred()) 627 Expect(fakeSupport.GetTxSimulatorCallCount()).To(Equal(0)) 628 }) 629 630 It("uses the local MSP to authorize the creator", func() { 631 _, err := e.ProcessProposal(context.Background(), signedProposal) 632 Expect(err).NotTo(HaveOccurred()) 633 Expect(fakeChannelMSPIdentityDeserializer.DeserializeIdentityCallCount()).To(Equal(0)) 634 635 Expect(fakeLocalMSPIdentityDeserializer.DeserializeIdentityCallCount()).To(Equal(1)) 636 identity := fakeLocalMSPIdentityDeserializer.DeserializeIdentityArgsForCall(0) 637 Expect(identity).To(Equal(protoutil.MarshalOrPanic(&mspproto.SerializedIdentity{ 638 Mspid: "msp-id", 639 }))) 640 }) 641 642 Context("when the proposal is not validly signed", func() { 643 BeforeEach(func() { 644 fakeLocalMSPIdentityDeserializer.DeserializeIdentityReturns(nil, fmt.Errorf("fake-deserialize-error")) 645 }) 646 647 It("wraps and returns an error and responds to the client", func() { 648 proposalResponse, err := e.ProcessProposal(context.Background(), signedProposal) 649 Expect(err).To(MatchError("error validating proposal: access denied: channel [] creator org unknown, creator is malformed")) 650 Expect(proposalResponse).To(Equal(&pb.ProposalResponse{ 651 Response: &pb.Response{ 652 Status: 500, 653 Message: "error validating proposal: access denied: channel [] creator org unknown, creator is malformed", 654 }, 655 })) 656 }) 657 }) 658 659 It("records metrics but without a channel ID set", func() { 660 _, err := e.ProcessProposal(context.Background(), signedProposal) 661 Expect(err).NotTo(HaveOccurred()) 662 Expect(fakeProposalsReceived.AddCallCount()).To(Equal(1)) 663 Expect(fakeSuccessfulProposals.AddCallCount()).To(Equal(1)) 664 Expect(fakeProposalValidationFailed.AddCallCount()).To(Equal(0)) 665 666 Expect(fakeProposalDuration.WithCallCount()).To(Equal(1)) 667 Expect(fakeProposalDuration.WithArgsForCall(0)).To(Equal([]string{ 668 "channel", "", 669 "chaincode", "chaincode-name", 670 "success", "true", 671 })) 672 Expect(fakeProposalACLCheckFailed.WithCallCount()).To(Equal(0)) 673 Expect(fakeInitFailed.WithCallCount()).To(Equal(0)) 674 Expect(fakeEndorsementsFailed.WithCallCount()).To(Equal(0)) 675 Expect(fakeDuplicateTxsFailure.WithCallCount()).To(Equal(0)) 676 }) 677 678 Context("when the chaincode response is >= 500", func() { 679 BeforeEach(func() { 680 chaincodeResponse.Status = 500 681 }) 682 683 It("returns the result, but with the proposal encoded, and no endorsements", func() { 684 proposalResponse, err := e.ProcessProposal(context.Background(), signedProposal) 685 Expect(err).NotTo(HaveOccurred()) 686 Expect(proposalResponse.Endorsement).To(BeNil()) 687 Expect(proposalResponse.Timestamp).To(BeNil()) 688 Expect(proposalResponse.Version).To(Equal(int32(0))) 689 Expect(proto.Equal(proposalResponse.Response, &pb.Response{ 690 Status: 500, 691 Payload: []byte("response-payload"), 692 })).To(BeTrue()) 693 694 // This is almost definitely a bug, but, adding a test in case someone is relying on this behavior. 695 // When the response is >= 500, we return a payload, but not on success. A payload is only meaningful 696 // if it is endorsed, so it's unclear why we're returning it here. 697 prp := &pb.ProposalResponsePayload{} 698 err = proto.Unmarshal(proposalResponse.Payload, prp) 699 Expect(err).NotTo(HaveOccurred()) 700 Expect(fmt.Sprintf("%x", prp.ProposalHash)).To(Equal("f2c27f04f897dc28fd1b2983e7b22ebc8fbbb3d0617c140d913b33e463886788")) 701 702 ccAct := &pb.ChaincodeAction{} 703 err = proto.Unmarshal(prp.Extension, ccAct) 704 Expect(err).NotTo(HaveOccurred()) 705 Expect(proto.Equal(ccAct.Response, &pb.Response{ 706 Status: 500, 707 Payload: []byte("response-payload"), 708 })).To(BeTrue()) 709 710 // This is an especially weird bit of the behavior, the chaincode event is nil-ed before creating 711 // the proposal response. (That probably shouldn't be created) 712 Expect(ccAct.Events).To(BeNil()) 713 }) 714 }) 715 716 Context("when the 200 < chaincode response < 500", func() { 717 BeforeEach(func() { 718 chaincodeResponse.Status = 499 719 }) 720 721 It("returns the result, but with the proposal encoded, and no endorsements", func() { 722 proposalResponse, err := e.ProcessProposal(context.Background(), signedProposal) 723 Expect(err).NotTo(HaveOccurred()) 724 Expect(proposalResponse.Endorsement).To(BeNil()) 725 Expect(proposalResponse.Timestamp).To(BeNil()) 726 Expect(proposalResponse.Version).To(Equal(int32(0))) 727 Expect(proto.Equal(proposalResponse.Response, &pb.Response{ 728 Status: 499, 729 Payload: []byte("response-payload"), 730 })).To(BeTrue()) 731 Expect(proposalResponse.Payload).To(BeNil()) 732 }) 733 }) 734 }) 735 736 Context("when the proposal is malformed", func() { 737 JustBeforeEach(func() { 738 signedProposal = &pb.SignedProposal{ 739 ProposalBytes: []byte("garbage"), 740 } 741 }) 742 743 It("wraps and returns an error and responds to the client", func() { 744 proposalResponse, err := e.ProcessProposal(context.Background(), signedProposal) 745 Expect(err).To(MatchError("error unmarshalling Proposal: proto: can't skip unknown wire type 7")) 746 Expect(proposalResponse).To(Equal(&pb.ProposalResponse{ 747 Response: &pb.Response{ 748 Status: 500, 749 Message: "error unmarshalling Proposal: proto: can't skip unknown wire type 7", 750 }, 751 })) 752 }) 753 }) 754 755 Context("when the chaincode response is >= 500", func() { 756 BeforeEach(func() { 757 chaincodeResponse.Status = 500 758 }) 759 760 It("returns the result, but with the proposal encoded, and no endorsements", func() { 761 proposalResponse, err := e.ProcessProposal(context.Background(), signedProposal) 762 Expect(err).NotTo(HaveOccurred()) 763 Expect(proposalResponse.Endorsement).To(BeNil()) 764 Expect(proposalResponse.Timestamp).To(BeNil()) 765 Expect(proposalResponse.Version).To(Equal(int32(0))) 766 Expect(proto.Equal(proposalResponse.Response, &pb.Response{ 767 Status: 500, 768 Payload: []byte("response-payload"), 769 })).To(BeTrue()) 770 Expect(proposalResponse.Payload).NotTo(BeNil()) 771 }) 772 }) 773 774 Context("when the chaincode name is qscc", func() { 775 BeforeEach(func() { 776 chaincodeName = "qscc" 777 }) 778 779 It("skips fetching the tx simulator and history query exucutor", func() { 780 _, err := e.ProcessProposal(context.Background(), signedProposal) 781 Expect(err).NotTo(HaveOccurred()) 782 Expect(fakeSupport.GetTxSimulatorCallCount()).To(Equal(0)) 783 Expect(fakeSupport.GetHistoryQueryExecutorCallCount()).To(Equal(0)) 784 }) 785 }) 786 787 Context("when the chaincode name is cscc", func() { 788 BeforeEach(func() { 789 chaincodeName = "cscc" 790 }) 791 792 It("skips fetching the tx simulator and history query exucutor", func() { 793 _, err := e.ProcessProposal(context.Background(), signedProposal) 794 Expect(err).NotTo(HaveOccurred()) 795 Expect(fakeSupport.GetTxSimulatorCallCount()).To(Equal(0)) 796 Expect(fakeSupport.GetHistoryQueryExecutorCallCount()).To(Equal(0)) 797 }) 798 }) 799 800 Context("when the chaincode response is >= 400 but < 500", func() { 801 BeforeEach(func() { 802 chaincodeResponse.Status = 400 803 }) 804 805 It("returns the response with no payload", func() { 806 proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal) 807 Expect(err).NotTo(HaveOccurred()) 808 Expect(proposalResponse.Payload).To(BeNil()) 809 Expect(proto.Equal(proposalResponse.Response, &pb.Response{ 810 Status: 400, 811 Payload: []byte("response-payload"), 812 })).To(BeTrue()) 813 }) 814 }) 815 816 Context("when we're in the degenerate legacy lifecycle case", func() { 817 BeforeEach(func() { 818 chaincodeName = "lscc" 819 chaincodeInput.Args = [][]byte{ 820 []byte("deploy"), 821 nil, 822 protoutil.MarshalOrPanic(&pb.ChaincodeDeploymentSpec{ 823 ChaincodeSpec: &pb.ChaincodeSpec{ 824 ChaincodeId: &pb.ChaincodeID{ 825 Name: "deploy-name", 826 Version: "deploy-version", 827 }, 828 Input: &pb.ChaincodeInput{ 829 Args: [][]byte{[]byte("target-arg")}, 830 }, 831 }, 832 }), 833 } 834 835 fakeTxSimulator.GetTxSimulationResultsReturns( 836 &ledger.TxSimulationResults{ 837 PubSimulationResults: &rwset.TxReadWriteSet{}, 838 // We don't return private data in this case because lscc forbids it 839 }, 840 nil, 841 ) 842 }) 843 844 It("triggers the legacy init, and returns the response from lscc", func() { 845 proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal) 846 Expect(err).NotTo(HaveOccurred()) 847 Expect(proto.Equal(proposalResponse.Response, &pb.Response{ 848 Status: 200, 849 Payload: []byte("response-payload"), 850 })).To(BeTrue()) 851 852 Expect(fakeSupport.ExecuteLegacyInitCallCount()).To(Equal(1)) 853 _, name, version, input := fakeSupport.ExecuteLegacyInitArgsForCall(0) 854 Expect(name).To(Equal("deploy-name")) 855 Expect(version).To(Equal("deploy-version")) 856 Expect(input.Args).To(Equal([][]byte{[]byte("target-arg")})) 857 }) 858 859 Context("when the chaincode spec contains a code package", func() { 860 BeforeEach(func() { 861 chaincodeInput.Args = [][]byte{ 862 []byte("deploy"), 863 nil, 864 protoutil.MarshalOrPanic(&pb.ChaincodeDeploymentSpec{ 865 ChaincodeSpec: &pb.ChaincodeSpec{ 866 ChaincodeId: &pb.ChaincodeID{ 867 Name: "deploy-name", 868 Version: "deploy-version", 869 }, 870 Input: &pb.ChaincodeInput{ 871 Args: [][]byte{[]byte("target-arg")}, 872 }, 873 }, 874 CodePackage: []byte("some-code"), 875 }), 876 } 877 }) 878 879 It("returns an error to the client", func() { 880 proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal) 881 Expect(err).NotTo(HaveOccurred()) 882 Expect(proposalResponse.Response).To(Equal(&pb.Response{ 883 Status: 500, 884 Message: "error in simulation: lscc upgrade/deploy should not include a code packages", 885 })) 886 Expect(fakeSimulateFailure.AddCallCount()).To(Equal(1)) 887 }) 888 }) 889 890 Context("when the simulation uses private data", func() { 891 BeforeEach(func() { 892 fakeTxSimulator.GetTxSimulationResultsReturns( 893 &ledger.TxSimulationResults{ 894 PubSimulationResults: &rwset.TxReadWriteSet{}, 895 PvtSimulationResults: &rwset.TxPvtReadWriteSet{}, 896 }, 897 nil, 898 ) 899 }) 900 901 It("returns an error to the client", func() { 902 proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal) 903 Expect(err).NotTo(HaveOccurred()) 904 Expect(proposalResponse.Response).To(Equal(&pb.Response{ 905 Status: 500, 906 Message: "error in simulation: Private data is forbidden to be used in instantiate", 907 })) 908 Expect(fakeSimulateFailure.AddCallCount()).To(Equal(1)) 909 }) 910 }) 911 912 Context("when retrieving simulation results", func() { 913 BeforeEach(func() { 914 fakeTxSimulator.GetTxSimulationResultsReturns( 915 &ledger.TxSimulationResults{ 916 PubSimulationResults: &rwset.TxReadWriteSet{}, 917 PvtSimulationResults: &rwset.TxPvtReadWriteSet{}, 918 }, 919 errors.New("bad simulation"), 920 ) 921 }) 922 923 It("returns an error to the client", func() { 924 proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal) 925 Expect(err).NotTo(HaveOccurred()) 926 Expect(proposalResponse.Response).To(Equal(&pb.Response{ 927 Status: 500, 928 Message: "error in simulation: bad simulation", 929 })) 930 Expect(fakeSimulateFailure.AddCallCount()).To(Equal(1)) 931 }) 932 }) 933 934 Context("when retrieving public simulation results fails", func() { 935 BeforeEach(func() { 936 fakeTxSimulator.GetTxSimulationResultsReturns( 937 &ledger.TxSimulationResults{}, 938 nil, 939 ) 940 }) 941 942 It("returns an error to the client", func() { 943 proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal) 944 Expect(err).NotTo(HaveOccurred()) 945 Expect(proposalResponse.Response).To(Equal(&pb.Response{ 946 Status: 500, 947 Message: "error in simulation: proto: Marshal called with nil", 948 })) 949 Expect(fakeSimulateFailure.AddCallCount()).To(Equal(1)) 950 }) 951 }) 952 953 Context("when the init fails", func() { 954 BeforeEach(func() { 955 fakeSupport.ExecuteLegacyInitReturns(nil, nil, fmt.Errorf("fake-legacy-init-error")) 956 }) 957 958 It("returns an error and increments the metric", func() { 959 proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal) 960 Expect(err).NotTo(HaveOccurred()) 961 Expect(proposalResponse.Response).To(Equal(&pb.Response{ 962 Status: 500, 963 Message: "error in simulation: fake-legacy-init-error", 964 })) 965 966 Expect(fakeInitFailed.WithCallCount()).To(Equal(1)) 967 Expect(fakeInitFailed.WithArgsForCall(0)).To(Equal([]string{ 968 "channel", "channel-id", 969 "chaincode", "deploy-name", 970 })) 971 Expect(fakeInitFailed.AddCallCount()).To(Equal(1)) 972 }) 973 }) 974 975 Context("when the deploying chaincode is the name of a builtin system chaincode", func() { 976 BeforeEach(func() { 977 fakeSupport.IsSysCCStub = func(name string) bool { 978 return name == "deploy-name" 979 } 980 }) 981 982 It("triggers the legacy init, and returns the response from lscc", func() { 983 proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal) 984 Expect(err).NotTo(HaveOccurred()) 985 Expect(proposalResponse.Response).To(Equal(&pb.Response{ 986 Status: 500, 987 Message: "error in simulation: attempting to deploy a system chaincode deploy-name/channel-id", 988 })) 989 Expect(fakeSimulateFailure.AddCallCount()).To(Equal(1)) 990 }) 991 }) 992 993 Context("when unmarshalling UnmarshalChaincodeDeploymentSpec fails", func() { 994 BeforeEach(func() { 995 chaincodeInput = &pb.ChaincodeInput{ 996 Args: [][]byte{[]byte("deploy"), nil, []byte("arg3")}, 997 } 998 }) 999 1000 It("returns an error to the client", func() { 1001 proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal) 1002 Expect(err).NotTo(HaveOccurred()) 1003 Expect(proposalResponse.Response).To(Equal(&pb.Response{ 1004 Status: 500, 1005 Message: "error in simulation: error unmarshalling ChaincodeDeploymentSpec: unexpected EOF", 1006 })) 1007 Expect(fakeSimulateFailure.AddCallCount()).To(Equal(1)) 1008 }) 1009 }) 1010 }) 1011 1012 Context("when retrieving simulation results", func() { 1013 BeforeEach(func() { 1014 mockDeployedCCInfoProvider := &ledgermock.DeployedChaincodeInfoProvider{} 1015 fakeSupport.GetDeployedCCInfoProviderReturns(mockDeployedCCInfoProvider) 1016 1017 pvtSimResults := &rwset.TxPvtReadWriteSet{ 1018 DataModel: rwset.TxReadWriteSet_KV, 1019 NsPvtRwset: []*rwset.NsPvtReadWriteSet{ 1020 { 1021 Namespace: "myCC", 1022 CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{ 1023 { 1024 CollectionName: "mycollection-1", 1025 Rwset: []byte{1, 2, 3, 4, 5, 6, 7, 8}, 1026 }, 1027 }, 1028 }, 1029 }, 1030 } 1031 1032 fakeTxSimulator.GetTxSimulationResultsReturns( 1033 &ledger.TxSimulationResults{ 1034 PvtSimulationResults: pvtSimResults, 1035 }, 1036 nil, 1037 ) 1038 }) 1039 1040 It("returns an error to the client", func() { 1041 proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal) 1042 Expect(err).NotTo(HaveOccurred()) 1043 Expect(proposalResponse.Response).To(Equal(&pb.Response{ 1044 Status: 500, 1045 Message: "error in simulation: failed to obtain collections config: no collection config for chaincode \"myCC\"", 1046 })) 1047 Expect(fakeSimulateFailure.AddCallCount()).To(Equal(1)) 1048 }) 1049 }) 1050 1051 Context("when building the ChaincodeInterest", func() { 1052 var pvtSimResults *rwset.TxPvtReadWriteSet 1053 var pubSimResults *rwset.TxReadWriteSet 1054 var readWrites []byte 1055 var readSet []byte 1056 1057 BeforeEach(func() { 1058 ccPkg := &pb.CollectionConfigPackage{ 1059 Config: []*pb.CollectionConfig{ 1060 { 1061 Payload: &pb.CollectionConfig_StaticCollectionConfig{ 1062 StaticCollectionConfig: &pb.StaticCollectionConfig{ 1063 Name: "myCC", 1064 MemberOrgsPolicy: &pb.CollectionPolicyConfig{ 1065 Payload: &pb.CollectionPolicyConfig_SignaturePolicy{ 1066 SignaturePolicy: &cb.SignaturePolicyEnvelope{}, 1067 }, 1068 }, 1069 }, 1070 }, 1071 }, 1072 }, 1073 } 1074 mockDeployedCCInfoProvider := &ledgermock.DeployedChaincodeInfoProvider{} 1075 mockDeployedCCInfoProvider.AllCollectionsConfigPkgReturns(ccPkg, nil) 1076 fakeSupport.GetDeployedCCInfoProviderReturns(mockDeployedCCInfoProvider) 1077 1078 var err error 1079 readWrites, err = proto.Marshal( 1080 &kvrwset.KVRWSet{ 1081 Writes: []*kvrwset.KVWrite{ 1082 {Key: "myKey", Value: []byte("myValue")}, 1083 }, 1084 }, 1085 ) 1086 Expect(err).NotTo(HaveOccurred()) 1087 readSet, err = proto.Marshal( 1088 &kvrwset.KVRWSet{ 1089 Reads: []*kvrwset.KVRead{ 1090 {Key: "myKey"}, 1091 }, 1092 }, 1093 ) 1094 Expect(err).NotTo(HaveOccurred()) 1095 1096 pubSimResults = &rwset.TxReadWriteSet{ 1097 DataModel: rwset.TxReadWriteSet_KV, 1098 NsRwset: []*rwset.NsReadWriteSet{ 1099 { 1100 Namespace: "myCC", 1101 Rwset: readWrites, 1102 }, 1103 }, 1104 } 1105 1106 pvtSimResults = &rwset.TxPvtReadWriteSet{ 1107 DataModel: rwset.TxReadWriteSet_KV, 1108 NsPvtRwset: []*rwset.NsPvtReadWriteSet{ 1109 { 1110 Namespace: "myCC", 1111 CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{ 1112 { 1113 CollectionName: "mycollection-1", 1114 Rwset: []byte("private RW set"), 1115 }, 1116 }, 1117 }, 1118 }, 1119 } 1120 }) 1121 1122 It("add private collection which gets read", func() { 1123 privateReads := ledger.PrivateReads{} 1124 privateReads.Add("myCC", "mycollection-1") 1125 writesetMetadata := ledger.WritesetMetadata{} 1126 writesetMetadata.Add("myCC", "mycollection-1", "mykey", nil) 1127 1128 fakeTxSimulator.GetTxSimulationResultsReturns( 1129 &ledger.TxSimulationResults{ 1130 PubSimulationResults: &rwset.TxReadWriteSet{}, 1131 PvtSimulationResults: pvtSimResults, 1132 PrivateReads: privateReads, 1133 WritesetMetadata: writesetMetadata, 1134 }, 1135 nil, 1136 ) 1137 1138 proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal) 1139 Expect(err).NotTo(HaveOccurred()) 1140 sort.Sort(CcInterest(*proposalResponse.Interest)) 1141 Expect(proposalResponse.Interest).To(Equal(&pb.ChaincodeInterest{ 1142 Chaincodes: []*pb.ChaincodeCall{{ 1143 Name: "myCC", 1144 CollectionNames: []string{"mycollection-1"}, 1145 }}, 1146 })) 1147 }) 1148 1149 It("add private collection which gets read, but not written", func() { 1150 privateReads := ledger.PrivateReads{} 1151 privateReads.Add("myCC", "mycollection-1") 1152 writesetMetadata := ledger.WritesetMetadata{} 1153 1154 // a private read will also have an entry in public hashed RWset 1155 pubSimResults = &rwset.TxReadWriteSet{ 1156 DataModel: rwset.TxReadWriteSet_KV, 1157 NsRwset: []*rwset.NsReadWriteSet{ 1158 { 1159 Namespace: "myCC", 1160 CollectionHashedRwset: []*rwset.CollectionHashedReadWriteSet{{CollectionName: "mycollection-1"}}, 1161 }, 1162 }, 1163 } 1164 1165 fakeTxSimulator.GetTxSimulationResultsReturns( 1166 &ledger.TxSimulationResults{ 1167 PubSimulationResults: pubSimResults, 1168 PvtSimulationResults: &rwset.TxPvtReadWriteSet{}, 1169 PrivateReads: privateReads, 1170 WritesetMetadata: writesetMetadata, 1171 }, 1172 nil, 1173 ) 1174 1175 proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal) 1176 Expect(err).NotTo(HaveOccurred()) 1177 sort.Sort(CcInterest(*proposalResponse.Interest)) 1178 Expect(proposalResponse.Interest).To(Equal(&pb.ChaincodeInterest{ 1179 Chaincodes: []*pb.ChaincodeCall{ 1180 { 1181 Name: "myCC", 1182 }, 1183 { 1184 Name: "myCC", 1185 CollectionNames: []string{"mycollection-1"}, 1186 }, 1187 }, 1188 })) 1189 }) 1190 1191 It("add private collection which is not read", func() { 1192 privateReads := ledger.PrivateReads{} 1193 writesetMetadata := ledger.WritesetMetadata{} 1194 writesetMetadata.Add("myCC", "mycollection-1", "mykey", nil) 1195 pubSimResults = &rwset.TxReadWriteSet{ 1196 DataModel: rwset.TxReadWriteSet_KV, 1197 NsRwset: []*rwset.NsReadWriteSet{ 1198 { 1199 Namespace: "myCC", 1200 Rwset: readSet, 1201 }, 1202 }, 1203 } 1204 1205 fakeTxSimulator.GetTxSimulationResultsReturns( 1206 &ledger.TxSimulationResults{ 1207 PubSimulationResults: pubSimResults, 1208 PvtSimulationResults: pvtSimResults, 1209 PrivateReads: privateReads, 1210 WritesetMetadata: writesetMetadata, 1211 }, 1212 nil, 1213 ) 1214 1215 proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal) 1216 Expect(err).NotTo(HaveOccurred()) 1217 sort.Sort(CcInterest(*proposalResponse.Interest)) 1218 Expect(proposalResponse.Interest).To(Equal(&pb.ChaincodeInterest{ 1219 Chaincodes: []*pb.ChaincodeCall{{ 1220 Name: "myCC", 1221 CollectionNames: []string{"mycollection-1"}, 1222 NoPrivateReads: true, 1223 }}, 1224 })) 1225 }) 1226 1227 It("add private collection and SBE", func() { 1228 privateReads := ledger.PrivateReads{} 1229 1230 sbe := &cb.SignaturePolicyEnvelope{ 1231 Rule: &cb.SignaturePolicy{ 1232 Type: &cb.SignaturePolicy_SignedBy{SignedBy: 0}, 1233 }, 1234 } 1235 sbeBytes, err := proto.Marshal(sbe) 1236 Expect(err).NotTo(HaveOccurred()) 1237 metadata := map[string][]byte{pb.MetaDataKeys_VALIDATION_PARAMETER.String(): sbeBytes} 1238 1239 writesetMetadata := ledger.WritesetMetadata{} 1240 writesetMetadata.Add("myCC", "mycollection-1", "mykey1", nil) 1241 writesetMetadata.Add("myCC", "mycollection-1", "mykey2", metadata) 1242 1243 fakeTxSimulator.GetTxSimulationResultsReturns( 1244 &ledger.TxSimulationResults{ 1245 PubSimulationResults: &rwset.TxReadWriteSet{}, 1246 PvtSimulationResults: pvtSimResults, 1247 PrivateReads: privateReads, 1248 WritesetMetadata: writesetMetadata, 1249 }, 1250 nil, 1251 ) 1252 1253 proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal) 1254 Expect(err).NotTo(HaveOccurred()) 1255 1256 sort.Sort(CcInterest(*proposalResponse.Interest)) 1257 Expect(proto.Equal( 1258 proposalResponse.Interest, 1259 &pb.ChaincodeInterest{ 1260 Chaincodes: []*pb.ChaincodeCall{ 1261 { 1262 Name: "myCC", 1263 KeyPolicies: []*cb.SignaturePolicyEnvelope{sbe}, 1264 DisregardNamespacePolicy: true, 1265 }, 1266 { 1267 Name: "myCC", 1268 CollectionNames: []string{"mycollection-1"}, 1269 NoPrivateReads: true, 1270 }, 1271 }, 1272 }, 1273 )).To(BeTrue()) 1274 }) 1275 1276 It("SBE only, no chaincode policy updates", func() { 1277 sbe := &cb.SignaturePolicyEnvelope{ 1278 Rule: &cb.SignaturePolicy{ 1279 Type: &cb.SignaturePolicy_SignedBy{SignedBy: 0}, 1280 }, 1281 } 1282 sbeBytes, err := proto.Marshal(sbe) 1283 Expect(err).NotTo(HaveOccurred()) 1284 metadata := map[string][]byte{pb.MetaDataKeys_VALIDATION_PARAMETER.String(): sbeBytes} 1285 1286 writesetMetadata := ledger.WritesetMetadata{} 1287 writesetMetadata.Add("myCC", "", "myKey", metadata) 1288 1289 fakeTxSimulator.GetTxSimulationResultsReturns( 1290 &ledger.TxSimulationResults{ 1291 PubSimulationResults: pubSimResults, 1292 PvtSimulationResults: pvtSimResults, 1293 WritesetMetadata: writesetMetadata, 1294 }, 1295 nil, 1296 ) 1297 1298 proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal) 1299 Expect(err).NotTo(HaveOccurred()) 1300 1301 sort.Sort(CcInterest(*proposalResponse.Interest)) 1302 Expect(proto.Equal( 1303 proposalResponse.Interest, 1304 &pb.ChaincodeInterest{ 1305 Chaincodes: []*pb.ChaincodeCall{{ 1306 Name: "myCC", 1307 KeyPolicies: []*cb.SignaturePolicyEnvelope{sbe}, 1308 DisregardNamespacePolicy: true, 1309 }}, 1310 }, 1311 )).To(BeTrue()) 1312 }) 1313 1314 It("chaincode to chaincode calls", func() { 1315 cc3ccSimResults := &rwset.TxReadWriteSet{ 1316 DataModel: rwset.TxReadWriteSet_KV, 1317 NsRwset: []*rwset.NsReadWriteSet{ 1318 { 1319 Namespace: "myCC", 1320 Rwset: readWrites, 1321 }, 1322 { 1323 Namespace: "otherCC", 1324 Rwset: readWrites, 1325 }, 1326 }, 1327 } 1328 1329 writesetMetadata := ledger.WritesetMetadata{} 1330 writesetMetadata.Add("myCC", "", "mykey", nil) 1331 writesetMetadata.Add("otherCC", "", "mykey", nil) 1332 1333 fakeTxSimulator.GetTxSimulationResultsReturns( 1334 &ledger.TxSimulationResults{ 1335 PubSimulationResults: cc3ccSimResults, 1336 PvtSimulationResults: &rwset.TxPvtReadWriteSet{}, 1337 WritesetMetadata: writesetMetadata, 1338 }, 1339 nil, 1340 ) 1341 1342 proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal) 1343 Expect(err).NotTo(HaveOccurred()) 1344 1345 Expect(proposalResponse.Interest.Chaincodes).To(ContainElements([]*pb.ChaincodeCall{ 1346 {Name: "myCC"}, 1347 {Name: "otherCC"}, 1348 })) 1349 }) 1350 1351 It("ignores system chaincodes", func() { 1352 fakeSupport.IsSysCCStub = func(cc string) bool { 1353 return cc == "_lifecycle" 1354 } 1355 pubSimResults = &rwset.TxReadWriteSet{ 1356 DataModel: rwset.TxReadWriteSet_KV, 1357 NsRwset: []*rwset.NsReadWriteSet{ 1358 { 1359 Namespace: "myCC", 1360 Rwset: readWrites, 1361 }, 1362 { 1363 Namespace: "_lifecycle", 1364 Rwset: readWrites, 1365 }, 1366 }, 1367 } 1368 1369 pvtSimResults = &rwset.TxPvtReadWriteSet{ 1370 DataModel: rwset.TxReadWriteSet_KV, 1371 NsPvtRwset: []*rwset.NsPvtReadWriteSet{ 1372 { 1373 Namespace: "myCC", 1374 CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{ 1375 { 1376 CollectionName: "mycollection-1", 1377 Rwset: []byte("private RW set"), 1378 }, 1379 }, 1380 }, 1381 { 1382 Namespace: "_lifecycle", 1383 CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{ 1384 { 1385 CollectionName: "mycollection-2", 1386 Rwset: []byte("should be ignored 2"), 1387 }, 1388 }, 1389 }, 1390 }, 1391 } 1392 privateReads := ledger.PrivateReads{} 1393 privateReads.Add("myCC", "mycollection-1") 1394 privateReads.Add("_lifecycle", "mycollection-1") 1395 writesetMetadata := ledger.WritesetMetadata{} 1396 writesetMetadata.Add("myCC", "mycollection-1", "mykey", nil) 1397 writesetMetadata.Add("_lifecycle", "mycollection-2", "mykey", nil) 1398 1399 fakeTxSimulator.GetTxSimulationResultsReturns( 1400 &ledger.TxSimulationResults{ 1401 PubSimulationResults: pubSimResults, 1402 PvtSimulationResults: pvtSimResults, 1403 PrivateReads: privateReads, 1404 WritesetMetadata: writesetMetadata, 1405 }, 1406 nil, 1407 ) 1408 1409 proposalResponse, err := e.ProcessProposal(context.TODO(), signedProposal) 1410 Expect(err).NotTo(HaveOccurred()) 1411 1412 Expect(proposalResponse.Interest.Chaincodes).To(ContainElements([]*pb.ChaincodeCall{ 1413 {Name: "myCC", CollectionNames: []string{"mycollection-1"}}, 1414 })) 1415 }) 1416 }) 1417 })