github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/chaincode/lifecycle/cache_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/chaincode" 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/chaincode/persistence" 18 "github.com/hechain20/hechain/core/container" 19 "github.com/hechain20/hechain/core/container/externalbuilder" 20 "github.com/hechain20/hechain/core/ledger" 21 ledgermock "github.com/hechain20/hechain/core/ledger/mock" 22 "github.com/hechain20/hechain/protoutil" 23 "github.com/hyperledger/fabric-protos-go/ledger/queryresult" 24 "github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset" 25 pb "github.com/hyperledger/fabric-protos-go/peer" 26 lb "github.com/hyperledger/fabric-protos-go/peer/lifecycle" 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 string(util.ComputeSHA256([]byte("chaincode-sources/fields/chaincode-name#3/PackageID"))), 120 }, 121 }, 122 }, 123 InterestingHashes: map[string]string{ 124 string(util.ComputeSHA256([]byte("namespaces/metadata/chaincode-name#3"))): "chaincode-name", 125 string(util.ComputeSHA256([]byte("namespaces/fields/chaincode-name#3/Sequence"))): "chaincode-name", 126 string(util.ComputeSHA256([]byte("namespaces/fields/chaincode-name#3/EndorsementInfo"))): "chaincode-name", 127 string(util.ComputeSHA256([]byte("namespaces/fields/chaincode-name#3/ValidationInfo"))): "chaincode-name", 128 string(util.ComputeSHA256([]byte("namespaces/fields/chaincode-name#3/Collections"))): "chaincode-name", 129 string(util.ComputeSHA256([]byte("chaincode-sources/fields/chaincode-name#3/PackageID"))): "chaincode-name", 130 }, 131 } 132 133 localChaincodes = map[string]*lifecycle.LocalChaincode{ 134 string(util.ComputeSHA256(protoutil.MarshalOrPanic(&lb.StateData{ 135 Type: &lb.StateData_String_{String_: "packageID"}, 136 }))): { 137 References: map[string]map[string]*lifecycle.CachedChaincodeDefinition{ 138 "channel-id": { 139 "chaincode-name": channelCache.Chaincodes["chaincode-name"], 140 }, 141 "another-channel-id": { 142 "chaincode-name": channelCache.Chaincodes["chaincode-name"], 143 }, 144 }, 145 Info: &lifecycle.ChaincodeInstallInfo{ 146 Label: "chaincode-label", 147 PackageID: "packageID", 148 }, 149 }, 150 string(util.ComputeSHA256(protoutil.MarshalOrPanic(&lb.StateData{ 151 Type: &lb.StateData_String_{String_: "notinstalled-packageID"}, 152 }))): { 153 References: map[string]map[string]*lifecycle.CachedChaincodeDefinition{ 154 "channel-id": { 155 "chaincode-name": channelCache.Chaincodes["chaincode-name"], 156 }, 157 }, 158 }, 159 } 160 161 lifecycle.SetChaincodeMap(c, "channel-id", channelCache) 162 lifecycle.SetLocalChaincodesMap(c, localChaincodes) 163 164 fakePublicState = MapLedgerShim(map[string][]byte{}) 165 fakePrivateState = MapLedgerShim(map[string][]byte{}) 166 fakeQueryExecutor = &mock.SimpleQueryExecutor{} 167 fakeQueryExecutor.GetStateStub = func(namespace, key string) ([]byte, error) { 168 return fakePublicState.GetState(key) 169 } 170 171 fakeQueryExecutor.GetStateRangeScanIteratorStub = func(namespace, begin, end string) (commonledger.ResultsIterator, error) { 172 fakeResultsIterator := &mock.ResultsIterator{} 173 i := 0 174 for key, value := range fakePublicState { 175 if key >= begin && key < end { 176 fakeResultsIterator.NextReturnsOnCall(i, &queryresult.KV{ 177 Key: key, 178 Value: value, 179 }, nil) 180 i++ 181 } 182 } 183 return fakeResultsIterator, nil 184 } 185 186 fakeQueryExecutor.GetPrivateDataHashStub = func(namespace, collection, key string) ([]byte, error) { 187 return fakePrivateState.GetStateHash(key) 188 } 189 }) 190 191 AfterEach(func() { 192 chaincodeCustodian.Close() 193 }) 194 195 Describe("ChaincodeInfo", func() { 196 BeforeEach(func() { 197 channelCache.Chaincodes["chaincode-name"].InstallInfo = &lifecycle.ChaincodeInstallInfo{ 198 Type: "cc-type", 199 Path: "cc-path", 200 PackageID: "hash", 201 } 202 }) 203 204 It("returns the cached chaincode definition", func() { 205 localInfo, err := c.ChaincodeInfo("channel-id", "chaincode-name") 206 Expect(err).NotTo(HaveOccurred()) 207 Expect(localInfo).To(Equal(&lifecycle.LocalChaincodeInfo{ 208 Definition: &lifecycle.ChaincodeDefinition{ 209 Sequence: 3, 210 EndorsementInfo: &lb.ChaincodeEndorsementInfo{ 211 Version: "chaincode-version", 212 }, 213 ValidationInfo: &lb.ChaincodeValidationInfo{ 214 ValidationParameter: []byte("validation-parameter"), 215 }, 216 Collections: &pb.CollectionConfigPackage{}, 217 }, 218 InstallInfo: &lifecycle.ChaincodeInstallInfo{ 219 Type: "cc-type", 220 Path: "cc-path", 221 PackageID: "hash", 222 }, 223 Approved: true, 224 })) 225 }) 226 227 Context("when the chaincode name is not in the cache", func() { 228 It("returns an error", func() { 229 _, err := c.ChaincodeInfo("channel-id", "missing-name") 230 Expect(err).To(MatchError("unknown chaincode 'missing-name' for channel 'channel-id'")) 231 }) 232 }) 233 234 Context("when the channel does not exist", func() { 235 It("returns an error", func() { 236 _, err := c.ChaincodeInfo("missing-channel-id", "chaincode-name") 237 Expect(err).To(MatchError("unknown channel 'missing-channel-id'")) 238 }) 239 }) 240 241 Context("when the chaincode name is the _lifecycle system chaincode", func() { 242 It("returns info about _lifecycle", func() { 243 localInfo, err := c.ChaincodeInfo("channel-id", "_lifecycle") 244 Expect(err).NotTo(HaveOccurred()) 245 Expect(localInfo).To(Equal(&lifecycle.LocalChaincodeInfo{ 246 Definition: &lifecycle.ChaincodeDefinition{ 247 Sequence: 1, 248 ValidationInfo: &lb.ChaincodeValidationInfo{ 249 ValidationParameter: lifecycle.LifecycleDefaultEndorsementPolicyBytes, 250 }, 251 }, 252 InstallInfo: &lifecycle.ChaincodeInstallInfo{}, 253 Approved: true, 254 })) 255 }) 256 }) 257 258 Context("when the application config cannot be found", func() { 259 BeforeEach(func() { 260 fakeChannelConfig.ApplicationConfigReturns(nil, false) 261 }) 262 263 It("returns an error", func() { 264 _, err := c.ChaincodeInfo("channel-id", "_lifecycle") 265 Expect(err).To(MatchError("application config does not exist for channel 'channel-id'")) 266 }) 267 }) 268 269 Context("when the application config V2_0 capabilities are not enabled", func() { 270 BeforeEach(func() { 271 fakeCapabilities.LifecycleV20Returns(false) 272 }) 273 274 It("returns an error", func() { 275 _, err := c.ChaincodeInfo("channel-id", "_lifecycle") 276 Expect(err).To(MatchError("cannot use _lifecycle without V2_0 application capabilities enabled for channel 'channel-id'")) 277 }) 278 }) 279 }) 280 281 Describe("ListInstalledChaincodes", func() { 282 It("returns the installed chaincodes", func() { 283 installedChaincodes := c.ListInstalledChaincodes() 284 Expect(installedChaincodes).To(Equal([]*chaincode.InstalledChaincode{ 285 { 286 Label: "chaincode-label", 287 PackageID: "packageID", 288 References: map[string][]*chaincode.Metadata{ 289 "channel-id": { 290 &chaincode.Metadata{ 291 Name: "chaincode-name", 292 Version: "chaincode-version", 293 }, 294 }, 295 "another-channel-id": { 296 &chaincode.Metadata{ 297 Name: "chaincode-name", 298 Version: "chaincode-version", 299 }, 300 }, 301 }, 302 }, 303 })) 304 }) 305 }) 306 307 Describe("GetInstalledChaincode", func() { 308 It("returns the requested installed chaincode", func() { 309 installedChaincode, err := c.GetInstalledChaincode("packageID") 310 Expect(installedChaincode).To(Equal(&chaincode.InstalledChaincode{ 311 Label: "chaincode-label", 312 PackageID: "packageID", 313 References: map[string][]*chaincode.Metadata{ 314 "channel-id": { 315 &chaincode.Metadata{ 316 Name: "chaincode-name", 317 Version: "chaincode-version", 318 }, 319 }, 320 "another-channel-id": { 321 &chaincode.Metadata{ 322 Name: "chaincode-name", 323 Version: "chaincode-version", 324 }, 325 }, 326 }, 327 })) 328 Expect(err).NotTo(HaveOccurred()) 329 }) 330 331 Context("when the chaincode is not installed", func() { 332 It("returns an error", func() { 333 installedChaincode, err := c.GetInstalledChaincode("notinstalled-packageID") 334 Expect(installedChaincode).To(BeNil()) 335 Expect(err).To(MatchError("could not find chaincode with package id 'notinstalled-packageID'")) 336 }) 337 }) 338 }) 339 340 Describe("InitializeLocalChaincodes", func() { 341 It("loads the already installed chaincodes into the cache", func() { 342 Expect(channelCache.Chaincodes["chaincode-name"].InstallInfo).To(BeNil()) 343 err := c.InitializeLocalChaincodes() 344 Expect(err).NotTo(HaveOccurred()) 345 Expect(channelCache.Chaincodes["chaincode-name"].InstallInfo).To(Equal(&lifecycle.ChaincodeInstallInfo{ 346 Type: "cc-type", 347 Path: "cc-path", 348 PackageID: "packageID", 349 })) 350 }) 351 352 Context("when the installed chaincode does not match any current definition", func() { 353 BeforeEach(func() { 354 fakeCCStore.ListInstalledChaincodesReturns([]chaincode.InstalledChaincode{ 355 { 356 Hash: []byte("other-hash"), 357 }, 358 }, nil) 359 }) 360 361 It("does not update the chaincode", func() { 362 err := c.InitializeLocalChaincodes() 363 Expect(err).NotTo(HaveOccurred()) 364 Expect(channelCache.Chaincodes["chaincode-name"].InstallInfo).To(BeNil()) 365 }) 366 }) 367 368 Context("when the chaincodes cannot be listed", func() { 369 BeforeEach(func() { 370 fakeCCStore.ListInstalledChaincodesReturns(nil, fmt.Errorf("list-error")) 371 }) 372 373 It("wraps and returns the error", func() { 374 err := c.InitializeLocalChaincodes() 375 Expect(err).To(MatchError("could not list installed chaincodes: list-error")) 376 }) 377 }) 378 379 Context("when the chaincodes cannot be loaded", func() { 380 BeforeEach(func() { 381 fakeCCStore.LoadReturns(nil, fmt.Errorf("load-error")) 382 }) 383 384 It("wraps and returns the error", func() { 385 err := c.InitializeLocalChaincodes() 386 Expect(err.Error()).To(ContainSubstring("could not load chaincode with package ID 'packageID'")) 387 }) 388 }) 389 390 Context("when the chaincode package cannot be parsed", func() { 391 BeforeEach(func() { 392 fakeParser.ParseReturns(nil, fmt.Errorf("parse-error")) 393 }) 394 395 It("wraps and returns the error", func() { 396 err := c.InitializeLocalChaincodes() 397 Expect(err.Error()).To(ContainSubstring("could not parse chaincode with package ID 'packageID'")) 398 }) 399 }) 400 }) 401 402 Describe("Initialize", func() { 403 BeforeEach(func() { 404 err := resources.Serializer.Serialize(lifecycle.NamespacesName, "chaincode-name", &lifecycle.ChaincodeDefinition{ 405 Sequence: 7, 406 }, fakePublicState) 407 Expect(err).NotTo(HaveOccurred()) 408 409 err = resources.Serializer.Serialize(lifecycle.NamespacesName, "chaincode-name#7", &lifecycle.ChaincodeParameters{}, fakePrivateState) 410 Expect(err).NotTo(HaveOccurred()) 411 412 err = resources.Serializer.Serialize(lifecycle.ChaincodeSourcesName, "chaincode-name#7", &lifecycle.ChaincodeLocalPackage{PackageID: "hash"}, fakePrivateState) 413 Expect(err).NotTo(HaveOccurred()) 414 }) 415 416 It("sets the definitions from the state", func() { 417 err := c.Initialize("channel-id", fakeQueryExecutor) 418 Expect(err).NotTo(HaveOccurred()) 419 Expect(channelCache.Chaincodes["chaincode-name"].Definition.Sequence).To(Equal(int64(7))) 420 Expect(channelCache.Chaincodes["chaincode-name"].Approved).To(BeTrue()) 421 Expect(channelCache.Chaincodes["chaincode-name"].Hashes).To(Equal([]string{ 422 string(util.ComputeSHA256([]byte("namespaces/metadata/chaincode-name#7"))), 423 string(util.ComputeSHA256([]byte("namespaces/fields/chaincode-name#7/EndorsementInfo"))), 424 string(util.ComputeSHA256([]byte("namespaces/fields/chaincode-name#7/ValidationInfo"))), 425 string(util.ComputeSHA256([]byte("namespaces/fields/chaincode-name#7/Collections"))), 426 string(util.ComputeSHA256([]byte("chaincode-sources/fields/chaincode-name#7/PackageID"))), 427 })) 428 for _, hash := range channelCache.Chaincodes["chaincode-name"].Hashes { 429 Expect(channelCache.InterestingHashes[hash]).To(Equal("chaincode-name")) 430 } 431 }) 432 433 Context("when the chaincode is not installed", func() { 434 BeforeEach(func() { 435 err := resources.Serializer.Serialize(lifecycle.NamespacesName, "chaincode-name", &lifecycle.ChaincodeDefinition{ 436 Sequence: 7, 437 }, fakePublicState) 438 Expect(err).NotTo(HaveOccurred()) 439 440 err = resources.Serializer.Serialize(lifecycle.NamespacesName, "chaincode-name#7", &lifecycle.ChaincodeParameters{}, fakePrivateState) 441 Expect(err).NotTo(HaveOccurred()) 442 443 err = resources.Serializer.Serialize(lifecycle.ChaincodeSourcesName, "chaincode-name#7", &lifecycle.ChaincodeLocalPackage{ 444 PackageID: "different-hash", 445 }, fakePrivateState) 446 Expect(err).NotTo(HaveOccurred()) 447 }) 448 449 It("does not have install info set", func() { 450 err := c.Initialize("channel-id", fakeQueryExecutor) 451 Expect(err).NotTo(HaveOccurred()) 452 Expect(channelCache.Chaincodes["chaincode-name"].InstallInfo).To(BeNil()) 453 }) 454 455 It("does not attempt to launch", func() { 456 err := c.Initialize("channel-id", fakeQueryExecutor) 457 Expect(err).NotTo(HaveOccurred()) 458 459 fakeLauncher := &mock.ChaincodeLauncher{} 460 go chaincodeCustodian.Work(nil, nil, fakeLauncher) 461 Consistently(fakeLauncher.LaunchCallCount).Should(Equal(0)) 462 }) 463 464 Context("when the chaincode is installed afterwards", func() { 465 It("gets its install info set and attempts to launch", func() { 466 err := c.Initialize("channel-id", fakeQueryExecutor) 467 Expect(err).NotTo(HaveOccurred()) 468 c.HandleChaincodeInstalled(&persistence.ChaincodePackageMetadata{ 469 Type: "some-type", 470 Path: "some-path", 471 }, "different-hash") 472 Expect(channelCache.Chaincodes["chaincode-name"].InstallInfo).To(Equal(&lifecycle.ChaincodeInstallInfo{ 473 Type: "some-type", 474 Path: "some-path", 475 PackageID: "different-hash", 476 })) 477 478 fakeLauncher := &mock.ChaincodeLauncher{} 479 go chaincodeCustodian.Work(nil, nil, fakeLauncher) 480 Eventually(fakeLauncher.LaunchCallCount).Should(Equal(1)) 481 }) 482 }) 483 }) 484 485 Context("when the namespaces query fails", func() { 486 BeforeEach(func() { 487 fakeQueryExecutor.GetStateRangeScanIteratorReturns(nil, fmt.Errorf("range-error")) 488 }) 489 490 It("wraps and returns the error", func() { 491 err := c.Initialize("channel-id", fakeQueryExecutor) 492 Expect(err).To(MatchError("could not query namespace metadata: could not get state range for namespace namespaces: could not get state iterator: range-error")) 493 }) 494 }) 495 496 Context("when the namespace is not of type chaincode", func() { 497 BeforeEach(func() { 498 err := resources.Serializer.Serialize(lifecycle.NamespacesName, "chaincode-name", &lifecycle.ChaincodeParameters{}, fakePublicState) 499 Expect(err).NotTo(HaveOccurred()) 500 }) 501 502 It("ignores the definition", func() { 503 err := c.Initialize("channel-id", fakeQueryExecutor) 504 Expect(err).NotTo(HaveOccurred()) 505 Expect(fakeQueryExecutor.GetStateCallCount()).To(Equal(0)) 506 }) 507 }) 508 509 Context("when the definition is not in the new state", func() { 510 BeforeEach(func() { 511 fakeQueryExecutor.GetStateReturns(nil, nil) 512 }) 513 514 It("deletes the cached definition", func() { 515 err := c.Initialize("channel-id", fakeQueryExecutor) 516 Expect(err).NotTo(HaveOccurred()) 517 Expect(channelCache.Chaincodes["chaincode-name"]).To(BeNil()) 518 }) 519 }) 520 521 Context("when the org's definition differs", func() { 522 BeforeEach(func() { 523 err := resources.Serializer.Serialize(lifecycle.NamespacesName, "chaincode-name#7", &lifecycle.ChaincodeParameters{ 524 EndorsementInfo: &lb.ChaincodeEndorsementInfo{ 525 EndorsementPlugin: "different", 526 }, 527 }, fakePrivateState) 528 Expect(err).NotTo(HaveOccurred()) 529 }) 530 531 It("does not mark the definition approved", func() { 532 err := c.Initialize("channel-id", fakeQueryExecutor) 533 Expect(err).NotTo(HaveOccurred()) 534 Expect(channelCache.Chaincodes["chaincode-name"].Approved).To(BeFalse()) 535 }) 536 537 Context("when the org has no definition", func() { 538 BeforeEach(func() { 539 fakeQueryExecutor.GetPrivateDataHashReturns(nil, nil) 540 }) 541 542 It("does not mark the definition approved", func() { 543 err := c.Initialize("channel-id", fakeQueryExecutor) 544 Expect(err).NotTo(HaveOccurred()) 545 Expect(channelCache.Chaincodes["chaincode-name"].Approved).To(BeFalse()) 546 }) 547 }) 548 }) 549 550 Context("when the chaincode is already installed", func() { 551 BeforeEach(func() { 552 c.HandleChaincodeInstalled(&persistence.ChaincodePackageMetadata{ 553 Type: "cc-type", 554 Path: "cc-path", 555 }, "hash") 556 }) 557 558 It("updates the install info", func() { 559 err := c.Initialize("channel-id", fakeQueryExecutor) 560 Expect(err).NotTo(HaveOccurred()) 561 Expect(channelCache.Chaincodes["chaincode-name"].InstallInfo).To(Equal(&lifecycle.ChaincodeInstallInfo{ 562 Type: "cc-type", 563 Path: "cc-path", 564 PackageID: "hash", 565 })) 566 }) 567 568 It("tells the custodian first to build, then to launch it", func() { 569 err := c.Initialize("channel-id", fakeQueryExecutor) 570 Expect(err).NotTo(HaveOccurred()) 571 572 fakeLauncher := &mock.ChaincodeLauncher{} 573 fakeBuilder := &mock.ChaincodeBuilder{} 574 buildRegistry := &container.BuildRegistry{} 575 go chaincodeCustodian.Work(buildRegistry, fakeBuilder, fakeLauncher) 576 Eventually(fakeBuilder.BuildCallCount).Should(Equal(1)) 577 Eventually(fakeLauncher.LaunchCallCount).Should(Equal(1)) 578 }) 579 }) 580 581 Context("when the update is for an unknown channel", func() { 582 It("creates the underlying map", func() { 583 Expect(lifecycle.GetChaincodeMap(c, "new-channel")).To(BeNil()) 584 err := c.Initialize("new-channel", fakeQueryExecutor) 585 Expect(err).NotTo(HaveOccurred()) 586 Expect(lifecycle.GetChaincodeMap(c, "new-channel")).NotTo(BeNil()) 587 }) 588 }) 589 590 Context("when the state returns an error", func() { 591 BeforeEach(func() { 592 fakeQueryExecutor.GetStateReturns(nil, fmt.Errorf("get-state-error")) 593 }) 594 595 It("wraps and returns the error", func() { 596 err := c.Initialize("channel-id", fakeQueryExecutor) 597 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")) 598 }) 599 }) 600 601 Context("when the private state returns an error", func() { 602 BeforeEach(func() { 603 fakeQueryExecutor.GetPrivateDataHashReturns(nil, fmt.Errorf("private-data-error")) 604 }) 605 606 It("wraps and returns the error", func() { 607 err := c.Initialize("channel-id", fakeQueryExecutor) 608 Expect(err).To(MatchError("could not check opaque org state for chaincode source hash for 'chaincode-name' on channel 'channel-id': private-data-error")) 609 }) 610 611 Context("when the private state returns an error for the chaincode source metadata", func() { 612 BeforeEach(func() { 613 fakeQueryExecutor.GetPrivateDataHashStub = func(channel, collection, key string) ([]byte, error) { 614 if key != "chaincode-sources/metadata/chaincode-name#7" { 615 return fakePrivateState.GetStateHash(key) 616 } 617 return nil, fmt.Errorf("private-data-error") 618 } 619 }) 620 621 It("wraps and returns the error", func() { 622 err := c.Initialize("channel-id", fakeQueryExecutor) 623 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")) 624 }) 625 }) 626 627 Context("when the private state returns an error for the chaincode source", func() { 628 BeforeEach(func() { 629 fakeQueryExecutor.GetPrivateDataHashStub = func(channel, collection, key string) ([]byte, error) { 630 if key != "chaincode-sources/fields/chaincode-name#7/PackageID" { 631 return fakePrivateState.GetStateHash(key) 632 } 633 return nil, fmt.Errorf("private-data-error") 634 } 635 }) 636 637 It("wraps and returns the error", func() { 638 err := c.Initialize("channel-id", fakeQueryExecutor) 639 Expect(err).To(MatchError("could not check opaque org state for chaincode source hash for 'chaincode-name' on channel 'channel-id': private-data-error")) 640 }) 641 }) 642 }) 643 644 Context("when the chaincode-source is not a local package", func() { 645 BeforeEach(func() { 646 fakePrivateState["chaincode-sources/metadata/chaincode-name#7"] = []byte("garbage") 647 }) 648 649 It("does not set the install info", func() { 650 err := c.Initialize("channel-id", fakeQueryExecutor) 651 Expect(err).NotTo(HaveOccurred()) 652 Expect(channelCache.Chaincodes["chaincode-name"].InstallInfo).To(BeNil()) 653 }) 654 }) 655 }) 656 657 Describe("InitializeMetadata", func() { 658 BeforeEach(func() { 659 channelCache = &lifecycle.ChannelCache{ 660 Chaincodes: map[string]*lifecycle.CachedChaincodeDefinition{ 661 "installedAndApprovedCC": { 662 Definition: &lifecycle.ChaincodeDefinition{ 663 Sequence: 3, 664 EndorsementInfo: &lb.ChaincodeEndorsementInfo{ 665 Version: "chaincode-version", 666 }, 667 ValidationInfo: &lb.ChaincodeValidationInfo{ 668 ValidationParameter: []byte("validation-parameter"), 669 }, 670 Collections: &pb.CollectionConfigPackage{}, 671 }, 672 Approved: true, 673 }, 674 "idontapprove": { 675 Definition: &lifecycle.ChaincodeDefinition{ 676 Sequence: 3, 677 EndorsementInfo: &lb.ChaincodeEndorsementInfo{ 678 Version: "chaincode-version", 679 }, 680 ValidationInfo: &lb.ChaincodeValidationInfo{ 681 ValidationParameter: []byte("validation-parameter"), 682 }, 683 Collections: &pb.CollectionConfigPackage{}, 684 }, 685 Approved: false, 686 }, 687 "ididntinstall": { 688 Definition: &lifecycle.ChaincodeDefinition{ 689 Sequence: 3, 690 EndorsementInfo: &lb.ChaincodeEndorsementInfo{ 691 Version: "chaincode-version", 692 }, 693 ValidationInfo: &lb.ChaincodeValidationInfo{ 694 ValidationParameter: []byte("validation-parameter"), 695 }, 696 Collections: &pb.CollectionConfigPackage{}, 697 }, 698 Approved: true, 699 }, 700 }, 701 } 702 703 localChaincodes = map[string]*lifecycle.LocalChaincode{ 704 string(util.ComputeSHA256(protoutil.MarshalOrPanic(&lb.StateData{ 705 Type: &lb.StateData_String_{String_: "packageID"}, 706 }))): { 707 References: map[string]map[string]*lifecycle.CachedChaincodeDefinition{ 708 "channel-id": { 709 "installedAndApprovedCC": channelCache.Chaincodes["installedAndApprovedCC"], 710 "idontapprove": channelCache.Chaincodes["idontapprove"], 711 }, 712 }, 713 }, 714 } 715 716 lifecycle.SetChaincodeMap(c, "channel-id", channelCache) 717 lifecycle.SetLocalChaincodesMap(c, localChaincodes) 718 err := c.InitializeLocalChaincodes() 719 Expect(err).NotTo(HaveOccurred()) 720 }) 721 722 It("initializes the chaincode metadata from the cache", func() { 723 c.InitializeMetadata("channel-id") 724 channel, metadata := fakeMetadataHandler.InitializeMetadataArgsForCall(0) 725 Expect(channel).To(Equal("channel-id")) 726 Expect(metadata).To(ConsistOf( 727 chaincode.Metadata{ 728 Name: "installedAndApprovedCC", 729 Version: "3", 730 Policy: []byte("validation-parameter"), 731 CollectionsConfig: &pb.CollectionConfigPackage{}, 732 Approved: true, 733 Installed: true, 734 }, 735 chaincode.Metadata{ 736 Name: "ididntinstall", 737 Version: "3", 738 Policy: []byte("validation-parameter"), 739 CollectionsConfig: &pb.CollectionConfigPackage{}, 740 Approved: true, 741 Installed: false, 742 }, 743 chaincode.Metadata{ 744 Name: "idontapprove", 745 Version: "3", 746 Policy: []byte("validation-parameter"), 747 CollectionsConfig: &pb.CollectionConfigPackage{}, 748 Approved: false, 749 Installed: true, 750 }, 751 chaincode.Metadata{ 752 Name: "_lifecycle", 753 Version: "1", 754 Policy: lifecycle.LifecycleDefaultEndorsementPolicyBytes, 755 CollectionsConfig: nil, 756 Approved: true, 757 Installed: true, 758 }, 759 )) 760 }) 761 762 Context("when the channel is unknown", func() { 763 It("returns without initializing metadata", func() { 764 c.InitializeMetadata("slurm") 765 Expect(fakeMetadataHandler.InitializeMetadataCallCount()).To(Equal(0)) 766 }) 767 }) 768 }) 769 770 Describe("RegisterListener", func() { 771 var fakeListener *ledgermock.ChaincodeLifecycleEventListener 772 BeforeEach(func() { 773 fakeListener = &ledgermock.ChaincodeLifecycleEventListener{} 774 channelCache = &lifecycle.ChannelCache{ 775 Chaincodes: map[string]*lifecycle.CachedChaincodeDefinition{ 776 "definedInstalledAndApprovedCC": { 777 Definition: &lifecycle.ChaincodeDefinition{ 778 Sequence: 3, 779 EndorsementInfo: &lb.ChaincodeEndorsementInfo{ 780 Version: "chaincode-version", 781 }, 782 ValidationInfo: &lb.ChaincodeValidationInfo{ 783 ValidationParameter: []byte("validation-parameter"), 784 }, 785 Collections: &pb.CollectionConfigPackage{}, 786 }, 787 Approved: true, 788 }, 789 "anotherDefinedInstalledAndApprovedCC": { 790 Definition: &lifecycle.ChaincodeDefinition{ 791 Sequence: 3, 792 EndorsementInfo: &lb.ChaincodeEndorsementInfo{ 793 Version: "chaincode-version", 794 }, 795 ValidationInfo: &lb.ChaincodeValidationInfo{ 796 ValidationParameter: []byte("validation-parameter"), 797 }, 798 Collections: &pb.CollectionConfigPackage{}, 799 }, 800 Approved: true, 801 }, 802 "idontapprove": { 803 Definition: &lifecycle.ChaincodeDefinition{ 804 Sequence: 3, 805 EndorsementInfo: &lb.ChaincodeEndorsementInfo{ 806 Version: "chaincode-version", 807 }, 808 ValidationInfo: &lb.ChaincodeValidationInfo{ 809 ValidationParameter: []byte("validation-parameter"), 810 }, 811 Collections: &pb.CollectionConfigPackage{}, 812 }, 813 Approved: false, 814 }, 815 "ididntinstall": { 816 Definition: &lifecycle.ChaincodeDefinition{ 817 Sequence: 3, 818 EndorsementInfo: &lb.ChaincodeEndorsementInfo{ 819 Version: "chaincode-version", 820 }, 821 ValidationInfo: &lb.ChaincodeValidationInfo{ 822 ValidationParameter: []byte("validation-parameter"), 823 }, 824 Collections: &pb.CollectionConfigPackage{}, 825 }, 826 Approved: true, 827 }, 828 }, 829 } 830 831 localChaincodes = map[string]*lifecycle.LocalChaincode{ 832 string(util.ComputeSHA256(protoutil.MarshalOrPanic(&lb.StateData{ 833 Type: &lb.StateData_String_{String_: "packageID"}, 834 }))): { 835 References: map[string]map[string]*lifecycle.CachedChaincodeDefinition{ 836 "channel-id": { 837 "definedInstalledAndApprovedCC": channelCache.Chaincodes["definedInstalledAndApprovedCC"], 838 "idontapprove": channelCache.Chaincodes["idontapprove"], 839 }, 840 }, 841 }, 842 843 string(util.ComputeSHA256(protoutil.MarshalOrPanic(&lb.StateData{ 844 Type: &lb.StateData_String_{String_: "anotherPackageID"}, 845 }))): { 846 References: map[string]map[string]*lifecycle.CachedChaincodeDefinition{ 847 "channel-id": { 848 "anotherDefinedInstalledAndApprovedCC": channelCache.Chaincodes["anotherDefinedInstalledAndApprovedCC"], 849 }, 850 }, 851 }, 852 } 853 854 fakeCCStore.ListInstalledChaincodesReturns([]chaincode.InstalledChaincode{ 855 { 856 Hash: []byte("hash"), 857 PackageID: "packageID", 858 }, 859 { 860 Hash: []byte("hash"), 861 PackageID: "anotherPackageID", 862 }, 863 }, nil) 864 865 lifecycle.SetChaincodeMap(c, "channel-id", channelCache) 866 lifecycle.SetLocalChaincodesMap(c, localChaincodes) 867 err := c.InitializeLocalChaincodes() 868 Expect(err).NotTo(HaveOccurred()) 869 }) 870 871 Context("when channel does not exist", func() { 872 It("returns error", func() { 873 err := c.RegisterListener("non-existing-channel", fakeListener, true) 874 Expect(err).To(MatchError("unknown channel 'non-existing-channel'")) 875 }) 876 }) 877 878 Context("when listener wants existing chaincode info", func() { 879 It("calls back the listener with only invocable chaincodes", func() { 880 err := c.RegisterListener("channel-id", fakeListener, true) 881 Expect(err).NotTo(HaveOccurred()) 882 Expect(fakeListener.HandleChaincodeDeployCallCount()).To(Equal(2)) 883 Expect(fakeListener.ChaincodeDeployDoneCallCount()).To(Equal(2)) 884 ccdef0, dbArtifacts0 := fakeListener.HandleChaincodeDeployArgsForCall(0) 885 ccdef1, dbArtifacts1 := fakeListener.HandleChaincodeDeployArgsForCall(1) 886 Expect( 887 []*ledger.ChaincodeDefinition{ 888 ccdef0, 889 ccdef1, 890 }, 891 ).To(ConsistOf( 892 []*ledger.ChaincodeDefinition{ 893 { 894 Name: "definedInstalledAndApprovedCC", 895 Version: "chaincode-version", 896 Hash: []byte("packageID"), 897 CollectionConfigs: &pb.CollectionConfigPackage{}, 898 }, 899 { 900 Name: "anotherDefinedInstalledAndApprovedCC", 901 Version: "chaincode-version", 902 Hash: []byte("anotherPackageID"), 903 CollectionConfigs: &pb.CollectionConfigPackage{}, 904 }, 905 }, 906 )) 907 Expect([][]byte{dbArtifacts0, dbArtifacts1}).To(Equal([][]byte{[]byte("db-artifacts"), []byte("db-artifacts")})) 908 }) 909 910 Context("when chaincode store returns error for one of the chaincodes", func() { 911 BeforeEach(func() { 912 fakeCCStore.LoadStub = func(packageID string) ([]byte, error) { 913 if packageID == "packageID" { 914 return nil, fmt.Errorf("loading-error") 915 } 916 return []byte("package-bytes"), nil 917 } 918 }) 919 It("suppresses the error", func() { 920 err := c.RegisterListener("channel-id", fakeListener, true) 921 Expect(err).NotTo(HaveOccurred()) 922 Expect(fakeListener.HandleChaincodeDeployCallCount()).To(Equal(1)) 923 Expect(fakeListener.ChaincodeDeployDoneCallCount()).To(Equal(1)) 924 }) 925 }) 926 927 Context("when chaincode package parser returns error for both the chaincodes", func() { 928 BeforeEach(func() { 929 fakeParser.ParseReturns(nil, fmt.Errorf("parsing-error")) 930 }) 931 It("suppresses the error", func() { 932 err := c.RegisterListener("channel-id", fakeListener, true) 933 Expect(err).NotTo(HaveOccurred()) 934 Expect(fakeListener.HandleChaincodeDeployCallCount()).To(Equal(0)) 935 Expect(fakeListener.ChaincodeDeployDoneCallCount()).To(Equal(0)) 936 }) 937 }) 938 939 Context("when listener returns error", func() { 940 BeforeEach(func() { 941 fakeListener.HandleChaincodeDeployReturns(fmt.Errorf("listener-error")) 942 }) 943 It("suppresses the error", func() { 944 err := c.RegisterListener("channel-id", fakeListener, true) 945 Expect(err).NotTo(HaveOccurred()) 946 Expect(fakeListener.HandleChaincodeDeployCallCount()).To(Equal(2)) 947 Expect(fakeListener.HandleChaincodeDeployCallCount()).To(Equal(2)) 948 }) 949 }) 950 }) 951 }) 952 953 Describe("StateListener", func() { 954 Describe("InterestedInNamespaces", func() { 955 It("returns _lifecycle", func() { 956 Expect(c.InterestedInNamespaces()).To(Equal([]string{"_lifecycle"})) 957 }) 958 }) 959 960 Describe("HandleStateUpdates", func() { 961 var ( 962 fakePublicState MapLedgerShim 963 trigger *ledger.StateUpdateTrigger 964 fakeQueryExecutor *mock.SimpleQueryExecutor 965 ) 966 967 BeforeEach(func() { 968 fakePublicState = map[string][]byte{} 969 fakeQueryExecutor = &mock.SimpleQueryExecutor{} 970 fakeQueryExecutor.GetStateStub = func(namespace, key string) ([]byte, error) { 971 return fakePublicState.GetState(key) 972 } 973 974 err := resources.Serializer.Serialize(lifecycle.NamespacesName, "chaincode-name", &lifecycle.ChaincodeDefinition{ 975 Sequence: 7, 976 }, fakePublicState) 977 978 Expect(err).NotTo(HaveOccurred()) 979 980 trigger = &ledger.StateUpdateTrigger{ 981 LedgerID: "channel-id", 982 StateUpdates: ledger.StateUpdates(map[string]*ledger.KVStateUpdates{ 983 "_lifecycle": { 984 PublicUpdates: []*kvrwset.KVWrite{ 985 {Key: "namespaces/fields/chaincode-name/Sequence"}, 986 }, 987 CollHashUpdates: map[string][]*kvrwset.KVWriteHash{}, 988 }, 989 }), 990 PostCommitQueryExecutor: fakeQueryExecutor, 991 } 992 }) 993 994 It("updates the modified chaincodes", func() { 995 err := c.HandleStateUpdates(trigger) 996 Expect(err).NotTo(HaveOccurred()) 997 Expect(channelCache.Chaincodes["chaincode-name"].Definition.Sequence).To(Equal(int64(7))) 998 }) 999 1000 Context("when the update is not to the sequence", func() { 1001 BeforeEach(func() { 1002 trigger.StateUpdates["_lifecycle"].PublicUpdates[0].Key = "namespaces/fields/chaincode-name/EndorsementInfo" 1003 }) 1004 1005 It("no update occurs", func() { 1006 err := c.HandleStateUpdates(trigger) 1007 Expect(err).NotTo(HaveOccurred()) 1008 Expect(channelCache.Chaincodes["chaincode-name"].Definition.Sequence).To(Equal(int64(3))) 1009 Expect(fakeQueryExecutor.GetStateCallCount()).To(Equal(0)) 1010 }) 1011 }) 1012 1013 Context("when the update encounters an error", func() { 1014 BeforeEach(func() { 1015 fakeQueryExecutor.GetStateReturns(nil, fmt.Errorf("state-error")) 1016 }) 1017 1018 It("wraps and returns the error", func() { 1019 err := c.HandleStateUpdates(trigger) 1020 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")) 1021 }) 1022 }) 1023 1024 Context("when the update is to private data", func() { 1025 BeforeEach(func() { 1026 trigger.StateUpdates["_lifecycle"].PublicUpdates = nil 1027 trigger.StateUpdates["_lifecycle"].CollHashUpdates["_implicit_org_my-mspid"] = []*kvrwset.KVWriteHash{ 1028 {KeyHash: util.ComputeSHA256([]byte("namespaces/fields/chaincode-name#3/EndorsementInfo"))}, 1029 } 1030 }) 1031 1032 It("updates the corresponding chaincode", func() { 1033 err := c.HandleStateUpdates(trigger) 1034 Expect(err).NotTo(HaveOccurred()) 1035 Expect(channelCache.Chaincodes["chaincode-name"].Definition.Sequence).To(Equal(int64(7))) 1036 }) 1037 }) 1038 1039 Context("when the private update is not in our implicit collection", func() { 1040 BeforeEach(func() { 1041 trigger.StateUpdates["_lifecycle"].PublicUpdates = nil 1042 trigger.StateUpdates["_lifecycle"].CollHashUpdates["_implicit_org_other-mspid"] = []*kvrwset.KVWriteHash{ 1043 {KeyHash: util.ComputeSHA256([]byte("namespaces/fields/chaincode-name#3/EndorsementInfo"))}, 1044 } 1045 }) 1046 1047 It("it does not mark the chaincode dirty", func() { 1048 err := c.HandleStateUpdates(trigger) 1049 Expect(err).NotTo(HaveOccurred()) 1050 Expect(channelCache.Chaincodes["chaincode-name"].Definition.Sequence).To(Equal(int64(3))) 1051 Expect(fakeQueryExecutor.GetStateCallCount()).To(Equal(0)) 1052 }) 1053 }) 1054 1055 Context("when the private update is not in an implicit collection", func() { 1056 BeforeEach(func() { 1057 trigger.StateUpdates["_lifecycle"].PublicUpdates = nil 1058 trigger.StateUpdates["_lifecycle"].CollHashUpdates["random-collection"] = []*kvrwset.KVWriteHash{ 1059 {KeyHash: util.ComputeSHA256([]byte("namespaces/fields/chaincode-name#3/EndorsementInfo"))}, 1060 } 1061 }) 1062 1063 It("it does not mark the chaincode dirty", func() { 1064 err := c.HandleStateUpdates(trigger) 1065 Expect(err).NotTo(HaveOccurred()) 1066 Expect(channelCache.Chaincodes["chaincode-name"].Definition.Sequence).To(Equal(int64(3))) 1067 Expect(fakeQueryExecutor.GetStateCallCount()).To(Equal(0)) 1068 }) 1069 }) 1070 1071 Context("when the state update contains no updates to _lifecycle", func() { 1072 BeforeEach(func() { 1073 trigger.StateUpdates = ledger.StateUpdates(map[string]*ledger.KVStateUpdates{}) 1074 }) 1075 1076 It("returns an error", func() { 1077 err := c.HandleStateUpdates(trigger) 1078 Expect(err).To(MatchError("no state updates for promised namespace _lifecycle")) 1079 }) 1080 }) 1081 }) 1082 }) 1083 1084 Describe("EventsOnCacheUpdates", func() { 1085 var fakeListener *ledgermock.ChaincodeLifecycleEventListener 1086 1087 BeforeEach(func() { 1088 fakeListener = &ledgermock.ChaincodeLifecycleEventListener{} 1089 c.RegisterListener("channel-id", fakeListener, false) 1090 }) 1091 1092 Context("when initializing cache", func() { 1093 BeforeEach(func() { 1094 fakeQueryExecutor.GetPrivateDataHashStub = func(namespace, collection, key string) ([]byte, error) { 1095 return fakePrivateState.GetStateHash(key) 1096 } 1097 err := resources.Serializer.Serialize(lifecycle.NamespacesName, "chaincode-name", &lifecycle.ChaincodeDefinition{ 1098 Sequence: 4, 1099 }, fakePublicState) 1100 Expect(err).NotTo(HaveOccurred()) 1101 1102 err = resources.Serializer.Serialize(lifecycle.NamespacesName, "chaincode-name#4", &lifecycle.ChaincodeParameters{}, 1103 fakePrivateState) 1104 Expect(err).NotTo(HaveOccurred()) 1105 1106 err = resources.Serializer.Serialize(lifecycle.ChaincodeSourcesName, "chaincode-name#4", &lifecycle.ChaincodeLocalPackage{PackageID: "packageID"}, 1107 fakePrivateState) 1108 Expect(err).NotTo(HaveOccurred()) 1109 }) 1110 1111 It("should not invoke listener", func() { 1112 err := c.InitializeLocalChaincodes() 1113 Expect(err).NotTo(HaveOccurred()) 1114 err = c.Initialize("channel-id", fakeQueryExecutor) 1115 Expect(err).NotTo(HaveOccurred()) 1116 chaincodeInfo, err := c.ChaincodeInfo("channel-id", "chaincode-name") 1117 Expect(err).NotTo(HaveOccurred()) 1118 Expect(chaincodeInfo.Approved).To(BeTrue()) 1119 Expect(chaincodeInfo.Definition).NotTo(BeNil()) 1120 Expect(chaincodeInfo.InstallInfo).NotTo(BeNil()) 1121 Expect(fakeListener.HandleChaincodeDeployCallCount()).To(Equal(0)) 1122 Expect(fakeListener.ChaincodeDeployDoneCallCount()).To(Equal(0)) 1123 }) 1124 }) 1125 1126 Context("when new chaincode becomes available", func() { 1127 var ( 1128 definitionTrigger *ledger.StateUpdateTrigger 1129 approvalTrigger *ledger.StateUpdateTrigger 1130 install func(string) 1131 define func(string, int64) 1132 approve func(string, string, int64) 1133 verifyNoEvent func() 1134 verifyEvent func(string, string) 1135 ) 1136 BeforeEach(func() { 1137 definitionTrigger = &ledger.StateUpdateTrigger{ 1138 LedgerID: "channel-id", 1139 StateUpdates: ledger.StateUpdates(map[string]*ledger.KVStateUpdates{ 1140 "_lifecycle": { 1141 PublicUpdates: []*kvrwset.KVWrite{ 1142 {Key: "namespaces/fields/chaincode-name-1/Sequence"}, 1143 }, 1144 CollHashUpdates: map[string][]*kvrwset.KVWriteHash{}, 1145 }, 1146 }), 1147 PostCommitQueryExecutor: fakeQueryExecutor, 1148 } 1149 1150 approvalTrigger = &ledger.StateUpdateTrigger{ 1151 LedgerID: "channel-id", 1152 StateUpdates: ledger.StateUpdates(map[string]*ledger.KVStateUpdates{ 1153 "_lifecycle": { 1154 PublicUpdates: []*kvrwset.KVWrite{}, 1155 CollHashUpdates: map[string][]*kvrwset.KVWriteHash{ 1156 "_implicit_org_my-mspid": { 1157 { 1158 KeyHash: util.ComputeSHA256([]byte("namespaces/fields/chaincode-name-1#1/EndorsementInfo")), 1159 }, 1160 }, 1161 }, 1162 }, 1163 }), 1164 PostCommitQueryExecutor: fakeQueryExecutor, 1165 } 1166 1167 install = func(packageID string) { 1168 c.HandleChaincodeInstalled( 1169 &persistence.ChaincodePackageMetadata{ 1170 Type: "cc-type", 1171 Path: "cc-path", 1172 Label: "label", 1173 }, 1174 packageID, 1175 ) 1176 } 1177 1178 define = func(chaincodeName string, sequence int64) { 1179 err := resources.Serializer.Serialize(lifecycle.NamespacesName, chaincodeName, 1180 &lifecycle.ChaincodeDefinition{ 1181 Sequence: sequence, 1182 EndorsementInfo: &lb.ChaincodeEndorsementInfo{Version: "version-1"}, 1183 }, fakePublicState) 1184 Expect(err).NotTo(HaveOccurred()) 1185 err = c.HandleStateUpdates(definitionTrigger) 1186 Expect(err).NotTo(HaveOccurred()) 1187 } 1188 1189 approve = func(packageID, chaincodeName string, sequence int64) { 1190 err := resources.Serializer.Serialize(lifecycle.NamespacesName, fmt.Sprintf("%s#%d", chaincodeName, sequence), 1191 &lifecycle.ChaincodeParameters{ 1192 EndorsementInfo: &lb.ChaincodeEndorsementInfo{Version: "version-1"}, 1193 }, 1194 fakePrivateState) 1195 Expect(err).NotTo(HaveOccurred()) 1196 err = resources.Serializer.Serialize(lifecycle.ChaincodeSourcesName, fmt.Sprintf("%s#%d", chaincodeName, sequence), 1197 1198 &lifecycle.ChaincodeLocalPackage{ 1199 PackageID: packageID, 1200 }, 1201 fakePrivateState) 1202 Expect(err).NotTo(HaveOccurred()) 1203 err = c.HandleStateUpdates(approvalTrigger) 1204 Expect(err).NotTo(HaveOccurred()) 1205 } 1206 1207 verifyNoEvent = func() { 1208 Expect(fakeListener.HandleChaincodeDeployCallCount()).To(Equal(0)) 1209 Expect(fakeListener.ChaincodeDeployDoneCallCount()).To(Equal(0)) 1210 } 1211 1212 verifyEvent = func(packageID, chaincodeName string) { 1213 Expect(fakeListener.HandleChaincodeDeployCallCount()).To(Equal(1)) 1214 ccdef, dbArtifacts := fakeListener.HandleChaincodeDeployArgsForCall(0) 1215 Expect(ccdef.Name).To(Equal(chaincodeName)) 1216 Expect(ccdef.Version).To(Equal("version-1")) 1217 Expect(ccdef.Hash).To(Equal([]byte(packageID))) 1218 Expect(dbArtifacts).To(Equal([]byte("db-artifacts"))) 1219 } 1220 }) 1221 1222 Context("when chaincode becomes invokable by the sequence of events define, install, and approve", func() { 1223 It("causes the event listener to receive event on approve step", func() { 1224 define("chaincode-name-1", 1) 1225 install("packageID-1") 1226 verifyNoEvent() 1227 approve("packageID-1", "chaincode-name-1", 1) 1228 verifyEvent("packageID-1", "chaincode-name-1") 1229 Expect(fakeListener.ChaincodeDeployDoneCallCount()).To(Equal(0)) 1230 c.StateCommitDone("channel-id") 1231 Expect(fakeListener.ChaincodeDeployDoneCallCount()).To(Equal(1)) 1232 }) 1233 }) 1234 1235 Context("when chaincode becomes invokable by the sequence of events install, define, and approve", func() { 1236 It("causes the event listener to receive event on approve step", func() { 1237 install("packageID-1") 1238 define("chaincode-name-1", 1) 1239 verifyNoEvent() 1240 approve("packageID-1", "chaincode-name-1", 1) 1241 verifyEvent("packageID-1", "chaincode-name-1") 1242 Expect(fakeListener.ChaincodeDeployDoneCallCount()).To(Equal(0)) 1243 c.StateCommitDone("channel-id") 1244 Expect(fakeListener.ChaincodeDeployDoneCallCount()).To(Equal(1)) 1245 }) 1246 }) 1247 1248 Context("when chaincode becomes invokable by the sequence of events install, approve, and define", func() { 1249 It("causes the event listener to receive event on define step", func() { 1250 install("packageID-1") 1251 approve("packageID-1", "chaincode-name-1", 1) 1252 verifyNoEvent() 1253 define("chaincode-name-1", 1) 1254 verifyEvent("packageID-1", "chaincode-name-1") 1255 Expect(fakeListener.ChaincodeDeployDoneCallCount()).To(Equal(0)) 1256 c.StateCommitDone("channel-id") 1257 Expect(fakeListener.ChaincodeDeployDoneCallCount()).To(Equal(1)) 1258 }) 1259 }) 1260 1261 Context("when chaincode becomes invokable by the sequence of events approve, install, and define", func() { 1262 It("causes the event listener to receive event on define step", func() { 1263 approve("packageID-1", "chaincode-name-1", 1) 1264 install("packageID-1") 1265 verifyNoEvent() 1266 define("chaincode-name-1", 1) 1267 verifyEvent("packageID-1", "chaincode-name-1") 1268 Expect(fakeListener.ChaincodeDeployDoneCallCount()).To(Equal(0)) 1269 c.StateCommitDone("channel-id") 1270 Expect(fakeListener.ChaincodeDeployDoneCallCount()).To(Equal(1)) 1271 }) 1272 }) 1273 1274 Context("when chaincode becomes invokable by the sequence of events define, approve, and install", func() { 1275 It("causes the event listener to receive event on install step", func() { 1276 define("chaincode-name-1", 1) 1277 approve("packageID-1", "chaincode-name-1", 1) 1278 verifyNoEvent() 1279 install("packageID-1") 1280 verifyEvent("packageID-1", "chaincode-name-1") 1281 Expect(fakeListener.ChaincodeDeployDoneCallCount()).To(Equal(1)) 1282 }) 1283 }) 1284 1285 Context("when chaincode becomes invokable by the sequence of events approve, define, and install", func() { 1286 It("causes the event listener to receive event on install step", func() { 1287 approve("packageID-1", "chaincode-name-1", 1) 1288 define("chaincode-name-1", 1) 1289 verifyNoEvent() 1290 install("packageID-1") 1291 verifyEvent("packageID-1", "chaincode-name-1") 1292 Expect(fakeListener.ChaincodeDeployDoneCallCount()).To(Equal(1)) 1293 }) 1294 }) 1295 1296 Context("when chaincode definition is updated by the sequence of events install, approve, and define for existing chaincode name", func() { 1297 BeforeEach(func() { 1298 channelCache.Chaincodes["chaincode-name"].InstallInfo = &lifecycle.ChaincodeInstallInfo{ 1299 Label: "chaincode-label", 1300 PackageID: "packageID", 1301 } 1302 definitionTrigger.StateUpdates["_lifecycle"].PublicUpdates[0].Key = "namespaces/fields/chaincode-name/Sequence" 1303 1304 approvalTrigger.StateUpdates["_lifecycle"].CollHashUpdates["_implicit_org_my-mspid"] = []*kvrwset.KVWriteHash{ 1305 {KeyHash: util.ComputeSHA256([]byte("namespaces/fields/chaincode-name#4/EndorsementInfo"))}, 1306 } 1307 }) 1308 1309 It("receives the event and cleans up stale chaincode definition references", func() { 1310 install("packageID-1") 1311 approve("packageID-1", "chaincode-name", 4) 1312 define("chaincode-name", 4) 1313 c.StateCommitDone("channel-id") 1314 verifyEvent("packageID-1", "chaincode-name") 1315 Expect(fakeListener.ChaincodeDeployDoneCallCount()).To(Equal(1)) 1316 1317 installedCC, err := c.GetInstalledChaincode("packageID") 1318 Expect(err).NotTo(HaveOccurred()) 1319 Expect(installedCC.References["channel-id"]).To(HaveLen(0)) 1320 Expect(installedCC.References["another-channel-id"]).To(HaveLen(1)) 1321 installedCC, err = c.GetInstalledChaincode("packageID-1") 1322 Expect(err).NotTo(HaveOccurred()) 1323 Expect(installedCC.References["channel-id"]).To(HaveLen(1)) 1324 }) 1325 }) 1326 1327 Context("when an existing chaincode definition with a package is updated", func() { 1328 BeforeEach(func() { 1329 channelCache.Chaincodes["chaincode-name"].InstallInfo = &lifecycle.ChaincodeInstallInfo{ 1330 Label: "chaincode-label", 1331 PackageID: "packageID", 1332 } 1333 1334 localChaincodes = map[string]*lifecycle.LocalChaincode{ 1335 string(util.ComputeSHA256(protoutil.MarshalOrPanic(&lb.StateData{ 1336 Type: &lb.StateData_String_{String_: "packageID"}, 1337 }))): { 1338 References: map[string]map[string]*lifecycle.CachedChaincodeDefinition{ 1339 "channel-id": { 1340 "chaincode-name": channelCache.Chaincodes["chaincode-name"], 1341 }, 1342 }, 1343 Info: &lifecycle.ChaincodeInstallInfo{ 1344 Label: "chaincode-label", 1345 PackageID: "packageID", 1346 }, 1347 }, 1348 } 1349 lifecycle.SetLocalChaincodesMap(c, localChaincodes) 1350 1351 err := resources.Serializer.Serialize(lifecycle.NamespacesName, "chaincode-name", &lifecycle.ChaincodeDefinition{ 1352 Sequence: 3, 1353 }, fakePublicState) 1354 Expect(err).NotTo(HaveOccurred()) 1355 }) 1356 1357 Context("by an approve event with an empty package ID for the current sequence number", func() { 1358 BeforeEach(func() { 1359 approvalTrigger.StateUpdates["_lifecycle"].CollHashUpdates["_implicit_org_my-mspid"] = []*kvrwset.KVWriteHash{ 1360 {KeyHash: util.ComputeSHA256([]byte("chaincode-sources/fields/chaincode-name#3/PackageID"))}, 1361 } 1362 }) 1363 1364 It("receives the event, cleans up stale chaincode definition references and stops the unreferenced chaincode", func() { 1365 approve("", "chaincode-name", 3) 1366 1367 installedCC, err := c.GetInstalledChaincode("packageID") 1368 Expect(err).NotTo(HaveOccurred()) 1369 Expect(installedCC.References["channel-id"]).To(HaveLen(0)) 1370 Expect(err).NotTo(HaveOccurred()) 1371 1372 fakeLauncher := &mock.ChaincodeLauncher{} 1373 go chaincodeCustodian.Work(nil, nil, fakeLauncher) 1374 Eventually(fakeLauncher.StopCallCount).Should(Equal(1)) 1375 }) 1376 }) 1377 1378 Context("by approve and commit events with an empty package ID for the next sequence number", func() { 1379 BeforeEach(func() { 1380 approvalTrigger.StateUpdates["_lifecycle"].CollHashUpdates["_implicit_org_my-mspid"] = []*kvrwset.KVWriteHash{ 1381 {KeyHash: util.ComputeSHA256([]byte("chaincode-sources/fields/chaincode-name#4/PackageID"))}, 1382 } 1383 1384 definitionTrigger.StateUpdates["_lifecycle"].PublicUpdates[0].Key = "namespaces/fields/chaincode-name/Sequence" 1385 }) 1386 1387 It("receives the event, cleans up stale chaincode definition references and stops the unreferenced chaincode", func() { 1388 approve("", "chaincode-name", 4) 1389 define("chaincode-name", 4) 1390 c.StateCommitDone("channel-id") 1391 1392 installedCC, err := c.GetInstalledChaincode("packageID") 1393 Expect(err).NotTo(HaveOccurred()) 1394 Expect(installedCC.References["channel-id"]).To(HaveLen(0)) 1395 Expect(err).NotTo(HaveOccurred()) 1396 1397 fakeLauncher := &mock.ChaincodeLauncher{} 1398 go chaincodeCustodian.Work(nil, nil, fakeLauncher) 1399 Eventually(fakeLauncher.StopCallCount).Should(Equal(1)) 1400 }) 1401 }) 1402 }) 1403 }) 1404 }) 1405 })