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