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