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

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