github.com/kaituanwang/hyperledger@v2.0.1+incompatible/core/chaincode/lifecycle/event_broker.go (about)

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