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  }