github.com/ewagmig/fabric@v2.1.1+incompatible/core/chaincode/lifecycle/cache_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 "github.com/hyperledger/fabric-protos-go/ledger/queryresult" 13 "github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset" 14 pb "github.com/hyperledger/fabric-protos-go/peer" 15 lb "github.com/hyperledger/fabric-protos-go/peer/lifecycle" 16 "github.com/hyperledger/fabric/common/chaincode" 17 commonledger "github.com/hyperledger/fabric/common/ledger" 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/chaincode/persistence" 22 "github.com/hyperledger/fabric/core/container" 23 "github.com/hyperledger/fabric/core/container/externalbuilder" 24 "github.com/hyperledger/fabric/core/ledger" 25 ledgermock "github.com/hyperledger/fabric/core/ledger/mock" 26 "github.com/hyperledger/fabric/protoutil" 27 28 . "github.com/onsi/ginkgo" 29 . "github.com/onsi/gomega" 30 ) 31 32 var _ = Describe("Cache", func() { 33 var ( 34 c *lifecycle.Cache 35 resources *lifecycle.Resources 36 fakeCCStore *mock.ChaincodeStore 37 fakeParser *mock.PackageParser 38 fakeChannelConfigSource *mock.ChannelConfigSource 39 fakeChannelConfig *mock.ChannelConfig 40 fakeApplicationConfig *mock.ApplicationConfig 41 fakeCapabilities *mock.ApplicationCapabilities 42 fakePolicyManager *mock.PolicyManager 43 fakeMetadataHandler *mock.MetadataHandler 44 channelCache *lifecycle.ChannelCache 45 localChaincodes map[string]*lifecycle.LocalChaincode 46 fakePublicState MapLedgerShim 47 fakePrivateState MapLedgerShim 48 fakeQueryExecutor *mock.SimpleQueryExecutor 49 chaincodeCustodian *lifecycle.ChaincodeCustodian 50 ) 51 52 BeforeEach(func() { 53 fakeCCStore = &mock.ChaincodeStore{} 54 fakeParser = &mock.PackageParser{} 55 fakeChannelConfigSource = &mock.ChannelConfigSource{} 56 fakeChannelConfig = &mock.ChannelConfig{} 57 fakeChannelConfigSource.GetStableChannelConfigReturns(fakeChannelConfig) 58 fakeApplicationConfig = &mock.ApplicationConfig{} 59 fakeChannelConfig.ApplicationConfigReturns(fakeApplicationConfig, true) 60 fakeCapabilities = &mock.ApplicationCapabilities{} 61 fakeCapabilities.LifecycleV20Returns(true) 62 fakeApplicationConfig.CapabilitiesReturns(fakeCapabilities) 63 fakePolicyManager = &mock.PolicyManager{} 64 fakePolicyManager.GetPolicyReturns(nil, true) 65 fakeChannelConfig.PolicyManagerReturns(fakePolicyManager) 66 resources = &lifecycle.Resources{ 67 PackageParser: fakeParser, 68 ChaincodeStore: fakeCCStore, 69 ChannelConfigSource: fakeChannelConfigSource, 70 Serializer: &lifecycle.Serializer{}, 71 } 72 73 fakeCCStore.ListInstalledChaincodesReturns([]chaincode.InstalledChaincode{ 74 { 75 Hash: []byte("hash"), 76 PackageID: "packageID", 77 }, 78 }, nil) 79 80 fakeCCStore.LoadReturns([]byte("package-bytes"), nil) 81 82 fakeParser.ParseReturns(&persistence.ChaincodePackage{ 83 Metadata: &persistence.ChaincodePackageMetadata{ 84 Path: "cc-path", 85 Type: "cc-type", 86 }, 87 CodePackage: []byte("code-package"), 88 DBArtifacts: []byte("db-artifacts"), 89 }, nil) 90 91 fakeMetadataHandler = &mock.MetadataHandler{} 92 93 chaincodeCustodian = lifecycle.NewChaincodeCustodian() 94 95 var err error 96 c = lifecycle.NewCache(resources, "my-mspid", fakeMetadataHandler, chaincodeCustodian, &externalbuilder.MetadataProvider{}) 97 Expect(err).NotTo(HaveOccurred()) 98 99 channelCache = &lifecycle.ChannelCache{ 100 Chaincodes: map[string]*lifecycle.CachedChaincodeDefinition{ 101 "chaincode-name": { 102 Definition: &lifecycle.ChaincodeDefinition{ 103 Sequence: 3, 104 EndorsementInfo: &lb.ChaincodeEndorsementInfo{ 105 Version: "chaincode-version", 106 }, 107 ValidationInfo: &lb.ChaincodeValidationInfo{ 108 ValidationParameter: []byte("validation-parameter"), 109 }, 110 Collections: &pb.CollectionConfigPackage{}, 111 }, 112 Approved: true, 113 Hashes: []string{ 114 string(util.ComputeSHA256([]byte("namespaces/metadata/chaincode-name#3"))), 115 string(util.ComputeSHA256([]byte("namespaces/fields/chaincode-name#3/Sequence"))), 116 string(util.ComputeSHA256([]byte("namespaces/fields/chaincode-name#3/EndorsementInfo"))), 117 string(util.ComputeSHA256([]byte("namespaces/fields/chaincode-name#3/ValidationInfo"))), 118 string(util.ComputeSHA256([]byte("namespaces/fields/chaincode-name#3/Collections"))), 119 }, 120 }, 121 }, 122 InterestingHashes: map[string]string{ 123 string(util.ComputeSHA256([]byte("namespaces/metadata/chaincode-name#3"))): "chaincode-name", 124 string(util.ComputeSHA256([]byte("namespaces/fields/chaincode-name#3/Sequence"))): "chaincode-name", 125 string(util.ComputeSHA256([]byte("namespaces/fields/chaincode-name#3/EndorsementInfo"))): "chaincode-name", 126 string(util.ComputeSHA256([]byte("namespaces/fields/chaincode-name#3/ValidationInfo"))): "chaincode-name", 127 string(util.ComputeSHA256([]byte("namespaces/fields/chaincode-name#3/Collections"))): "chaincode-name", 128 }, 129 } 130 131 localChaincodes = map[string]*lifecycle.LocalChaincode{ 132 string(util.ComputeSHA256(protoutil.MarshalOrPanic(&lb.StateData{ 133 Type: &lb.StateData_String_{String_: "packageID"}, 134 }))): { 135 References: map[string]map[string]*lifecycle.CachedChaincodeDefinition{ 136 "channel-id": { 137 "chaincode-name": channelCache.Chaincodes["chaincode-name"], 138 }, 139 "another-channel-id": { 140 "chaincode-name": channelCache.Chaincodes["chaincode-name"], 141 }, 142 }, 143 Info: &lifecycle.ChaincodeInstallInfo{ 144 Label: "chaincode-label", 145 PackageID: "packageID", 146 }, 147 }, 148 string(util.ComputeSHA256(protoutil.MarshalOrPanic(&lb.StateData{ 149 Type: &lb.StateData_String_{String_: "notinstalled-packageID"}, 150 }))): { 151 References: map[string]map[string]*lifecycle.CachedChaincodeDefinition{ 152 "channel-id": { 153 "chaincode-name": channelCache.Chaincodes["chaincode-name"], 154 }, 155 }, 156 }, 157 } 158 159 lifecycle.SetChaincodeMap(c, "channel-id", channelCache) 160 lifecycle.SetLocalChaincodesMap(c, localChaincodes) 161 162 fakePublicState = MapLedgerShim(map[string][]byte{}) 163 fakePrivateState = MapLedgerShim(map[string][]byte{}) 164 fakeQueryExecutor = &mock.SimpleQueryExecutor{} 165 fakeQueryExecutor.GetStateStub = func(namespace, key string) ([]byte, error) { 166 return fakePublicState.GetState(key) 167 } 168 169 fakeQueryExecutor.GetStateRangeScanIteratorStub = func(namespace, begin, end string) (commonledger.ResultsIterator, error) { 170 fakeResultsIterator := &mock.ResultsIterator{} 171 i := 0 172 for key, value := range fakePublicState { 173 if key >= begin && key < end { 174 fakeResultsIterator.NextReturnsOnCall(i, &queryresult.KV{ 175 Key: key, 176 Value: value, 177 }, nil) 178 i++ 179 } 180 } 181 return fakeResultsIterator, nil 182 } 183 184 fakeQueryExecutor.GetPrivateDataHashStub = func(namespace, collection, key string) ([]byte, error) { 185 return fakePrivateState.GetStateHash(key) 186 } 187 }) 188 189 AfterEach(func() { 190 chaincodeCustodian.Close() 191 }) 192 193 Describe("ChaincodeInfo", func() { 194 BeforeEach(func() { 195 channelCache.Chaincodes["chaincode-name"].InstallInfo = &lifecycle.ChaincodeInstallInfo{ 196 Type: "cc-type", 197 Path: "cc-path", 198 PackageID: "hash", 199 } 200 }) 201 202 It("returns the cached chaincode definition", func() { 203 localInfo, err := c.ChaincodeInfo("channel-id", "chaincode-name") 204 Expect(err).NotTo(HaveOccurred()) 205 Expect(localInfo).To(Equal(&lifecycle.LocalChaincodeInfo{ 206 Definition: &lifecycle.ChaincodeDefinition{ 207 Sequence: 3, 208 EndorsementInfo: &lb.ChaincodeEndorsementInfo{ 209 Version: "chaincode-version", 210 }, 211 ValidationInfo: &lb.ChaincodeValidationInfo{ 212 ValidationParameter: []byte("validation-parameter"), 213 }, 214 Collections: &pb.CollectionConfigPackage{}, 215 }, 216 InstallInfo: &lifecycle.ChaincodeInstallInfo{ 217 Type: "cc-type", 218 Path: "cc-path", 219 PackageID: "hash", 220 }, 221 Approved: true, 222 })) 223 }) 224 225 Context("when the chaincode name is not in the cache", func() { 226 It("returns an error", func() { 227 _, err := c.ChaincodeInfo("channel-id", "missing-name") 228 Expect(err).To(MatchError("unknown chaincode 'missing-name' for channel 'channel-id'")) 229 }) 230 }) 231 232 Context("when the channel does not exist", func() { 233 It("returns an error", func() { 234 _, err := c.ChaincodeInfo("missing-channel-id", "chaincode-name") 235 Expect(err).To(MatchError("unknown channel 'missing-channel-id'")) 236 }) 237 }) 238 239 Context("when the chaincode name is the _lifecycle system chaincode", func() { 240 It("returns info about _lifecycle", func() { 241 localInfo, err := c.ChaincodeInfo("channel-id", "_lifecycle") 242 Expect(err).NotTo(HaveOccurred()) 243 Expect(localInfo).To(Equal(&lifecycle.LocalChaincodeInfo{ 244 Definition: &lifecycle.ChaincodeDefinition{ 245 Sequence: 1, 246 ValidationInfo: &lb.ChaincodeValidationInfo{ 247 ValidationParameter: lifecycle.LifecycleDefaultEndorsementPolicyBytes, 248 }, 249 }, 250 InstallInfo: &lifecycle.ChaincodeInstallInfo{}, 251 Approved: true, 252 })) 253 }) 254 }) 255 256 Context("when the application config cannot be found", func() { 257 BeforeEach(func() { 258 fakeChannelConfig.ApplicationConfigReturns(nil, false) 259 }) 260 261 It("returns an error", func() { 262 _, err := c.ChaincodeInfo("channel-id", "_lifecycle") 263 Expect(err).To(MatchError("application config does not exist for channel 'channel-id'")) 264 }) 265 }) 266 267 Context("when the application config V2_0 capabilities are not enabled", func() { 268 BeforeEach(func() { 269 fakeCapabilities.LifecycleV20Returns(false) 270 }) 271 272 It("returns an error", func() { 273 _, err := c.ChaincodeInfo("channel-id", "_lifecycle") 274 Expect(err).To(MatchError("cannot use _lifecycle without V2_0 application capabilities enabled for channel 'channel-id'")) 275 }) 276 }) 277 }) 278 279 Describe("ListInstalledChaincodes", func() { 280 It("returns the installed chaincodes", func() { 281 installedChaincodes := c.ListInstalledChaincodes() 282 Expect(installedChaincodes).To(Equal([]*chaincode.InstalledChaincode{ 283 { 284 Label: "chaincode-label", 285 PackageID: "packageID", 286 References: map[string][]*chaincode.Metadata{ 287 "channel-id": { 288 &chaincode.Metadata{ 289 Name: "chaincode-name", 290 Version: "chaincode-version", 291 }, 292 }, 293 "another-channel-id": { 294 &chaincode.Metadata{ 295 Name: "chaincode-name", 296 Version: "chaincode-version", 297 }, 298 }, 299 }, 300 }, 301 })) 302 }) 303 }) 304 305 Describe("GetInstalledChaincode", func() { 306 It("returns the requested installed chaincode", func() { 307 installedChaincode, err := c.GetInstalledChaincode("packageID") 308 Expect(installedChaincode).To(Equal(&chaincode.InstalledChaincode{ 309 Label: "chaincode-label", 310 PackageID: "packageID", 311 References: map[string][]*chaincode.Metadata{ 312 "channel-id": { 313 &chaincode.Metadata{ 314 Name: "chaincode-name", 315 Version: "chaincode-version", 316 }, 317 }, 318 "another-channel-id": { 319 &chaincode.Metadata{ 320 Name: "chaincode-name", 321 Version: "chaincode-version", 322 }, 323 }, 324 }, 325 })) 326 Expect(err).NotTo(HaveOccurred()) 327 }) 328 329 Context("when the chaincode is not installed", func() { 330 It("returns an error", func() { 331 installedChaincode, err := c.GetInstalledChaincode("notinstalled-packageID") 332 Expect(installedChaincode).To(BeNil()) 333 Expect(err).To(MatchError("could not find chaincode with package id 'notinstalled-packageID'")) 334 }) 335 }) 336 }) 337 338 Describe("InitializeLocalChaincodes", func() { 339 It("loads the already installed chaincodes into the cache", func() { 340 Expect(channelCache.Chaincodes["chaincode-name"].InstallInfo).To(BeNil()) 341 err := c.InitializeLocalChaincodes() 342 Expect(err).NotTo(HaveOccurred()) 343 Expect(channelCache.Chaincodes["chaincode-name"].InstallInfo).To(Equal(&lifecycle.ChaincodeInstallInfo{ 344 Type: "cc-type", 345 Path: "cc-path", 346 PackageID: "packageID", 347 })) 348 }) 349 350 Context("when the installed chaincode does not match any current definition", func() { 351 BeforeEach(func() { 352 fakeCCStore.ListInstalledChaincodesReturns([]chaincode.InstalledChaincode{ 353 { 354 Hash: []byte("other-hash"), 355 }, 356 }, nil) 357 }) 358 359 It("does not update the chaincode", func() { 360 err := c.InitializeLocalChaincodes() 361 Expect(err).NotTo(HaveOccurred()) 362 Expect(channelCache.Chaincodes["chaincode-name"].InstallInfo).To(BeNil()) 363 }) 364 }) 365 366 Context("when the chaincodes cannot be listed", func() { 367 BeforeEach(func() { 368 fakeCCStore.ListInstalledChaincodesReturns(nil, fmt.Errorf("list-error")) 369 }) 370 371 It("wraps and returns the error", func() { 372 err := c.InitializeLocalChaincodes() 373 Expect(err).To(MatchError("could not list installed chaincodes: list-error")) 374 }) 375 }) 376 377 Context("when the chaincodes cannot be loaded", func() { 378 BeforeEach(func() { 379 fakeCCStore.LoadReturns(nil, fmt.Errorf("load-error")) 380 }) 381 382 It("wraps and returns the error", func() { 383 err := c.InitializeLocalChaincodes() 384 Expect(err.Error()).To(ContainSubstring("could not load chaincode with package ID 'packageID'")) 385 }) 386 }) 387 388 Context("when the chaincode package cannot be parsed", func() { 389 BeforeEach(func() { 390 fakeParser.ParseReturns(nil, fmt.Errorf("parse-error")) 391 }) 392 393 It("wraps and returns the error", func() { 394 err := c.InitializeLocalChaincodes() 395 Expect(err.Error()).To(ContainSubstring("could not parse chaincode with package ID 'packageID'")) 396 }) 397 }) 398 }) 399 400 Describe("Initialize", func() { 401 BeforeEach(func() { 402 err := resources.Serializer.Serialize(lifecycle.NamespacesName, "chaincode-name", &lifecycle.ChaincodeDefinition{ 403 Sequence: 7, 404 }, fakePublicState) 405 Expect(err).NotTo(HaveOccurred()) 406 407 err = resources.Serializer.Serialize(lifecycle.NamespacesName, "chaincode-name#7", &lifecycle.ChaincodeParameters{}, fakePrivateState) 408 Expect(err).NotTo(HaveOccurred()) 409 410 err = resources.Serializer.Serialize(lifecycle.ChaincodeSourcesName, "chaincode-name#7", &lifecycle.ChaincodeLocalPackage{PackageID: "hash"}, fakePrivateState) 411 Expect(err).NotTo(HaveOccurred()) 412 }) 413 414 It("sets the definitions from the state", func() { 415 err := c.Initialize("channel-id", fakeQueryExecutor) 416 Expect(err).NotTo(HaveOccurred()) 417 Expect(channelCache.Chaincodes["chaincode-name"].Definition.Sequence).To(Equal(int64(7))) 418 Expect(channelCache.Chaincodes["chaincode-name"].Approved).To(BeTrue()) 419 Expect(channelCache.Chaincodes["chaincode-name"].Hashes).To(Equal([]string{ 420 string(util.ComputeSHA256([]byte("namespaces/metadata/chaincode-name#7"))), 421 string(util.ComputeSHA256([]byte("namespaces/fields/chaincode-name#7/EndorsementInfo"))), 422 string(util.ComputeSHA256([]byte("namespaces/fields/chaincode-name#7/ValidationInfo"))), 423 string(util.ComputeSHA256([]byte("namespaces/fields/chaincode-name#7/Collections"))), 424 string(util.ComputeSHA256([]byte("chaincode-sources/fields/chaincode-name#7/PackageID"))), 425 })) 426 for _, hash := range channelCache.Chaincodes["chaincode-name"].Hashes { 427 Expect(channelCache.InterestingHashes[hash]).To(Equal("chaincode-name")) 428 } 429 }) 430 431 Context("when the chaincode is not installed", func() { 432 BeforeEach(func() { 433 err := resources.Serializer.Serialize(lifecycle.NamespacesName, "chaincode-name", &lifecycle.ChaincodeDefinition{ 434 Sequence: 7, 435 }, fakePublicState) 436 Expect(err).NotTo(HaveOccurred()) 437 438 err = resources.Serializer.Serialize(lifecycle.NamespacesName, "chaincode-name#7", &lifecycle.ChaincodeParameters{}, fakePrivateState) 439 Expect(err).NotTo(HaveOccurred()) 440 441 err = resources.Serializer.Serialize(lifecycle.ChaincodeSourcesName, "chaincode-name#7", &lifecycle.ChaincodeLocalPackage{ 442 PackageID: "different-hash", 443 }, fakePrivateState) 444 Expect(err).NotTo(HaveOccurred()) 445 }) 446 447 It("does not have install info set", func() { 448 err := c.Initialize("channel-id", fakeQueryExecutor) 449 Expect(err).NotTo(HaveOccurred()) 450 Expect(channelCache.Chaincodes["chaincode-name"].InstallInfo).To(BeNil()) 451 }) 452 453 It("does not attempt to launch", func() { 454 err := c.Initialize("channel-id", fakeQueryExecutor) 455 Expect(err).NotTo(HaveOccurred()) 456 457 fakeLauncher := &mock.ChaincodeLauncher{} 458 go chaincodeCustodian.Work(nil, nil, fakeLauncher) 459 Consistently(fakeLauncher.LaunchCallCount).Should(Equal(0)) 460 }) 461 462 Context("when the chaincode is installed afterwards", func() { 463 It("gets its install info set and attempts to launch", func() { 464 err := c.Initialize("channel-id", fakeQueryExecutor) 465 Expect(err).NotTo(HaveOccurred()) 466 c.HandleChaincodeInstalled(&persistence.ChaincodePackageMetadata{ 467 Type: "some-type", 468 Path: "some-path", 469 }, "different-hash") 470 Expect(channelCache.Chaincodes["chaincode-name"].InstallInfo).To(Equal(&lifecycle.ChaincodeInstallInfo{ 471 Type: "some-type", 472 Path: "some-path", 473 PackageID: "different-hash", 474 })) 475 476 fakeLauncher := &mock.ChaincodeLauncher{} 477 go chaincodeCustodian.Work(nil, nil, fakeLauncher) 478 Eventually(fakeLauncher.LaunchCallCount).Should(Equal(1)) 479 }) 480 481 }) 482 }) 483 484 Context("when the namespaces query fails", func() { 485 BeforeEach(func() { 486 fakeQueryExecutor.GetStateRangeScanIteratorReturns(nil, fmt.Errorf("range-error")) 487 }) 488 489 It("wraps and returns the error", func() { 490 err := c.Initialize("channel-id", fakeQueryExecutor) 491 Expect(err).To(MatchError("could not query namespace metadata: could not get state range for namespace namespaces: could not get state iterator: range-error")) 492 }) 493 }) 494 495 Context("when the namespace is not of type chaincode", func() { 496 BeforeEach(func() { 497 err := resources.Serializer.Serialize(lifecycle.NamespacesName, "chaincode-name", &lifecycle.ChaincodeParameters{}, fakePublicState) 498 Expect(err).NotTo(HaveOccurred()) 499 }) 500 501 It("ignores the definition", func() { 502 err := c.Initialize("channel-id", fakeQueryExecutor) 503 Expect(err).NotTo(HaveOccurred()) 504 Expect(fakeQueryExecutor.GetStateCallCount()).To(Equal(0)) 505 }) 506 }) 507 508 Context("when the definition is not in the new state", func() { 509 BeforeEach(func() { 510 fakeQueryExecutor.GetStateReturns(nil, nil) 511 }) 512 513 It("deletes the cached definition", func() { 514 err := c.Initialize("channel-id", fakeQueryExecutor) 515 Expect(err).NotTo(HaveOccurred()) 516 Expect(channelCache.Chaincodes["chaincode-name"]).To(BeNil()) 517 }) 518 }) 519 520 Context("when the org's definition differs", func() { 521 BeforeEach(func() { 522 err := resources.Serializer.Serialize(lifecycle.NamespacesName, "chaincode-name#7", &lifecycle.ChaincodeParameters{ 523 EndorsementInfo: &lb.ChaincodeEndorsementInfo{ 524 EndorsementPlugin: "different", 525 }, 526 }, fakePrivateState) 527 Expect(err).NotTo(HaveOccurred()) 528 }) 529 530 It("does not mark the definition approved", func() { 531 err := c.Initialize("channel-id", fakeQueryExecutor) 532 Expect(err).NotTo(HaveOccurred()) 533 Expect(channelCache.Chaincodes["chaincode-name"].Approved).To(BeFalse()) 534 }) 535 536 Context("when the org has no definition", func() { 537 BeforeEach(func() { 538 fakeQueryExecutor.GetPrivateDataHashReturns(nil, nil) 539 }) 540 541 It("does not mark the definition approved", func() { 542 err := c.Initialize("channel-id", fakeQueryExecutor) 543 Expect(err).NotTo(HaveOccurred()) 544 Expect(channelCache.Chaincodes["chaincode-name"].Approved).To(BeFalse()) 545 }) 546 }) 547 }) 548 549 Context("when the chaincode is already installed", func() { 550 BeforeEach(func() { 551 c.HandleChaincodeInstalled(&persistence.ChaincodePackageMetadata{ 552 Type: "cc-type", 553 Path: "cc-path", 554 }, "hash") 555 }) 556 557 It("updates the install info", func() { 558 err := c.Initialize("channel-id", fakeQueryExecutor) 559 Expect(err).NotTo(HaveOccurred()) 560 Expect(channelCache.Chaincodes["chaincode-name"].InstallInfo).To(Equal(&lifecycle.ChaincodeInstallInfo{ 561 Type: "cc-type", 562 Path: "cc-path", 563 PackageID: "hash", 564 })) 565 }) 566 567 It("tells the custodian first to build, then to launch it", func() { 568 err := c.Initialize("channel-id", fakeQueryExecutor) 569 Expect(err).NotTo(HaveOccurred()) 570 571 fakeLauncher := &mock.ChaincodeLauncher{} 572 fakeBuilder := &mock.ChaincodeBuilder{} 573 buildRegistry := &container.BuildRegistry{} 574 go chaincodeCustodian.Work(buildRegistry, fakeBuilder, fakeLauncher) 575 Eventually(fakeBuilder.BuildCallCount).Should(Equal(1)) 576 Eventually(fakeLauncher.LaunchCallCount).Should(Equal(1)) 577 }) 578 }) 579 580 Context("when the update is for an unknown channel", func() { 581 It("creates the underlying map", func() { 582 Expect(lifecycle.GetChaincodeMap(c, "new-channel")).To(BeNil()) 583 err := c.Initialize("new-channel", fakeQueryExecutor) 584 Expect(err).NotTo(HaveOccurred()) 585 Expect(lifecycle.GetChaincodeMap(c, "new-channel")).NotTo(BeNil()) 586 }) 587 }) 588 589 Context("when the state returns an error", func() { 590 BeforeEach(func() { 591 fakeQueryExecutor.GetStateReturns(nil, fmt.Errorf("get-state-error")) 592 }) 593 594 It("wraps and returns the error", func() { 595 err := c.Initialize("channel-id", fakeQueryExecutor) 596 Expect(err).To(MatchError("could not get chaincode definition for 'chaincode-name' on channel 'channel-id': could not deserialize metadata for chaincode chaincode-name: could not query metadata for namespace namespaces/chaincode-name: get-state-error")) 597 }) 598 }) 599 600 Context("when the private state returns an error", func() { 601 BeforeEach(func() { 602 fakeQueryExecutor.GetPrivateDataHashReturns(nil, fmt.Errorf("private-data-error")) 603 }) 604 605 It("wraps and returns the error", func() { 606 err := c.Initialize("channel-id", fakeQueryExecutor) 607 Expect(err).To(MatchError("could not check opaque org state for chaincode source hash for 'chaincode-name' on channel 'channel-id': private-data-error")) 608 }) 609 610 Context("when the private state returns an error for the chaincode source metadata", func() { 611 BeforeEach(func() { 612 fakeQueryExecutor.GetPrivateDataHashStub = func(channel, collection, key string) ([]byte, error) { 613 if key != "chaincode-sources/metadata/chaincode-name#7" { 614 return fakePrivateState.GetStateHash(key) 615 } 616 return nil, fmt.Errorf("private-data-error") 617 } 618 }) 619 620 It("wraps and returns the error", func() { 621 err := c.Initialize("channel-id", fakeQueryExecutor) 622 Expect(err).To(MatchError("could not check opaque org state for chaincode source for 'chaincode-name' on channel 'channel-id': could not get state hash for metadata key chaincode-sources/metadata/chaincode-name#7: private-data-error")) 623 }) 624 }) 625 626 Context("when the private state returns an error for the chaincode source", func() { 627 BeforeEach(func() { 628 fakeQueryExecutor.GetPrivateDataHashStub = func(channel, collection, key string) ([]byte, error) { 629 if key != "chaincode-sources/fields/chaincode-name#7/PackageID" { 630 return fakePrivateState.GetStateHash(key) 631 } 632 return nil, fmt.Errorf("private-data-error") 633 } 634 }) 635 636 It("wraps and returns the error", func() { 637 err := c.Initialize("channel-id", fakeQueryExecutor) 638 Expect(err).To(MatchError("could not check opaque org state for chaincode source hash for 'chaincode-name' on channel 'channel-id': private-data-error")) 639 }) 640 }) 641 }) 642 643 Context("when the chaincode-source is not a local package", func() { 644 BeforeEach(func() { 645 fakePrivateState["chaincode-sources/metadata/chaincode-name#7"] = []byte("garbage") 646 }) 647 648 It("does not set the install info", func() { 649 err := c.Initialize("channel-id", fakeQueryExecutor) 650 Expect(err).NotTo(HaveOccurred()) 651 Expect(channelCache.Chaincodes["chaincode-name"].InstallInfo).To(BeNil()) 652 }) 653 }) 654 }) 655 656 Describe("InitializeMetadata", func() { 657 BeforeEach(func() { 658 channelCache = &lifecycle.ChannelCache{ 659 Chaincodes: map[string]*lifecycle.CachedChaincodeDefinition{ 660 "installedAndApprovedCC": { 661 Definition: &lifecycle.ChaincodeDefinition{ 662 Sequence: 3, 663 EndorsementInfo: &lb.ChaincodeEndorsementInfo{ 664 Version: "chaincode-version", 665 }, 666 ValidationInfo: &lb.ChaincodeValidationInfo{ 667 ValidationParameter: []byte("validation-parameter"), 668 }, 669 Collections: &pb.CollectionConfigPackage{}, 670 }, 671 Approved: true, 672 }, 673 "idontapprove": { 674 Definition: &lifecycle.ChaincodeDefinition{ 675 Sequence: 3, 676 EndorsementInfo: &lb.ChaincodeEndorsementInfo{ 677 Version: "chaincode-version", 678 }, 679 ValidationInfo: &lb.ChaincodeValidationInfo{ 680 ValidationParameter: []byte("validation-parameter"), 681 }, 682 Collections: &pb.CollectionConfigPackage{}, 683 }, 684 Approved: false, 685 }, 686 "ididntinstall": { 687 Definition: &lifecycle.ChaincodeDefinition{ 688 Sequence: 3, 689 EndorsementInfo: &lb.ChaincodeEndorsementInfo{ 690 Version: "chaincode-version", 691 }, 692 ValidationInfo: &lb.ChaincodeValidationInfo{ 693 ValidationParameter: []byte("validation-parameter"), 694 }, 695 Collections: &pb.CollectionConfigPackage{}, 696 }, 697 Approved: true, 698 }, 699 }, 700 } 701 702 localChaincodes = map[string]*lifecycle.LocalChaincode{ 703 string(util.ComputeSHA256(protoutil.MarshalOrPanic(&lb.StateData{ 704 Type: &lb.StateData_String_{String_: "packageID"}, 705 }))): { 706 References: map[string]map[string]*lifecycle.CachedChaincodeDefinition{ 707 "channel-id": { 708 "installedAndApprovedCC": channelCache.Chaincodes["installedAndApprovedCC"], 709 "idontapprove": channelCache.Chaincodes["idontapprove"], 710 }, 711 }, 712 }, 713 } 714 715 lifecycle.SetChaincodeMap(c, "channel-id", channelCache) 716 lifecycle.SetLocalChaincodesMap(c, localChaincodes) 717 err := c.InitializeLocalChaincodes() 718 Expect(err).NotTo(HaveOccurred()) 719 }) 720 721 It("initializes the chaincode metadata from the cache", func() { 722 c.InitializeMetadata("channel-id") 723 channel, metadata := fakeMetadataHandler.InitializeMetadataArgsForCall(0) 724 Expect(channel).To(Equal("channel-id")) 725 Expect(metadata).To(ConsistOf( 726 chaincode.Metadata{ 727 Name: "installedAndApprovedCC", 728 Version: "3", 729 Policy: []byte("validation-parameter"), 730 CollectionsConfig: &pb.CollectionConfigPackage{}, 731 Approved: true, 732 Installed: true, 733 }, 734 chaincode.Metadata{ 735 Name: "ididntinstall", 736 Version: "3", 737 Policy: []byte("validation-parameter"), 738 CollectionsConfig: &pb.CollectionConfigPackage{}, 739 Approved: true, 740 Installed: false, 741 }, 742 chaincode.Metadata{ 743 Name: "idontapprove", 744 Version: "3", 745 Policy: []byte("validation-parameter"), 746 CollectionsConfig: &pb.CollectionConfigPackage{}, 747 Approved: false, 748 Installed: true, 749 }, 750 chaincode.Metadata{ 751 Name: "_lifecycle", 752 Version: "1", 753 Policy: lifecycle.LifecycleDefaultEndorsementPolicyBytes, 754 CollectionsConfig: nil, 755 Approved: true, 756 Installed: true, 757 }, 758 )) 759 }) 760 761 Context("when the channel is unknown", func() { 762 It("returns without initializing metadata", func() { 763 c.InitializeMetadata("slurm") 764 Expect(fakeMetadataHandler.InitializeMetadataCallCount()).To(Equal(0)) 765 }) 766 }) 767 }) 768 769 Describe("StateListener", func() { 770 Describe("InterestedInNamespaces", func() { 771 It("returns _lifecycle", func() { 772 Expect(c.InterestedInNamespaces()).To(Equal([]string{"_lifecycle"})) 773 }) 774 }) 775 776 Describe("HandleStateUpdates", func() { 777 var ( 778 fakePublicState MapLedgerShim 779 trigger *ledger.StateUpdateTrigger 780 fakeQueryExecutor *mock.SimpleQueryExecutor 781 ) 782 783 BeforeEach(func() { 784 fakePublicState = map[string][]byte{} 785 fakeQueryExecutor = &mock.SimpleQueryExecutor{} 786 fakeQueryExecutor.GetStateStub = func(namespace, key string) ([]byte, error) { 787 return fakePublicState.GetState(key) 788 } 789 790 err := resources.Serializer.Serialize(lifecycle.NamespacesName, "chaincode-name", &lifecycle.ChaincodeDefinition{ 791 Sequence: 7, 792 }, fakePublicState) 793 794 Expect(err).NotTo(HaveOccurred()) 795 796 trigger = &ledger.StateUpdateTrigger{ 797 LedgerID: "channel-id", 798 StateUpdates: ledger.StateUpdates(map[string]*ledger.KVStateUpdates{ 799 "_lifecycle": { 800 PublicUpdates: []*kvrwset.KVWrite{ 801 {Key: "namespaces/fields/chaincode-name/Sequence"}, 802 }, 803 CollHashUpdates: map[string][]*kvrwset.KVWriteHash{}, 804 }, 805 }), 806 PostCommitQueryExecutor: fakeQueryExecutor, 807 } 808 }) 809 810 It("updates the modified chaincodes", func() { 811 err := c.HandleStateUpdates(trigger) 812 Expect(err).NotTo(HaveOccurred()) 813 Expect(channelCache.Chaincodes["chaincode-name"].Definition.Sequence).To(Equal(int64(7))) 814 }) 815 816 Context("when the update is not to the sequence", func() { 817 BeforeEach(func() { 818 trigger.StateUpdates["_lifecycle"].PublicUpdates[0].Key = "namespaces/fields/chaincode-name/EndorsementInfo" 819 }) 820 821 It("no update occurs", func() { 822 err := c.HandleStateUpdates(trigger) 823 Expect(err).NotTo(HaveOccurred()) 824 Expect(channelCache.Chaincodes["chaincode-name"].Definition.Sequence).To(Equal(int64(3))) 825 Expect(fakeQueryExecutor.GetStateCallCount()).To(Equal(0)) 826 }) 827 }) 828 829 Context("when the update encounters an error", func() { 830 BeforeEach(func() { 831 fakeQueryExecutor.GetStateReturns(nil, fmt.Errorf("state-error")) 832 }) 833 834 It("wraps and returns the error", func() { 835 err := c.HandleStateUpdates(trigger) 836 Expect(err.Error()).To(Equal("error updating cache: could not get chaincode definition for 'chaincode-name' on channel 'channel-id': could not deserialize metadata for chaincode chaincode-name: could not query metadata for namespace namespaces/chaincode-name: state-error")) 837 }) 838 }) 839 840 Context("when the update is to private data", func() { 841 BeforeEach(func() { 842 trigger.StateUpdates["_lifecycle"].PublicUpdates = nil 843 trigger.StateUpdates["_lifecycle"].CollHashUpdates["_implicit_org_my-mspid"] = []*kvrwset.KVWriteHash{ 844 {KeyHash: util.ComputeSHA256([]byte("namespaces/fields/chaincode-name#3/EndorsementInfo"))}, 845 } 846 }) 847 848 It("updates the corresponding chaincode", func() { 849 err := c.HandleStateUpdates(trigger) 850 Expect(err).NotTo(HaveOccurred()) 851 Expect(channelCache.Chaincodes["chaincode-name"].Definition.Sequence).To(Equal(int64(7))) 852 }) 853 }) 854 855 Context("when the private update is not in our implicit collection", func() { 856 BeforeEach(func() { 857 trigger.StateUpdates["_lifecycle"].PublicUpdates = nil 858 trigger.StateUpdates["_lifecycle"].CollHashUpdates["_implicit_org_other-mspid"] = []*kvrwset.KVWriteHash{ 859 {KeyHash: util.ComputeSHA256([]byte("namespaces/fields/chaincode-name#3/EndorsementInfo"))}, 860 } 861 }) 862 863 It("it does not mark the chaincode dirty", func() { 864 err := c.HandleStateUpdates(trigger) 865 Expect(err).NotTo(HaveOccurred()) 866 Expect(channelCache.Chaincodes["chaincode-name"].Definition.Sequence).To(Equal(int64(3))) 867 Expect(fakeQueryExecutor.GetStateCallCount()).To(Equal(0)) 868 }) 869 }) 870 871 Context("when the private update is not in an implicit collection", func() { 872 BeforeEach(func() { 873 trigger.StateUpdates["_lifecycle"].PublicUpdates = nil 874 trigger.StateUpdates["_lifecycle"].CollHashUpdates["random-collection"] = []*kvrwset.KVWriteHash{ 875 {KeyHash: util.ComputeSHA256([]byte("namespaces/fields/chaincode-name#3/EndorsementInfo"))}, 876 } 877 }) 878 879 It("it does not mark the chaincode dirty", func() { 880 err := c.HandleStateUpdates(trigger) 881 Expect(err).NotTo(HaveOccurred()) 882 Expect(channelCache.Chaincodes["chaincode-name"].Definition.Sequence).To(Equal(int64(3))) 883 Expect(fakeQueryExecutor.GetStateCallCount()).To(Equal(0)) 884 }) 885 }) 886 887 Context("when the state update contains no updates to _lifecycle", func() { 888 BeforeEach(func() { 889 trigger.StateUpdates = ledger.StateUpdates(map[string]*ledger.KVStateUpdates{}) 890 }) 891 892 It("returns an error", func() { 893 err := c.HandleStateUpdates(trigger) 894 Expect(err).To(MatchError("no state updates for promised namespace _lifecycle")) 895 }) 896 }) 897 }) 898 }) 899 900 Describe("EventsOnCacheUpdates", func() { 901 var ( 902 fakeListener *ledgermock.ChaincodeLifecycleEventListener 903 ) 904 905 BeforeEach(func() { 906 fakeListener = &ledgermock.ChaincodeLifecycleEventListener{} 907 c.RegisterListener("channel-id", fakeListener) 908 909 }) 910 911 Context("when initializing cache", func() { 912 BeforeEach(func() { 913 fakeQueryExecutor.GetPrivateDataHashStub = func(namespace, collection, key string) ([]byte, error) { 914 return fakePrivateState.GetStateHash(key) 915 } 916 err := resources.Serializer.Serialize(lifecycle.NamespacesName, "chaincode-name", &lifecycle.ChaincodeDefinition{ 917 Sequence: 4, 918 }, fakePublicState) 919 Expect(err).NotTo(HaveOccurred()) 920 921 err = resources.Serializer.Serialize(lifecycle.NamespacesName, "chaincode-name#4", &lifecycle.ChaincodeParameters{}, 922 fakePrivateState) 923 Expect(err).NotTo(HaveOccurred()) 924 925 err = resources.Serializer.Serialize(lifecycle.ChaincodeSourcesName, "chaincode-name#4", &lifecycle.ChaincodeLocalPackage{PackageID: "packageID"}, 926 fakePrivateState) 927 Expect(err).NotTo(HaveOccurred()) 928 }) 929 930 It("should not invoke listener", func() { 931 err := c.InitializeLocalChaincodes() 932 Expect(err).NotTo(HaveOccurred()) 933 err = c.Initialize("channel-id", fakeQueryExecutor) 934 Expect(err).NotTo(HaveOccurred()) 935 chaincodeInfo, err := c.ChaincodeInfo("channel-id", "chaincode-name") 936 Expect(err).NotTo(HaveOccurred()) 937 Expect(chaincodeInfo.Approved).To(BeTrue()) 938 Expect(chaincodeInfo.Definition).NotTo(BeNil()) 939 Expect(chaincodeInfo.InstallInfo).NotTo(BeNil()) 940 Expect(fakeListener.HandleChaincodeDeployCallCount()).To(Equal(0)) 941 Expect(fakeListener.ChaincodeDeployDoneCallCount()).To(Equal(0)) 942 }) 943 }) 944 945 Context("when new chaincode becomes available", func() { 946 var ( 947 definitionTrigger *ledger.StateUpdateTrigger 948 approvalTrigger *ledger.StateUpdateTrigger 949 install func(string) 950 define func(string, int64) 951 approve func(string, string, int64) 952 verifyNoEvent func() 953 verifyEvent func(string, string) 954 ) 955 BeforeEach(func() { 956 definitionTrigger = &ledger.StateUpdateTrigger{ 957 LedgerID: "channel-id", 958 StateUpdates: ledger.StateUpdates(map[string]*ledger.KVStateUpdates{ 959 "_lifecycle": { 960 PublicUpdates: []*kvrwset.KVWrite{ 961 {Key: "namespaces/fields/chaincode-name-1/Sequence"}, 962 }, 963 CollHashUpdates: map[string][]*kvrwset.KVWriteHash{}, 964 }, 965 }), 966 PostCommitQueryExecutor: fakeQueryExecutor, 967 } 968 969 approvalTrigger = &ledger.StateUpdateTrigger{ 970 LedgerID: "channel-id", 971 StateUpdates: ledger.StateUpdates(map[string]*ledger.KVStateUpdates{ 972 "_lifecycle": { 973 PublicUpdates: []*kvrwset.KVWrite{}, 974 CollHashUpdates: map[string][]*kvrwset.KVWriteHash{ 975 "_implicit_org_my-mspid": { 976 { 977 KeyHash: util.ComputeSHA256([]byte("namespaces/fields/chaincode-name-1#1/EndorsementInfo")), 978 }, 979 }, 980 }, 981 }, 982 }), 983 PostCommitQueryExecutor: fakeQueryExecutor, 984 } 985 986 install = func(packageID string) { 987 c.HandleChaincodeInstalled( 988 &persistence.ChaincodePackageMetadata{ 989 Type: "cc-type", 990 Path: "cc-path", 991 Label: "label", 992 }, 993 packageID, 994 ) 995 } 996 997 define = func(chaincodeName string, sequence int64) { 998 err := resources.Serializer.Serialize(lifecycle.NamespacesName, chaincodeName, 999 &lifecycle.ChaincodeDefinition{ 1000 Sequence: sequence, 1001 EndorsementInfo: &lb.ChaincodeEndorsementInfo{Version: "version-1"}, 1002 }, fakePublicState) 1003 Expect(err).NotTo(HaveOccurred()) 1004 err = c.HandleStateUpdates(definitionTrigger) 1005 Expect(err).NotTo(HaveOccurred()) 1006 } 1007 1008 approve = func(packageID, chaincodeName string, sequence int64) { 1009 err := resources.Serializer.Serialize(lifecycle.NamespacesName, fmt.Sprintf("%s#%d", chaincodeName, sequence), 1010 &lifecycle.ChaincodeParameters{ 1011 EndorsementInfo: &lb.ChaincodeEndorsementInfo{Version: "version-1"}, 1012 }, 1013 fakePrivateState) 1014 Expect(err).NotTo(HaveOccurred()) 1015 err = resources.Serializer.Serialize(lifecycle.ChaincodeSourcesName, fmt.Sprintf("%s#%d", chaincodeName, sequence), 1016 1017 &lifecycle.ChaincodeLocalPackage{ 1018 PackageID: packageID, 1019 }, 1020 fakePrivateState) 1021 Expect(err).NotTo(HaveOccurred()) 1022 err = c.HandleStateUpdates(approvalTrigger) 1023 Expect(err).NotTo(HaveOccurred()) 1024 } 1025 1026 verifyNoEvent = func() { 1027 Expect(fakeListener.HandleChaincodeDeployCallCount()).To(Equal(0)) 1028 Expect(fakeListener.ChaincodeDeployDoneCallCount()).To(Equal(0)) 1029 } 1030 1031 verifyEvent = func(packageID, chaincodeName string) { 1032 Expect(fakeListener.HandleChaincodeDeployCallCount()).To(Equal(1)) 1033 ccdef, dbArtifacts := fakeListener.HandleChaincodeDeployArgsForCall(0) 1034 Expect(ccdef.Name).To(Equal(chaincodeName)) 1035 Expect(ccdef.Version).To(Equal("version-1")) 1036 Expect(ccdef.Hash).To(Equal([]byte(packageID))) 1037 Expect(dbArtifacts).To(Equal([]byte("db-artifacts"))) 1038 } 1039 }) 1040 1041 Context("when chaincode becomes invokable by the sequence of events define, install, and approve", func() { 1042 It("causes the event listener to receive event on approve step", func() { 1043 define("chaincode-name-1", 1) 1044 install("packageID-1") 1045 verifyNoEvent() 1046 approve("packageID-1", "chaincode-name-1", 1) 1047 verifyEvent("packageID-1", "chaincode-name-1") 1048 Expect(fakeListener.ChaincodeDeployDoneCallCount()).To(Equal(0)) 1049 c.StateCommitDone("channel-id") 1050 Expect(fakeListener.ChaincodeDeployDoneCallCount()).To(Equal(1)) 1051 }) 1052 }) 1053 1054 Context("when chaincode becomes invokable by the sequence of events install, define, and approve", func() { 1055 It("causes the event listener to receive event on approve step", func() { 1056 install("packageID-1") 1057 define("chaincode-name-1", 1) 1058 verifyNoEvent() 1059 approve("packageID-1", "chaincode-name-1", 1) 1060 verifyEvent("packageID-1", "chaincode-name-1") 1061 Expect(fakeListener.ChaincodeDeployDoneCallCount()).To(Equal(0)) 1062 c.StateCommitDone("channel-id") 1063 Expect(fakeListener.ChaincodeDeployDoneCallCount()).To(Equal(1)) 1064 }) 1065 }) 1066 1067 Context("when chaincode becomes invokable by the sequence of events install, approve, and define", func() { 1068 It("causes the event listener to receive event on define step", func() { 1069 install("packageID-1") 1070 approve("packageID-1", "chaincode-name-1", 1) 1071 verifyNoEvent() 1072 define("chaincode-name-1", 1) 1073 verifyEvent("packageID-1", "chaincode-name-1") 1074 Expect(fakeListener.ChaincodeDeployDoneCallCount()).To(Equal(0)) 1075 c.StateCommitDone("channel-id") 1076 Expect(fakeListener.ChaincodeDeployDoneCallCount()).To(Equal(1)) 1077 }) 1078 }) 1079 1080 Context("when chaincode becomes invokable by the sequence of events approve, install, and define", func() { 1081 It("causes the event listener to receive event on define step", func() { 1082 approve("packageID-1", "chaincode-name-1", 1) 1083 install("packageID-1") 1084 verifyNoEvent() 1085 define("chaincode-name-1", 1) 1086 verifyEvent("packageID-1", "chaincode-name-1") 1087 Expect(fakeListener.ChaincodeDeployDoneCallCount()).To(Equal(0)) 1088 c.StateCommitDone("channel-id") 1089 Expect(fakeListener.ChaincodeDeployDoneCallCount()).To(Equal(1)) 1090 }) 1091 }) 1092 1093 Context("when chaincode becomes invokable by the sequence of events define, approve, and install", func() { 1094 It("causes the event listener to receive event on install step", func() { 1095 define("chaincode-name-1", 1) 1096 approve("packageID-1", "chaincode-name-1", 1) 1097 verifyNoEvent() 1098 install("packageID-1") 1099 verifyEvent("packageID-1", "chaincode-name-1") 1100 Expect(fakeListener.ChaincodeDeployDoneCallCount()).To(Equal(1)) 1101 }) 1102 }) 1103 1104 Context("when chaincode becomes invokable by the sequence of events approve, define, and install", func() { 1105 It("causes the event listener to receive event on install step", func() { 1106 approve("packageID-1", "chaincode-name-1", 1) 1107 define("chaincode-name-1", 1) 1108 verifyNoEvent() 1109 install("packageID-1") 1110 verifyEvent("packageID-1", "chaincode-name-1") 1111 Expect(fakeListener.ChaincodeDeployDoneCallCount()).To(Equal(1)) 1112 }) 1113 }) 1114 1115 Context("when chaincode definition is updated by the sequence of events install, approve, and define for existing chaincode name", func() { 1116 BeforeEach(func() { 1117 channelCache.Chaincodes["chaincode-name"].InstallInfo = &lifecycle.ChaincodeInstallInfo{ 1118 Label: "chaincode-label", 1119 PackageID: "packageID", 1120 } 1121 definitionTrigger.StateUpdates["_lifecycle"].PublicUpdates[0].Key = "namespaces/fields/chaincode-name/Sequence" 1122 1123 approvalTrigger.StateUpdates["_lifecycle"].CollHashUpdates["_implicit_org_my-mspid"] = []*kvrwset.KVWriteHash{ 1124 {KeyHash: util.ComputeSHA256([]byte("namespaces/fields/chaincode-name#4/EndorsementInfo"))}, 1125 } 1126 1127 }) 1128 1129 It("receives the event and cleans up stale chaincode definition references", func() { 1130 install("packageID-1") 1131 approve("packageID-1", "chaincode-name", 4) 1132 define("chaincode-name", 4) 1133 c.StateCommitDone("channel-id") 1134 verifyEvent("packageID-1", "chaincode-name") 1135 Expect(fakeListener.ChaincodeDeployDoneCallCount()).To(Equal(1)) 1136 1137 installedCC, err := c.GetInstalledChaincode("packageID") 1138 Expect(err).NotTo(HaveOccurred()) 1139 Expect(installedCC.References["channel-id"]).To(HaveLen(0)) 1140 Expect(installedCC.References["another-channel-id"]).To(HaveLen(1)) 1141 installedCC, err = c.GetInstalledChaincode("packageID-1") 1142 Expect(err).NotTo(HaveOccurred()) 1143 Expect(installedCC.References["channel-id"]).To(HaveLen(1)) 1144 }) 1145 }) 1146 }) 1147 }) 1148 })