github.com/defanghe/fabric@v2.1.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 }