github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/core/ledger/kvledger/coll_elg_notifier.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package kvledger
     8  
     9  import (
    10  	"github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset"
    11  	"github.com/hyperledger/fabric-protos-go/peer"
    12  	"github.com/osdi23p228/fabric/core/ledger"
    13  )
    14  
    15  // collElgNotifier listens for the chaincode events and determines whether the peer has become eligible for one or more existing
    16  // private data collections and notifies the registered listener
    17  type collElgNotifier struct {
    18  	deployedChaincodeInfoProvider ledger.DeployedChaincodeInfoProvider
    19  	membershipInfoProvider        ledger.MembershipInfoProvider
    20  	listeners                     map[string]collElgListener
    21  }
    22  
    23  // Name returns the name of the listener
    24  func (n *collElgNotifier) Name() string {
    25  	return "collection eligibility listener"
    26  }
    27  
    28  func (n *collElgNotifier) Initialize(ledgerID string, qe ledger.SimpleQueryExecutor) error {
    29  	// Noop
    30  	return nil
    31  }
    32  
    33  // InterestedInNamespaces implements function in interface ledger.StateListener
    34  func (n *collElgNotifier) InterestedInNamespaces() []string {
    35  	return n.deployedChaincodeInfoProvider.Namespaces()
    36  }
    37  
    38  // HandleStateUpdates implements function in interface ledger.StateListener
    39  // This function gets invoked when one or more chaincodes are deployed or upgraded by a block.
    40  // This function, for each upgraded chaincode, performs the following
    41  // 1) Retrieves the existing collection configurations and new collection configurations
    42  // 2) Computes the collections for which the peer is not eligible as per the existing collection configuration
    43  //    but is eligible as per the new collection configuration
    44  // Finally, it causes an invocation to function 'ProcessCollsEligibilityEnabled' on ledger store with a map {ns:colls}
    45  // that contains the details of <ns, coll> combination for which the eligibility of the peer is switched on.
    46  func (n *collElgNotifier) HandleStateUpdates(trigger *ledger.StateUpdateTrigger) error {
    47  	nsCollMap := map[string][]string{}
    48  	qe := trigger.CommittedStateQueryExecutor
    49  	postCommitQE := trigger.PostCommitQueryExecutor
    50  
    51  	stateUpdates := extractPublicUpdates(trigger.StateUpdates)
    52  	ccLifecycleInfo, err := n.deployedChaincodeInfoProvider.UpdatedChaincodes(stateUpdates)
    53  	if err != nil {
    54  		return err
    55  	}
    56  	var existingCCInfo, postCommitCCInfo *ledger.DeployedChaincodeInfo
    57  	for _, ccInfo := range ccLifecycleInfo {
    58  		ledgerid := trigger.LedgerID
    59  		ccName := ccInfo.Name
    60  		if existingCCInfo, err = n.deployedChaincodeInfoProvider.ChaincodeInfo(ledgerid, ccName, qe); err != nil {
    61  			return err
    62  		}
    63  		if existingCCInfo == nil { // not an upgrade transaction
    64  			continue
    65  		}
    66  		if postCommitCCInfo, err = n.deployedChaincodeInfoProvider.ChaincodeInfo(ledgerid, ccName, postCommitQE); err != nil {
    67  			return err
    68  		}
    69  		elgEnabledCollNames, err := n.elgEnabledCollNames(
    70  			ledgerid,
    71  			existingCCInfo.ExplicitCollectionConfigPkg,
    72  			postCommitCCInfo.ExplicitCollectionConfigPkg,
    73  		)
    74  		if err != nil {
    75  			return err
    76  		}
    77  		logger.Debugf("[%s] collections of chaincode [%s] for which peer was not eligible before and now the eligiblity is enabled - [%s]",
    78  			ledgerid, ccName, elgEnabledCollNames,
    79  		)
    80  		if len(elgEnabledCollNames) > 0 {
    81  			nsCollMap[ccName] = elgEnabledCollNames
    82  		}
    83  	}
    84  	if len(nsCollMap) > 0 {
    85  		n.invokeLedgerSpecificNotifier(trigger.LedgerID, trigger.CommittingBlockNum, nsCollMap)
    86  	}
    87  	return nil
    88  }
    89  
    90  func (n *collElgNotifier) registerListener(ledgerID string, listener collElgListener) {
    91  	n.listeners[ledgerID] = listener
    92  }
    93  
    94  func (n *collElgNotifier) invokeLedgerSpecificNotifier(ledgerID string, commtingBlk uint64, nsCollMap map[string][]string) {
    95  	listener := n.listeners[ledgerID]
    96  	listener.ProcessCollsEligibilityEnabled(commtingBlk, nsCollMap)
    97  }
    98  
    99  // elgEnabledCollNames returns the names of the collections for which the peer is not eligible as per 'existingPkg' and is eligible as per 'postCommitPkg'
   100  func (n *collElgNotifier) elgEnabledCollNames(ledgerID string,
   101  	existingPkg, postCommitPkg *peer.CollectionConfigPackage) ([]string, error) {
   102  
   103  	collectionNames := []string{}
   104  	exisingConfs := retrieveCollConfs(existingPkg)
   105  	postCommitConfs := retrieveCollConfs(postCommitPkg)
   106  	existingConfMap := map[string]*peer.StaticCollectionConfig{}
   107  	for _, existingConf := range exisingConfs {
   108  		existingConfMap[existingConf.Name] = existingConf
   109  	}
   110  
   111  	for _, postCommitConf := range postCommitConfs {
   112  		collName := postCommitConf.Name
   113  		existingConf, ok := existingConfMap[collName]
   114  		if !ok { // brand new collection
   115  			continue
   116  		}
   117  		membershipEnabled, err := n.elgEnabled(ledgerID, existingConf.MemberOrgsPolicy, postCommitConf.MemberOrgsPolicy)
   118  		if err != nil {
   119  			return nil, err
   120  		}
   121  		if !membershipEnabled {
   122  			continue
   123  		}
   124  		// not an existing member and added now
   125  		collectionNames = append(collectionNames, collName)
   126  	}
   127  	return collectionNames, nil
   128  }
   129  
   130  // elgEnabled returns true if the peer is not eligible for a collection as per 'existingPolicy' and is eligible as per 'postCommitPolicy'
   131  func (n *collElgNotifier) elgEnabled(ledgerID string, existingPolicy, postCommitPolicy *peer.CollectionPolicyConfig) (bool, error) {
   132  	existingMember, err := n.membershipInfoProvider.AmMemberOf(ledgerID, existingPolicy)
   133  	if err != nil || existingMember {
   134  		return false, err
   135  	}
   136  	return n.membershipInfoProvider.AmMemberOf(ledgerID, postCommitPolicy)
   137  }
   138  
   139  func extractPublicUpdates(stateUpdates ledger.StateUpdates) map[string][]*kvrwset.KVWrite {
   140  	m := map[string][]*kvrwset.KVWrite{}
   141  	for ns, updates := range stateUpdates {
   142  		m[ns] = updates.PublicUpdates
   143  	}
   144  	return m
   145  }
   146  
   147  // StateCommitDone implements function in interface ledger.StateListener
   148  func (n *collElgNotifier) StateCommitDone(ledgerID string) {
   149  	// Noop
   150  }
   151  
   152  type collElgListener interface {
   153  	ProcessCollsEligibilityEnabled(commitingBlk uint64, nsCollMap map[string][]string) error
   154  }
   155  
   156  func retrieveCollConfs(collConfPkg *peer.CollectionConfigPackage) []*peer.StaticCollectionConfig {
   157  	if collConfPkg == nil {
   158  		return nil
   159  	}
   160  	var staticCollConfs []*peer.StaticCollectionConfig
   161  	protoConfArray := collConfPkg.Config
   162  	for _, protoConf := range protoConfArray {
   163  		staticCollConfs = append(staticCollConfs, protoConf.GetStaticCollectionConfig())
   164  	}
   165  	return staticCollConfs
   166  }