github.com/kaituanwang/hyperledger@v2.0.1+incompatible/core/chaincode/lifecycle/event_broker.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 SPDX-License-Identifier: Apache-2.0 4 */ 5 6 package lifecycle 7 8 import ( 9 "sync" 10 11 "github.com/hyperledger/fabric/core/container/externalbuilder" 12 "github.com/hyperledger/fabric/core/ledger" 13 "github.com/pkg/errors" 14 ) 15 16 // EventBroker receives events from lifecycle cache and in turn invokes the registered listeners 17 type EventBroker struct { 18 chaincodeStore ChaincodeStore 19 ebMetadata *externalbuilder.MetadataProvider 20 pkgParser PackageParser 21 defineCallbackStatus *sync.Map 22 23 mutex sync.Mutex 24 listeners map[string][]ledger.ChaincodeLifecycleEventListener 25 } 26 27 func NewEventBroker(chaincodeStore ChaincodeStore, pkgParser PackageParser, ebMetadata *externalbuilder.MetadataProvider) *EventBroker { 28 return &EventBroker{ 29 chaincodeStore: chaincodeStore, 30 ebMetadata: ebMetadata, 31 pkgParser: pkgParser, 32 listeners: make(map[string][]ledger.ChaincodeLifecycleEventListener), 33 defineCallbackStatus: &sync.Map{}, 34 } 35 } 36 37 func (b *EventBroker) RegisterListener(channelID string, listener ledger.ChaincodeLifecycleEventListener) { 38 b.mutex.Lock() 39 defer b.mutex.Unlock() 40 b.listeners[channelID] = append(b.listeners[channelID], listener) 41 } 42 43 // ProcessInstallEvent gets invoked when a chaincode is installed 44 func (b *EventBroker) ProcessInstallEvent(localChaincode *LocalChaincode) { 45 logger.Debugf("ProcessInstallEvent() - localChaincode = %s", localChaincode.Info) 46 dbArtifacts, err := b.loadDBArtifacts(localChaincode.Info.PackageID) 47 if err != nil { 48 logger.Errorf("Error while loading db artifacts for chaincode package with package ID [%s]: %s", 49 localChaincode.Info.PackageID, err) 50 return 51 } 52 for channelID, channelCache := range localChaincode.References { 53 listenersInvokedOnChannel := false 54 for chaincodeName, cachedChaincode := range channelCache { 55 if !isChaincodeInvokable(cachedChaincode) { 56 continue 57 } 58 ccdef := &ledger.ChaincodeDefinition{ 59 Name: chaincodeName, 60 Version: cachedChaincode.Definition.EndorsementInfo.Version, 61 Hash: []byte(cachedChaincode.InstallInfo.PackageID), 62 CollectionConfigs: cachedChaincode.Definition.Collections, 63 } 64 b.invokeListeners(channelID, ccdef, dbArtifacts) 65 listenersInvokedOnChannel = true 66 } 67 if listenersInvokedOnChannel { 68 // In the legacy lscc the install was split into two phases 69 // In the first phase, all the listener will be invoked and in the second phase, 70 // the install will proceed and finally will, give a call back whether the install 71 // is succeeded. 72 // The purpose of splitting this in two phases was to essentially not miss on an install 73 // event in the case of a system crash immediatly after install and before the listeners 74 // gets a chance. 75 // However, in the current install model, the lifecycle cache receives the event only after 76 // the install is complete. So, for now, call the done on the listeners with a hard-wired 'true' 77 b.invokeDoneOnListeners(channelID, true) 78 } 79 } 80 return 81 } 82 83 // ProcessApproveOrDefineEvent gets invoked by an event that makes approve and define to be true 84 // This should be OK even if this function gets invoked on defined and approved events separately because 85 // the first check in this fucntion evaluates the final condition. However, the current cache implementation 86 // invokes this function when approve and define both become true. 87 func (b *EventBroker) ProcessApproveOrDefineEvent(channelID string, chaincodeName string, cachedChaincode *CachedChaincodeDefinition) { 88 logger.Debugw("processApproveOrDefineEvent()", "channelID", channelID, "chaincodeName", chaincodeName, "cachedChaincode", cachedChaincode) 89 if !isChaincodeInvokable(cachedChaincode) { 90 return 91 } 92 dbArtifacts, err := b.loadDBArtifacts(cachedChaincode.InstallInfo.PackageID) 93 if err != nil { 94 logger.Errorf("Error while loading db artifacts for chaincode package with package ID [%s]: %s", 95 cachedChaincode.InstallInfo.PackageID, err) 96 return 97 } 98 ccdef := &ledger.ChaincodeDefinition{ 99 Name: chaincodeName, 100 Version: cachedChaincode.Definition.EndorsementInfo.Version, 101 Hash: []byte(cachedChaincode.InstallInfo.PackageID), 102 CollectionConfigs: cachedChaincode.Definition.Collections, 103 } 104 b.invokeListeners(channelID, ccdef, dbArtifacts) 105 b.defineCallbackStatus.Store(channelID, struct{}{}) 106 return 107 } 108 109 // ApproveOrDefineCommitted gets invoked after the commit of state updates that triggered the invocaiton of 110 // "ProcessApproveOrDefineEvent" function 111 func (b *EventBroker) ApproveOrDefineCommitted(channelID string) { 112 _, ok := b.defineCallbackStatus.Load(channelID) 113 if !ok { 114 return 115 } 116 b.invokeDoneOnListeners(channelID, true) 117 b.defineCallbackStatus.Delete(channelID) 118 } 119 120 func (b *EventBroker) invokeListeners(channelID string, legacyDefinition *ledger.ChaincodeDefinition, dbArtifacts []byte) { 121 b.mutex.Lock() 122 defer b.mutex.Unlock() 123 channelListeners := b.listeners[channelID] 124 for _, l := range channelListeners { 125 if err := l.HandleChaincodeDeploy(legacyDefinition, dbArtifacts); err != nil { 126 // If a listener return this error and we propagate this error up the stack, 127 // following are the implications: 128 // 129 // 1) If this path gets called from the chaincode install operation, the install operation will need to 130 // handle the error, perhaps by aborting the install operation 131 // 2) If this path gets called from the block commit (that includes chaincode approve/define transaction) 132 // it will result in a peer panic. 133 // 134 // The behavior mentioned in (2) i.e., the installation of malformed chaincode package resulting in a 135 // peer panic on approve/define transaction commit may not be a desired behavior. 136 // Primarily because, a) the installation of chaincode is not a fundamental requirement for committer to function 137 // and b) typically, it may take longer dev cycles to fix the chaincode package issues as opposed to some admin 138 // operation (say, restart couchdb). Note that chaincode uninstall is not currently supported. 139 // 140 // In addition, another implication is that the behavior will be inconsistent on different peers. In the case of 141 // a faulty package, some peers may fail on install while others will report a success in installation and fail 142 // later at the approve/define commit time. 143 // 144 // So, instead of throwing this error up logging this here. 145 logger.Errorf("Error from listener during processing chaincode lifecycle event - %+v", errors.WithStack(err)) 146 } 147 } 148 } 149 150 func (b *EventBroker) invokeDoneOnListeners(channelID string, succeeded bool) { 151 b.mutex.Lock() 152 defer b.mutex.Unlock() 153 channelListeners := b.listeners[channelID] 154 for _, l := range channelListeners { 155 l.ChaincodeDeployDone(succeeded) 156 } 157 } 158 159 func (b *EventBroker) loadDBArtifacts(packageID string) ([]byte, error) { 160 md, err := b.ebMetadata.PackageMetadata(packageID) 161 if err != nil { 162 return nil, err 163 } 164 165 if md != nil { 166 return md, nil 167 } 168 169 pkgBytes, err := b.chaincodeStore.Load(packageID) 170 if err != nil { 171 return nil, err 172 } 173 pkg, err := b.pkgParser.Parse(pkgBytes) 174 if err != nil { 175 return nil, err 176 } 177 return pkg.DBArtifacts, nil 178 } 179 180 // isChaincodeInvokable returns true iff a chaincode is approved and installed and defined 181 func isChaincodeInvokable(ccInfo *CachedChaincodeDefinition) bool { 182 return ccInfo.Approved && ccInfo.InstallInfo != nil && ccInfo.Definition != nil 183 }