github.com/suchongming/fabric@v2.1.1+incompatible/core/cclifecycle/lifecycle.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package cclifecycle
     8  
     9  import (
    10  	"sync"
    11  
    12  	"github.com/hyperledger/fabric/common/chaincode"
    13  	"github.com/hyperledger/fabric/common/flogging"
    14  	"github.com/pkg/errors"
    15  )
    16  
    17  var (
    18  	// Logger is the logging instance for this package.
    19  	// It's exported because the tests override its backend
    20  	Logger = flogging.MustGetLogger("discovery.lifecycle")
    21  )
    22  
    23  // MetadataManager manages information about lscc chaincodes.
    24  type MetadataManager struct {
    25  	sync.RWMutex
    26  	listeners              []MetadataChangeListener
    27  	installedCCs           []chaincode.InstalledChaincode
    28  	deployedCCsByChannel   map[string]*chaincode.MetadataMapping
    29  	queryCreatorsByChannel map[string]QueryCreator
    30  }
    31  
    32  //go:generate mockery -dir . -name MetadataChangeListener -case underscore  -output mocks/
    33  
    34  // MetadataChangeListener runs whenever there is a change to the metadata
    35  // of a chaincode in the context of a specific channel
    36  type MetadataChangeListener interface {
    37  	HandleMetadataUpdate(channel string, chaincodes chaincode.MetadataSet)
    38  }
    39  
    40  // HandleMetadataUpdateFunc is triggered upon a change in the chaincode lifecycle
    41  type HandleMetadataUpdateFunc func(channel string, chaincodes chaincode.MetadataSet)
    42  
    43  // HandleMetadataUpdate runs whenever there is a change to the metadata
    44  // of a chaincode in the context of a specific channel
    45  func (handleMetadataUpdate HandleMetadataUpdateFunc) HandleMetadataUpdate(channel string, chaincodes chaincode.MetadataSet) {
    46  	handleMetadataUpdate(channel, chaincodes)
    47  }
    48  
    49  //go:generate mockery -dir . -name Enumerator -case underscore  -output mocks/
    50  
    51  // Enumerator enumerates chaincodes
    52  type Enumerator interface {
    53  	// Enumerate returns the installed chaincodes
    54  	Enumerate() ([]chaincode.InstalledChaincode, error)
    55  }
    56  
    57  // EnumerateFunc enumerates installed chaincodes
    58  type EnumerateFunc func() ([]chaincode.InstalledChaincode, error)
    59  
    60  // Enumerate enumerates chaincodes
    61  func (enumerate EnumerateFunc) Enumerate() ([]chaincode.InstalledChaincode, error) {
    62  	return enumerate()
    63  }
    64  
    65  //go:generate mockery -dir . -name Query -case underscore  -output mocks/
    66  
    67  // Query queries the state
    68  type Query interface {
    69  	// GetState gets the value for given namespace and key. For a chaincode, the namespace corresponds to the chaincodeId
    70  	GetState(namespace string, key string) ([]byte, error)
    71  
    72  	// Done releases resources occupied by the QueryExecutor
    73  	Done()
    74  }
    75  
    76  //go:generate mockery -dir . -name QueryCreator -case underscore  -output mocks/
    77  
    78  // QueryCreator creates queries
    79  type QueryCreator interface {
    80  	// NewQuery creates a new Query, or error on failure
    81  	NewQuery() (Query, error)
    82  }
    83  
    84  // QueryCreatorFunc creates a new query
    85  type QueryCreatorFunc func() (Query, error)
    86  
    87  // NewQuery creates a new Query, or error on failure
    88  func (queryCreator QueryCreatorFunc) NewQuery() (Query, error) {
    89  	return queryCreator()
    90  }
    91  
    92  // NewMetadataManager creates a metadata manager for lscc chaincodes.
    93  func NewMetadataManager(installedChaincodes Enumerator) (*MetadataManager, error) {
    94  	installedCCs, err := installedChaincodes.Enumerate()
    95  	if err != nil {
    96  		return nil, errors.Wrap(err, "failed listing installed chaincodes")
    97  	}
    98  
    99  	return &MetadataManager{
   100  		installedCCs:           installedCCs,
   101  		deployedCCsByChannel:   map[string]*chaincode.MetadataMapping{},
   102  		queryCreatorsByChannel: map[string]QueryCreator{},
   103  	}, nil
   104  }
   105  
   106  // Metadata returns the metadata of the chaincode on the given channel,
   107  // or nil if not found or an error occurred at retrieving it
   108  func (m *MetadataManager) Metadata(channel string, cc string, collections ...string) *chaincode.Metadata {
   109  	queryCreator := m.queryCreatorsByChannel[channel]
   110  	if queryCreator == nil {
   111  		Logger.Warning("Requested Metadata for non-existent channel", channel)
   112  		return nil
   113  	}
   114  	// Search the metadata in our local cache, and if it exists - return it, but only if
   115  	// no collections were specified in the invocation.
   116  	if md, found := m.deployedCCsByChannel[channel].Lookup(cc); found && len(collections) == 0 {
   117  		Logger.Debug("Returning metadata for channel", channel, ", chaincode", cc, ":", md)
   118  		return &md
   119  	}
   120  	query, err := queryCreator.NewQuery()
   121  	if err != nil {
   122  		Logger.Error("Failed obtaining new query for channel", channel, ":", err)
   123  		return nil
   124  	}
   125  	md, err := DeployedChaincodes(query, AcceptAll, len(collections) > 0, cc)
   126  	if err != nil {
   127  		Logger.Error("Failed querying LSCC for channel", channel, ":", err)
   128  		return nil
   129  	}
   130  	if len(md) == 0 {
   131  		Logger.Info("Chaincode", cc, "isn't defined in channel", channel)
   132  		return nil
   133  	}
   134  
   135  	return &md[0]
   136  }
   137  
   138  func (m *MetadataManager) initMetadataForChannel(channel string, queryCreator QueryCreator) error {
   139  	if m.isChannelMetadataInitialized(channel) {
   140  		return nil
   141  	}
   142  	// Create a new metadata mapping for the channel
   143  	query, err := queryCreator.NewQuery()
   144  	if err != nil {
   145  		return errors.WithStack(err)
   146  	}
   147  	ccs, err := queryChaincodeDefinitions(query, m.installedCCs, DeployedChaincodes)
   148  	if err != nil {
   149  		return errors.WithStack(err)
   150  	}
   151  	m.createMetadataForChannel(channel, queryCreator)
   152  	m.updateState(channel, ccs)
   153  	return nil
   154  }
   155  
   156  func (m *MetadataManager) createMetadataForChannel(channel string, newQuery QueryCreator) {
   157  	m.Lock()
   158  	defer m.Unlock()
   159  	m.deployedCCsByChannel[channel] = chaincode.NewMetadataMapping()
   160  	m.queryCreatorsByChannel[channel] = newQuery
   161  }
   162  
   163  func (m *MetadataManager) isChannelMetadataInitialized(channel string) bool {
   164  	m.RLock()
   165  	defer m.RUnlock()
   166  	_, exists := m.deployedCCsByChannel[channel]
   167  	return exists
   168  }
   169  
   170  func (m *MetadataManager) updateState(channel string, ccUpdate chaincode.MetadataSet) {
   171  	m.RLock()
   172  	defer m.RUnlock()
   173  	for _, cc := range ccUpdate {
   174  		m.deployedCCsByChannel[channel].Update(cc)
   175  	}
   176  }
   177  
   178  func (m *MetadataManager) fireChangeListeners(channel string) {
   179  	m.RLock()
   180  	md := m.deployedCCsByChannel[channel]
   181  	m.RUnlock()
   182  	for _, listener := range m.listeners {
   183  		aggregatedMD := md.Aggregate()
   184  		listener.HandleMetadataUpdate(channel, aggregatedMD)
   185  	}
   186  	Logger.Debug("Listeners for channel", channel, "invoked")
   187  }
   188  
   189  // NewChannelSubscription subscribes to a channel
   190  func (m *MetadataManager) NewChannelSubscription(channel string, queryCreator QueryCreator) (*Subscription, error) {
   191  	sub := &Subscription{
   192  		metadataManager: m,
   193  		channel:         channel,
   194  		queryCreator:    queryCreator,
   195  	}
   196  	// Initialize metadata for the channel.
   197  	// This loads metadata about all installed chaincodes
   198  	if err := m.initMetadataForChannel(channel, queryCreator); err != nil {
   199  		return nil, errors.WithStack(err)
   200  	}
   201  	m.fireChangeListeners(channel)
   202  	return sub, nil
   203  }
   204  
   205  // AddListener registers the given listener to be triggered upon a lifecycle change
   206  func (m *MetadataManager) AddListener(listener MetadataChangeListener) {
   207  	m.Lock()
   208  	defer m.Unlock()
   209  	m.listeners = append(m.listeners, listener)
   210  }