github.com/kaituanwang/hyperledger@v2.0.1+incompatible/core/cclifecycle/subscription.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  	"bytes"
    11  	"sync"
    12  
    13  	"github.com/hyperledger/fabric/common/chaincode"
    14  	"github.com/hyperledger/fabric/core/ledger/cceventmgmt"
    15  )
    16  
    17  // Subscription channels information flow
    18  // about a specific channel into the Lifecycle.
    19  type Subscription struct {
    20  	sync.Mutex
    21  	metadataManager *MetadataManager
    22  	channel         string
    23  	queryCreator    QueryCreator
    24  	pendingUpdates  []*cceventmgmt.ChaincodeDefinition
    25  }
    26  
    27  type deployedCCsRetrieverFunc func(Query, ChaincodePredicate, bool, ...string) (chaincode.MetadataSet, error)
    28  
    29  // HandleChaincodeDeploy is expected to be invoked when a chaincode is
    30  // deployed via a deploy transaction and the chaincode was already installed
    31  // on the peer. This also gets invoked when an already deployed chaincode is
    32  // installed on the peer.
    33  func (sub *Subscription) HandleChaincodeDeploy(chaincodeDefinition *cceventmgmt.ChaincodeDefinition, dbArtifactsTar []byte) error {
    34  	Logger.Debug("Channel", sub.channel, "got a new deployment:", chaincodeDefinition)
    35  	sub.Lock()
    36  	defer sub.Unlock()
    37  	sub.pendingUpdates = append(sub.pendingUpdates, chaincodeDefinition)
    38  	return nil
    39  }
    40  
    41  func (sub *Subscription) processPendingUpdate(ccDef *cceventmgmt.ChaincodeDefinition) {
    42  	query, err := sub.queryCreator.NewQuery()
    43  	if err != nil {
    44  		Logger.Errorf("Failed creating a new query for channel %s: %v", sub.channel, err)
    45  		return
    46  	}
    47  	installedCC := []chaincode.InstalledChaincode{{
    48  		Name:    ccDef.Name,
    49  		Version: ccDef.Version,
    50  		Hash:    ccDef.Hash,
    51  	}}
    52  	ccs, err := queryChaincodeDefinitions(query, installedCC, DeployedChaincodes)
    53  	if err != nil {
    54  		Logger.Errorf("Query for channel %s for %v failed with error %v", sub.channel, ccDef, err)
    55  		return
    56  	}
    57  	Logger.Debug("Updating channel", sub.channel, "with", ccs.AsChaincodes())
    58  	sub.metadataManager.updateState(sub.channel, ccs)
    59  	sub.metadataManager.fireChangeListeners(sub.channel)
    60  }
    61  
    62  // ChaincodeDeployDone gets invoked when the chaincode deploy transaction or
    63  // chaincode install (the context in which the above function was invoked).
    64  func (sub *Subscription) ChaincodeDeployDone(succeeded bool) {
    65  	// Run a new goroutine which would dispatch a single pending update.
    66  	// This is to prevent any ledger locks being obtained during the state query
    67  	// to affect the locks held while invoking this method by the ledger itself.
    68  	// We first lock and then take the pending update, to preserve order.
    69  	sub.Lock()
    70  	go func() {
    71  		defer func() {
    72  			sub.pendingUpdates = nil
    73  			sub.Unlock()
    74  		}()
    75  		// If we haven't succeeded in deploying the chaincode, just skip the update
    76  		if !succeeded {
    77  			Logger.Errorf("Chaincode deploy for updates %v failed", sub.pendingUpdates)
    78  			return
    79  		}
    80  		for _, update := range sub.pendingUpdates {
    81  			sub.processPendingUpdate(update)
    82  		}
    83  	}()
    84  }
    85  
    86  func queryChaincodeDefinitions(query Query, installedCCs []chaincode.InstalledChaincode, deployedCCs deployedCCsRetrieverFunc) (chaincode.MetadataSet, error) {
    87  	// map from string and version to chaincode ID
    88  	installedCCsToIDs := map[nameVersion][]byte{}
    89  	// Populate the map
    90  	for _, cc := range installedCCs {
    91  		Logger.Debug("Chaincode", cc, "'s version is", cc.Version, "and Id is", cc.Hash)
    92  		installedCCsToIDs[installedCCToNameVersion(cc)] = cc.Hash
    93  	}
    94  
    95  	filter := func(cc chaincode.Metadata) bool {
    96  		installedID, exists := installedCCsToIDs[deployedCCToNameVersion(cc)]
    97  		if !exists {
    98  			Logger.Debug("Chaincode", cc, "is instantiated but a different version is installed")
    99  			return false
   100  		}
   101  		if !bytes.Equal(installedID, cc.Id) {
   102  			Logger.Debug("ID of chaincode", cc, "on filesystem doesn't match ID in ledger")
   103  			return false
   104  		}
   105  		return true
   106  	}
   107  
   108  	return deployedCCs(query, filter, false, names(installedCCs)...)
   109  }