github.com/ewagmig/fabric@v2.1.1+incompatible/core/chaincode/lifecycle/scc_test.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package lifecycle_test 8 9 import ( 10 "bytes" 11 "encoding/gob" 12 "fmt" 13 14 "github.com/hyperledger/fabric/core/ledger" 15 16 "github.com/golang/protobuf/proto" 17 "github.com/hyperledger/fabric-chaincode-go/shim" 18 mspprotos "github.com/hyperledger/fabric-protos-go/msp" 19 pb "github.com/hyperledger/fabric-protos-go/peer" 20 lb "github.com/hyperledger/fabric-protos-go/peer/lifecycle" 21 "github.com/hyperledger/fabric/common/chaincode" 22 "github.com/hyperledger/fabric/common/channelconfig" 23 "github.com/hyperledger/fabric/common/policydsl" 24 "github.com/hyperledger/fabric/core/chaincode/lifecycle" 25 "github.com/hyperledger/fabric/core/chaincode/lifecycle/mock" 26 "github.com/hyperledger/fabric/core/chaincode/persistence" 27 "github.com/hyperledger/fabric/core/dispatcher" 28 "github.com/hyperledger/fabric/msp" 29 "github.com/pkg/errors" 30 31 . "github.com/onsi/ginkgo" 32 . "github.com/onsi/gomega" 33 ) 34 35 var _ = Describe("SCC", func() { 36 var ( 37 scc *lifecycle.SCC 38 fakeSCCFuncs *mock.SCCFunctions 39 fakeChannelConfigSource *mock.ChannelConfigSource 40 fakeChannelConfig *mock.ChannelConfig 41 fakeApplicationConfig *mock.ApplicationConfig 42 fakeCapabilities *mock.ApplicationCapabilities 43 fakeACLProvider *mock.ACLProvider 44 fakeMSPManager *mock.MSPManager 45 fakeQueryExecutorProvider *mock.QueryExecutorProvider 46 fakeQueryExecutor *mock.SimpleQueryExecutor 47 fakeDeployedCCInfoProvider *mock.LegacyDeployedCCInfoProvider 48 fakeStub *mock.ChaincodeStub 49 ) 50 51 BeforeEach(func() { 52 fakeSCCFuncs = &mock.SCCFunctions{} 53 fakeChannelConfigSource = &mock.ChannelConfigSource{} 54 fakeChannelConfig = &mock.ChannelConfig{} 55 fakeChannelConfigSource.GetStableChannelConfigReturns(fakeChannelConfig) 56 fakeApplicationConfig = &mock.ApplicationConfig{} 57 fakeChannelConfig.ApplicationConfigReturns(fakeApplicationConfig, true) 58 fakeCapabilities = &mock.ApplicationCapabilities{} 59 fakeCapabilities.LifecycleV20Returns(true) 60 fakeApplicationConfig.CapabilitiesReturns(fakeCapabilities) 61 fakeACLProvider = &mock.ACLProvider{} 62 fakeMSPManager = &mock.MSPManager{} 63 fakeChannelConfig.MSPManagerReturns(fakeMSPManager) 64 fakeQueryExecutorProvider = &mock.QueryExecutorProvider{} 65 fakeQueryExecutor = &mock.SimpleQueryExecutor{} 66 fakeQueryExecutorProvider.TxQueryExecutorReturns(fakeQueryExecutor) 67 fakeStub = &mock.ChaincodeStub{} 68 fakeDeployedCCInfoProvider = &mock.LegacyDeployedCCInfoProvider{} 69 70 scc = &lifecycle.SCC{ 71 Dispatcher: &dispatcher.Dispatcher{ 72 Protobuf: &dispatcher.ProtobufImpl{}, 73 }, 74 Functions: fakeSCCFuncs, 75 OrgMSPID: "fake-mspid", 76 ChannelConfigSource: fakeChannelConfigSource, 77 ACLProvider: fakeACLProvider, 78 QueryExecutorProvider: fakeQueryExecutorProvider, 79 DeployedCCInfoProvider: fakeDeployedCCInfoProvider, 80 } 81 }) 82 83 Describe("Name", func() { 84 It("returns the name", func() { 85 Expect(scc.Name()).To(Equal("_lifecycle")) 86 }) 87 }) 88 89 Describe("Chaincode", func() { 90 It("returns a reference to itself", func() { 91 Expect(scc.Chaincode()).To(Equal(scc)) 92 }) 93 }) 94 95 Describe("Init", func() { 96 It("does nothing", func() { 97 Expect(scc.Init(nil)).To(Equal(shim.Success(nil))) 98 }) 99 }) 100 101 Describe("Invoke", func() { 102 BeforeEach(func() { 103 fakeStub.GetChannelIDReturns("test-channel") 104 }) 105 106 Context("when no arguments are provided", func() { 107 It("returns an error", func() { 108 Expect(scc.Invoke(fakeStub)).To(Equal(shim.Error("lifecycle scc must be invoked with arguments"))) 109 }) 110 }) 111 112 Context("when too many arguments are provided", func() { 113 BeforeEach(func() { 114 fakeStub.GetArgsReturns([][]byte{nil, nil, nil}) 115 }) 116 117 It("returns an error", func() { 118 Expect(scc.Invoke(fakeStub)).To(Equal(shim.Error("lifecycle scc operations require exactly two arguments but received 3"))) 119 }) 120 }) 121 122 Context("when an unknown function is provided as the first argument", func() { 123 BeforeEach(func() { 124 fakeStub.GetArgsReturns([][]byte{[]byte("bad-function"), nil}) 125 }) 126 127 It("returns an error", func() { 128 Expect(scc.Invoke(fakeStub)).To(Equal(shim.Error("failed to invoke backing implementation of 'bad-function': receiver *lifecycle.Invocation.bad-function does not exist"))) 129 }) 130 }) 131 132 Context("when the ACL provider disapproves of the function", func() { 133 BeforeEach(func() { 134 fakeStub.GetArgsReturns([][]byte{[]byte("any-function"), nil}) 135 fakeACLProvider.CheckACLReturns(fmt.Errorf("acl-error")) 136 }) 137 138 It("returns an error", func() { 139 Expect(scc.Invoke(fakeStub)).To(Equal(shim.Error("Failed to authorize invocation due to failed ACL check: acl-error"))) 140 }) 141 142 Context("when the signed data for the tx cannot be retrieved", func() { 143 BeforeEach(func() { 144 fakeStub.GetSignedProposalReturns(nil, fmt.Errorf("shim-error")) 145 }) 146 147 It("returns an error", func() { 148 Expect(scc.Invoke(fakeStub)).To(Equal(shim.Error("Failed getting signed proposal from stub: [shim-error]"))) 149 }) 150 }) 151 }) 152 153 Describe("InstallChaincode", func() { 154 var ( 155 arg *lb.InstallChaincodeArgs 156 marshaledArg []byte 157 ) 158 159 BeforeEach(func() { 160 arg = &lb.InstallChaincodeArgs{ 161 ChaincodeInstallPackage: []byte("chaincode-package"), 162 } 163 164 var err error 165 marshaledArg, err = proto.Marshal(arg) 166 Expect(err).NotTo(HaveOccurred()) 167 168 fakeStub.GetArgsReturns([][]byte{[]byte("InstallChaincode"), marshaledArg}) 169 170 fakeSCCFuncs.InstallChaincodeReturns(&chaincode.InstalledChaincode{ 171 Label: "label", 172 PackageID: "packageid", 173 }, nil) 174 }) 175 176 It("passes the arguments to and returns the results from the backing scc function implementation", func() { 177 res := scc.Invoke(fakeStub) 178 Expect(res.Status).To(Equal(int32(200))) 179 payload := &lb.InstallChaincodeResult{} 180 err := proto.Unmarshal(res.Payload, payload) 181 Expect(err).NotTo(HaveOccurred()) 182 Expect(payload.PackageId).To(Equal("packageid")) 183 184 Expect(fakeSCCFuncs.InstallChaincodeCallCount()).To(Equal(1)) 185 ccInstallPackage := fakeSCCFuncs.InstallChaincodeArgsForCall(0) 186 Expect(ccInstallPackage).To(Equal([]byte("chaincode-package"))) 187 }) 188 189 Context("when the underlying function implementation fails", func() { 190 BeforeEach(func() { 191 fakeSCCFuncs.InstallChaincodeReturns(nil, fmt.Errorf("underlying-error")) 192 }) 193 194 It("wraps and returns the error", func() { 195 res := scc.Invoke(fakeStub) 196 Expect(res.Status).To(Equal(int32(500))) 197 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'InstallChaincode': underlying-error")) 198 }) 199 }) 200 }) 201 202 Describe("QueryInstalledChaincode", func() { 203 var ( 204 arg *lb.QueryInstalledChaincodeArgs 205 marshaledArg []byte 206 ) 207 208 BeforeEach(func() { 209 arg = &lb.QueryInstalledChaincodeArgs{ 210 PackageId: "awesome_package", 211 } 212 213 var err error 214 marshaledArg, err = proto.Marshal(arg) 215 Expect(err).NotTo(HaveOccurred()) 216 217 fakeStub.GetArgsReturns([][]byte{[]byte("QueryInstalledChaincode"), marshaledArg}) 218 219 fakeSCCFuncs.QueryInstalledChaincodeReturns(&chaincode.InstalledChaincode{ 220 PackageID: "awesome_package", 221 Label: "awesome_package_label", 222 References: map[string][]*chaincode.Metadata{ 223 "test-channel": { 224 &chaincode.Metadata{ 225 Name: "cc0", 226 Version: "cc0-version", 227 }, 228 }, 229 }, 230 }, nil) 231 }) 232 233 It("passes the arguments to and returns the results from the backing scc function implementation", func() { 234 res := scc.Invoke(fakeStub) 235 Expect(res.Status).To(Equal(int32(200))) 236 payload := &lb.QueryInstalledChaincodeResult{} 237 err := proto.Unmarshal(res.Payload, payload) 238 Expect(err).NotTo(HaveOccurred()) 239 Expect(payload.Label).To(Equal("awesome_package_label")) 240 Expect(payload.PackageId).To(Equal("awesome_package")) 241 Expect(payload.References).To(Equal(map[string]*lb.QueryInstalledChaincodeResult_References{ 242 "test-channel": { 243 Chaincodes: []*lb.QueryInstalledChaincodeResult_Chaincode{ 244 { 245 Name: "cc0", 246 Version: "cc0-version", 247 }, 248 }, 249 }, 250 })) 251 252 Expect(fakeSCCFuncs.QueryInstalledChaincodeCallCount()).To(Equal(1)) 253 name := fakeSCCFuncs.QueryInstalledChaincodeArgsForCall(0) 254 Expect(name).To(Equal("awesome_package")) 255 }) 256 257 Context("when the code package cannot be found", func() { 258 BeforeEach(func() { 259 fakeSCCFuncs.QueryInstalledChaincodeReturns(nil, persistence.CodePackageNotFoundErr{PackageID: "less_awesome_package"}) 260 }) 261 262 It("returns 404 Not Found", func() { 263 res := scc.Invoke(fakeStub) 264 Expect(res.Status).To(Equal(int32(404))) 265 Expect(res.Message).To(Equal("chaincode install package 'less_awesome_package' not found")) 266 }) 267 }) 268 269 Context("when the underlying function implementation fails", func() { 270 BeforeEach(func() { 271 fakeSCCFuncs.QueryInstalledChaincodeReturns(nil, fmt.Errorf("underlying-error")) 272 }) 273 274 It("wraps and returns the error", func() { 275 res := scc.Invoke(fakeStub) 276 Expect(res.Status).To(Equal(int32(500))) 277 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'QueryInstalledChaincode': underlying-error")) 278 }) 279 }) 280 }) 281 282 Describe("GetInstalledChaincodePackage", func() { 283 var ( 284 arg *lb.GetInstalledChaincodePackageArgs 285 marshaledArg []byte 286 ) 287 288 BeforeEach(func() { 289 arg = &lb.GetInstalledChaincodePackageArgs{ 290 PackageId: "package-id", 291 } 292 293 var err error 294 marshaledArg, err = proto.Marshal(arg) 295 Expect(err).NotTo(HaveOccurred()) 296 297 fakeStub.GetArgsReturns([][]byte{[]byte("GetInstalledChaincodePackage"), marshaledArg}) 298 299 fakeSCCFuncs.GetInstalledChaincodePackageReturns([]byte("chaincode-package"), nil) 300 }) 301 302 It("passes the arguments to and returns the results from the backing scc function implementation", func() { 303 res := scc.Invoke(fakeStub) 304 Expect(res.Status).To(Equal(int32(200))) 305 payload := &lb.GetInstalledChaincodePackageResult{} 306 err := proto.Unmarshal(res.Payload, payload) 307 Expect(err).NotTo(HaveOccurred()) 308 Expect(payload.ChaincodeInstallPackage).To(Equal([]byte("chaincode-package"))) 309 310 Expect(fakeSCCFuncs.GetInstalledChaincodePackageCallCount()).To(Equal(1)) 311 packageID := fakeSCCFuncs.GetInstalledChaincodePackageArgsForCall(0) 312 Expect(packageID).To(Equal("package-id")) 313 }) 314 315 Context("when the underlying function implementation fails", func() { 316 BeforeEach(func() { 317 fakeSCCFuncs.GetInstalledChaincodePackageReturns(nil, fmt.Errorf("underlying-error")) 318 }) 319 320 It("wraps and returns the error", func() { 321 res := scc.Invoke(fakeStub) 322 Expect(res.Status).To(Equal(int32(500))) 323 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'GetInstalledChaincodePackage': underlying-error")) 324 }) 325 }) 326 }) 327 328 Describe("QueryInstalledChaincodes", func() { 329 var ( 330 arg *lb.QueryInstalledChaincodesArgs 331 marshaledArg []byte 332 ) 333 334 BeforeEach(func() { 335 arg = &lb.QueryInstalledChaincodesArgs{} 336 337 var err error 338 marshaledArg, err = proto.Marshal(arg) 339 Expect(err).NotTo(HaveOccurred()) 340 341 fakeStub.GetArgsReturns([][]byte{[]byte("QueryInstalledChaincodes"), marshaledArg}) 342 343 fakeSCCFuncs.QueryInstalledChaincodesReturns([]*chaincode.InstalledChaincode{ 344 { 345 Label: "cc0-label", 346 PackageID: "cc0-package-id", 347 References: map[string][]*chaincode.Metadata{ 348 "test-channel": { 349 &chaincode.Metadata{ 350 Name: "cc0", 351 Version: "cc0-version", 352 }, 353 }, 354 }, 355 }, 356 { 357 Label: "cc1-label", 358 PackageID: "cc1-package-id", 359 }, 360 }) 361 }) 362 363 It("passes the arguments to and returns the results from the backing scc function implementation", func() { 364 res := scc.Invoke(fakeStub) 365 Expect(res.Status).To(Equal(int32(200))) 366 payload := &lb.QueryInstalledChaincodesResult{} 367 err := proto.Unmarshal(res.Payload, payload) 368 Expect(err).NotTo(HaveOccurred()) 369 370 Expect(payload.InstalledChaincodes).To(HaveLen(2)) 371 372 Expect(payload.InstalledChaincodes[0].Label).To(Equal("cc0-label")) 373 Expect(payload.InstalledChaincodes[0].PackageId).To(Equal("cc0-package-id")) 374 Expect(payload.InstalledChaincodes[0].References).To(Equal(map[string]*lb.QueryInstalledChaincodesResult_References{ 375 "test-channel": { 376 Chaincodes: []*lb.QueryInstalledChaincodesResult_Chaincode{ 377 { 378 Name: "cc0", 379 Version: "cc0-version", 380 }, 381 }, 382 }, 383 })) 384 385 Expect(payload.InstalledChaincodes[1].Label).To(Equal("cc1-label")) 386 Expect(payload.InstalledChaincodes[1].PackageId).To(Equal("cc1-package-id")) 387 388 Expect(fakeSCCFuncs.QueryInstalledChaincodesCallCount()).To(Equal(1)) 389 }) 390 }) 391 392 Describe("ApproveChaincodeDefinitionForMyOrg", func() { 393 var ( 394 err error 395 collConfigs collectionConfigs 396 fakeMsp *mock.MSP 397 398 arg *lb.ApproveChaincodeDefinitionForMyOrgArgs 399 marshaledArg []byte 400 ) 401 402 BeforeEach(func() { 403 // identity1 of type MSPRole 404 mspPrincipal := &mspprotos.MSPRole{ 405 MspIdentifier: "test-member-role", 406 } 407 mspPrincipalBytes, err := proto.Marshal(mspPrincipal) 408 Expect(err).NotTo(HaveOccurred()) 409 identity1 := &mspprotos.MSPPrincipal{ 410 PrincipalClassification: mspprotos.MSPPrincipal_ROLE, 411 Principal: mspPrincipalBytes, 412 } 413 414 // identity2 of type OU 415 mspou := &mspprotos.OrganizationUnit{ 416 MspIdentifier: "test-member-ou", 417 } 418 mspouBytes, err := proto.Marshal(mspou) 419 Expect(err).NotTo(HaveOccurred()) 420 identity2 := &mspprotos.MSPPrincipal{ 421 PrincipalClassification: mspprotos.MSPPrincipal_ORGANIZATION_UNIT, 422 Principal: mspouBytes, 423 } 424 425 // identity3 of type identity 426 identity3 := &mspprotos.MSPPrincipal{ 427 PrincipalClassification: mspprotos.MSPPrincipal_IDENTITY, 428 Principal: []byte("test-member-identity"), 429 } 430 431 fakeIdentities := []*mspprotos.MSPPrincipal{ 432 identity1, identity2, identity3, 433 } 434 435 fakeMsp = &mock.MSP{} 436 fakeMSPManager.GetMSPsReturns( 437 map[string]msp.MSP{ 438 "test-member-role": fakeMsp, 439 "test-member-ou": fakeMsp, 440 "test-member-identity": fakeMsp, 441 }, 442 nil, 443 ) 444 445 collConfigs = []*collectionConfig{ 446 { 447 Name: "test-collection", 448 Policy: "OR('fakeOrg1.member', 'fakeOrg2.member', 'fakeOrg3.member')", 449 RequiredPeerCount: 2, 450 MaxPeerCount: 3, 451 BlockToLive: 0, 452 Identities: fakeIdentities, 453 }, 454 } 455 456 arg = &lb.ApproveChaincodeDefinitionForMyOrgArgs{ 457 Sequence: 7, 458 Name: "cc_name", 459 Version: "version_1.0", 460 EndorsementPlugin: "endorsement-plugin", 461 ValidationPlugin: "validation-plugin", 462 ValidationParameter: []byte("validation-parameter"), 463 InitRequired: true, 464 Source: &lb.ChaincodeSource{ 465 Type: &lb.ChaincodeSource_LocalPackage{ 466 LocalPackage: &lb.ChaincodeSource_Local{ 467 PackageId: "hash", 468 }, 469 }, 470 }, 471 } 472 }) 473 474 JustBeforeEach(func() { 475 arg.Collections = collConfigs.toProtoCollectionConfigPackage() 476 marshaledArg, err = proto.Marshal(arg) 477 Expect(err).NotTo(HaveOccurred()) 478 fakeStub.GetArgsReturns([][]byte{[]byte("ApproveChaincodeDefinitionForMyOrg"), marshaledArg}) 479 }) 480 481 It("passes the arguments to and returns the results from the backing scc function implementation", func() { 482 res := scc.Invoke(fakeStub) 483 Expect(res.Status).To(Equal(int32(200))) 484 payload := &lb.ApproveChaincodeDefinitionForMyOrgResult{} 485 err = proto.Unmarshal(res.Payload, payload) 486 Expect(err).NotTo(HaveOccurred()) 487 488 Expect(fakeSCCFuncs.ApproveChaincodeDefinitionForOrgCallCount()).To(Equal(1)) 489 chname, ccname, cd, packageID, pubState, privState := fakeSCCFuncs.ApproveChaincodeDefinitionForOrgArgsForCall(0) 490 Expect(chname).To(Equal("test-channel")) 491 Expect(ccname).To(Equal("cc_name")) 492 Expect(cd.Sequence).To(Equal(int64(7))) 493 Expect(cd.EndorsementInfo).To(Equal(&lb.ChaincodeEndorsementInfo{ 494 Version: "version_1.0", 495 EndorsementPlugin: "endorsement-plugin", 496 InitRequired: true, 497 })) 498 Expect(cd.ValidationInfo).To(Equal(&lb.ChaincodeValidationInfo{ 499 ValidationPlugin: "validation-plugin", 500 ValidationParameter: []byte("validation-parameter"), 501 })) 502 Expect(proto.Equal( 503 cd.Collections, 504 collConfigs.toProtoCollectionConfigPackage(), 505 )).Should(BeTrue()) 506 507 Expect(packageID).To(Equal("hash")) 508 Expect(pubState).To(Equal(fakeStub)) 509 Expect(privState).To(BeAssignableToTypeOf(&lifecycle.ChaincodePrivateLedgerShim{})) 510 Expect(privState.(*lifecycle.ChaincodePrivateLedgerShim).Collection).To(Equal("_implicit_org_fake-mspid")) 511 }) 512 513 Context("when the chaincode name contains invalid characters", func() { 514 BeforeEach(func() { 515 arg.Name = "!nvalid" 516 }) 517 518 It("wraps and returns the error", func() { 519 res := scc.Invoke(fakeStub) 520 Expect(res.Status).To(Equal(int32(500))) 521 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'ApproveChaincodeDefinitionForMyOrg': invalid chaincode name '!nvalid'. Names can only consist of alphanumerics, '_', and '-' and can only begin with alphanumerics")) 522 }) 523 }) 524 525 Context("when the chaincode version contains invalid characters", func() { 526 BeforeEach(func() { 527 arg.Version = "$money$" 528 }) 529 530 It("wraps and returns the error", func() { 531 res := scc.Invoke(fakeStub) 532 Expect(res.Status).To(Equal(int32(500))) 533 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'ApproveChaincodeDefinitionForMyOrg': invalid chaincode version '$money$'. Versions can only consist of alphanumerics, '_', '-', '+', and '.'")) 534 }) 535 }) 536 537 Context("when the chaincode name matches an existing system chaincode name", func() { 538 BeforeEach(func() { 539 arg.Name = "cscc" 540 }) 541 542 It("wraps and returns the error", func() { 543 res := scc.Invoke(fakeStub) 544 Expect(res.Status).To(Equal(int32(500))) 545 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'ApproveChaincodeDefinitionForMyOrg': chaincode name 'cscc' is the name of a system chaincode")) 546 }) 547 }) 548 549 Context("when a collection name contains invalid characters", func() { 550 BeforeEach(func() { 551 collConfigs[0].Name = "collection@test" 552 }) 553 554 It("wraps and returns the error", func() { 555 res := scc.Invoke(fakeStub) 556 Expect(res.Status).To(Equal(int32(500))) 557 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'ApproveChaincodeDefinitionForMyOrg': invalid collection name 'collection@test'. Names can only consist of alphanumerics, '_', and '-' and cannot begin with '_'")) 558 }) 559 }) 560 561 Context("when a collection name begins with an invalid character", func() { 562 BeforeEach(func() { 563 collConfigs[0].Name = "_collection" 564 }) 565 566 It("wraps and returns the error", func() { 567 res := scc.Invoke(fakeStub) 568 Expect(res.Status).To(Equal(int32(500))) 569 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'ApproveChaincodeDefinitionForMyOrg': invalid collection name '_collection'. Names can only consist of alphanumerics, '_', and '-' and cannot begin with '_'")) 570 }) 571 }) 572 573 Context("when collection member-org-policy is nil", func() { 574 BeforeEach(func() { 575 collConfigs[0].UseGivenMemberOrgPolicy = true 576 collConfigs[0].MemberOrgPolicy = nil 577 }) 578 579 It("wraps and returns error", func() { 580 res := scc.Invoke(fakeStub) 581 Expect(res.Status).To(Equal(int32(500))) 582 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'ApproveChaincodeDefinitionForMyOrg': collection member policy is not set for collection 'test-collection'")) 583 }) 584 }) 585 586 Context("when collection member-org-policy signature policy is nil", func() { 587 BeforeEach(func() { 588 collConfigs[0].UseGivenMemberOrgPolicy = true 589 collConfigs[0].MemberOrgPolicy = &pb.CollectionPolicyConfig{} 590 }) 591 592 It("wraps and returns error", func() { 593 res := scc.Invoke(fakeStub) 594 Expect(res.Status).To(Equal(int32(500))) 595 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'ApproveChaincodeDefinitionForMyOrg': collection member org policy is empty for collection 'test-collection'")) 596 }) 597 }) 598 599 Context("when collection member-org-policy signature policy is not an OR only policy", func() { 600 BeforeEach(func() { 601 collConfigs[0].Policy = "OR('fakeOrg1.member', AND('fakeOrg2.member', 'fakeOrg3.member'))" 602 }) 603 604 It("wraps and returns error", func() { 605 res := scc.Invoke(fakeStub) 606 Expect(res.Status).To(Equal(int32(500))) 607 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'ApproveChaincodeDefinitionForMyOrg': collection-name: test-collection -- error in member org policy: signature policy is not an OR concatenation, NOutOf 2")) 608 }) 609 }) 610 611 Context("when collection member-org-policy signature policy contains unmarshable MSPRole", func() { 612 BeforeEach(func() { 613 collConfigs[0].Identities[0] = &mspprotos.MSPPrincipal{ 614 PrincipalClassification: mspprotos.MSPPrincipal_ROLE, 615 Principal: []byte("unmarshable bytes"), 616 } 617 }) 618 619 It("wraps and returns error", func() { 620 res := scc.Invoke(fakeStub) 621 Expect(res.Status).To(Equal(int32(500))) 622 Expect(res.Message).Should(ContainSubstring("failed to invoke backing implementation of 'ApproveChaincodeDefinitionForMyOrg': collection-name: test-collection -- cannot unmarshal identity bytes into MSPRole")) 623 }) 624 }) 625 626 Context("when collection member-org-policy signature policy contains too few principals", func() { 627 BeforeEach(func() { 628 collConfigs[0].Identities = collConfigs[0].Identities[0:1] 629 }) 630 631 It("wraps and returns error", func() { 632 res := scc.Invoke(fakeStub) 633 Expect(res.Status).To(Equal(int32(500))) 634 Expect(res.Message).Should(ContainSubstring("failed to invoke backing implementation of 'ApproveChaincodeDefinitionForMyOrg': invalid member org policy for collection 'test-collection': identity index out of range, requested 1, but identities length is 1")) 635 }) 636 }) 637 638 Context("when collection MSPRole in member-org-policy in not a channel member", func() { 639 BeforeEach(func() { 640 fakeMSPManager.GetMSPsReturns( 641 map[string]msp.MSP{ 642 "test-member-ou": fakeMsp, 643 "test-member-identity": fakeMsp, 644 }, 645 nil, 646 ) 647 }) 648 649 It("wraps and returns error", func() { 650 res := scc.Invoke(fakeStub) 651 Expect(res.Status).To(Equal(int32(500))) 652 Expect(res.Message).Should(ContainSubstring("failed to invoke backing implementation of 'ApproveChaincodeDefinitionForMyOrg': collection-name: test-collection -- collection member 'test-member-role' is not part of the channel")) 653 }) 654 }) 655 656 Context("when collection member-org-policy signature policy contains unmarshable ORGANIZATION_UNIT", func() { 657 BeforeEach(func() { 658 collConfigs[0].Identities[0] = &mspprotos.MSPPrincipal{ 659 PrincipalClassification: mspprotos.MSPPrincipal_ORGANIZATION_UNIT, 660 Principal: []byte("unmarshable bytes"), 661 } 662 }) 663 664 It("wraps and returns error", func() { 665 res := scc.Invoke(fakeStub) 666 Expect(res.Status).To(Equal(int32(500))) 667 Expect(res.Message).Should(ContainSubstring("failed to invoke backing implementation of 'ApproveChaincodeDefinitionForMyOrg': collection-name: test-collection -- cannot unmarshal identity bytes into OrganizationUnit")) 668 }) 669 }) 670 671 Context("when collection MSPOU in member-org-policy in not a channel member", func() { 672 BeforeEach(func() { 673 fakeMSPManager.GetMSPsReturns( 674 map[string]msp.MSP{ 675 "test-member-role": fakeMsp, 676 "test-member-identity": fakeMsp, 677 }, 678 nil, 679 ) 680 }) 681 682 It("wraps and returns error", func() { 683 res := scc.Invoke(fakeStub) 684 Expect(res.Status).To(Equal(int32(500))) 685 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'ApproveChaincodeDefinitionForMyOrg': collection-name: test-collection -- collection member 'test-member-ou' is not part of the channel")) 686 }) 687 }) 688 689 Context("when collection MSP identity in member-org-policy in not a channel member", func() { 690 BeforeEach(func() { 691 fakeMSPManager.DeserializeIdentityReturns(nil, errors.New("Nope")) 692 }) 693 694 It("wraps and returns error", func() { 695 res := scc.Invoke(fakeStub) 696 Expect(res.Status).To(Equal(int32(500))) 697 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'ApproveChaincodeDefinitionForMyOrg': collection-name: test-collection -- contains an identity that is not part of the channel")) 698 }) 699 }) 700 701 Context("when collection member-org-policy signature policy contains unsupported principal type", func() { 702 BeforeEach(func() { 703 collConfigs[0].Identities[0] = &mspprotos.MSPPrincipal{ 704 PrincipalClassification: mspprotos.MSPPrincipal_ANONYMITY, 705 } 706 }) 707 708 It("wraps and returns error", func() { 709 res := scc.Invoke(fakeStub) 710 Expect(res.Status).To(Equal(int32(500))) 711 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'ApproveChaincodeDefinitionForMyOrg': collection-name: test-collection -- principal type ANONYMITY is not supported")) 712 }) 713 }) 714 715 Context("when collection config contains duplicate collections", func() { 716 BeforeEach(func() { 717 collConfigs = append(collConfigs, collConfigs[0]) 718 }) 719 720 It("wraps and returns error", func() { 721 res := scc.Invoke(fakeStub) 722 Expect(res.Status).To(Equal(int32(500))) 723 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'ApproveChaincodeDefinitionForMyOrg': collection-name: test-collection -- found duplicate in collection configuration")) 724 }) 725 }) 726 727 Context("when collection config contains requiredPeerCount < zero", func() { 728 BeforeEach(func() { 729 collConfigs[0].RequiredPeerCount = -2 730 }) 731 732 It("wraps and returns error", func() { 733 res := scc.Invoke(fakeStub) 734 Expect(res.Status).To(Equal(int32(500))) 735 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'ApproveChaincodeDefinitionForMyOrg': collection-name: test-collection -- requiredPeerCount (-2) cannot be less than zero")) 736 }) 737 }) 738 739 Context("when collection config contains requiredPeerCount > maxPeerCount", func() { 740 BeforeEach(func() { 741 collConfigs[0].MaxPeerCount = 10 742 collConfigs[0].RequiredPeerCount = 20 743 }) 744 745 It("wraps and returns error", func() { 746 res := scc.Invoke(fakeStub) 747 Expect(res.Status).To(Equal(int32(500))) 748 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'ApproveChaincodeDefinitionForMyOrg': collection-name: test-collection -- maximum peer count (10) cannot be less than the required peer count (20)")) 749 }) 750 }) 751 752 Context("when committed definition and proposed definition both contains no collection config", func() { 753 BeforeEach(func() { 754 fakeDeployedCCInfoProvider.ChaincodeInfoReturns(&ledger.DeployedChaincodeInfo{}, nil) 755 arg.Collections = nil 756 }) 757 758 It("does not return error", func() { 759 res := scc.Invoke(fakeStub) 760 Expect(res.Status).To(Equal(int32(200))) 761 }) 762 }) 763 764 Context("when committed definition and proposed definition both contains same collection config", func() { 765 BeforeEach(func() { 766 fakeDeployedCCInfoProvider.ChaincodeInfoReturns( 767 &ledger.DeployedChaincodeInfo{ 768 ExplicitCollectionConfigPkg: collConfigs.toProtoCollectionConfigPackage(), 769 }, 770 nil, 771 ) 772 }) 773 774 It("does not return error", func() { 775 res := scc.Invoke(fakeStub) 776 Expect(res.Status).To(Equal(int32(200))) 777 }) 778 }) 779 780 Context("when committed definition contains collection config and the proposed definition contains no collection config", func() { 781 BeforeEach(func() { 782 fakeDeployedCCInfoProvider.ChaincodeInfoReturns( 783 &ledger.DeployedChaincodeInfo{ 784 ExplicitCollectionConfigPkg: collConfigs.toProtoCollectionConfigPackage(), 785 }, 786 nil, 787 ) 788 collConfigs = nil 789 }) 790 791 It("wraps and returns error", func() { 792 res := scc.Invoke(fakeStub) 793 Expect(res.Status).To(Equal(int32(500))) 794 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'ApproveChaincodeDefinitionForMyOrg': the proposed collection config does not contain previously defined collections")) 795 }) 796 }) 797 798 Context("when committed definition contains a collection that is not defined in the proposed definition", func() { 799 BeforeEach(func() { 800 committedCollConfigs := collConfigs.deepCopy() 801 additionalCommittedConfigs := collConfigs.deepCopy() 802 additionalCommittedConfigs[0].Name = "missing-collection" 803 committedCollConfigs = append(committedCollConfigs, additionalCommittedConfigs...) 804 fakeDeployedCCInfoProvider.ChaincodeInfoReturns( 805 &ledger.DeployedChaincodeInfo{ 806 ExplicitCollectionConfigPkg: committedCollConfigs.toProtoCollectionConfigPackage(), 807 }, 808 nil, 809 ) 810 }) 811 812 It("wraps and returns error", func() { 813 res := scc.Invoke(fakeStub) 814 Expect(res.Status).To(Equal(int32(500))) 815 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'ApproveChaincodeDefinitionForMyOrg': existing collection [missing-collection] missing in the proposed collection configuration")) 816 }) 817 }) 818 819 Context("when committed definition contains a collection that has different BTL than defined in the proposed definition", func() { 820 var ( 821 committedCollConfigs collectionConfigs 822 ) 823 BeforeEach(func() { 824 committedCollConfigs = collConfigs.deepCopy() 825 committedCollConfigs[0].BlockToLive = committedCollConfigs[0].BlockToLive + 1 826 fakeDeployedCCInfoProvider.ChaincodeInfoReturns( 827 &ledger.DeployedChaincodeInfo{ 828 ExplicitCollectionConfigPkg: committedCollConfigs.toProtoCollectionConfigPackage(), 829 }, 830 nil, 831 ) 832 }) 833 834 It("wraps and returns error", func() { 835 res := scc.Invoke(fakeStub) 836 Expect(res.Status).To(Equal(int32(500))) 837 Expect(res.Message).To(Equal( 838 fmt.Sprintf( 839 "failed to invoke backing implementation of 'ApproveChaincodeDefinitionForMyOrg': the BlockToLive in an existing collection [test-collection] modified. Existing value [%d]", 840 committedCollConfigs[0].BlockToLive, 841 ), 842 )) 843 }) 844 }) 845 846 Context("when not able to get MSPManager for evaluating collection config", func() { 847 BeforeEach(func() { 848 fakeChannelConfig.MSPManagerReturns(nil) 849 }) 850 851 It("wraps and returns error", func() { 852 res := scc.Invoke(fakeStub) 853 Expect(res.Status).To(Equal(int32(500))) 854 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'ApproveChaincodeDefinitionForMyOrg': could not get MSP manager for channel 'test-channel'")) 855 }) 856 }) 857 858 Context("when not able to get MSPs for evaluating collection config", func() { 859 BeforeEach(func() { 860 fakeMSPManager.GetMSPsReturns(nil, errors.New("No MSPs")) 861 }) 862 863 It("wraps and returns error", func() { 864 res := scc.Invoke(fakeStub) 865 Expect(res.Status).To(Equal(int32(500))) 866 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'ApproveChaincodeDefinitionForMyOrg': could not get MSPs: No MSPs")) 867 }) 868 }) 869 870 Context("when not able to get committed definition for evaluating collection config", func() { 871 BeforeEach(func() { 872 fakeDeployedCCInfoProvider.ChaincodeInfoReturns(nil, errors.New("could not fetch definition")) 873 }) 874 875 It("wraps and returns error", func() { 876 res := scc.Invoke(fakeStub) 877 Expect(res.Status).To(Equal(int32(500))) 878 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'ApproveChaincodeDefinitionForMyOrg': could not retrieve committed definition for chaincode 'cc_name': could not fetch definition")) 879 }) 880 }) 881 882 Context("when the underlying function implementation fails", func() { 883 BeforeEach(func() { 884 fakeSCCFuncs.ApproveChaincodeDefinitionForOrgReturns(fmt.Errorf("underlying-error")) 885 }) 886 887 It("wraps and returns the error", func() { 888 res := scc.Invoke(fakeStub) 889 Expect(res.Status).To(Equal(int32(500))) 890 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'ApproveChaincodeDefinitionForMyOrg': underlying-error")) 891 }) 892 }) 893 894 Context("when the lifecycle capability is not enabled", func() { 895 BeforeEach(func() { 896 fakeCapabilities.LifecycleV20Returns(false) 897 }) 898 899 It("returns an error", func() { 900 Expect(scc.Invoke(fakeStub)).To(Equal(shim.Error("cannot use new lifecycle for channel 'test-channel' as it does not have the required capabilities enabled"))) 901 }) 902 }) 903 }) 904 905 Describe("CommitChaincodeDefinition", func() { 906 var ( 907 err error 908 arg *lb.CommitChaincodeDefinitionArgs 909 marshaledArg []byte 910 fakeOrgConfigs []*mock.ApplicationOrgConfig 911 ) 912 913 BeforeEach(func() { 914 arg = &lb.CommitChaincodeDefinitionArgs{ 915 Sequence: 7, 916 Name: "cc-name2", 917 Version: "version-2+2", 918 EndorsementPlugin: "endorsement-plugin", 919 ValidationPlugin: "validation-plugin", 920 ValidationParameter: []byte("validation-parameter"), 921 Collections: &pb.CollectionConfigPackage{ 922 Config: []*pb.CollectionConfig{ 923 { 924 Payload: &pb.CollectionConfig_StaticCollectionConfig{ 925 StaticCollectionConfig: &pb.StaticCollectionConfig{ 926 Name: "test_collection", 927 MemberOrgsPolicy: &pb.CollectionPolicyConfig{ 928 Payload: &pb.CollectionPolicyConfig_SignaturePolicy{ 929 SignaturePolicy: policydsl.SignedByMspMember("org0"), 930 }, 931 }, 932 }, 933 }, 934 }, 935 }, 936 }, 937 InitRequired: true, 938 } 939 940 marshaledArg, err = proto.Marshal(arg) 941 Expect(err).NotTo(HaveOccurred()) 942 943 fakeStub.GetArgsReturns([][]byte{[]byte("CommitChaincodeDefinition"), marshaledArg}) 944 945 fakeOrgConfigs = []*mock.ApplicationOrgConfig{{}, {}} 946 fakeOrgConfigs[0].MSPIDReturns("fake-mspid") 947 fakeOrgConfigs[1].MSPIDReturns("other-mspid") 948 949 fakeApplicationConfig.OrganizationsReturns(map[string]channelconfig.ApplicationOrg{ 950 "org0": fakeOrgConfigs[0], 951 "org1": fakeOrgConfigs[1], 952 }) 953 954 fakeSCCFuncs.CommitChaincodeDefinitionReturns(map[string]bool{ 955 "fake-mspid": true, 956 "other-mspid": true, 957 }, nil) 958 959 fakeMsp := &mock.MSP{} 960 fakeMSPManager.GetMSPsReturns( 961 map[string]msp.MSP{ 962 "org0": fakeMsp, 963 "org1": fakeMsp, 964 }, 965 nil, 966 ) 967 }) 968 969 It("passes the arguments to and returns the results from the backing scc function implementation", func() { 970 res := scc.Invoke(fakeStub) 971 Expect(res.Message).To(Equal("")) 972 Expect(res.Status).To(Equal(int32(200))) 973 payload := &lb.CommitChaincodeDefinitionResult{} 974 err = proto.Unmarshal(res.Payload, payload) 975 Expect(err).NotTo(HaveOccurred()) 976 977 Expect(fakeSCCFuncs.CommitChaincodeDefinitionCallCount()).To(Equal(1)) 978 chname, ccname, cd, pubState, orgStates := fakeSCCFuncs.CommitChaincodeDefinitionArgsForCall(0) 979 Expect(chname).To(Equal("test-channel")) 980 Expect(ccname).To(Equal("cc-name2")) 981 Expect(cd).To(Equal(&lifecycle.ChaincodeDefinition{ 982 Sequence: 7, 983 EndorsementInfo: &lb.ChaincodeEndorsementInfo{ 984 Version: "version-2+2", 985 EndorsementPlugin: "endorsement-plugin", 986 InitRequired: true, 987 }, 988 ValidationInfo: &lb.ChaincodeValidationInfo{ 989 ValidationPlugin: "validation-plugin", 990 ValidationParameter: []byte("validation-parameter"), 991 }, 992 Collections: &pb.CollectionConfigPackage{ 993 Config: []*pb.CollectionConfig{ 994 { 995 Payload: &pb.CollectionConfig_StaticCollectionConfig{ 996 StaticCollectionConfig: &pb.StaticCollectionConfig{ 997 Name: "test_collection", 998 MemberOrgsPolicy: &pb.CollectionPolicyConfig{ 999 Payload: &pb.CollectionPolicyConfig_SignaturePolicy{ 1000 SignaturePolicy: policydsl.SignedByMspMember("org0"), 1001 }, 1002 }, 1003 }, 1004 }, 1005 }, 1006 }, 1007 }, 1008 })) 1009 Expect(pubState).To(Equal(fakeStub)) 1010 Expect(len(orgStates)).To(Equal(2)) 1011 Expect(orgStates[0]).To(BeAssignableToTypeOf(&lifecycle.ChaincodePrivateLedgerShim{})) 1012 Expect(orgStates[1]).To(BeAssignableToTypeOf(&lifecycle.ChaincodePrivateLedgerShim{})) 1013 collection0 := orgStates[0].(*lifecycle.ChaincodePrivateLedgerShim).Collection 1014 collection1 := orgStates[1].(*lifecycle.ChaincodePrivateLedgerShim).Collection 1015 Expect([]string{collection0, collection1}).To(ConsistOf("_implicit_org_fake-mspid", "_implicit_org_other-mspid")) 1016 }) 1017 1018 Context("when the chaincode name begins with an invalid character", func() { 1019 BeforeEach(func() { 1020 arg.Name = "_invalid" 1021 1022 marshaledArg, err = proto.Marshal(arg) 1023 Expect(err).NotTo(HaveOccurred()) 1024 fakeStub.GetArgsReturns([][]byte{[]byte("CommitChaincodeDefinition"), marshaledArg}) 1025 }) 1026 1027 It("wraps and returns the error", func() { 1028 res := scc.Invoke(fakeStub) 1029 Expect(res.Status).To(Equal(int32(500))) 1030 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'CommitChaincodeDefinition': invalid chaincode name '_invalid'. Names can only consist of alphanumerics, '_', and '-' and can only begin with alphanumerics")) 1031 }) 1032 }) 1033 1034 Context("when the chaincode version contains invalid characters", func() { 1035 BeforeEach(func() { 1036 arg.Version = "$money$" 1037 1038 marshaledArg, err = proto.Marshal(arg) 1039 Expect(err).NotTo(HaveOccurred()) 1040 fakeStub.GetArgsReturns([][]byte{[]byte("CommitChaincodeDefinition"), marshaledArg}) 1041 }) 1042 1043 It("wraps and returns the error", func() { 1044 res := scc.Invoke(fakeStub) 1045 Expect(res.Status).To(Equal(int32(500))) 1046 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'CommitChaincodeDefinition': invalid chaincode version '$money$'. Versions can only consist of alphanumerics, '_', '-', '+', and '.'")) 1047 }) 1048 }) 1049 1050 Context("when the chaincode name matches an existing system chaincode name", func() { 1051 BeforeEach(func() { 1052 arg.Name = "qscc" 1053 1054 marshaledArg, err = proto.Marshal(arg) 1055 Expect(err).NotTo(HaveOccurred()) 1056 fakeStub.GetArgsReturns([][]byte{[]byte("CommitChaincodeDefinition"), marshaledArg}) 1057 }) 1058 1059 It("wraps and returns the error", func() { 1060 res := scc.Invoke(fakeStub) 1061 Expect(res.Status).To(Equal(int32(500))) 1062 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'CommitChaincodeDefinition': chaincode name 'qscc' is the name of a system chaincode")) 1063 }) 1064 }) 1065 1066 Context("when a collection name contains invalid characters", func() { 1067 BeforeEach(func() { 1068 arg.Collections = &pb.CollectionConfigPackage{ 1069 Config: []*pb.CollectionConfig{ 1070 { 1071 Payload: &pb.CollectionConfig_StaticCollectionConfig{ 1072 StaticCollectionConfig: &pb.StaticCollectionConfig{ 1073 Name: "collection(test", 1074 }, 1075 }, 1076 }, 1077 }, 1078 } 1079 1080 marshaledArg, err = proto.Marshal(arg) 1081 Expect(err).NotTo(HaveOccurred()) 1082 fakeStub.GetArgsReturns([][]byte{[]byte("CommitChaincodeDefinition"), marshaledArg}) 1083 }) 1084 1085 It("wraps and returns the error", func() { 1086 res := scc.Invoke(fakeStub) 1087 Expect(res.Status).To(Equal(int32(500))) 1088 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'CommitChaincodeDefinition': invalid collection name 'collection(test'. Names can only consist of alphanumerics, '_', and '-' and cannot begin with '_'")) 1089 }) 1090 }) 1091 1092 Context("when a collection name begins with an invalid character", func() { 1093 BeforeEach(func() { 1094 arg.Collections = &pb.CollectionConfigPackage{ 1095 Config: []*pb.CollectionConfig{ 1096 { 1097 Payload: &pb.CollectionConfig_StaticCollectionConfig{ 1098 StaticCollectionConfig: &pb.StaticCollectionConfig{ 1099 Name: "&collection", 1100 }, 1101 }, 1102 }, 1103 }, 1104 } 1105 1106 marshaledArg, err = proto.Marshal(arg) 1107 Expect(err).NotTo(HaveOccurred()) 1108 fakeStub.GetArgsReturns([][]byte{[]byte("CommitChaincodeDefinition"), marshaledArg}) 1109 }) 1110 1111 It("wraps and returns the error", func() { 1112 res := scc.Invoke(fakeStub) 1113 Expect(res.Status).To(Equal(int32(500))) 1114 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'CommitChaincodeDefinition': invalid collection name '&collection'. Names can only consist of alphanumerics, '_', and '-' and cannot begin with '_'")) 1115 }) 1116 }) 1117 1118 Context("when there is no agreement from this peer's org", func() { 1119 BeforeEach(func() { 1120 fakeSCCFuncs.CommitChaincodeDefinitionReturns(map[string]bool{ 1121 "fake-mspid": false, 1122 "other-mspid": false, 1123 }, nil) 1124 }) 1125 1126 It("returns an error indicating the lack of agreement", func() { 1127 res := scc.Invoke(fakeStub) 1128 Expect(res.Status).To(Equal(int32(500))) 1129 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'CommitChaincodeDefinition': chaincode definition not agreed to by this org (fake-mspid)")) 1130 }) 1131 }) 1132 1133 Context("when there is no match for this peer's org's MSPID", func() { 1134 BeforeEach(func() { 1135 fakeOrgConfigs[0].MSPIDReturns("other-mspid") 1136 }) 1137 1138 It("returns an error indicating the lack of agreement", func() { 1139 res := scc.Invoke(fakeStub) 1140 Expect(res.Status).To(Equal(int32(500))) 1141 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'CommitChaincodeDefinition': impossibly, this peer's org is processing requests for a channel it is not a member of")) 1142 }) 1143 }) 1144 1145 Context("when there is no channel config", func() { 1146 BeforeEach(func() { 1147 fakeChannelConfigSource.GetStableChannelConfigReturns(nil) 1148 }) 1149 1150 It("returns an error indicating the lack of agreement", func() { 1151 res := scc.Invoke(fakeStub) 1152 Expect(res.Status).To(Equal(int32(500))) 1153 Expect(res.Message).To(Equal("could not get channelconfig for channel 'test-channel'")) 1154 }) 1155 }) 1156 1157 Context("when there is no application config", func() { 1158 BeforeEach(func() { 1159 fakeChannelConfig.ApplicationConfigReturns(nil, false) 1160 }) 1161 1162 It("returns an error indicating the lack of agreement", func() { 1163 res := scc.Invoke(fakeStub) 1164 Expect(res.Status).To(Equal(int32(500))) 1165 Expect(res.Message).To(Equal("could not get application config for channel 'test-channel'")) 1166 }) 1167 1168 Context("when there is no application config because there is no channel", func() { 1169 BeforeEach(func() { 1170 fakeStub.GetChannelIDReturns("") 1171 }) 1172 1173 It("returns an error indicating the lack of agreement", func() { 1174 res := scc.Invoke(fakeStub) 1175 Expect(res.Status).To(Equal(int32(500))) 1176 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'CommitChaincodeDefinition': no application config for channel ''")) 1177 }) 1178 }) 1179 }) 1180 1181 Context("when the underlying function implementation fails", func() { 1182 BeforeEach(func() { 1183 fakeSCCFuncs.CommitChaincodeDefinitionReturns(nil, fmt.Errorf("underlying-error")) 1184 }) 1185 1186 It("wraps and returns the error", func() { 1187 res := scc.Invoke(fakeStub) 1188 Expect(res.Status).To(Equal(int32(500))) 1189 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'CommitChaincodeDefinition': underlying-error")) 1190 }) 1191 }) 1192 }) 1193 1194 Describe("CheckCommitReadiness", func() { 1195 var ( 1196 err error 1197 arg *lb.CheckCommitReadinessArgs 1198 marshaledArg []byte 1199 fakeOrgConfigs []*mock.ApplicationOrgConfig 1200 ) 1201 1202 BeforeEach(func() { 1203 arg = &lb.CheckCommitReadinessArgs{ 1204 Sequence: 7, 1205 Name: "name", 1206 Version: "version", 1207 EndorsementPlugin: "endorsement-plugin", 1208 ValidationPlugin: "validation-plugin", 1209 ValidationParameter: []byte("validation-parameter"), 1210 Collections: &pb.CollectionConfigPackage{}, 1211 InitRequired: true, 1212 } 1213 1214 marshaledArg, err = proto.Marshal(arg) 1215 Expect(err).NotTo(HaveOccurred()) 1216 1217 fakeStub.GetArgsReturns([][]byte{[]byte("CheckCommitReadiness"), marshaledArg}) 1218 1219 fakeOrgConfigs = []*mock.ApplicationOrgConfig{{}, {}} 1220 fakeOrgConfigs[0].MSPIDReturns("fake-mspid") 1221 fakeOrgConfigs[1].MSPIDReturns("other-mspid") 1222 1223 fakeApplicationConfig.OrganizationsReturns(map[string]channelconfig.ApplicationOrg{ 1224 "org0": fakeOrgConfigs[0], 1225 "org1": fakeOrgConfigs[1], 1226 }) 1227 1228 fakeSCCFuncs.CheckCommitReadinessReturns(map[string]bool{ 1229 "fake-mspid": true, 1230 "other-mspid": true, 1231 }, nil) 1232 }) 1233 1234 It("passes the arguments to and returns the results from the backing scc function implementation", func() { 1235 res := scc.Invoke(fakeStub) 1236 Expect(res.Message).To(Equal("")) 1237 Expect(res.Status).To(Equal(int32(200))) 1238 payload := &lb.CheckCommitReadinessResult{} 1239 err = proto.Unmarshal(res.Payload, payload) 1240 Expect(err).NotTo(HaveOccurred()) 1241 1242 orgApprovals := payload.GetApprovals() 1243 Expect(orgApprovals).NotTo(BeNil()) 1244 Expect(len(orgApprovals)).To(Equal(2)) 1245 Expect(orgApprovals["fake-mspid"]).To(BeTrue()) 1246 Expect(orgApprovals["other-mspid"]).To(BeTrue()) 1247 1248 Expect(fakeSCCFuncs.CheckCommitReadinessCallCount()).To(Equal(1)) 1249 chname, ccname, cd, pubState, orgStates := fakeSCCFuncs.CheckCommitReadinessArgsForCall(0) 1250 Expect(chname).To(Equal("test-channel")) 1251 Expect(ccname).To(Equal("name")) 1252 Expect(cd).To(Equal(&lifecycle.ChaincodeDefinition{ 1253 Sequence: 7, 1254 EndorsementInfo: &lb.ChaincodeEndorsementInfo{ 1255 Version: "version", 1256 EndorsementPlugin: "endorsement-plugin", 1257 InitRequired: true, 1258 }, 1259 ValidationInfo: &lb.ChaincodeValidationInfo{ 1260 ValidationPlugin: "validation-plugin", 1261 ValidationParameter: []byte("validation-parameter"), 1262 }, 1263 Collections: arg.Collections, 1264 })) 1265 Expect(pubState).To(Equal(fakeStub)) 1266 Expect(orgStates).To(HaveLen(2)) 1267 Expect(orgStates[0]).To(BeAssignableToTypeOf(&lifecycle.ChaincodePrivateLedgerShim{})) 1268 Expect(orgStates[1]).To(BeAssignableToTypeOf(&lifecycle.ChaincodePrivateLedgerShim{})) 1269 collection0 := orgStates[0].(*lifecycle.ChaincodePrivateLedgerShim).Collection 1270 collection1 := orgStates[1].(*lifecycle.ChaincodePrivateLedgerShim).Collection 1271 Expect([]string{collection0, collection1}).To(ConsistOf("_implicit_org_fake-mspid", "_implicit_org_other-mspid")) 1272 }) 1273 1274 Context("when there is no application config", func() { 1275 BeforeEach(func() { 1276 fakeChannelConfig.ApplicationConfigReturns(nil, false) 1277 }) 1278 1279 It("returns an error", func() { 1280 res := scc.Invoke(fakeStub) 1281 Expect(res.Status).To(Equal(int32(500))) 1282 Expect(res.Message).To(Equal("could not get application config for channel 'test-channel'")) 1283 }) 1284 1285 Context("when there is no application config because there is no channel", func() { 1286 BeforeEach(func() { 1287 fakeStub.GetChannelIDReturns("") 1288 }) 1289 1290 It("returns an error", func() { 1291 res := scc.Invoke(fakeStub) 1292 Expect(res.Status).To(Equal(int32(500))) 1293 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'CheckCommitReadiness': no application config for channel ''")) 1294 }) 1295 }) 1296 }) 1297 1298 Context("when the underlying function implementation fails", func() { 1299 BeforeEach(func() { 1300 fakeSCCFuncs.CheckCommitReadinessReturns(nil, fmt.Errorf("underlying-error")) 1301 }) 1302 1303 It("wraps and returns the error", func() { 1304 res := scc.Invoke(fakeStub) 1305 Expect(res.Status).To(Equal(int32(500))) 1306 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'CheckCommitReadiness': underlying-error")) 1307 }) 1308 }) 1309 }) 1310 1311 Describe("QueryChaincodeDefinition", func() { 1312 var ( 1313 arg *lb.QueryChaincodeDefinitionArgs 1314 marshaledArg []byte 1315 fakeOrgConfigs []*mock.ApplicationOrgConfig 1316 ) 1317 1318 BeforeEach(func() { 1319 arg = &lb.QueryChaincodeDefinitionArgs{ 1320 Name: "cc-name", 1321 } 1322 1323 var err error 1324 marshaledArg, err = proto.Marshal(arg) 1325 Expect(err).NotTo(HaveOccurred()) 1326 1327 fakeOrgConfigs = []*mock.ApplicationOrgConfig{{}, {}} 1328 fakeOrgConfigs[0].MSPIDReturns("fake-mspid") 1329 fakeOrgConfigs[1].MSPIDReturns("other-mspid") 1330 1331 fakeApplicationConfig.OrganizationsReturns(map[string]channelconfig.ApplicationOrg{ 1332 "org0": fakeOrgConfigs[0], 1333 "org1": fakeOrgConfigs[1], 1334 }) 1335 1336 fakeStub.GetArgsReturns([][]byte{[]byte("QueryChaincodeDefinition"), marshaledArg}) 1337 fakeSCCFuncs.QueryChaincodeDefinitionReturns( 1338 &lifecycle.ChaincodeDefinition{ 1339 Sequence: 2, 1340 EndorsementInfo: &lb.ChaincodeEndorsementInfo{ 1341 Version: "version", 1342 EndorsementPlugin: "endorsement-plugin", 1343 }, 1344 ValidationInfo: &lb.ChaincodeValidationInfo{ 1345 ValidationPlugin: "validation-plugin", 1346 ValidationParameter: []byte("validation-parameter"), 1347 }, 1348 Collections: &pb.CollectionConfigPackage{}, 1349 }, 1350 nil, 1351 ) 1352 1353 fakeSCCFuncs.QueryOrgApprovalsReturns( 1354 map[string]bool{ 1355 "fake-mspid": true, 1356 "other-mspid": true, 1357 }, 1358 nil, 1359 ) 1360 }) 1361 1362 It("passes the arguments to and returns the results from the backing scc function implementation", func() { 1363 res := scc.Invoke(fakeStub) 1364 Expect(res.Status).To(Equal(int32(200))) 1365 payload := &lb.QueryChaincodeDefinitionResult{} 1366 err := proto.Unmarshal(res.Payload, payload) 1367 Expect(err).NotTo(HaveOccurred()) 1368 Expect(proto.Equal(payload, &lb.QueryChaincodeDefinitionResult{ 1369 Sequence: 2, 1370 Version: "version", 1371 EndorsementPlugin: "endorsement-plugin", 1372 ValidationPlugin: "validation-plugin", 1373 ValidationParameter: []byte("validation-parameter"), 1374 Collections: &pb.CollectionConfigPackage{}, 1375 Approvals: map[string]bool{ 1376 "fake-mspid": true, 1377 "other-mspid": true, 1378 }, 1379 })).To(BeTrue()) 1380 1381 Expect(fakeSCCFuncs.QueryChaincodeDefinitionCallCount()).To(Equal(1)) 1382 name, pubState := fakeSCCFuncs.QueryChaincodeDefinitionArgsForCall(0) 1383 Expect(name).To(Equal("cc-name")) 1384 Expect(pubState).To(Equal(fakeStub)) 1385 name, _, orgStates := fakeSCCFuncs.QueryOrgApprovalsArgsForCall(0) 1386 Expect(name).To(Equal("cc-name")) 1387 Expect(orgStates).To(HaveLen(2)) 1388 Expect(orgStates[0]).To(BeAssignableToTypeOf(&lifecycle.ChaincodePrivateLedgerShim{})) 1389 Expect(orgStates[1]).To(BeAssignableToTypeOf(&lifecycle.ChaincodePrivateLedgerShim{})) 1390 collection0 := orgStates[0].(*lifecycle.ChaincodePrivateLedgerShim).Collection 1391 collection1 := orgStates[1].(*lifecycle.ChaincodePrivateLedgerShim).Collection 1392 Expect([]string{collection0, collection1}).To(ConsistOf("_implicit_org_fake-mspid", "_implicit_org_other-mspid")) 1393 }) 1394 1395 Context("when the underlying QueryChaincodeDefinition function implementation fails", func() { 1396 BeforeEach(func() { 1397 fakeSCCFuncs.QueryChaincodeDefinitionReturns(nil, fmt.Errorf("underlying-error")) 1398 }) 1399 1400 It("wraps and returns the error", func() { 1401 res := scc.Invoke(fakeStub) 1402 Expect(res.Status).To(Equal(int32(500))) 1403 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'QueryChaincodeDefinition': underlying-error")) 1404 }) 1405 }) 1406 1407 Context("when the underlying QueryOrgApprovals function implementation fails", func() { 1408 BeforeEach(func() { 1409 fakeSCCFuncs.QueryOrgApprovalsReturns(nil, fmt.Errorf("underlying-error")) 1410 }) 1411 1412 It("wraps and returns the error", func() { 1413 res := scc.Invoke(fakeStub) 1414 Expect(res.Status).To(Equal(int32(500))) 1415 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'QueryChaincodeDefinition': underlying-error")) 1416 }) 1417 }) 1418 1419 Context("when the namespace cannot be found", func() { 1420 BeforeEach(func() { 1421 fakeSCCFuncs.QueryChaincodeDefinitionReturns(nil, lifecycle.ErrNamespaceNotDefined{Namespace: "nicetry"}) 1422 }) 1423 1424 It("returns 404 Not Found", func() { 1425 res := scc.Invoke(fakeStub) 1426 Expect(res.Status).To(Equal(int32(404))) 1427 Expect(res.Message).To(Equal("namespace nicetry is not defined")) 1428 }) 1429 }) 1430 1431 Context("when there is no application config", func() { 1432 BeforeEach(func() { 1433 fakeChannelConfig.ApplicationConfigReturns(nil, false) 1434 }) 1435 1436 It("returns an error", func() { 1437 res := scc.Invoke(fakeStub) 1438 Expect(res.Status).To(Equal(int32(500))) 1439 Expect(res.Message).To(Equal("could not get application config for channel 'test-channel'")) 1440 }) 1441 1442 Context("when there is no application config because there is no channel", func() { 1443 BeforeEach(func() { 1444 fakeStub.GetChannelIDReturns("") 1445 }) 1446 1447 It("returns an error", func() { 1448 res := scc.Invoke(fakeStub) 1449 Expect(res.Status).To(Equal(int32(500))) 1450 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'QueryChaincodeDefinition': no application config for channel ''")) 1451 }) 1452 }) 1453 }) 1454 }) 1455 1456 Describe("QueryChaincodeDefinitions", func() { 1457 var ( 1458 arg *lb.QueryChaincodeDefinitionsArgs 1459 marshaledArg []byte 1460 ) 1461 1462 BeforeEach(func() { 1463 arg = &lb.QueryChaincodeDefinitionsArgs{} 1464 1465 var err error 1466 marshaledArg, err = proto.Marshal(arg) 1467 Expect(err).NotTo(HaveOccurred()) 1468 1469 fakeStub.GetArgsReturns([][]byte{[]byte("QueryChaincodeDefinitions"), marshaledArg}) 1470 fakeSCCFuncs.QueryNamespaceDefinitionsReturns(map[string]string{ 1471 "foo": "Chaincode", 1472 "bar": "Token", 1473 "woo": "Chaincode", 1474 }, nil) 1475 fakeSCCFuncs.QueryChaincodeDefinitionStub = func(name string, rs lifecycle.ReadableState) (*lifecycle.ChaincodeDefinition, error) { 1476 cd := &lifecycle.ChaincodeDefinition{ 1477 Sequence: 2, 1478 EndorsementInfo: &lb.ChaincodeEndorsementInfo{ 1479 Version: "version", 1480 EndorsementPlugin: "endorsement-plugin", 1481 }, 1482 ValidationInfo: &lb.ChaincodeValidationInfo{ 1483 ValidationPlugin: "validation-plugin", 1484 ValidationParameter: []byte("validation-parameter"), 1485 }, 1486 Collections: &pb.CollectionConfigPackage{}, 1487 } 1488 1489 if name == "woo" { 1490 cd.Sequence = 5 1491 } 1492 1493 return cd, nil 1494 } 1495 }) 1496 1497 It("passes the arguments to and returns the results from the backing scc function implementation", func() { 1498 res := scc.Invoke(fakeStub) 1499 Expect(res.Status).To(Equal(int32(200))) 1500 payload := &lb.QueryChaincodeDefinitionsResult{} 1501 err := proto.Unmarshal(res.Payload, payload) 1502 Expect(err).NotTo(HaveOccurred()) 1503 Expect(payload.GetChaincodeDefinitions()).To(ConsistOf( 1504 &lb.QueryChaincodeDefinitionsResult_ChaincodeDefinition{ 1505 Name: "foo", 1506 Sequence: 2, 1507 Version: "version", 1508 EndorsementPlugin: "endorsement-plugin", 1509 ValidationPlugin: "validation-plugin", 1510 ValidationParameter: []byte("validation-parameter"), 1511 Collections: &pb.CollectionConfigPackage{}, 1512 }, 1513 &lb.QueryChaincodeDefinitionsResult_ChaincodeDefinition{ 1514 Name: "woo", 1515 Sequence: 5, 1516 Version: "version", 1517 EndorsementPlugin: "endorsement-plugin", 1518 ValidationPlugin: "validation-plugin", 1519 ValidationParameter: []byte("validation-parameter"), 1520 Collections: &pb.CollectionConfigPackage{}, 1521 }, 1522 )) 1523 Expect(fakeSCCFuncs.QueryNamespaceDefinitionsCallCount()).To(Equal(1)) 1524 Expect(fakeSCCFuncs.QueryChaincodeDefinitionCallCount()).To(Equal(2)) 1525 }) 1526 1527 Context("when the underlying QueryChaincodeDefinition function implementation fails", func() { 1528 BeforeEach(func() { 1529 fakeSCCFuncs.QueryChaincodeDefinitionReturns(nil, fmt.Errorf("underlying-error")) 1530 }) 1531 1532 It("wraps and returns the error", func() { 1533 res := scc.Invoke(fakeStub) 1534 Expect(res.Status).To(Equal(int32(500))) 1535 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'QueryChaincodeDefinitions': underlying-error")) 1536 }) 1537 }) 1538 1539 Context("when the underlying QueryNamespaceDefinitions function implementation fails", func() { 1540 BeforeEach(func() { 1541 fakeSCCFuncs.QueryNamespaceDefinitionsReturns(nil, fmt.Errorf("underlying-error")) 1542 }) 1543 1544 It("wraps and returns the error", func() { 1545 res := scc.Invoke(fakeStub) 1546 Expect(res.Status).To(Equal(int32(500))) 1547 Expect(res.Message).To(Equal("failed to invoke backing implementation of 'QueryChaincodeDefinitions': underlying-error")) 1548 }) 1549 }) 1550 }) 1551 }) 1552 }) 1553 1554 type collectionConfigs []*collectionConfig 1555 1556 func (ccs collectionConfigs) deepCopy() collectionConfigs { 1557 var buf bytes.Buffer 1558 enc := gob.NewEncoder(&buf) 1559 dec := gob.NewDecoder(&buf) 1560 1561 err := enc.Encode(ccs) 1562 Expect(err).NotTo(HaveOccurred()) 1563 var newCCs collectionConfigs 1564 err = dec.Decode(&newCCs) 1565 Expect(err).NotTo(HaveOccurred()) 1566 return newCCs 1567 } 1568 1569 func (ccs collectionConfigs) toProtoCollectionConfigPackage() *pb.CollectionConfigPackage { 1570 if len(ccs) == 0 { 1571 return nil 1572 } 1573 collConfigsProtos := make([]*pb.CollectionConfig, len(ccs)) 1574 for i, c := range ccs { 1575 collConfigsProtos[i] = c.toCollectionConfigProto() 1576 } 1577 return &pb.CollectionConfigPackage{ 1578 Config: collConfigsProtos, 1579 } 1580 } 1581 1582 type collectionConfig struct { 1583 Name string 1584 RequiredPeerCount int32 1585 MaxPeerCount int32 1586 BlockToLive uint64 1587 1588 Policy string 1589 Identities []*mspprotos.MSPPrincipal 1590 UseGivenMemberOrgPolicy bool 1591 MemberOrgPolicy *pb.CollectionPolicyConfig 1592 } 1593 1594 func (cc *collectionConfig) toCollectionConfigProto() *pb.CollectionConfig { 1595 memberOrgPolicy := cc.MemberOrgPolicy 1596 if !cc.UseGivenMemberOrgPolicy { 1597 spe, err := policydsl.FromString(cc.Policy) 1598 Expect(err).NotTo(HaveOccurred()) 1599 spe.Identities = cc.Identities 1600 memberOrgPolicy = &pb.CollectionPolicyConfig{ 1601 Payload: &pb.CollectionPolicyConfig_SignaturePolicy{ 1602 SignaturePolicy: spe, 1603 }, 1604 } 1605 } 1606 return &pb.CollectionConfig{ 1607 Payload: &pb.CollectionConfig_StaticCollectionConfig{ 1608 StaticCollectionConfig: &pb.StaticCollectionConfig{ 1609 Name: cc.Name, 1610 MaximumPeerCount: cc.MaxPeerCount, 1611 RequiredPeerCount: cc.RequiredPeerCount, 1612 BlockToLive: cc.BlockToLive, 1613 MemberOrgsPolicy: memberOrgPolicy, 1614 }, 1615 }, 1616 } 1617 }