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

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package confighistory
     8  
     9  import (
    10  	"fmt"
    11  
    12  	"github.com/golang/protobuf/proto"
    13  	"github.com/hyperledger/fabric-protos-go/common"
    14  	"github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset"
    15  	"github.com/hyperledger/fabric-protos-go/peer"
    16  	"github.com/hyperledger/fabric/common/flogging"
    17  	"github.com/hyperledger/fabric/core/ledger"
    18  	"github.com/pkg/errors"
    19  )
    20  
    21  var logger = flogging.MustGetLogger("confighistory")
    22  
    23  const (
    24  	collectionConfigNamespace = "lscc" // lscc namespace was introduced in version 1.2 and we continue to use this in order to be compatible with existing data
    25  )
    26  
    27  // Mgr should be registered as a state listener. The state listener builds the history and retriever helps in querying the history
    28  type Mgr interface {
    29  	ledger.StateListener
    30  	GetRetriever(ledgerID string, ledgerInfoRetriever LedgerInfoRetriever) ledger.ConfigHistoryRetriever
    31  	Close()
    32  }
    33  
    34  type mgr struct {
    35  	ccInfoProvider ledger.DeployedChaincodeInfoProvider
    36  	dbProvider     *dbProvider
    37  }
    38  
    39  // NewMgr constructs an instance that implements interface `Mgr`
    40  func NewMgr(dbPath string, ccInfoProvider ledger.DeployedChaincodeInfoProvider) (Mgr, error) {
    41  	p, err := newDBProvider(dbPath)
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  	return &mgr{ccInfoProvider, p}, nil
    46  }
    47  
    48  func (m *mgr) Initialize(ledgerID string, qe ledger.SimpleQueryExecutor) error {
    49  	// Noop
    50  	return nil
    51  }
    52  
    53  // InterestedInNamespaces implements function from the interface ledger.StateListener
    54  func (m *mgr) InterestedInNamespaces() []string {
    55  	return m.ccInfoProvider.Namespaces()
    56  }
    57  
    58  // StateCommitDone implements function from the interface ledger.StateListener
    59  func (m *mgr) StateCommitDone(ledgerID string) {
    60  	// Noop
    61  }
    62  
    63  // HandleStateUpdates implements function from the interface ledger.StateListener
    64  // In this implementation, the latest collection config package is retrieved via
    65  // ledger.DeployedChaincodeInfoProvider and is persisted as a separate entry in a separate db.
    66  // The composite key for the entry is a tuple of <blockNum, namespace, key>
    67  func (m *mgr) HandleStateUpdates(trigger *ledger.StateUpdateTrigger) error {
    68  	updatedCCs, err := m.ccInfoProvider.UpdatedChaincodes(extractPublicUpdates(trigger.StateUpdates))
    69  	if err != nil {
    70  		return err
    71  	}
    72  	// updated chaincodes can be empty if the invocation to this function is triggered
    73  	// because of state updates that contains only chaincode approval transaction output
    74  	if len(updatedCCs) == 0 {
    75  		return nil
    76  	}
    77  	updatedCollConfigs := map[string]*peer.CollectionConfigPackage{}
    78  	for _, cc := range updatedCCs {
    79  		ccInfo, err := m.ccInfoProvider.ChaincodeInfo(trigger.LedgerID, cc.Name, trigger.PostCommitQueryExecutor)
    80  		if err != nil {
    81  			return err
    82  		}
    83  
    84  		// DeployedChaincodeInfoProvider implementation in new lifecycle return an empty 'CollectionConfigPackage'
    85  		// (instead of a nil) to indicate the absence of collection config, so check for both conditions
    86  		if ccInfo.ExplicitCollectionConfigPkg == nil || len(ccInfo.ExplicitCollectionConfigPkg.Config) == 0 {
    87  			continue
    88  		}
    89  		updatedCollConfigs[ccInfo.Name] = ccInfo.ExplicitCollectionConfigPkg
    90  	}
    91  	if len(updatedCollConfigs) == 0 {
    92  		return nil
    93  	}
    94  	batch, err := prepareDBBatch(updatedCollConfigs, trigger.CommittingBlockNum)
    95  	if err != nil {
    96  		return err
    97  	}
    98  	dbHandle := m.dbProvider.getDB(trigger.LedgerID)
    99  	return dbHandle.writeBatch(batch, true)
   100  }
   101  
   102  // GetRetriever returns an implementation of `ledger.ConfigHistoryRetriever` for the given ledger id.
   103  func (m *mgr) GetRetriever(ledgerID string, ledgerInfoRetriever LedgerInfoRetriever) ledger.ConfigHistoryRetriever {
   104  	return &retriever{
   105  		ledgerInfoRetriever:    ledgerInfoRetriever,
   106  		ledgerID:               ledgerID,
   107  		deployedCCInfoProvider: m.ccInfoProvider,
   108  		dbHandle:               m.dbProvider.getDB(ledgerID),
   109  	}
   110  }
   111  
   112  // Close implements the function in the interface 'Mgr'
   113  func (m *mgr) Close() {
   114  	m.dbProvider.Close()
   115  }
   116  
   117  type retriever struct {
   118  	ledgerInfoRetriever    LedgerInfoRetriever
   119  	ledgerID               string
   120  	deployedCCInfoProvider ledger.DeployedChaincodeInfoProvider
   121  	dbHandle               *db
   122  }
   123  
   124  // MostRecentCollectionConfigBelow implements function from the interface ledger.ConfigHistoryRetriever
   125  func (r *retriever) MostRecentCollectionConfigBelow(blockNum uint64, chaincodeName string) (*ledger.CollectionConfigInfo, error) {
   126  	compositeKV, err := r.dbHandle.mostRecentEntryBelow(blockNum, collectionConfigNamespace, constructCollectionConfigKey(chaincodeName))
   127  	if err != nil {
   128  		return nil, err
   129  	}
   130  	implicitColls, err := r.getImplicitCollection(chaincodeName)
   131  	if err != nil {
   132  		return nil, err
   133  	}
   134  
   135  	return constructCollectionConfigInfo(compositeKV, implicitColls)
   136  }
   137  
   138  // CollectionConfigAt implements function from the interface ledger.ConfigHistoryRetriever
   139  func (r *retriever) CollectionConfigAt(blockNum uint64, chaincodeName string) (*ledger.CollectionConfigInfo, error) {
   140  	info, err := r.ledgerInfoRetriever.GetBlockchainInfo()
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  	maxCommittedBlockNum := info.Height - 1
   145  	if maxCommittedBlockNum < blockNum {
   146  		return nil, &ledger.ErrCollectionConfigNotYetAvailable{MaxBlockNumCommitted: maxCommittedBlockNum,
   147  			Msg: fmt.Sprintf("The maximum block number committed [%d] is less than the requested block number [%d]", maxCommittedBlockNum, blockNum)}
   148  	}
   149  
   150  	compositeKV, err := r.dbHandle.entryAt(blockNum, collectionConfigNamespace, constructCollectionConfigKey(chaincodeName))
   151  	if err != nil {
   152  		return nil, err
   153  	}
   154  	implicitColls, err := r.getImplicitCollection(chaincodeName)
   155  	if err != nil {
   156  		return nil, err
   157  	}
   158  	return constructCollectionConfigInfo(compositeKV, implicitColls)
   159  }
   160  
   161  func (r *retriever) getImplicitCollection(chaincodeName string) ([]*peer.StaticCollectionConfig, error) {
   162  	qe, err := r.ledgerInfoRetriever.NewQueryExecutor()
   163  	if err != nil {
   164  		return nil, err
   165  	}
   166  	defer qe.Done()
   167  	return r.deployedCCInfoProvider.ImplicitCollections(r.ledgerID, chaincodeName, qe)
   168  }
   169  
   170  func prepareDBBatch(chaincodeCollConfigs map[string]*peer.CollectionConfigPackage, committingBlockNum uint64) (*batch, error) {
   171  	batch := newBatch()
   172  	for ccName, collConfig := range chaincodeCollConfigs {
   173  		key := constructCollectionConfigKey(ccName)
   174  		var configBytes []byte
   175  		var err error
   176  		if configBytes, err = proto.Marshal(collConfig); err != nil {
   177  			return nil, errors.WithStack(err)
   178  		}
   179  		batch.add(collectionConfigNamespace, key, committingBlockNum, configBytes)
   180  	}
   181  	return batch, nil
   182  }
   183  
   184  func compositeKVToCollectionConfig(compositeKV *compositeKV) (*ledger.CollectionConfigInfo, error) {
   185  	conf := &peer.CollectionConfigPackage{}
   186  	if err := proto.Unmarshal(compositeKV.value, conf); err != nil {
   187  		return nil, errors.Wrap(err, "error unmarshalling compositeKV to collection config")
   188  	}
   189  	return &ledger.CollectionConfigInfo{
   190  		CollectionConfig:   conf,
   191  		CommittingBlockNum: compositeKV.blockNum,
   192  	}, nil
   193  }
   194  
   195  func constructCollectionConfigKey(chaincodeName string) string {
   196  	return chaincodeName + "~collection" // collection config key as in version 1.2 and we continue to use this in order to be compatible with existing data
   197  }
   198  
   199  func extractPublicUpdates(stateUpdates ledger.StateUpdates) map[string][]*kvrwset.KVWrite {
   200  	m := map[string][]*kvrwset.KVWrite{}
   201  	for ns, updates := range stateUpdates {
   202  		m[ns] = updates.PublicUpdates
   203  	}
   204  	return m
   205  }
   206  
   207  func constructCollectionConfigInfo(
   208  	compositeKV *compositeKV,
   209  	implicitColls []*peer.StaticCollectionConfig,
   210  ) (*ledger.CollectionConfigInfo, error) {
   211  	var collConf *ledger.CollectionConfigInfo
   212  	var err error
   213  
   214  	if compositeKV == nil && len(implicitColls) == 0 {
   215  		return nil, nil
   216  	}
   217  
   218  	collConf = &ledger.CollectionConfigInfo{
   219  		CollectionConfig: &peer.CollectionConfigPackage{},
   220  	}
   221  	if compositeKV != nil {
   222  		if collConf, err = compositeKVToCollectionConfig(compositeKV); err != nil {
   223  			return nil, err
   224  		}
   225  	}
   226  
   227  	for _, implicitColl := range implicitColls {
   228  		cc := &peer.CollectionConfig{}
   229  		cc.Payload = &peer.CollectionConfig_StaticCollectionConfig{StaticCollectionConfig: implicitColl}
   230  		collConf.CollectionConfig.Config = append(
   231  			collConf.CollectionConfig.Config,
   232  			cc,
   233  		)
   234  	}
   235  	return collConf, nil
   236  }
   237  
   238  // LedgerInfoRetriever retrieves the relevant info from ledger
   239  type LedgerInfoRetriever interface {
   240  	GetBlockchainInfo() (*common.BlockchainInfo, error)
   241  	NewQueryExecutor() (ledger.QueryExecutor, error)
   242  }