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