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  }