github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/ledger/cceventmgmt/mgr.go (about) 1 /* 2 Copyright hechain. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package cceventmgmt 8 9 import ( 10 "sync" 11 12 "github.com/hechain20/hechain/common/flogging" 13 "github.com/hechain20/hechain/core/ledger" 14 ) 15 16 var logger = flogging.MustGetLogger("cceventmgmt") 17 18 var mgr *Mgr 19 20 // Initialize initializes event mgmt 21 func Initialize(ccInfoProvider ChaincodeInfoProvider) { 22 initialize(ccInfoProvider) 23 } 24 25 func initialize(ccInfoProvider ChaincodeInfoProvider) { 26 mgr = newMgr(ccInfoProvider) 27 } 28 29 // GetMgr returns the reference to singleton event manager 30 func GetMgr() *Mgr { 31 return mgr 32 } 33 34 // Mgr encapsulate important interactions for events related to the interest of ledger 35 type Mgr struct { 36 // rwlock is mainly used to synchronize across deploy transaction, chaincode install, and channel creation 37 // Ideally, different services in the peer should be designed such that they expose locks for different important 38 // events so that a code on top can synchronize across if needs to. However, in the lack of any such system-wide design, 39 // we use this lock for contextual use 40 rwlock sync.RWMutex 41 infoProvider ChaincodeInfoProvider 42 ccLifecycleListeners map[string][]ChaincodeLifecycleEventListener 43 callbackStatus *callbackStatus 44 } 45 46 func newMgr(chaincodeInfoProvider ChaincodeInfoProvider) *Mgr { 47 return &Mgr{ 48 infoProvider: chaincodeInfoProvider, 49 ccLifecycleListeners: make(map[string][]ChaincodeLifecycleEventListener), 50 callbackStatus: newCallbackStatus(), 51 } 52 } 53 54 // Register registers a ChaincodeLifecycleEventListener for given ledgerid 55 // Since, `Register` is expected to be invoked when creating/opening a ledger instance 56 func (m *Mgr) Register(ledgerid string, l ChaincodeLifecycleEventListener) { 57 // write lock to synchronize concurrent 'chaincode install' operations with ledger creation/open 58 m.rwlock.Lock() 59 defer m.rwlock.Unlock() 60 m.ccLifecycleListeners[ledgerid] = append(m.ccLifecycleListeners[ledgerid], l) 61 } 62 63 // RegisterAndInvokeFor registers the listener and in addition invokes the listener for each chaincode that is present in the supplied 64 // list of legacyChaincodes and is installed on the peer 65 func (m *Mgr) RegisterAndInvokeFor(legacyChaincodes []*ChaincodeDefinition, ledgerid string, l ChaincodeLifecycleEventListener) error { 66 m.rwlock.Lock() 67 defer m.rwlock.Unlock() 68 m.ccLifecycleListeners[ledgerid] = append(m.ccLifecycleListeners[ledgerid], l) 69 for _, chaincodeDefinition := range legacyChaincodes { 70 installed, dbArtifacts, err := m.infoProvider.RetrieveChaincodeArtifacts(chaincodeDefinition) 71 if err != nil { 72 return err 73 } 74 if !installed { 75 continue 76 } 77 if err := l.HandleChaincodeDeploy(chaincodeDefinition, dbArtifacts); err != nil { 78 return err 79 } 80 l.ChaincodeDeployDone(true) 81 } 82 return nil 83 } 84 85 // HandleChaincodeDeploy is expected to be invoked when a chaincode is deployed via a deploy transaction 86 // The `chaincodeDefinitions` parameter contains all the chaincodes deployed in a block 87 // We need to store the last received `chaincodeDefinitions` because this function is expected to be invoked 88 // after the deploy transactions validation is performed but not committed yet to the ledger. Further, we 89 // release the read lock after this function. This leaves a small window when a `chaincode install` can happen 90 // before the deploy transaction is committed and hence the function `HandleChaincodeInstall` may miss finding 91 // the deployed chaincode. So, in function `HandleChaincodeInstall`, we explicitly check for chaincode deployed 92 // in this stored `chaincodeDefinitions` 93 func (m *Mgr) HandleChaincodeDeploy(chainid string, chaincodeDefinitions []*ChaincodeDefinition) error { 94 logger.Debugf("Channel [%s]: Handling chaincode deploy event for chaincode [%s]", chainid, chaincodeDefinitions) 95 // Read lock to allow concurrent deploy on multiple channels but to synchronize concurrent `chaincode install` operation 96 m.rwlock.RLock() 97 for _, chaincodeDefinition := range chaincodeDefinitions { 98 installed, dbArtifacts, err := m.infoProvider.RetrieveChaincodeArtifacts(chaincodeDefinition) 99 if err != nil { 100 return err 101 } 102 if !installed { 103 logger.Infof("Channel [%s]: Chaincode [%s] is not installed hence no need to create chaincode artifacts for endorsement", 104 chainid, chaincodeDefinition) 105 continue 106 } 107 m.callbackStatus.setDeployPending(chainid) 108 if err := m.invokeHandler(chainid, chaincodeDefinition, dbArtifacts); err != nil { 109 logger.Warningf("Channel [%s]: Error while invoking a listener for handling chaincode install event: %s", chainid, err) 110 return err 111 } 112 logger.Debugf("Channel [%s]: Handled chaincode deploy event for chaincode [%s]", chainid, chaincodeDefinitions) 113 } 114 return nil 115 } 116 117 // ChaincodeDeployDone is expected to be called when the deploy transaction state is committed 118 func (m *Mgr) ChaincodeDeployDone(chainid string) { 119 // release the lock acquired in function `HandleChaincodeDeploy` 120 defer m.rwlock.RUnlock() 121 if m.callbackStatus.isDeployPending(chainid) { 122 m.invokeDoneOnHandlers(chainid, true) 123 m.callbackStatus.unsetDeployPending(chainid) 124 } 125 } 126 127 // HandleChaincodeInstall is expected to get invoked during installation of a chaincode package 128 func (m *Mgr) HandleChaincodeInstall(chaincodeDefinition *ChaincodeDefinition, dbArtifacts []byte) error { 129 logger.Debugf("HandleChaincodeInstall() - chaincodeDefinition=%#v", chaincodeDefinition) 130 // Write lock prevents concurrent deploy operations 131 m.rwlock.Lock() 132 for chainid := range m.ccLifecycleListeners { 133 logger.Debugf("Channel [%s]: Handling chaincode install event for chaincode [%s]", chainid, chaincodeDefinition) 134 var deployedCCInfo *ledger.DeployedChaincodeInfo 135 var err error 136 if deployedCCInfo, err = m.infoProvider.GetDeployedChaincodeInfo(chainid, chaincodeDefinition); err != nil { 137 logger.Warningf("Channel [%s]: Error while getting the deployment status of chaincode: %s", chainid, err) 138 return err 139 } 140 if deployedCCInfo == nil { 141 logger.Debugf("Channel [%s]: Chaincode [%s] is not deployed on channel hence not creating chaincode artifacts.", 142 chainid, chaincodeDefinition) 143 continue 144 } 145 if !deployedCCInfo.IsLegacy { 146 // the chaincode has already been defined via new lifecycle, we reach here because of a subsequent 147 // install of chaincode using legacy package. So, ignoring this event 148 logger.Debugf("Channel [%s]: Chaincode [%s] is already defined in new lifecycle hence not creating chaincode artifacts.", 149 chainid, chaincodeDefinition) 150 continue 151 } 152 m.callbackStatus.setInstallPending(chainid) 153 chaincodeDefinition.CollectionConfigs = deployedCCInfo.ExplicitCollectionConfigPkg 154 if err := m.invokeHandler(chainid, chaincodeDefinition, dbArtifacts); err != nil { 155 logger.Warningf("Channel [%s]: Error while invoking a listener for handling chaincode install event: %s", chainid, err) 156 return err 157 } 158 logger.Debugf("Channel [%s]: Handled chaincode install event for chaincode [%s]", chainid, chaincodeDefinition) 159 } 160 return nil 161 } 162 163 // ChaincodeInstallDone is expected to get invoked when chaincode install finishes 164 func (m *Mgr) ChaincodeInstallDone(succeeded bool) { 165 // release the lock acquired in function `HandleChaincodeInstall` 166 defer m.rwlock.Unlock() 167 for chainid := range m.callbackStatus.installPending { 168 m.invokeDoneOnHandlers(chainid, succeeded) 169 m.callbackStatus.unsetInstallPending(chainid) 170 } 171 } 172 173 func (m *Mgr) invokeHandler(chainid string, chaincodeDefinition *ChaincodeDefinition, dbArtifactsTar []byte) error { 174 listeners := m.ccLifecycleListeners[chainid] 175 for _, listener := range listeners { 176 if err := listener.HandleChaincodeDeploy(chaincodeDefinition, dbArtifactsTar); err != nil { 177 return err 178 } 179 } 180 return nil 181 } 182 183 func (m *Mgr) invokeDoneOnHandlers(chainid string, succeeded bool) { 184 listeners := m.ccLifecycleListeners[chainid] 185 for _, listener := range listeners { 186 listener.ChaincodeDeployDone(succeeded) 187 } 188 } 189 190 type callbackStatus struct { 191 l sync.Mutex 192 deployPending map[string]bool 193 installPending map[string]bool 194 } 195 196 func newCallbackStatus() *callbackStatus { 197 return &callbackStatus{ 198 deployPending: make(map[string]bool), 199 installPending: make(map[string]bool), 200 } 201 } 202 203 func (s *callbackStatus) setDeployPending(channelID string) { 204 s.l.Lock() 205 defer s.l.Unlock() 206 s.deployPending[channelID] = true 207 } 208 209 func (s *callbackStatus) unsetDeployPending(channelID string) { 210 s.l.Lock() 211 defer s.l.Unlock() 212 delete(s.deployPending, channelID) 213 } 214 215 func (s *callbackStatus) isDeployPending(channelID string) bool { 216 s.l.Lock() 217 defer s.l.Unlock() 218 return s.deployPending[channelID] 219 } 220 221 func (s *callbackStatus) setInstallPending(channelID string) { 222 s.l.Lock() 223 defer s.l.Unlock() 224 s.installPending[channelID] = true 225 } 226 227 func (s *callbackStatus) unsetInstallPending(channelID string) { 228 s.l.Lock() 229 defer s.l.Unlock() 230 delete(s.installPending, channelID) 231 }