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  })