github.com/ahlemtn/fabric@v2.1.1+incompatible/core/ledger/cceventmgmt/mgr.go (about)

     1  /*
     2  Copyright IBM Corp. 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/hyperledger/fabric/common/flogging"
    13  	"github.com/hyperledger/fabric/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  // Register registers a ChaincodeLifecycleEventListener for given ledgerid
    54  // Since, `Register` is expected to be invoked when creating/opening a ledger instance
    55  func (m *Mgr) Register(ledgerid string, l ChaincodeLifecycleEventListener) {
    56  	// write lock to synchronize concurrent 'chaincode install' operations with ledger creation/open
    57  	m.rwlock.Lock()
    58  	defer m.rwlock.Unlock()
    59  	m.ccLifecycleListeners[ledgerid] = append(m.ccLifecycleListeners[ledgerid], l)
    60  }
    61  
    62  // HandleChaincodeDeploy is expected to be invoked when a chaincode is deployed via a deploy transaction
    63  // The `chaincodeDefinitions` parameter contains all the chaincodes deployed in a block
    64  // We need to store the last received `chaincodeDefinitions` because this function is expected to be invoked
    65  // after the deploy transactions validation is performed but not committed yet to the ledger. Further, we
    66  // release the read lock after this function. This leaves a small window when a `chaincode install` can happen
    67  // before the deploy transaction is committed and hence the function `HandleChaincodeInstall` may miss finding
    68  // the deployed chaincode. So, in function `HandleChaincodeInstall`, we explicitly check for chaincode deployed
    69  // in this stored `chaincodeDefinitions`
    70  func (m *Mgr) HandleChaincodeDeploy(chainid string, chaincodeDefinitions []*ChaincodeDefinition) error {
    71  	logger.Debugf("Channel [%s]: Handling chaincode deploy event for chaincode [%s]", chainid, chaincodeDefinitions)
    72  	// Read lock to allow concurrent deploy on multiple channels but to synchronize concurrent `chaincode install` operation
    73  	m.rwlock.RLock()
    74  	for _, chaincodeDefinition := range chaincodeDefinitions {
    75  		installed, dbArtifacts, err := m.infoProvider.RetrieveChaincodeArtifacts(chaincodeDefinition)
    76  		if err != nil {
    77  			return err
    78  		}
    79  		if !installed {
    80  			logger.Infof("Channel [%s]: Chaincode [%s] is not installed hence no need to create chaincode artifacts for endorsement",
    81  				chainid, chaincodeDefinition)
    82  			continue
    83  		}
    84  		m.callbackStatus.setDeployPending(chainid)
    85  		if err := m.invokeHandler(chainid, chaincodeDefinition, dbArtifacts); err != nil {
    86  			logger.Warningf("Channel [%s]: Error while invoking a listener for handling chaincode install event: %s", chainid, err)
    87  			return err
    88  		}
    89  		logger.Debugf("Channel [%s]: Handled chaincode deploy event for chaincode [%s]", chainid, chaincodeDefinitions)
    90  	}
    91  	return nil
    92  }
    93  
    94  // ChaincodeDeployDone is expected to be called when the deploy transaction state is committed
    95  func (m *Mgr) ChaincodeDeployDone(chainid string) {
    96  	// release the lock acquired in function `HandleChaincodeDeploy`
    97  	defer m.rwlock.RUnlock()
    98  	if m.callbackStatus.isDeployPending(chainid) {
    99  		m.invokeDoneOnHandlers(chainid, true)
   100  		m.callbackStatus.unsetDeployPending(chainid)
   101  	}
   102  }
   103  
   104  // HandleChaincodeInstall is expected to get invoked during installation of a chaincode package
   105  func (m *Mgr) HandleChaincodeInstall(chaincodeDefinition *ChaincodeDefinition, dbArtifacts []byte) error {
   106  	logger.Debugf("HandleChaincodeInstall() - chaincodeDefinition=%#v", chaincodeDefinition)
   107  	// Write lock prevents concurrent deploy operations
   108  	m.rwlock.Lock()
   109  	for chainid := range m.ccLifecycleListeners {
   110  		logger.Debugf("Channel [%s]: Handling chaincode install event for chaincode [%s]", chainid, chaincodeDefinition)
   111  		var deployedCCInfo *ledger.DeployedChaincodeInfo
   112  		var err error
   113  		if deployedCCInfo, err = m.infoProvider.GetDeployedChaincodeInfo(chainid, chaincodeDefinition); err != nil {
   114  			logger.Warningf("Channel [%s]: Error while getting the deployment status of chaincode: %s", chainid, err)
   115  			return err
   116  		}
   117  		if deployedCCInfo == nil {
   118  			logger.Debugf("Channel [%s]: Chaincode [%s] is not deployed on channel hence not creating chaincode artifacts.",
   119  				chainid, chaincodeDefinition)
   120  			continue
   121  		}
   122  		if !deployedCCInfo.IsLegacy {
   123  			// the chaincode has already been defined via new lifecycle, we reach here because of a subsequent
   124  			// install of chaincode using legacy package. So, ignoring this event
   125  			logger.Debugf("Channel [%s]: Chaincode [%s] is already defined in new lifecycle hence not creating chaincode artifacts.",
   126  				chainid, chaincodeDefinition)
   127  			continue
   128  		}
   129  		m.callbackStatus.setInstallPending(chainid)
   130  		chaincodeDefinition.CollectionConfigs = deployedCCInfo.ExplicitCollectionConfigPkg
   131  		if err := m.invokeHandler(chainid, chaincodeDefinition, dbArtifacts); err != nil {
   132  			logger.Warningf("Channel [%s]: Error while invoking a listener for handling chaincode install event: %s", chainid, err)
   133  			return err
   134  		}
   135  		logger.Debugf("Channel [%s]: Handled chaincode install event for chaincode [%s]", chainid, chaincodeDefinition)
   136  	}
   137  	return nil
   138  }
   139  
   140  // ChaincodeInstallDone is expected to get invoked when chaincode install finishes
   141  func (m *Mgr) ChaincodeInstallDone(succeeded bool) {
   142  	// release the lock acquired in function `HandleChaincodeInstall`
   143  	defer m.rwlock.Unlock()
   144  	for chainid := range m.callbackStatus.installPending {
   145  		m.invokeDoneOnHandlers(chainid, succeeded)
   146  		m.callbackStatus.unsetInstallPending(chainid)
   147  	}
   148  }
   149  
   150  func (m *Mgr) invokeHandler(chainid string, chaincodeDefinition *ChaincodeDefinition, dbArtifactsTar []byte) error {
   151  	listeners := m.ccLifecycleListeners[chainid]
   152  	for _, listener := range listeners {
   153  		if err := listener.HandleChaincodeDeploy(chaincodeDefinition, dbArtifactsTar); err != nil {
   154  			return err
   155  		}
   156  	}
   157  	return nil
   158  }
   159  
   160  func (m *Mgr) invokeDoneOnHandlers(chainid string, succeeded bool) {
   161  	listeners := m.ccLifecycleListeners[chainid]
   162  	for _, listener := range listeners {
   163  		listener.ChaincodeDeployDone(succeeded)
   164  	}
   165  }
   166  
   167  type callbackStatus struct {
   168  	l              sync.Mutex
   169  	deployPending  map[string]bool
   170  	installPending map[string]bool
   171  }
   172  
   173  func newCallbackStatus() *callbackStatus {
   174  	return &callbackStatus{
   175  		deployPending:  make(map[string]bool),
   176  		installPending: make(map[string]bool)}
   177  }
   178  
   179  func (s *callbackStatus) setDeployPending(channelID string) {
   180  	s.l.Lock()
   181  	defer s.l.Unlock()
   182  	s.deployPending[channelID] = true
   183  }
   184  
   185  func (s *callbackStatus) unsetDeployPending(channelID string) {
   186  	s.l.Lock()
   187  	defer s.l.Unlock()
   188  	delete(s.deployPending, channelID)
   189  }
   190  
   191  func (s *callbackStatus) isDeployPending(channelID string) bool {
   192  	s.l.Lock()
   193  	defer s.l.Unlock()
   194  	return s.deployPending[channelID]
   195  }
   196  
   197  func (s *callbackStatus) setInstallPending(channelID string) {
   198  	s.l.Lock()
   199  	defer s.l.Unlock()
   200  	s.installPending[channelID] = true
   201  }
   202  
   203  func (s *callbackStatus) unsetInstallPending(channelID string) {
   204  	s.l.Lock()
   205  	defer s.l.Unlock()
   206  	delete(s.installPending, channelID)
   207  }