github.com/defanghe/fabric@v2.1.1+incompatible/core/chaincode/lifecycle/deployedcc_infoprovider_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 "fmt" 11 12 cb "github.com/hyperledger/fabric-protos-go/common" 13 "github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset" 14 "github.com/hyperledger/fabric-protos-go/msp" 15 pb "github.com/hyperledger/fabric-protos-go/peer" 16 lb "github.com/hyperledger/fabric-protos-go/peer/lifecycle" 17 "github.com/hyperledger/fabric/common/channelconfig" 18 "github.com/hyperledger/fabric/common/util" 19 "github.com/hyperledger/fabric/core/chaincode/lifecycle" 20 "github.com/hyperledger/fabric/core/chaincode/lifecycle/mock" 21 "github.com/hyperledger/fabric/core/ledger" 22 "github.com/hyperledger/fabric/core/peer" 23 "github.com/hyperledger/fabric/gossip/privdata" 24 "github.com/hyperledger/fabric/protoutil" 25 26 "github.com/golang/protobuf/proto" 27 28 . "github.com/onsi/ginkgo" 29 . "github.com/onsi/gomega" 30 ) 31 32 var _ = Describe("ValidatorCommitter", func() { 33 var ( 34 vc *lifecycle.ValidatorCommitter 35 resources *lifecycle.Resources 36 privdataConfig *privdata.PrivdataConfig 37 fakeLegacyProvider *mock.LegacyDeployedCCInfoProvider 38 fakeQueryExecutor *mock.SimpleQueryExecutor 39 fakeChannelConfigSource *mock.ChannelConfigSource 40 fakeChannelConfig *mock.ChannelConfig 41 fakeApplicationConfig *mock.ApplicationConfig 42 fakeOrgConfigs []*mock.ApplicationOrgConfig 43 fakePolicyManager *mock.PolicyManager 44 45 fakePublicState MapLedgerShim 46 ) 47 48 BeforeEach(func() { 49 fakeLegacyProvider = &mock.LegacyDeployedCCInfoProvider{} 50 fakeChannelConfigSource = &mock.ChannelConfigSource{} 51 fakeChannelConfig = &mock.ChannelConfig{} 52 fakeChannelConfigSource.GetStableChannelConfigReturns(fakeChannelConfig) 53 fakeApplicationConfig = &mock.ApplicationConfig{} 54 fakeChannelConfig.ApplicationConfigReturns(fakeApplicationConfig, true) 55 fakeOrgConfigs = []*mock.ApplicationOrgConfig{{}, {}} 56 fakeOrgConfigs[0].MSPIDReturns("first-mspid") 57 fakeOrgConfigs[1].MSPIDReturns("second-mspid") 58 fakePolicyManager = &mock.PolicyManager{} 59 fakePolicyManager.GetPolicyReturns(nil, true) 60 fakeChannelConfig.PolicyManagerReturns(fakePolicyManager) 61 62 fakeApplicationConfig.OrganizationsReturns(map[string]channelconfig.ApplicationOrg{ 63 "org0": fakeOrgConfigs[0], 64 "org1": fakeOrgConfigs[1], 65 }) 66 67 resources = &lifecycle.Resources{ 68 ChannelConfigSource: fakeChannelConfigSource, 69 Serializer: &lifecycle.Serializer{}, 70 } 71 72 privdataConfig = &privdata.PrivdataConfig{ 73 ImplicitCollDisseminationPolicy: privdata.ImplicitCollectionDisseminationPolicy{ 74 RequiredPeerCount: 1, 75 MaxPeerCount: 2, 76 }, 77 } 78 vc = &lifecycle.ValidatorCommitter{ 79 CoreConfig: &peer.Config{LocalMSPID: "first-mspid"}, 80 PrivdataConfig: privdataConfig, 81 Resources: resources, 82 LegacyDeployedCCInfoProvider: fakeLegacyProvider, 83 } 84 85 fakePublicState = MapLedgerShim(map[string][]byte{}) 86 fakeQueryExecutor = &mock.SimpleQueryExecutor{} 87 fakeQueryExecutor.GetStateStub = func(namespace, key string) ([]byte, error) { 88 return fakePublicState.GetState(key) 89 } 90 91 err := resources.Serializer.Serialize(lifecycle.NamespacesName, "cc-name", &lifecycle.ChaincodeDefinition{ 92 EndorsementInfo: &lb.ChaincodeEndorsementInfo{ 93 Version: "version", 94 }, 95 ValidationInfo: &lb.ChaincodeValidationInfo{ 96 ValidationPlugin: "validation-plugin", 97 ValidationParameter: []byte("validation-parameter"), 98 }, 99 Collections: &pb.CollectionConfigPackage{ 100 Config: []*pb.CollectionConfig{ 101 { 102 Payload: &pb.CollectionConfig_StaticCollectionConfig{ 103 StaticCollectionConfig: &pb.StaticCollectionConfig{ 104 Name: "collection-name", 105 }, 106 }, 107 }, 108 }, 109 }, 110 }, fakePublicState) 111 Expect(err).NotTo(HaveOccurred()) 112 }) 113 114 Describe("Namespaces", func() { 115 BeforeEach(func() { 116 fakeLegacyProvider.NamespacesReturns([]string{"a", "b", "c"}) 117 }) 118 119 It("appends its own namespaces the legacy impl", func() { 120 res := vc.Namespaces() 121 Expect(res).To(Equal([]string{"_lifecycle", "a", "b", "c"})) 122 Expect(fakeLegacyProvider.NamespacesCallCount()).To(Equal(1)) 123 }) 124 }) 125 126 Describe("UpdatedChaincodes", func() { 127 var ( 128 updates map[string][]*kvrwset.KVWrite 129 ) 130 131 BeforeEach(func() { 132 updates = map[string][]*kvrwset.KVWrite{ 133 "_lifecycle": { 134 {Key: "some/random/value"}, 135 {Key: "namespaces/fields/cc-name/Sequence"}, 136 {Key: "prefix/namespaces/fields/cc-name/Sequence"}, 137 {Key: "namespaces/fields/Sequence/infix"}, 138 {Key: "namespaces/fields/cc-name/Sequence/Postfix"}, 139 }, 140 "other-namespace": nil, 141 } 142 fakeLegacyProvider.UpdatedChaincodesReturns([]*ledger.ChaincodeLifecycleInfo{ 143 {Name: "foo"}, 144 {Name: "bar"}, 145 }, nil) 146 }) 147 148 It("checks its own namespace, then passes through to the legacy impl", func() { 149 res, err := vc.UpdatedChaincodes(updates) 150 Expect(res).To(Equal([]*ledger.ChaincodeLifecycleInfo{ 151 {Name: "cc-name"}, 152 {Name: "foo"}, 153 {Name: "bar"}, 154 })) 155 Expect(err).NotTo(HaveOccurred()) 156 Expect(fakeLegacyProvider.UpdatedChaincodesCallCount()).To(Equal(1)) 157 Expect(fakeLegacyProvider.UpdatedChaincodesArgsForCall(0)).To(Equal(updates)) 158 }) 159 160 Context("when the legacy provider returns an error", func() { 161 BeforeEach(func() { 162 fakeLegacyProvider.UpdatedChaincodesReturns(nil, fmt.Errorf("legacy-error")) 163 }) 164 165 It("wraps and returns the error", func() { 166 _, err := vc.UpdatedChaincodes(updates) 167 Expect(err).To(MatchError("error invoking legacy deployed cc info provider: legacy-error")) 168 }) 169 }) 170 }) 171 172 Describe("ChaincodeInfo", func() { 173 It("returns the info found in the new lifecycle", func() { 174 res, err := vc.ChaincodeInfo("channel-name", "cc-name", fakeQueryExecutor) 175 Expect(err).NotTo(HaveOccurred()) 176 Expect(res.Name).To(Equal("cc-name")) 177 Expect(res.Version).To(Equal("version")) 178 Expect(res.Hash).To(Equal(util.ComputeSHA256([]byte("cc-name:version")))) 179 Expect(len(res.ExplicitCollectionConfigPkg.Config)).To(Equal(1)) 180 }) 181 182 Context("when the requested chaincode is _lifecycle", func() { 183 It("returns the implicit collections only", func() { 184 res, err := vc.ChaincodeInfo("channel-name", "_lifecycle", fakeQueryExecutor) 185 Expect(err).NotTo(HaveOccurred()) 186 Expect(res.Name).To(Equal("_lifecycle")) 187 Expect(res.ExplicitCollectionConfigPkg).To(BeNil()) 188 }) 189 }) 190 191 Context("when the ledger returns an error", func() { 192 BeforeEach(func() { 193 fakeQueryExecutor.GetStateReturns(nil, fmt.Errorf("state-error")) 194 }) 195 196 It("wraps and returns the error", func() { 197 _, err := vc.ChaincodeInfo("channel-name", "cc-name", fakeQueryExecutor) 198 Expect(err).To(MatchError("could not get info about chaincode: could not deserialize metadata for chaincode cc-name: could not query metadata for namespace namespaces/cc-name: state-error")) 199 }) 200 }) 201 202 Context("when the chaincode cannot be found in the new lifecycle", func() { 203 BeforeEach(func() { 204 fakeLegacyProvider.ChaincodeInfoReturns(&ledger.DeployedChaincodeInfo{ 205 Name: "legacy-name", 206 Hash: []byte("hash"), 207 Version: "cc-version", 208 }, fmt.Errorf("chaincode-info-error")) 209 }) 210 211 It("passes through to the legacy impl", func() { 212 res, err := vc.ChaincodeInfo("channel-name", "legacy-name", fakeQueryExecutor) 213 Expect(res).To(Equal(&ledger.DeployedChaincodeInfo{ 214 Name: "legacy-name", 215 Hash: []byte("hash"), 216 Version: "cc-version", 217 })) 218 Expect(err).To(MatchError("chaincode-info-error")) 219 Expect(fakeLegacyProvider.ChaincodeInfoCallCount()).To(Equal(1)) 220 channelID, ccName, qe := fakeLegacyProvider.ChaincodeInfoArgsForCall(0) 221 Expect(channelID).To(Equal("channel-name")) 222 Expect(ccName).To(Equal("legacy-name")) 223 Expect(qe).To(Equal(fakeQueryExecutor)) 224 }) 225 }) 226 227 Context("when the data is corrupt", func() { 228 BeforeEach(func() { 229 fakePublicState["namespaces/fields/cc-name/ValidationInfo"] = []byte("garbage") 230 }) 231 232 It("wraps and returns that error", func() { 233 _, err := vc.ChaincodeInfo("channel-name", "cc-name", fakeQueryExecutor) 234 Expect(err).To(MatchError("could not get info about chaincode: could not deserialize chaincode definition for chaincode cc-name: could not unmarshal state for key namespaces/fields/cc-name/ValidationInfo: proto: can't skip unknown wire type 7")) 235 }) 236 }) 237 }) 238 239 Describe("CollectionInfo", func() { 240 It("returns the collection info as defined in the new lifecycle", func() { 241 res, err := vc.CollectionInfo("channel-name", "cc-name", "collection-name", fakeQueryExecutor) 242 Expect(err).NotTo(HaveOccurred()) 243 Expect(proto.Equal(res, &pb.StaticCollectionConfig{ 244 Name: "collection-name", 245 })).To(BeTrue()) 246 }) 247 248 Context("when no matching collection is found", func() { 249 It("returns nil", func() { 250 res, err := vc.CollectionInfo("channel-name", "cc-name", "non-extant-name", fakeQueryExecutor) 251 Expect(err).NotTo(HaveOccurred()) 252 Expect(res).To(BeNil()) 253 }) 254 }) 255 256 Context("when the chaincode in question is _lifecycle", func() { 257 It("skips the existence checks and checks the implicit collections", func() { 258 res, err := vc.CollectionInfo("channel-name", "_lifecycle", "_implicit_org_first-mspid", fakeQueryExecutor) 259 Expect(err).NotTo(HaveOccurred()) 260 Expect(res).NotTo(BeNil()) 261 }) 262 }) 263 264 Context("when the ledger returns an error", func() { 265 BeforeEach(func() { 266 fakeQueryExecutor.GetStateReturns(nil, fmt.Errorf("state-error")) 267 }) 268 269 It("wraps and returns the error", func() { 270 _, err := vc.CollectionInfo("channel-name", "cc-name", "collection-name", fakeQueryExecutor) 271 Expect(err).To(MatchError("could not get chaincode: could not deserialize metadata for chaincode cc-name: could not query metadata for namespace namespaces/cc-name: state-error")) 272 }) 273 }) 274 275 Context("when the chaincode is not in the new lifecycle", func() { 276 var ( 277 collInfo *pb.StaticCollectionConfig 278 ) 279 280 BeforeEach(func() { 281 collInfo = &pb.StaticCollectionConfig{} 282 fakeLegacyProvider.CollectionInfoReturns(collInfo, fmt.Errorf("collection-info-error")) 283 }) 284 285 It("passes through to the legacy impl", func() { 286 res, err := vc.CollectionInfo("channel-name", "legacy-name", "collection-name", fakeQueryExecutor) 287 Expect(res).To(Equal(collInfo)) 288 Expect(err).To(MatchError("collection-info-error")) 289 Expect(fakeLegacyProvider.CollectionInfoCallCount()).To(Equal(1)) 290 channelID, ccName, collName, qe := fakeLegacyProvider.CollectionInfoArgsForCall(0) 291 Expect(channelID).To(Equal("channel-name")) 292 Expect(ccName).To(Equal("legacy-name")) 293 Expect(collName).To(Equal("collection-name")) 294 Expect(qe).To(Equal(fakeQueryExecutor)) 295 }) 296 }) 297 298 Context("when the data is corrupt", func() { 299 BeforeEach(func() { 300 fakePublicState["namespaces/fields/cc-name/ValidationInfo"] = []byte("garbage") 301 }) 302 303 It("wraps and returns that error", func() { 304 _, err := vc.CollectionInfo("channel-name", "cc-name", "collection-name", fakeQueryExecutor) 305 Expect(err).To(MatchError("could not get chaincode: could not deserialize chaincode definition for chaincode cc-name: could not unmarshal state for key namespaces/fields/cc-name/ValidationInfo: proto: can't skip unknown wire type 7")) 306 }) 307 }) 308 }) 309 310 Describe("ImplicitCollections", func() { 311 It("returns an implicit collection for every org", func() { 312 res, err := vc.ImplicitCollections("channel-id", "cc-name", fakeQueryExecutor) 313 Expect(err).NotTo(HaveOccurred()) 314 Expect(len(res)).To(Equal(2)) 315 var firstOrg, secondOrg *pb.StaticCollectionConfig 316 for _, collection := range res { 317 switch collection.Name { 318 case "_implicit_org_first-mspid": 319 firstOrg = collection 320 case "_implicit_org_second-mspid": 321 secondOrg = collection 322 } 323 } 324 // Required/MaxPeerCount should match privdataConfig when the implicit collection is for peer's own org 325 Expect(firstOrg).NotTo(BeNil()) 326 Expect(firstOrg.RequiredPeerCount).To(Equal(int32(privdataConfig.ImplicitCollDisseminationPolicy.RequiredPeerCount))) 327 Expect(firstOrg.MaximumPeerCount).To(Equal(int32(privdataConfig.ImplicitCollDisseminationPolicy.MaxPeerCount))) 328 // Required/MaxPeerCount should be 0 when the implicit collection is for other org 329 Expect(secondOrg).NotTo(BeNil()) 330 Expect(secondOrg.RequiredPeerCount).To(Equal(int32(0))) 331 Expect(secondOrg.MaximumPeerCount).To(Equal(int32(0))) 332 }) 333 334 Context("when the chaincode does not exist", func() { 335 It("returns nil, nil", func() { 336 res, err := vc.ImplicitCollections("channel-id", "missing-name", fakeQueryExecutor) 337 Expect(err).NotTo(HaveOccurred()) 338 Expect(res).To(BeNil()) 339 }) 340 }) 341 342 Context("when the ledger returns an error", func() { 343 BeforeEach(func() { 344 fakeQueryExecutor.GetStateReturns(nil, fmt.Errorf("state-error")) 345 }) 346 347 It("wraps and returns the error", func() { 348 _, err := vc.ImplicitCollections("channel-id", "missing-name", fakeQueryExecutor) 349 Expect(err).To(MatchError("could not get info about chaincode: could not deserialize metadata for chaincode missing-name: could not query metadata for namespace namespaces/missing-name: state-error")) 350 }) 351 }) 352 353 Context("when there is no channel config", func() { 354 BeforeEach(func() { 355 fakeChannelConfigSource.GetStableChannelConfigReturns(nil) 356 }) 357 358 It("returns an error", func() { 359 _, err := vc.ImplicitCollections("channel-id", "cc-name", fakeQueryExecutor) 360 Expect(err).To(MatchError("could not get channelconfig for channel channel-id")) 361 }) 362 }) 363 364 Context("when there is no application config", func() { 365 BeforeEach(func() { 366 fakeChannelConfig.ApplicationConfigReturns(nil, false) 367 }) 368 369 It("returns an error", func() { 370 _, err := vc.ImplicitCollections("channel-id", "cc-name", fakeQueryExecutor) 371 Expect(err).To(MatchError("could not get application config for channel channel-id")) 372 }) 373 }) 374 }) 375 376 Describe("AllCollectionsConfigPkg", func() { 377 It("returns the collection config package that includes both explicit and implicit collections as defined in the new lifecycle", func() { 378 ccPkg, err := vc.AllCollectionsConfigPkg("channel-name", "cc-name", fakeQueryExecutor) 379 Expect(err).NotTo(HaveOccurred()) 380 collectionNames := []string{} 381 for _, config := range ccPkg.Config { 382 collectionNames = append(collectionNames, config.GetStaticCollectionConfig().GetName()) 383 } 384 Expect(collectionNames).Should(ConsistOf("collection-name", "_implicit_org_first-mspid", "_implicit_org_second-mspid")) 385 }) 386 387 Context("when no explicit collection config is defined", func() { 388 BeforeEach(func() { 389 err := resources.Serializer.Serialize(lifecycle.NamespacesName, "cc-without-explicit-collection", 390 &lifecycle.ChaincodeDefinition{ 391 EndorsementInfo: &lb.ChaincodeEndorsementInfo{ 392 Version: "version", 393 }, 394 ValidationInfo: &lb.ChaincodeValidationInfo{ 395 ValidationPlugin: "validation-plugin", 396 ValidationParameter: []byte("validation-parameter"), 397 }, 398 }, fakePublicState) 399 Expect(err).NotTo(HaveOccurred()) 400 }) 401 402 It("returns only implicit collections", func() { 403 ccPkg, err := vc.AllCollectionsConfigPkg("channel-name", "cc-without-explicit-collection", fakeQueryExecutor) 404 Expect(err).NotTo(HaveOccurred()) 405 collectionNames := []string{} 406 for _, config := range ccPkg.Config { 407 collectionNames = append(collectionNames, config.GetStaticCollectionConfig().GetName()) 408 } 409 Expect(collectionNames).Should(ConsistOf("_implicit_org_first-mspid", "_implicit_org_second-mspid")) 410 }) 411 }) 412 413 Context("when the ledger returns an error", func() { 414 BeforeEach(func() { 415 fakeQueryExecutor.GetStateReturns(nil, fmt.Errorf("state-error")) 416 }) 417 418 It("wraps and returns the error", func() { 419 _, err := vc.AllCollectionsConfigPkg("channel-name", "cc-name", fakeQueryExecutor) 420 Expect(err).To(MatchError("could not get info about chaincode: could not deserialize metadata for chaincode cc-name: could not query metadata for namespace namespaces/cc-name: state-error")) 421 }) 422 }) 423 424 Context("when there is no channel config", func() { 425 BeforeEach(func() { 426 fakeChannelConfigSource.GetStableChannelConfigReturns(nil) 427 }) 428 429 It("returns an error", func() { 430 _, err := vc.AllCollectionsConfigPkg("channel-id", "cc-name", fakeQueryExecutor) 431 Expect(err).To(MatchError("could not get channelconfig for channel channel-id")) 432 }) 433 }) 434 435 Context("when there is no application config", func() { 436 BeforeEach(func() { 437 fakeChannelConfig.ApplicationConfigReturns(nil, false) 438 }) 439 440 It("returns an error", func() { 441 _, err := vc.AllCollectionsConfigPkg("channel-id", "cc-name", fakeQueryExecutor) 442 Expect(err).To(MatchError("could not get application config for channel channel-id")) 443 }) 444 }) 445 446 Context("when the chaincode is not in the new lifecycle", func() { 447 var ( 448 ccPkg *pb.CollectionConfigPackage 449 ) 450 451 BeforeEach(func() { 452 ccPkg = &pb.CollectionConfigPackage{} 453 fakeLegacyProvider.ChaincodeInfoReturns( 454 &ledger.DeployedChaincodeInfo{ 455 ExplicitCollectionConfigPkg: ccPkg, 456 IsLegacy: true, 457 }, 458 nil, 459 ) 460 }) 461 462 It("passes through to the legacy impl", func() { 463 res, err := vc.AllCollectionsConfigPkg("channel-name", "legacy-name", fakeQueryExecutor) 464 Expect(fakeLegacyProvider.ChaincodeInfoCallCount()).To(Equal(1)) 465 channelID, ccName, qe := fakeLegacyProvider.ChaincodeInfoArgsForCall(0) 466 Expect(channelID).To(Equal("channel-name")) 467 Expect(ccName).To(Equal("legacy-name")) 468 Expect(qe).To(Equal(fakeQueryExecutor)) 469 Expect(res).To(Equal(ccPkg)) 470 Expect(err).NotTo(HaveOccurred()) 471 }) 472 }) 473 474 Context("when the chaincode is not in the new lifecycle and legacy info provider returns error", func() { 475 BeforeEach(func() { 476 fakeLegacyProvider.ChaincodeInfoReturns(nil, fmt.Errorf("legacy-chaincode-info-error")) 477 }) 478 479 It("passes through to the legacy impl", func() { 480 _, err := vc.AllCollectionsConfigPkg("channel-name", "legacy-name", fakeQueryExecutor) 481 Expect(err).To(MatchError("legacy-chaincode-info-error")) 482 }) 483 }) 484 485 Context("when the data is corrupt", func() { 486 BeforeEach(func() { 487 fakePublicState["namespaces/fields/cc-name/ValidationInfo"] = []byte("garbage") 488 }) 489 490 It("wraps and returns that error", func() { 491 _, err := vc.AllCollectionsConfigPkg("channel-name", "cc-name", fakeQueryExecutor) 492 Expect(err).To(MatchError("could not get info about chaincode: could not deserialize chaincode definition for chaincode cc-name: could not unmarshal state for key namespaces/fields/cc-name/ValidationInfo: proto: can't skip unknown wire type 7")) 493 }) 494 }) 495 }) 496 497 Describe("ValidationInfo", func() { 498 It("returns the validation info as defined in the new lifecycle", func() { 499 vPlugin, vParm, uerr, verr := vc.ValidationInfo("channel-id", "cc-name", fakeQueryExecutor) 500 Expect(uerr).NotTo(HaveOccurred()) 501 Expect(verr).NotTo(HaveOccurred()) 502 Expect(vPlugin).To(Equal("validation-plugin")) 503 Expect(vParm).To(Equal([]byte("validation-parameter"))) 504 }) 505 506 Context("when the chaincode in question is _lifecycle", func() { 507 It("returns the builtin plugin and the endorsement policy", func() { 508 vPlugin, vParm, uerr, verr := vc.ValidationInfo("channel-id", "_lifecycle", fakeQueryExecutor) 509 Expect(uerr).NotTo(HaveOccurred()) 510 Expect(verr).NotTo(HaveOccurred()) 511 Expect(vPlugin).To(Equal("vscc")) 512 Expect(vParm).NotTo(BeNil()) 513 }) 514 515 Context("when fetching the lifecycle endorsement policy returns an error", func() { 516 BeforeEach(func() { 517 fakeChannelConfigSource.GetStableChannelConfigReturns(nil) 518 }) 519 520 It("treats the error as non-deterministic", func() { 521 _, _, uerr, _ := vc.ValidationInfo("channel-id", "_lifecycle", fakeQueryExecutor) 522 Expect(uerr).To(MatchError("unexpected failure to create lifecycle endorsement policy: could not get channel config for channel 'channel-id'")) 523 }) 524 }) 525 }) 526 527 Context("when the ledger returns an error", func() { 528 BeforeEach(func() { 529 fakeQueryExecutor.GetStateReturns(nil, fmt.Errorf("state-error")) 530 }) 531 532 It("wraps and returns the error", func() { 533 _, _, uerr, _ := vc.ValidationInfo("channel-id", "cc-name", fakeQueryExecutor) 534 Expect(uerr).To(MatchError("could not get chaincode: could not deserialize metadata for chaincode cc-name: could not query metadata for namespace namespaces/cc-name: state-error")) 535 }) 536 }) 537 538 Context("when the chaincode is not in the new lifecycle", func() { 539 It("passes through to the legacy impl", func() { 540 vPlugin, vParm, uerr, verr := vc.ValidationInfo("channel-id", "missing-name", fakeQueryExecutor) 541 Expect(vPlugin).To(BeEmpty()) 542 Expect(vParm).To(BeNil()) 543 Expect(uerr).NotTo(HaveOccurred()) 544 Expect(verr).NotTo(HaveOccurred()) 545 }) 546 }) 547 548 Context("when the data is corrupt", func() { 549 BeforeEach(func() { 550 fakePublicState["namespaces/fields/cc-name/ValidationInfo"] = []byte("garbage") 551 }) 552 553 It("wraps and returns that error", func() { 554 _, _, uerr, _ := vc.ValidationInfo("channel-id", "cc-name", fakeQueryExecutor) 555 Expect(uerr).To(MatchError("could not get chaincode: could not deserialize chaincode definition for chaincode cc-name: could not unmarshal state for key namespaces/fields/cc-name/ValidationInfo: proto: can't skip unknown wire type 7")) 556 }) 557 }) 558 }) 559 560 Describe("CollectionValidationInfo", func() { 561 var ( 562 fakeValidationState *mock.ValidationState 563 ) 564 565 BeforeEach(func() { 566 fakeValidationState = &mock.ValidationState{} 567 fakeValidationState.GetStateMultipleKeysStub = func(namespace string, keys []string) ([][]byte, error) { 568 return [][]byte{fakePublicState[keys[0]]}, nil 569 } 570 }) 571 572 It("returns the endorsement policy for the collection", func() { 573 ep, uErr, vErr := vc.CollectionValidationInfo("channel-id", "cc-name", "collection-name", fakeValidationState) 574 Expect(uErr).NotTo(HaveOccurred()) 575 Expect(vErr).NotTo(HaveOccurred()) 576 Expect(ep).To(Equal([]byte("validation-parameter"))) 577 }) 578 579 Context("when the chaincode definition cannot be retrieved", func() { 580 BeforeEach(func() { 581 fakeValidationState.GetStateMultipleKeysReturns(nil, fmt.Errorf("state-error")) 582 }) 583 584 It("returns an unexpected error", func() { 585 _, uErr, vErr := vc.CollectionValidationInfo("channel-id", "cc-name", "collection-name", fakeValidationState) 586 Expect(vErr).NotTo(HaveOccurred()) 587 Expect(uErr).To(MatchError("could not get chaincode: could not deserialize metadata for chaincode cc-name: could not query metadata for namespace namespaces/cc-name: could not get state thought validatorstate shim: state-error")) 588 }) 589 }) 590 591 Context("when the chaincode does not exist in the new lifecycle", func() { 592 It("returns nil nil nil", func() { 593 ep, uErr, vErr := vc.CollectionValidationInfo("channel-id", "missing-name", "collection-name", fakeValidationState) 594 Expect(uErr).NotTo(HaveOccurred()) 595 Expect(vErr).NotTo(HaveOccurred()) 596 Expect(ep).To(BeNil()) 597 }) 598 }) 599 600 Context("when the collection does not exist", func() { 601 It("returns a validation error", func() { 602 _, uErr, vErr := vc.CollectionValidationInfo("channel-id", "cc-name", "missing-collection-name", fakeValidationState) 603 Expect(uErr).NotTo(HaveOccurred()) 604 Expect(vErr).To(MatchError("no such collection 'missing-collection-name'")) 605 }) 606 }) 607 608 Context("when the collection is an implicit collection", func() { 609 It("returns the implicit endorsement policy", func() { 610 ep, uErr, vErr := vc.CollectionValidationInfo("channel-id", "cc-name", "_implicit_org_first-mspid", fakeValidationState) 611 Expect(uErr).NotTo(HaveOccurred()) 612 Expect(vErr).NotTo(HaveOccurred()) 613 Expect(ep).NotTo(Equal([]byte("validation-parameter"))) 614 }) 615 616 Context("when the implicit endorsement policy returns an error", func() { 617 It("returns the error", func() { 618 _, _, vErr := vc.CollectionValidationInfo("channel-id", "cc-name", "_implicit_org_bad-mspid", fakeValidationState) 619 Expect(vErr).To(MatchError("no org found in channel with MSPID 'bad-mspid'")) 620 }) 621 }) 622 }) 623 624 Context("when the endorsement policy is specified in the collection config", func() { 625 var expectedPolicy *pb.ApplicationPolicy 626 627 BeforeEach(func() { 628 expectedPolicy = &pb.ApplicationPolicy{ 629 Type: &pb.ApplicationPolicy_SignaturePolicy{ 630 SignaturePolicy: &cb.SignaturePolicyEnvelope{ 631 Identities: []*msp.MSPPrincipal{ 632 { 633 Principal: []byte("test"), 634 }, 635 }, 636 }, 637 }, 638 } 639 err := resources.Serializer.Serialize(lifecycle.NamespacesName, "cc-name", &lifecycle.ChaincodeDefinition{ 640 EndorsementInfo: &lb.ChaincodeEndorsementInfo{ 641 Version: "version", 642 }, 643 ValidationInfo: &lb.ChaincodeValidationInfo{ 644 ValidationPlugin: "validation-plugin", 645 ValidationParameter: []byte("validation-parameter"), 646 }, 647 Collections: &pb.CollectionConfigPackage{ 648 Config: []*pb.CollectionConfig{ 649 { 650 Payload: &pb.CollectionConfig_StaticCollectionConfig{ 651 StaticCollectionConfig: &pb.StaticCollectionConfig{ 652 Name: "collection-name", 653 EndorsementPolicy: expectedPolicy, 654 }, 655 }, 656 }, 657 }, 658 }, 659 }, fakePublicState) 660 Expect(err).NotTo(HaveOccurred()) 661 }) 662 663 It("returns the endorsement policy from the collection config", func() { 664 ep, uErr, vErr := vc.CollectionValidationInfo("channel-id", "cc-name", "collection-name", fakeValidationState) 665 Expect(uErr).NotTo(HaveOccurred()) 666 Expect(vErr).NotTo(HaveOccurred()) 667 Expect(ep).To(Equal(protoutil.MarshalOrPanic(expectedPolicy))) 668 }) 669 }) 670 }) 671 672 Describe("ImplicitCollectionEndorsementPolicyAsBytes", func() { 673 It("returns the marshaled standard EP for an implicit collection", func() { 674 ep, uErr, vErr := vc.ImplicitCollectionEndorsementPolicyAsBytes("channel-id", "first-mspid") 675 Expect(uErr).NotTo(HaveOccurred()) 676 Expect(vErr).NotTo(HaveOccurred()) 677 policy := &cb.ApplicationPolicy{} 678 err := proto.Unmarshal(ep, policy) 679 Expect(err).NotTo(HaveOccurred()) 680 Expect(policy.GetChannelConfigPolicyReference()).To(Equal("/Channel/Application/org0/Endorsement")) 681 }) 682 683 Context("when the standard channel policy for endorsement does not exist", func() { 684 BeforeEach(func() { 685 fakePolicyManager.GetPolicyReturns(nil, false) 686 }) 687 688 It("returns a signature policy", func() { 689 ep, uErr, vErr := vc.ImplicitCollectionEndorsementPolicyAsBytes("channel-id", "first-mspid") 690 Expect(uErr).NotTo(HaveOccurred()) 691 Expect(vErr).NotTo(HaveOccurred()) 692 policy := &cb.ApplicationPolicy{} 693 err := proto.Unmarshal(ep, policy) 694 Expect(err).NotTo(HaveOccurred()) 695 Expect(policy.GetSignaturePolicy()).NotTo(BeNil()) 696 }) 697 }) 698 699 Context("when the channel config cannot be retrieved", func() { 700 BeforeEach(func() { 701 fakeChannelConfigSource.GetStableChannelConfigReturns(nil) 702 }) 703 704 It("returns an unexpected error", func() { 705 _, uErr, vErr := vc.ImplicitCollectionEndorsementPolicyAsBytes("channel-id", "first-mspid") 706 Expect(vErr).NotTo(HaveOccurred()) 707 Expect(uErr).To(MatchError("could not get channel config for channel 'channel-id'")) 708 }) 709 }) 710 711 Context("when the application config cannot be retrieved", func() { 712 BeforeEach(func() { 713 fakeChannelConfig.ApplicationConfigReturns(nil, false) 714 }) 715 716 It("returns an unexpected error", func() { 717 _, uErr, vErr := vc.ImplicitCollectionEndorsementPolicyAsBytes("channel-id", "first-mspid") 718 Expect(vErr).NotTo(HaveOccurred()) 719 Expect(uErr).To(MatchError("could not get application config for channel 'channel-id'")) 720 }) 721 }) 722 723 Context("when the MSPID is not for any application org", func() { 724 It("returns a validation error", func() { 725 _, uErr, vErr := vc.ImplicitCollectionEndorsementPolicyAsBytes("channel-id", "bad-mspid") 726 Expect(uErr).NotTo(HaveOccurred()) 727 Expect(vErr).To(MatchError("no org found in channel with MSPID 'bad-mspid'")) 728 }) 729 }) 730 731 }) 732 })