github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/chaincode/lifecycle/cache.go (about) 1 /* 2 Copyright hechain. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package lifecycle 8 9 import ( 10 "fmt" 11 "sort" 12 "strconv" 13 "sync" 14 15 "github.com/hechain20/hechain/common/chaincode" 16 "github.com/hechain20/hechain/common/util" 17 "github.com/hechain20/hechain/core/chaincode/implicitcollection" 18 "github.com/hechain20/hechain/core/chaincode/persistence" 19 "github.com/hechain20/hechain/core/container/externalbuilder" 20 "github.com/hechain20/hechain/core/ledger" 21 "github.com/hechain20/hechain/protoutil" 22 lb "github.com/hyperledger/fabric-protos-go/peer/lifecycle" 23 24 "github.com/pkg/errors" 25 ) 26 27 type LocalChaincodeInfo struct { 28 Definition *ChaincodeDefinition 29 Approved bool 30 InstallInfo *ChaincodeInstallInfo 31 } 32 33 type ChaincodeInstallInfo struct { 34 PackageID string 35 Type string 36 Path string 37 Label string 38 } 39 40 type CachedChaincodeDefinition struct { 41 Definition *ChaincodeDefinition 42 Approved bool 43 InstallInfo *ChaincodeInstallInfo 44 45 // Hashes is the list of hashed keys in the implicit collection referring to this definition. 46 // These hashes are determined by the current sequence number of chaincode definition. When dirty, 47 // these hashes will be empty, and when not, they will be populated. 48 Hashes []string 49 } 50 51 type ChannelCache struct { 52 Chaincodes map[string]*CachedChaincodeDefinition 53 54 // InterestingHashes is a map of hashed key names to the chaincode name which they affect. 55 // These are to be used for the state listener, to mark chaincode definitions dirty when 56 // a write is made into the implicit collection for this org. Interesting hashes are 57 // added when marking a definition clean, and deleted when marking it dirty. 58 InterestingHashes map[string]string 59 } 60 61 // MetadataHandler is the interface through which the cache drives 62 // metadata updates for listeners such as gossip and service discovery 63 type MetadataHandler interface { 64 InitializeMetadata(channel string, chaincodes chaincode.MetadataSet) 65 UpdateMetadata(channel string, chaincodes chaincode.MetadataSet) 66 } 67 68 type Cache struct { 69 definedChaincodes map[string]*ChannelCache 70 Resources *Resources 71 MyOrgMSPID string 72 73 // mutex serializes lifecycle operations globally for the peer. It will cause a lifecycle update 74 // in one channel to potentially affect the throughput of another. However, relative to standard 75 // transactions, lifecycle updates should be quite rare, and this is a RW lock so in general, there 76 // should not be contention in the normal case. Because chaincode package installation is a peer global 77 // event, by synchronizing at a peer global level, we drastically simplify accounting for which 78 // chaincodes are installed and which channels that installed chaincode is currently in use on. 79 mutex sync.RWMutex 80 81 // localChaincodes is a map from the hash of the locally installed chaincode's proto 82 // encoded hash (yes, the hash of the hash), to a set of channels, to a set of chaincode 83 // definitions which reference this local installed chaincode hash. 84 localChaincodes map[string]*LocalChaincode 85 eventBroker *EventBroker 86 MetadataHandler MetadataHandler 87 88 chaincodeCustodian *ChaincodeCustodian 89 } 90 91 type LocalChaincode struct { 92 Info *ChaincodeInstallInfo 93 References map[string]map[string]*CachedChaincodeDefinition 94 } 95 96 // ToInstalledChaincode converts a LocalChaincode to an InstalledChaincode, 97 // which is returned by lifecycle queries. 98 func (l *LocalChaincode) ToInstalledChaincode() *chaincode.InstalledChaincode { 99 references := l.createMetadataMapFromReferences() 100 return &chaincode.InstalledChaincode{ 101 PackageID: l.Info.PackageID, 102 Label: l.Info.Label, 103 References: references, 104 } 105 } 106 107 // createMetadataMapFromReferences returns a map of channel name to a slice 108 // of chaincode metadata for the chaincode definitions that reference a 109 // specific local chaincode. This function should only be called by code that 110 // holds the lock on the cache. 111 func (l *LocalChaincode) createMetadataMapFromReferences() map[string][]*chaincode.Metadata { 112 references := map[string][]*chaincode.Metadata{} 113 for channel, chaincodeMap := range l.References { 114 metadata := []*chaincode.Metadata{} 115 for cc, cachedDefinition := range chaincodeMap { 116 metadata = append(metadata, &chaincode.Metadata{ 117 Name: cc, 118 Version: cachedDefinition.Definition.EndorsementInfo.Version, 119 }) 120 } 121 references[channel] = metadata 122 } 123 return references 124 } 125 126 func NewCache(resources *Resources, myOrgMSPID string, metadataManager MetadataHandler, custodian *ChaincodeCustodian, ebMetadata *externalbuilder.MetadataProvider) *Cache { 127 return &Cache{ 128 chaincodeCustodian: custodian, 129 definedChaincodes: map[string]*ChannelCache{}, 130 localChaincodes: map[string]*LocalChaincode{}, 131 Resources: resources, 132 MyOrgMSPID: myOrgMSPID, 133 eventBroker: NewEventBroker(resources.ChaincodeStore, resources.PackageParser, ebMetadata), 134 MetadataHandler: metadataManager, 135 } 136 } 137 138 // InitializeLocalChaincodes should be called once after cache creation (timing doesn't matter, 139 // though already installed chaincodes will not be invokable until it it completes). Ideally, 140 // this would be part of the constructor, but, we cannot rely on the chaincode store being created 141 // before the cache is created. 142 func (c *Cache) InitializeLocalChaincodes() error { 143 c.mutex.Lock() 144 defer c.mutex.Unlock() 145 ccPackages, err := c.Resources.ChaincodeStore.ListInstalledChaincodes() 146 if err != nil { 147 return errors.WithMessage(err, "could not list installed chaincodes") 148 } 149 150 for _, ccPackage := range ccPackages { 151 ccPackageBytes, err := c.Resources.ChaincodeStore.Load(ccPackage.PackageID) 152 if err != nil { 153 return errors.WithMessagef(err, "could not load chaincode with package ID '%s'", ccPackage.PackageID) 154 } 155 parsedCCPackage, err := c.Resources.PackageParser.Parse(ccPackageBytes) 156 if err != nil { 157 return errors.WithMessagef(err, "could not parse chaincode with package ID '%s'", ccPackage.PackageID) 158 } 159 c.handleChaincodeInstalledWhileLocked(true, parsedCCPackage.Metadata, ccPackage.PackageID) 160 } 161 162 logger.Infof("Initialized lifecycle cache with %d already installed chaincodes", len(c.localChaincodes)) 163 for channelID, chaincodeCache := range c.definedChaincodes { 164 approved, installed, runnable := 0, 0, 0 165 for _, cachedChaincode := range chaincodeCache.Chaincodes { 166 if cachedChaincode.Approved { 167 approved++ 168 } 169 if cachedChaincode.InstallInfo != nil { 170 installed++ 171 } 172 if cachedChaincode.Approved && cachedChaincode.InstallInfo != nil { 173 runnable++ 174 } 175 } 176 177 logger.Infof("Initialized lifecycle cache for channel '%s' with %d chaincodes runnable (%d approved, %d installed)", channelID, runnable, approved, installed) 178 } 179 180 return nil 181 } 182 183 // Name returns the name of the listener 184 func (c *Cache) Name() string { 185 return "lifecycle cache listener" 186 } 187 188 // Initialize will populate the set of currently committed chaincode definitions 189 // for a channel into the cache. Note, it this looks like a bit of a DRY violation 190 // with respect to 'Update', but, the error handling is quite different and attempting 191 // to factor out the common pieces results in a net total of more code. 192 func (c *Cache) Initialize(channelID string, qe ledger.SimpleQueryExecutor) error { 193 c.mutex.Lock() 194 defer c.mutex.Unlock() 195 196 publicState := &SimpleQueryExecutorShim{ 197 Namespace: LifecycleNamespace, 198 SimpleQueryExecutor: qe, 199 } 200 201 metadatas, err := c.Resources.Serializer.DeserializeAllMetadata(NamespacesName, publicState) 202 if err != nil { 203 return errors.WithMessage(err, "could not query namespace metadata") 204 } 205 206 dirtyChaincodes := map[string]struct{}{} 207 208 for namespace, metadata := range metadatas { 209 switch metadata.Datatype { 210 case ChaincodeDefinitionType: 211 dirtyChaincodes[namespace] = struct{}{} 212 default: 213 // non-chaincode 214 } 215 } 216 217 return c.update(true, channelID, dirtyChaincodes, qe) 218 } 219 220 // HandleChaincodeInstalled should be invoked whenever a new chaincode is installed 221 func (c *Cache) HandleChaincodeInstalled(md *persistence.ChaincodePackageMetadata, packageID string) { 222 c.mutex.Lock() 223 defer c.mutex.Unlock() 224 c.handleChaincodeInstalledWhileLocked(false, md, packageID) 225 } 226 227 func (c *Cache) handleChaincodeInstalledWhileLocked(initializing bool, md *persistence.ChaincodePackageMetadata, packageID string) { 228 // it would be nice to get this value from the serialization package, but it was not obvious 229 // how to expose this in a nice way, so we manually compute it. 230 encodedCCHash := protoutil.MarshalOrPanic(&lb.StateData{ 231 Type: &lb.StateData_String_{String_: packageID}, 232 }) 233 hashOfCCHash := string(util.ComputeSHA256(encodedCCHash)) 234 localChaincode, ok := c.localChaincodes[hashOfCCHash] 235 if !ok { 236 localChaincode = &LocalChaincode{ 237 References: map[string]map[string]*CachedChaincodeDefinition{}, 238 } 239 c.localChaincodes[hashOfCCHash] = localChaincode 240 c.chaincodeCustodian.NotifyInstalled(packageID) 241 } 242 localChaincode.Info = &ChaincodeInstallInfo{ 243 PackageID: packageID, 244 Type: md.Type, 245 Path: md.Path, 246 Label: md.Label, 247 } 248 for channelID, channelCache := range localChaincode.References { 249 for chaincodeName, cachedChaincode := range channelCache { 250 cachedChaincode.InstallInfo = localChaincode.Info 251 logger.Infof("Installed chaincode with package ID '%s' now available on channel %s for chaincode definition %s:%s", packageID, channelID, chaincodeName, cachedChaincode.Definition.EndorsementInfo.Version) 252 c.chaincodeCustodian.NotifyInstalledAndRunnable(packageID) 253 } 254 } 255 256 if !initializing { 257 c.eventBroker.ProcessInstallEvent(localChaincode) 258 c.handleMetadataUpdates(localChaincode) 259 } 260 } 261 262 // HandleStateUpdates is required to implement the ledger state listener interface. It applies 263 // any state updates to the cache. 264 func (c *Cache) HandleStateUpdates(trigger *ledger.StateUpdateTrigger) error { 265 c.mutex.Lock() 266 defer c.mutex.Unlock() 267 channelID := trigger.LedgerID 268 updates, ok := trigger.StateUpdates[LifecycleNamespace] 269 if !ok { 270 return errors.Errorf("no state updates for promised namespace _lifecycle") 271 } 272 273 dirtyChaincodes := map[string]struct{}{} 274 275 for _, publicUpdate := range updates.PublicUpdates { 276 matches := SequenceMatcher.FindStringSubmatch(publicUpdate.Key) 277 if len(matches) != 2 { 278 continue 279 } 280 281 dirtyChaincodes[matches[1]] = struct{}{} 282 } 283 284 channelCache, ok := c.definedChaincodes[channelID] 285 286 // if the channel cache does not yet exist, there are no interesting hashes, so skip 287 if ok { 288 for collection, privateUpdates := range updates.CollHashUpdates { 289 isImplicitCollection, mspID := implicitcollection.MspIDIfImplicitCollection(collection) 290 if !isImplicitCollection { 291 continue 292 } 293 294 if mspID != c.MyOrgMSPID { 295 // This is not our implicit collection 296 continue 297 } 298 299 for _, privateUpdate := range privateUpdates { 300 chaincodeName, ok := channelCache.InterestingHashes[string(privateUpdate.KeyHash)] 301 if ok { 302 dirtyChaincodes[chaincodeName] = struct{}{} 303 } 304 } 305 } 306 } 307 308 err := c.update(false, channelID, dirtyChaincodes, trigger.PostCommitQueryExecutor) 309 if err != nil { 310 return errors.WithMessage(err, "error updating cache") 311 } 312 313 return nil 314 } 315 316 // InterestedInNamespaces is required to implement the ledger state listener interface 317 func (c *Cache) InterestedInNamespaces() []string { 318 return []string{LifecycleNamespace} 319 } 320 321 // StateCommitDone is required to implement the ledger state listener interface 322 func (c *Cache) StateCommitDone(channelName string) { 323 // NOTE: It's extremely tempting to acquire the write lock in HandleStateUpdate 324 // and release it here, however, this is asking for a deadlock. In particular, 325 // because the 'write lock' on the state is only held for a short period 326 // between HandleStateUpdate and StateCommitDone, it's possible (in fact likely) 327 // that a chaincode invocation will acquire a read-lock on the world state, then attempt 328 // to get chaincode info from the cache, resulting in a deadlock. So, we choose 329 // potential inconsistency between the cache and the world state which the callers 330 // must detect and cope with as necessary. Note, the cache will always be _at least_ 331 // as current as the committed state. 332 c.eventBroker.ApproveOrDefineCommitted(channelName) 333 } 334 335 // ChaincodeInfo returns the chaincode definition and its install info. 336 // An error is returned only if either the channel or the chaincode do not exist. 337 func (c *Cache) ChaincodeInfo(channelID, name string) (*LocalChaincodeInfo, error) { 338 if name == LifecycleNamespace { 339 ac, ok := c.Resources.ChannelConfigSource.GetStableChannelConfig(channelID).ApplicationConfig() 340 if !ok { 341 return nil, errors.Errorf("application config does not exist for channel '%s'", channelID) 342 } 343 if !ac.Capabilities().LifecycleV20() { 344 return nil, errors.Errorf("cannot use _lifecycle without V2_0 application capabilities enabled for channel '%s'", channelID) 345 } 346 return c.getLifecycleSCCChaincodeInfo(channelID) 347 } 348 349 c.mutex.RLock() 350 defer c.mutex.RUnlock() 351 channelChaincodes, ok := c.definedChaincodes[channelID] 352 if !ok { 353 return nil, errors.Errorf("unknown channel '%s'", channelID) 354 } 355 356 cachedChaincode, ok := channelChaincodes.Chaincodes[name] 357 if !ok { 358 return nil, errors.Errorf("unknown chaincode '%s' for channel '%s'", name, channelID) 359 } 360 361 return &LocalChaincodeInfo{ 362 Definition: cachedChaincode.Definition, 363 InstallInfo: cachedChaincode.InstallInfo, 364 Approved: cachedChaincode.Approved, 365 }, nil 366 } 367 368 func (c *Cache) getLifecycleSCCChaincodeInfo(channelID string) (*LocalChaincodeInfo, error) { 369 policyBytes, err := c.Resources.LifecycleEndorsementPolicyAsBytes(channelID) 370 if err != nil { 371 return nil, err 372 } 373 374 return &LocalChaincodeInfo{ 375 Definition: &ChaincodeDefinition{ 376 ValidationInfo: &lb.ChaincodeValidationInfo{ 377 ValidationParameter: policyBytes, 378 }, 379 Sequence: 1, 380 }, 381 Approved: true, 382 InstallInfo: &ChaincodeInstallInfo{}, 383 }, nil 384 } 385 386 // ListInstalledChaincodes returns a slice containing all of the information 387 // about the installed chaincodes. 388 func (c *Cache) ListInstalledChaincodes() []*chaincode.InstalledChaincode { 389 c.mutex.RLock() 390 defer c.mutex.RUnlock() 391 392 installedChaincodes := []*chaincode.InstalledChaincode{} 393 for _, lc := range c.localChaincodes { 394 if lc.Info == nil { 395 // the update function adds an entry to localChaincodes 396 // even if it isn't yet installed 397 continue 398 } 399 installedChaincodes = append(installedChaincodes, lc.ToInstalledChaincode()) 400 } 401 402 return installedChaincodes 403 } 404 405 // GetInstalledChaincode returns all of the information about a specific 406 // installed chaincode. 407 func (c *Cache) GetInstalledChaincode(packageID string) (*chaincode.InstalledChaincode, error) { 408 c.mutex.RLock() 409 defer c.mutex.RUnlock() 410 411 for _, lc := range c.localChaincodes { 412 if lc.Info == nil { 413 // the update function adds an entry to localChaincodes 414 // even if it isn't yet installed 415 continue 416 } 417 if lc.Info.PackageID == packageID { 418 return lc.ToInstalledChaincode(), nil 419 } 420 } 421 422 return nil, errors.Errorf("could not find chaincode with package id '%s'", packageID) 423 } 424 425 // update should only be called with the write lock already held 426 func (c *Cache) update(initializing bool, channelID string, dirtyChaincodes map[string]struct{}, qe ledger.SimpleQueryExecutor) error { 427 channelCache, ok := c.definedChaincodes[channelID] 428 if !ok { 429 channelCache = &ChannelCache{ 430 Chaincodes: map[string]*CachedChaincodeDefinition{}, 431 InterestingHashes: map[string]string{}, 432 } 433 c.definedChaincodes[channelID] = channelCache 434 } 435 436 publicState := &SimpleQueryExecutorShim{ 437 Namespace: LifecycleNamespace, 438 SimpleQueryExecutor: qe, 439 } 440 441 orgState := &PrivateQueryExecutorShim{ 442 Namespace: LifecycleNamespace, 443 Collection: implicitcollection.NameForOrg(c.MyOrgMSPID), 444 State: qe, 445 } 446 447 for name := range dirtyChaincodes { 448 logger.Infof("Updating cached definition for chaincode '%s' on channel '%s'", name, channelID) 449 cachedChaincode, ok := channelCache.Chaincodes[name] 450 if !ok { 451 cachedChaincode = &CachedChaincodeDefinition{} 452 channelCache.Chaincodes[name] = cachedChaincode 453 } 454 455 for _, hash := range cachedChaincode.Hashes { 456 delete(channelCache.InterestingHashes, hash) 457 } 458 459 exists, chaincodeDefinition, err := c.Resources.ChaincodeDefinitionIfDefined(name, publicState) 460 if err != nil { 461 return errors.WithMessagef(err, "could not get chaincode definition for '%s' on channel '%s'", name, channelID) 462 } 463 464 if !exists { 465 // the chaincode definition was deleted, this is currently not 466 // possible, but there should be no problems with that. 467 delete(channelCache.Chaincodes, name) 468 continue 469 } 470 471 privateName := fmt.Sprintf("%s#%d", name, chaincodeDefinition.Sequence) 472 hashKey := FieldKey(ChaincodeSourcesName, privateName, "PackageID") 473 hashOfCCHash, err := orgState.GetStateHash(hashKey) 474 if err != nil { 475 return errors.WithMessagef(err, "could not check opaque org state for chaincode source hash for '%s' on channel '%s'", name, channelID) 476 } 477 478 localChaincode, ok := c.localChaincodes[string(hashOfCCHash)] 479 if !ok { 480 localChaincode = &LocalChaincode{ 481 References: map[string]map[string]*CachedChaincodeDefinition{}, 482 } 483 c.localChaincodes[string(hashOfCCHash)] = localChaincode 484 } 485 486 if !initializing { 487 // check for existing local chaincodes that reference this chaincode 488 // name on this channel 489 for _, lc := range c.localChaincodes { 490 if ref, ok := lc.References[channelID][name]; ok { 491 if ref.InstallInfo == nil { 492 continue 493 } 494 if localChaincode.Info != nil { 495 if ref.InstallInfo.PackageID == localChaincode.Info.PackageID { 496 continue 497 } 498 } 499 500 // remove existing local chaincode reference, which referred to a 501 // previous chaincode definition 502 delete(lc.References[channelID], name) 503 if len(lc.References[channelID]) == 0 { 504 delete(lc.References, channelID) 505 506 // check to see if this "local" chaincode is installed (an entry 507 // is added into local chaincodes for active chaincode definition 508 // references regardless of whether the peer has a chaincode 509 // package installed) 510 if lc.Info == nil { 511 continue 512 } 513 514 // finally, check to see if this chaincode is referenced in any 515 // channel. if not, stop the chaincode here 516 if len(lc.References) == 0 { 517 logger.Debugf("chaincode package with label %s is no longer referenced and will be stopped", lc.Info.Label) 518 c.chaincodeCustodian.NotifyStoppable(lc.Info.PackageID) 519 } 520 } 521 } 522 } 523 } 524 525 cachedChaincode.Definition = chaincodeDefinition 526 cachedChaincode.Approved = false 527 528 cachedChaincode.Hashes = []string{ 529 string(util.ComputeSHA256([]byte(MetadataKey(NamespacesName, privateName)))), 530 string(util.ComputeSHA256([]byte(FieldKey(NamespacesName, privateName, "EndorsementInfo")))), 531 string(util.ComputeSHA256([]byte(FieldKey(NamespacesName, privateName, "ValidationInfo")))), 532 string(util.ComputeSHA256([]byte(FieldKey(NamespacesName, privateName, "Collections")))), 533 string(util.ComputeSHA256([]byte(FieldKey(ChaincodeSourcesName, privateName, "PackageID")))), 534 } 535 536 for _, hash := range cachedChaincode.Hashes { 537 channelCache.InterestingHashes[hash] = name 538 } 539 540 ok, err = c.Resources.Serializer.IsSerialized(NamespacesName, privateName, chaincodeDefinition.Parameters(), orgState) 541 542 if err != nil { 543 return errors.WithMessagef(err, "could not check opaque org state for '%s' on channel '%s'", name, channelID) 544 } 545 if !ok { 546 logger.Debugf("Channel %s for chaincode definition %s:%s does not have our org's approval", channelID, name, chaincodeDefinition.EndorsementInfo.Version) 547 continue 548 } 549 550 cachedChaincode.Approved = true 551 552 isLocalPackage, err := c.Resources.Serializer.IsMetadataSerialized(ChaincodeSourcesName, privateName, &ChaincodeLocalPackage{}, orgState) 553 if err != nil { 554 return errors.WithMessagef(err, "could not check opaque org state for chaincode source for '%s' on channel '%s'", name, channelID) 555 } 556 557 if !isLocalPackage { 558 logger.Debugf("Channel %s for chaincode definition %s:%s does not have a chaincode source defined", channelID, name, chaincodeDefinition.EndorsementInfo.Version) 559 continue 560 } 561 562 cachedChaincode.InstallInfo = localChaincode.Info 563 if localChaincode.Info != nil { 564 logger.Infof("Chaincode with package ID '%s' now available on channel %s for chaincode definition %s:%s", localChaincode.Info.PackageID, channelID, name, cachedChaincode.Definition.EndorsementInfo.Version) 565 c.chaincodeCustodian.NotifyInstalledAndRunnable(localChaincode.Info.PackageID) 566 } else { 567 logger.Debugf("Chaincode definition for chaincode '%s' on channel '%s' is approved, but not installed", name, channelID) 568 } 569 570 channelReferences, ok := localChaincode.References[channelID] 571 if !ok { 572 channelReferences = map[string]*CachedChaincodeDefinition{} 573 localChaincode.References[channelID] = channelReferences 574 } 575 576 channelReferences[name] = cachedChaincode 577 578 if !initializing { 579 c.eventBroker.ProcessApproveOrDefineEvent(channelID, name, cachedChaincode) 580 } 581 } 582 583 if !initializing { 584 c.handleMetadataUpdatesForChannel(channelID) 585 } 586 587 return nil 588 } 589 590 // RegisterListener registers an event listener for receiving an event when a chaincode becomes invokable 591 func (c *Cache) RegisterListener( 592 channelID string, 593 listener ledger.ChaincodeLifecycleEventListener, 594 needsExistingChaincodesDefinitions bool, 595 ) error { 596 c.mutex.RLock() 597 defer c.mutex.RUnlock() 598 599 channelChaincodes, ok := c.definedChaincodes[channelID] 600 if !ok { 601 return errors.Errorf("unknown channel '%s'", channelID) 602 } 603 604 if needsExistingChaincodesDefinitions { 605 c.eventBroker.RegisterListener(channelID, listener, channelChaincodes.Chaincodes) 606 } else { 607 c.eventBroker.RegisterListener(channelID, listener, nil) 608 } 609 610 return nil 611 } 612 613 func (c *Cache) InitializeMetadata(channel string) { 614 c.mutex.RLock() 615 defer c.mutex.RUnlock() 616 617 ms, err := c.retrieveChaincodesMetadataSetWhileLocked(channel) 618 if err != nil { 619 logger.Warningf("no metadata found on channel '%s', err %s", channel, err) 620 return 621 } 622 623 c.MetadataHandler.InitializeMetadata(channel, ms) 624 } 625 626 func (c *Cache) retrieveChaincodesMetadataSetWhileLocked(channelID string) (chaincode.MetadataSet, error) { 627 channelChaincodes, ok := c.definedChaincodes[channelID] 628 if !ok { 629 return nil, errors.Errorf("unknown channel '%s'", channelID) 630 } 631 632 keys := make([]string, 0, len(channelChaincodes.Chaincodes)) 633 for name := range channelChaincodes.Chaincodes { 634 keys = append(keys, name) 635 } 636 sort.Strings(keys) 637 638 metadataSet := chaincode.MetadataSet{} 639 for _, name := range keys { 640 def := channelChaincodes.Chaincodes[name] 641 642 // report the sequence as the version to service discovery since 643 // the version is no longer required to change when updating any 644 // part of the chaincode definition 645 metadataSet = append(metadataSet, 646 chaincode.Metadata{ 647 Name: name, 648 Version: strconv.FormatInt(def.Definition.Sequence, 10), 649 Policy: def.Definition.ValidationInfo.ValidationParameter, 650 CollectionsConfig: def.Definition.Collections, 651 Approved: def.Approved, 652 Installed: def.InstallInfo != nil, 653 }, 654 ) 655 } 656 657 // get the chaincode info for _lifecycle 658 lc, err := c.getLifecycleSCCChaincodeInfo(channelID) 659 if err != nil { 660 return nil, err 661 } 662 663 // add it to the metadataset so _lifecycle can also be queried 664 // via service discovery 665 metadataSet = append(metadataSet, 666 chaincode.Metadata{ 667 Name: LifecycleNamespace, 668 Version: strconv.FormatInt(lc.Definition.Sequence, 10), 669 Policy: lc.Definition.ValidationInfo.ValidationParameter, 670 Approved: lc.Approved, 671 Installed: lc.InstallInfo != nil, 672 }, 673 ) 674 675 return metadataSet, nil 676 } 677 678 func (c *Cache) handleMetadataUpdates(localChaincode *LocalChaincode) { 679 for channelID := range localChaincode.References { 680 c.handleMetadataUpdatesForChannel(channelID) 681 } 682 } 683 684 func (c *Cache) handleMetadataUpdatesForChannel(channelID string) { 685 ms, err := c.retrieveChaincodesMetadataSetWhileLocked(channelID) 686 if err != nil { 687 logger.Warningf("no metadata found on channel '%s': %s", channelID, err) 688 return 689 } 690 691 c.MetadataHandler.UpdateMetadata(channelID, ms) 692 }