github.com/ewagmig/fabric@v2.1.1+incompatible/core/chaincode/lifecycle/cache_test.go (about)

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