github.com/defanghe/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 }