github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/chaincode/lifecycle/cache.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package lifecycle
     8  
     9  import (
    10  	"fmt"
    11  	"sort"
    12  	"strconv"
    13  	"sync"
    14  
    15  	"github.com/hechain20/hechain/common/chaincode"
    16  	"github.com/hechain20/hechain/common/util"
    17  	"github.com/hechain20/hechain/core/chaincode/implicitcollection"
    18  	"github.com/hechain20/hechain/core/chaincode/persistence"
    19  	"github.com/hechain20/hechain/core/container/externalbuilder"
    20  	"github.com/hechain20/hechain/core/ledger"
    21  	"github.com/hechain20/hechain/protoutil"
    22  	lb "github.com/hyperledger/fabric-protos-go/peer/lifecycle"
    23  
    24  	"github.com/pkg/errors"
    25  )
    26  
    27  type LocalChaincodeInfo struct {
    28  	Definition  *ChaincodeDefinition
    29  	Approved    bool
    30  	InstallInfo *ChaincodeInstallInfo
    31  }
    32  
    33  type ChaincodeInstallInfo struct {
    34  	PackageID string
    35  	Type      string
    36  	Path      string
    37  	Label     string
    38  }
    39  
    40  type CachedChaincodeDefinition struct {
    41  	Definition  *ChaincodeDefinition
    42  	Approved    bool
    43  	InstallInfo *ChaincodeInstallInfo
    44  
    45  	// Hashes is the list of hashed keys in the implicit collection referring to this definition.
    46  	// These hashes are determined by the current sequence number of chaincode definition.  When dirty,
    47  	// these hashes will be empty, and when not, they will be populated.
    48  	Hashes []string
    49  }
    50  
    51  type ChannelCache struct {
    52  	Chaincodes map[string]*CachedChaincodeDefinition
    53  
    54  	// InterestingHashes is a map of hashed key names to the chaincode name which they affect.
    55  	// These are to be used for the state listener, to mark chaincode definitions dirty when
    56  	// a write is made into the implicit collection for this org.  Interesting hashes are
    57  	// added when marking a definition clean, and deleted when marking it dirty.
    58  	InterestingHashes map[string]string
    59  }
    60  
    61  // MetadataHandler is the interface through which the cache drives
    62  // metadata updates for listeners such as gossip and service discovery
    63  type MetadataHandler interface {
    64  	InitializeMetadata(channel string, chaincodes chaincode.MetadataSet)
    65  	UpdateMetadata(channel string, chaincodes chaincode.MetadataSet)
    66  }
    67  
    68  type Cache struct {
    69  	definedChaincodes map[string]*ChannelCache
    70  	Resources         *Resources
    71  	MyOrgMSPID        string
    72  
    73  	// mutex serializes lifecycle operations globally for the peer.  It will cause a lifecycle update
    74  	// in one channel to potentially affect the throughput of another.  However, relative to standard
    75  	// transactions, lifecycle updates should be quite rare, and this is a RW lock so in general, there
    76  	// should not be contention in the normal case.  Because chaincode package installation is a peer global
    77  	// event, by synchronizing at a peer global level, we drastically simplify accounting for which
    78  	// chaincodes are installed and which channels that installed chaincode is currently in use on.
    79  	mutex sync.RWMutex
    80  
    81  	// localChaincodes is a map from the hash of the locally installed chaincode's proto
    82  	// encoded hash (yes, the hash of the hash), to a set of channels, to a set of chaincode
    83  	// definitions which reference this local installed chaincode hash.
    84  	localChaincodes map[string]*LocalChaincode
    85  	eventBroker     *EventBroker
    86  	MetadataHandler MetadataHandler
    87  
    88  	chaincodeCustodian *ChaincodeCustodian
    89  }
    90  
    91  type LocalChaincode struct {
    92  	Info       *ChaincodeInstallInfo
    93  	References map[string]map[string]*CachedChaincodeDefinition
    94  }
    95  
    96  // ToInstalledChaincode converts a LocalChaincode to an InstalledChaincode,
    97  // which is returned by lifecycle queries.
    98  func (l *LocalChaincode) ToInstalledChaincode() *chaincode.InstalledChaincode {
    99  	references := l.createMetadataMapFromReferences()
   100  	return &chaincode.InstalledChaincode{
   101  		PackageID:  l.Info.PackageID,
   102  		Label:      l.Info.Label,
   103  		References: references,
   104  	}
   105  }
   106  
   107  // createMetadataMapFromReferences returns a map of channel name to a slice
   108  // of chaincode metadata for the chaincode definitions that reference a
   109  // specific local chaincode. This function should only be called by code that
   110  // holds the lock on the cache.
   111  func (l *LocalChaincode) createMetadataMapFromReferences() map[string][]*chaincode.Metadata {
   112  	references := map[string][]*chaincode.Metadata{}
   113  	for channel, chaincodeMap := range l.References {
   114  		metadata := []*chaincode.Metadata{}
   115  		for cc, cachedDefinition := range chaincodeMap {
   116  			metadata = append(metadata, &chaincode.Metadata{
   117  				Name:    cc,
   118  				Version: cachedDefinition.Definition.EndorsementInfo.Version,
   119  			})
   120  		}
   121  		references[channel] = metadata
   122  	}
   123  	return references
   124  }
   125  
   126  func NewCache(resources *Resources, myOrgMSPID string, metadataManager MetadataHandler, custodian *ChaincodeCustodian, ebMetadata *externalbuilder.MetadataProvider) *Cache {
   127  	return &Cache{
   128  		chaincodeCustodian: custodian,
   129  		definedChaincodes:  map[string]*ChannelCache{},
   130  		localChaincodes:    map[string]*LocalChaincode{},
   131  		Resources:          resources,
   132  		MyOrgMSPID:         myOrgMSPID,
   133  		eventBroker:        NewEventBroker(resources.ChaincodeStore, resources.PackageParser, ebMetadata),
   134  		MetadataHandler:    metadataManager,
   135  	}
   136  }
   137  
   138  // InitializeLocalChaincodes should be called once after cache creation (timing doesn't matter,
   139  // though already installed chaincodes will not be invokable until it it completes).  Ideally,
   140  // this would be part of the constructor, but, we cannot rely on the chaincode store being created
   141  // before the cache is created.
   142  func (c *Cache) InitializeLocalChaincodes() error {
   143  	c.mutex.Lock()
   144  	defer c.mutex.Unlock()
   145  	ccPackages, err := c.Resources.ChaincodeStore.ListInstalledChaincodes()
   146  	if err != nil {
   147  		return errors.WithMessage(err, "could not list installed chaincodes")
   148  	}
   149  
   150  	for _, ccPackage := range ccPackages {
   151  		ccPackageBytes, err := c.Resources.ChaincodeStore.Load(ccPackage.PackageID)
   152  		if err != nil {
   153  			return errors.WithMessagef(err, "could not load chaincode with package ID '%s'", ccPackage.PackageID)
   154  		}
   155  		parsedCCPackage, err := c.Resources.PackageParser.Parse(ccPackageBytes)
   156  		if err != nil {
   157  			return errors.WithMessagef(err, "could not parse chaincode with package ID '%s'", ccPackage.PackageID)
   158  		}
   159  		c.handleChaincodeInstalledWhileLocked(true, parsedCCPackage.Metadata, ccPackage.PackageID)
   160  	}
   161  
   162  	logger.Infof("Initialized lifecycle cache with %d already installed chaincodes", len(c.localChaincodes))
   163  	for channelID, chaincodeCache := range c.definedChaincodes {
   164  		approved, installed, runnable := 0, 0, 0
   165  		for _, cachedChaincode := range chaincodeCache.Chaincodes {
   166  			if cachedChaincode.Approved {
   167  				approved++
   168  			}
   169  			if cachedChaincode.InstallInfo != nil {
   170  				installed++
   171  			}
   172  			if cachedChaincode.Approved && cachedChaincode.InstallInfo != nil {
   173  				runnable++
   174  			}
   175  		}
   176  
   177  		logger.Infof("Initialized lifecycle cache for channel '%s' with %d chaincodes runnable (%d approved, %d installed)", channelID, runnable, approved, installed)
   178  	}
   179  
   180  	return nil
   181  }
   182  
   183  // Name returns the name of the listener
   184  func (c *Cache) Name() string {
   185  	return "lifecycle cache listener"
   186  }
   187  
   188  // Initialize will populate the set of currently committed chaincode definitions
   189  // for a channel into the cache.  Note, it this looks like a bit of a DRY violation
   190  // with respect to 'Update', but, the error handling is quite different and attempting
   191  // to factor out the common pieces results in a net total of more code.
   192  func (c *Cache) Initialize(channelID string, qe ledger.SimpleQueryExecutor) error {
   193  	c.mutex.Lock()
   194  	defer c.mutex.Unlock()
   195  
   196  	publicState := &SimpleQueryExecutorShim{
   197  		Namespace:           LifecycleNamespace,
   198  		SimpleQueryExecutor: qe,
   199  	}
   200  
   201  	metadatas, err := c.Resources.Serializer.DeserializeAllMetadata(NamespacesName, publicState)
   202  	if err != nil {
   203  		return errors.WithMessage(err, "could not query namespace metadata")
   204  	}
   205  
   206  	dirtyChaincodes := map[string]struct{}{}
   207  
   208  	for namespace, metadata := range metadatas {
   209  		switch metadata.Datatype {
   210  		case ChaincodeDefinitionType:
   211  			dirtyChaincodes[namespace] = struct{}{}
   212  		default:
   213  			// non-chaincode
   214  		}
   215  	}
   216  
   217  	return c.update(true, channelID, dirtyChaincodes, qe)
   218  }
   219  
   220  // HandleChaincodeInstalled should be invoked whenever a new chaincode is installed
   221  func (c *Cache) HandleChaincodeInstalled(md *persistence.ChaincodePackageMetadata, packageID string) {
   222  	c.mutex.Lock()
   223  	defer c.mutex.Unlock()
   224  	c.handleChaincodeInstalledWhileLocked(false, md, packageID)
   225  }
   226  
   227  func (c *Cache) handleChaincodeInstalledWhileLocked(initializing bool, md *persistence.ChaincodePackageMetadata, packageID string) {
   228  	// it would be nice to get this value from the serialization package, but it was not obvious
   229  	// how to expose this in a nice way, so we manually compute it.
   230  	encodedCCHash := protoutil.MarshalOrPanic(&lb.StateData{
   231  		Type: &lb.StateData_String_{String_: packageID},
   232  	})
   233  	hashOfCCHash := string(util.ComputeSHA256(encodedCCHash))
   234  	localChaincode, ok := c.localChaincodes[hashOfCCHash]
   235  	if !ok {
   236  		localChaincode = &LocalChaincode{
   237  			References: map[string]map[string]*CachedChaincodeDefinition{},
   238  		}
   239  		c.localChaincodes[hashOfCCHash] = localChaincode
   240  		c.chaincodeCustodian.NotifyInstalled(packageID)
   241  	}
   242  	localChaincode.Info = &ChaincodeInstallInfo{
   243  		PackageID: packageID,
   244  		Type:      md.Type,
   245  		Path:      md.Path,
   246  		Label:     md.Label,
   247  	}
   248  	for channelID, channelCache := range localChaincode.References {
   249  		for chaincodeName, cachedChaincode := range channelCache {
   250  			cachedChaincode.InstallInfo = localChaincode.Info
   251  			logger.Infof("Installed chaincode with package ID '%s' now available on channel %s for chaincode definition %s:%s", packageID, channelID, chaincodeName, cachedChaincode.Definition.EndorsementInfo.Version)
   252  			c.chaincodeCustodian.NotifyInstalledAndRunnable(packageID)
   253  		}
   254  	}
   255  
   256  	if !initializing {
   257  		c.eventBroker.ProcessInstallEvent(localChaincode)
   258  		c.handleMetadataUpdates(localChaincode)
   259  	}
   260  }
   261  
   262  // HandleStateUpdates is required to implement the ledger state listener interface.  It applies
   263  // any state updates to the cache.
   264  func (c *Cache) HandleStateUpdates(trigger *ledger.StateUpdateTrigger) error {
   265  	c.mutex.Lock()
   266  	defer c.mutex.Unlock()
   267  	channelID := trigger.LedgerID
   268  	updates, ok := trigger.StateUpdates[LifecycleNamespace]
   269  	if !ok {
   270  		return errors.Errorf("no state updates for promised namespace _lifecycle")
   271  	}
   272  
   273  	dirtyChaincodes := map[string]struct{}{}
   274  
   275  	for _, publicUpdate := range updates.PublicUpdates {
   276  		matches := SequenceMatcher.FindStringSubmatch(publicUpdate.Key)
   277  		if len(matches) != 2 {
   278  			continue
   279  		}
   280  
   281  		dirtyChaincodes[matches[1]] = struct{}{}
   282  	}
   283  
   284  	channelCache, ok := c.definedChaincodes[channelID]
   285  
   286  	// if the channel cache does not yet exist, there are no interesting hashes, so skip
   287  	if ok {
   288  		for collection, privateUpdates := range updates.CollHashUpdates {
   289  			isImplicitCollection, mspID := implicitcollection.MspIDIfImplicitCollection(collection)
   290  			if !isImplicitCollection {
   291  				continue
   292  			}
   293  
   294  			if mspID != c.MyOrgMSPID {
   295  				// This is not our implicit collection
   296  				continue
   297  			}
   298  
   299  			for _, privateUpdate := range privateUpdates {
   300  				chaincodeName, ok := channelCache.InterestingHashes[string(privateUpdate.KeyHash)]
   301  				if ok {
   302  					dirtyChaincodes[chaincodeName] = struct{}{}
   303  				}
   304  			}
   305  		}
   306  	}
   307  
   308  	err := c.update(false, channelID, dirtyChaincodes, trigger.PostCommitQueryExecutor)
   309  	if err != nil {
   310  		return errors.WithMessage(err, "error updating cache")
   311  	}
   312  
   313  	return nil
   314  }
   315  
   316  // InterestedInNamespaces is required to implement the ledger state listener interface
   317  func (c *Cache) InterestedInNamespaces() []string {
   318  	return []string{LifecycleNamespace}
   319  }
   320  
   321  // StateCommitDone is required to implement the ledger state listener interface
   322  func (c *Cache) StateCommitDone(channelName string) {
   323  	// NOTE: It's extremely tempting to acquire the write lock in HandleStateUpdate
   324  	// and release it here, however, this is asking for a deadlock.  In particular,
   325  	// because the 'write lock' on the state is only held for a short period
   326  	// between HandleStateUpdate and StateCommitDone, it's possible (in fact likely)
   327  	// that a chaincode invocation will acquire a read-lock on the world state, then attempt
   328  	// to get chaincode info from the cache, resulting in a deadlock.  So, we choose
   329  	// potential inconsistency between the cache and the world state which the callers
   330  	// must detect and cope with as necessary.  Note, the cache will always be _at least_
   331  	// as current as the committed state.
   332  	c.eventBroker.ApproveOrDefineCommitted(channelName)
   333  }
   334  
   335  // ChaincodeInfo returns the chaincode definition and its install info.
   336  // An error is returned only if either the channel or the chaincode do not exist.
   337  func (c *Cache) ChaincodeInfo(channelID, name string) (*LocalChaincodeInfo, error) {
   338  	if name == LifecycleNamespace {
   339  		ac, ok := c.Resources.ChannelConfigSource.GetStableChannelConfig(channelID).ApplicationConfig()
   340  		if !ok {
   341  			return nil, errors.Errorf("application config does not exist for channel '%s'", channelID)
   342  		}
   343  		if !ac.Capabilities().LifecycleV20() {
   344  			return nil, errors.Errorf("cannot use _lifecycle without V2_0 application capabilities enabled for channel '%s'", channelID)
   345  		}
   346  		return c.getLifecycleSCCChaincodeInfo(channelID)
   347  	}
   348  
   349  	c.mutex.RLock()
   350  	defer c.mutex.RUnlock()
   351  	channelChaincodes, ok := c.definedChaincodes[channelID]
   352  	if !ok {
   353  		return nil, errors.Errorf("unknown channel '%s'", channelID)
   354  	}
   355  
   356  	cachedChaincode, ok := channelChaincodes.Chaincodes[name]
   357  	if !ok {
   358  		return nil, errors.Errorf("unknown chaincode '%s' for channel '%s'", name, channelID)
   359  	}
   360  
   361  	return &LocalChaincodeInfo{
   362  		Definition:  cachedChaincode.Definition,
   363  		InstallInfo: cachedChaincode.InstallInfo,
   364  		Approved:    cachedChaincode.Approved,
   365  	}, nil
   366  }
   367  
   368  func (c *Cache) getLifecycleSCCChaincodeInfo(channelID string) (*LocalChaincodeInfo, error) {
   369  	policyBytes, err := c.Resources.LifecycleEndorsementPolicyAsBytes(channelID)
   370  	if err != nil {
   371  		return nil, err
   372  	}
   373  
   374  	return &LocalChaincodeInfo{
   375  		Definition: &ChaincodeDefinition{
   376  			ValidationInfo: &lb.ChaincodeValidationInfo{
   377  				ValidationParameter: policyBytes,
   378  			},
   379  			Sequence: 1,
   380  		},
   381  		Approved:    true,
   382  		InstallInfo: &ChaincodeInstallInfo{},
   383  	}, nil
   384  }
   385  
   386  // ListInstalledChaincodes returns a slice containing all of the information
   387  // about the installed chaincodes.
   388  func (c *Cache) ListInstalledChaincodes() []*chaincode.InstalledChaincode {
   389  	c.mutex.RLock()
   390  	defer c.mutex.RUnlock()
   391  
   392  	installedChaincodes := []*chaincode.InstalledChaincode{}
   393  	for _, lc := range c.localChaincodes {
   394  		if lc.Info == nil {
   395  			// the update function adds an entry to localChaincodes
   396  			// even if it isn't yet installed
   397  			continue
   398  		}
   399  		installedChaincodes = append(installedChaincodes, lc.ToInstalledChaincode())
   400  	}
   401  
   402  	return installedChaincodes
   403  }
   404  
   405  // GetInstalledChaincode returns all of the information about a specific
   406  // installed chaincode.
   407  func (c *Cache) GetInstalledChaincode(packageID string) (*chaincode.InstalledChaincode, error) {
   408  	c.mutex.RLock()
   409  	defer c.mutex.RUnlock()
   410  
   411  	for _, lc := range c.localChaincodes {
   412  		if lc.Info == nil {
   413  			// the update function adds an entry to localChaincodes
   414  			// even if it isn't yet installed
   415  			continue
   416  		}
   417  		if lc.Info.PackageID == packageID {
   418  			return lc.ToInstalledChaincode(), nil
   419  		}
   420  	}
   421  
   422  	return nil, errors.Errorf("could not find chaincode with package id '%s'", packageID)
   423  }
   424  
   425  // update should only be called with the write lock already held
   426  func (c *Cache) update(initializing bool, channelID string, dirtyChaincodes map[string]struct{}, qe ledger.SimpleQueryExecutor) error {
   427  	channelCache, ok := c.definedChaincodes[channelID]
   428  	if !ok {
   429  		channelCache = &ChannelCache{
   430  			Chaincodes:        map[string]*CachedChaincodeDefinition{},
   431  			InterestingHashes: map[string]string{},
   432  		}
   433  		c.definedChaincodes[channelID] = channelCache
   434  	}
   435  
   436  	publicState := &SimpleQueryExecutorShim{
   437  		Namespace:           LifecycleNamespace,
   438  		SimpleQueryExecutor: qe,
   439  	}
   440  
   441  	orgState := &PrivateQueryExecutorShim{
   442  		Namespace:  LifecycleNamespace,
   443  		Collection: implicitcollection.NameForOrg(c.MyOrgMSPID),
   444  		State:      qe,
   445  	}
   446  
   447  	for name := range dirtyChaincodes {
   448  		logger.Infof("Updating cached definition for chaincode '%s' on channel '%s'", name, channelID)
   449  		cachedChaincode, ok := channelCache.Chaincodes[name]
   450  		if !ok {
   451  			cachedChaincode = &CachedChaincodeDefinition{}
   452  			channelCache.Chaincodes[name] = cachedChaincode
   453  		}
   454  
   455  		for _, hash := range cachedChaincode.Hashes {
   456  			delete(channelCache.InterestingHashes, hash)
   457  		}
   458  
   459  		exists, chaincodeDefinition, err := c.Resources.ChaincodeDefinitionIfDefined(name, publicState)
   460  		if err != nil {
   461  			return errors.WithMessagef(err, "could not get chaincode definition for '%s' on channel '%s'", name, channelID)
   462  		}
   463  
   464  		if !exists {
   465  			// the chaincode definition was deleted, this is currently not
   466  			// possible, but there should be no problems with that.
   467  			delete(channelCache.Chaincodes, name)
   468  			continue
   469  		}
   470  
   471  		privateName := fmt.Sprintf("%s#%d", name, chaincodeDefinition.Sequence)
   472  		hashKey := FieldKey(ChaincodeSourcesName, privateName, "PackageID")
   473  		hashOfCCHash, err := orgState.GetStateHash(hashKey)
   474  		if err != nil {
   475  			return errors.WithMessagef(err, "could not check opaque org state for chaincode source hash for '%s' on channel '%s'", name, channelID)
   476  		}
   477  
   478  		localChaincode, ok := c.localChaincodes[string(hashOfCCHash)]
   479  		if !ok {
   480  			localChaincode = &LocalChaincode{
   481  				References: map[string]map[string]*CachedChaincodeDefinition{},
   482  			}
   483  			c.localChaincodes[string(hashOfCCHash)] = localChaincode
   484  		}
   485  
   486  		if !initializing {
   487  			// check for existing local chaincodes that reference this chaincode
   488  			// name on this channel
   489  			for _, lc := range c.localChaincodes {
   490  				if ref, ok := lc.References[channelID][name]; ok {
   491  					if ref.InstallInfo == nil {
   492  						continue
   493  					}
   494  					if localChaincode.Info != nil {
   495  						if ref.InstallInfo.PackageID == localChaincode.Info.PackageID {
   496  							continue
   497  						}
   498  					}
   499  
   500  					// remove existing local chaincode reference, which referred to a
   501  					// previous chaincode definition
   502  					delete(lc.References[channelID], name)
   503  					if len(lc.References[channelID]) == 0 {
   504  						delete(lc.References, channelID)
   505  
   506  						// check to see if this "local" chaincode is installed (an entry
   507  						// is added into local chaincodes for active chaincode definition
   508  						// references regardless of whether the peer has a chaincode
   509  						// package installed)
   510  						if lc.Info == nil {
   511  							continue
   512  						}
   513  
   514  						// finally, check to see if this chaincode is referenced in any
   515  						// channel. if not, stop the chaincode here
   516  						if len(lc.References) == 0 {
   517  							logger.Debugf("chaincode package with label %s is no longer referenced and will be stopped", lc.Info.Label)
   518  							c.chaincodeCustodian.NotifyStoppable(lc.Info.PackageID)
   519  						}
   520  					}
   521  				}
   522  			}
   523  		}
   524  
   525  		cachedChaincode.Definition = chaincodeDefinition
   526  		cachedChaincode.Approved = false
   527  
   528  		cachedChaincode.Hashes = []string{
   529  			string(util.ComputeSHA256([]byte(MetadataKey(NamespacesName, privateName)))),
   530  			string(util.ComputeSHA256([]byte(FieldKey(NamespacesName, privateName, "EndorsementInfo")))),
   531  			string(util.ComputeSHA256([]byte(FieldKey(NamespacesName, privateName, "ValidationInfo")))),
   532  			string(util.ComputeSHA256([]byte(FieldKey(NamespacesName, privateName, "Collections")))),
   533  			string(util.ComputeSHA256([]byte(FieldKey(ChaincodeSourcesName, privateName, "PackageID")))),
   534  		}
   535  
   536  		for _, hash := range cachedChaincode.Hashes {
   537  			channelCache.InterestingHashes[hash] = name
   538  		}
   539  
   540  		ok, err = c.Resources.Serializer.IsSerialized(NamespacesName, privateName, chaincodeDefinition.Parameters(), orgState)
   541  
   542  		if err != nil {
   543  			return errors.WithMessagef(err, "could not check opaque org state for '%s' on channel '%s'", name, channelID)
   544  		}
   545  		if !ok {
   546  			logger.Debugf("Channel %s for chaincode definition %s:%s does not have our org's approval", channelID, name, chaincodeDefinition.EndorsementInfo.Version)
   547  			continue
   548  		}
   549  
   550  		cachedChaincode.Approved = true
   551  
   552  		isLocalPackage, err := c.Resources.Serializer.IsMetadataSerialized(ChaincodeSourcesName, privateName, &ChaincodeLocalPackage{}, orgState)
   553  		if err != nil {
   554  			return errors.WithMessagef(err, "could not check opaque org state for chaincode source for '%s' on channel '%s'", name, channelID)
   555  		}
   556  
   557  		if !isLocalPackage {
   558  			logger.Debugf("Channel %s for chaincode definition %s:%s does not have a chaincode source defined", channelID, name, chaincodeDefinition.EndorsementInfo.Version)
   559  			continue
   560  		}
   561  
   562  		cachedChaincode.InstallInfo = localChaincode.Info
   563  		if localChaincode.Info != nil {
   564  			logger.Infof("Chaincode with package ID '%s' now available on channel %s for chaincode definition %s:%s", localChaincode.Info.PackageID, channelID, name, cachedChaincode.Definition.EndorsementInfo.Version)
   565  			c.chaincodeCustodian.NotifyInstalledAndRunnable(localChaincode.Info.PackageID)
   566  		} else {
   567  			logger.Debugf("Chaincode definition for chaincode '%s' on channel '%s' is approved, but not installed", name, channelID)
   568  		}
   569  
   570  		channelReferences, ok := localChaincode.References[channelID]
   571  		if !ok {
   572  			channelReferences = map[string]*CachedChaincodeDefinition{}
   573  			localChaincode.References[channelID] = channelReferences
   574  		}
   575  
   576  		channelReferences[name] = cachedChaincode
   577  
   578  		if !initializing {
   579  			c.eventBroker.ProcessApproveOrDefineEvent(channelID, name, cachedChaincode)
   580  		}
   581  	}
   582  
   583  	if !initializing {
   584  		c.handleMetadataUpdatesForChannel(channelID)
   585  	}
   586  
   587  	return nil
   588  }
   589  
   590  // RegisterListener registers an event listener for receiving an event when a chaincode becomes invokable
   591  func (c *Cache) RegisterListener(
   592  	channelID string,
   593  	listener ledger.ChaincodeLifecycleEventListener,
   594  	needsExistingChaincodesDefinitions bool,
   595  ) error {
   596  	c.mutex.RLock()
   597  	defer c.mutex.RUnlock()
   598  
   599  	channelChaincodes, ok := c.definedChaincodes[channelID]
   600  	if !ok {
   601  		return errors.Errorf("unknown channel '%s'", channelID)
   602  	}
   603  
   604  	if needsExistingChaincodesDefinitions {
   605  		c.eventBroker.RegisterListener(channelID, listener, channelChaincodes.Chaincodes)
   606  	} else {
   607  		c.eventBroker.RegisterListener(channelID, listener, nil)
   608  	}
   609  
   610  	return nil
   611  }
   612  
   613  func (c *Cache) InitializeMetadata(channel string) {
   614  	c.mutex.RLock()
   615  	defer c.mutex.RUnlock()
   616  
   617  	ms, err := c.retrieveChaincodesMetadataSetWhileLocked(channel)
   618  	if err != nil {
   619  		logger.Warningf("no metadata found on channel '%s', err %s", channel, err)
   620  		return
   621  	}
   622  
   623  	c.MetadataHandler.InitializeMetadata(channel, ms)
   624  }
   625  
   626  func (c *Cache) retrieveChaincodesMetadataSetWhileLocked(channelID string) (chaincode.MetadataSet, error) {
   627  	channelChaincodes, ok := c.definedChaincodes[channelID]
   628  	if !ok {
   629  		return nil, errors.Errorf("unknown channel '%s'", channelID)
   630  	}
   631  
   632  	keys := make([]string, 0, len(channelChaincodes.Chaincodes))
   633  	for name := range channelChaincodes.Chaincodes {
   634  		keys = append(keys, name)
   635  	}
   636  	sort.Strings(keys)
   637  
   638  	metadataSet := chaincode.MetadataSet{}
   639  	for _, name := range keys {
   640  		def := channelChaincodes.Chaincodes[name]
   641  
   642  		// report the sequence as the version to service discovery since
   643  		// the version is no longer required to change when updating any
   644  		// part of the chaincode definition
   645  		metadataSet = append(metadataSet,
   646  			chaincode.Metadata{
   647  				Name:              name,
   648  				Version:           strconv.FormatInt(def.Definition.Sequence, 10),
   649  				Policy:            def.Definition.ValidationInfo.ValidationParameter,
   650  				CollectionsConfig: def.Definition.Collections,
   651  				Approved:          def.Approved,
   652  				Installed:         def.InstallInfo != nil,
   653  			},
   654  		)
   655  	}
   656  
   657  	// get the chaincode info for _lifecycle
   658  	lc, err := c.getLifecycleSCCChaincodeInfo(channelID)
   659  	if err != nil {
   660  		return nil, err
   661  	}
   662  
   663  	// add it to the metadataset so _lifecycle can also be queried
   664  	// via service discovery
   665  	metadataSet = append(metadataSet,
   666  		chaincode.Metadata{
   667  			Name:      LifecycleNamespace,
   668  			Version:   strconv.FormatInt(lc.Definition.Sequence, 10),
   669  			Policy:    lc.Definition.ValidationInfo.ValidationParameter,
   670  			Approved:  lc.Approved,
   671  			Installed: lc.InstallInfo != nil,
   672  		},
   673  	)
   674  
   675  	return metadataSet, nil
   676  }
   677  
   678  func (c *Cache) handleMetadataUpdates(localChaincode *LocalChaincode) {
   679  	for channelID := range localChaincode.References {
   680  		c.handleMetadataUpdatesForChannel(channelID)
   681  	}
   682  }
   683  
   684  func (c *Cache) handleMetadataUpdatesForChannel(channelID string) {
   685  	ms, err := c.retrieveChaincodesMetadataSetWhileLocked(channelID)
   686  	if err != nil {
   687  		logger.Warningf("no metadata found on channel '%s': %s", channelID, err)
   688  		return
   689  	}
   690  
   691  	c.MetadataHandler.UpdateMetadata(channelID, ms)
   692  }