github.com/Hnampk/fabric@v2.1.1+incompatible/core/ledger/kvledger/txmgmt/privacyenabledstate/common_storage_db.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package privacyenabledstate
     8  
     9  import (
    10  	"encoding/base64"
    11  	"strings"
    12  
    13  	"github.com/hyperledger/fabric-lib-go/healthz"
    14  	"github.com/hyperledger/fabric/common/flogging"
    15  	"github.com/hyperledger/fabric/common/metrics"
    16  	"github.com/hyperledger/fabric/core/common/ccprovider"
    17  	"github.com/hyperledger/fabric/core/ledger"
    18  	"github.com/hyperledger/fabric/core/ledger/cceventmgmt"
    19  	"github.com/hyperledger/fabric/core/ledger/kvledger/bookkeeping"
    20  	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb"
    21  	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb/statecouchdb"
    22  	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb/stateleveldb"
    23  	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/version"
    24  	"github.com/hyperledger/fabric/core/ledger/util"
    25  	"github.com/pkg/errors"
    26  )
    27  
    28  var logger = flogging.MustGetLogger("privacyenabledstate")
    29  
    30  const (
    31  	nsJoiner       = "$$"
    32  	pvtDataPrefix  = "p"
    33  	hashDataPrefix = "h"
    34  	couchDB        = "CouchDB"
    35  )
    36  
    37  // StateDBConfig encapsulates the configuration for stateDB on the ledger.
    38  type StateDBConfig struct {
    39  	// ledger.StateDBConfig is used to configure the stateDB for the ledger.
    40  	*ledger.StateDBConfig
    41  	// LevelDBPath is the filesystem path when statedb type is "goleveldb".
    42  	// It is internally computed by the ledger component,
    43  	// so it is not in ledger.StateDBConfig and not exposed to other components.
    44  	LevelDBPath string
    45  }
    46  
    47  // CommonStorageDBProvider implements interface DBProvider
    48  type CommonStorageDBProvider struct {
    49  	statedb.VersionedDBProvider
    50  	HealthCheckRegistry ledger.HealthCheckRegistry
    51  	bookkeepingProvider bookkeeping.Provider
    52  }
    53  
    54  // NewCommonStorageDBProvider constructs an instance of DBProvider
    55  func NewCommonStorageDBProvider(
    56  	bookkeeperProvider bookkeeping.Provider,
    57  	metricsProvider metrics.Provider,
    58  	healthCheckRegistry ledger.HealthCheckRegistry,
    59  	stateDBConf *StateDBConfig,
    60  	sysNamespaces []string,
    61  ) (DBProvider, error) {
    62  
    63  	var vdbProvider statedb.VersionedDBProvider
    64  	var err error
    65  
    66  	if stateDBConf != nil && stateDBConf.StateDatabase == couchDB {
    67  		cache := statedb.NewCache(stateDBConf.CouchDB.UserCacheSizeMBs, sysNamespaces)
    68  		if vdbProvider, err = statecouchdb.NewVersionedDBProvider(stateDBConf.CouchDB, metricsProvider, cache); err != nil {
    69  			return nil, err
    70  		}
    71  	} else {
    72  		if vdbProvider, err = stateleveldb.NewVersionedDBProvider(stateDBConf.LevelDBPath); err != nil {
    73  			return nil, err
    74  		}
    75  	}
    76  
    77  	dbProvider := &CommonStorageDBProvider{vdbProvider, healthCheckRegistry, bookkeeperProvider}
    78  
    79  	err = dbProvider.RegisterHealthChecker()
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  
    84  	return dbProvider, nil
    85  }
    86  
    87  // RegisterHealthChecker implements function from interface DBProvider
    88  func (p *CommonStorageDBProvider) RegisterHealthChecker() error {
    89  	if healthChecker, ok := p.VersionedDBProvider.(healthz.HealthChecker); ok {
    90  		return p.HealthCheckRegistry.RegisterChecker("couchdb", healthChecker)
    91  	}
    92  	return nil
    93  }
    94  
    95  // GetDBHandle implements function from interface DBProvider
    96  func (p *CommonStorageDBProvider) GetDBHandle(id string) (DB, error) {
    97  	vdb, err := p.VersionedDBProvider.GetDBHandle(id)
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  	bookkeeper := p.bookkeepingProvider.GetDBHandle(id, bookkeeping.MetadataPresenceIndicator)
   102  	metadataHint := newMetadataHint(bookkeeper)
   103  	return NewCommonStorageDB(vdb, id, metadataHint)
   104  }
   105  
   106  // Close implements function from interface DBProvider
   107  func (p *CommonStorageDBProvider) Close() {
   108  	p.VersionedDBProvider.Close()
   109  }
   110  
   111  // CommonStorageDB implements interface DB. This implementation uses a single database to maintain
   112  // both the public and private data
   113  type CommonStorageDB struct {
   114  	statedb.VersionedDB
   115  	metadataHint *metadataHint
   116  }
   117  
   118  // NewCommonStorageDB wraps a VersionedDB instance. The public data is managed directly by the wrapped versionedDB.
   119  // For managing the hashed data and private data, this implementation creates separate namespaces in the wrapped db
   120  func NewCommonStorageDB(vdb statedb.VersionedDB, ledgerid string, metadataHint *metadataHint) (DB, error) {
   121  	return &CommonStorageDB{vdb, metadataHint}, nil
   122  }
   123  
   124  // IsBulkOptimizable implements corresponding function in interface DB
   125  func (s *CommonStorageDB) IsBulkOptimizable() bool {
   126  	_, ok := s.VersionedDB.(statedb.BulkOptimizable)
   127  	return ok
   128  }
   129  
   130  // LoadCommittedVersionsOfPubAndHashedKeys implements corresponding function in interface DB
   131  func (s *CommonStorageDB) LoadCommittedVersionsOfPubAndHashedKeys(pubKeys []*statedb.CompositeKey,
   132  	hashedKeys []*HashedCompositeKey) error {
   133  
   134  	bulkOptimizable, ok := s.VersionedDB.(statedb.BulkOptimizable)
   135  	if !ok {
   136  		return nil
   137  	}
   138  	// Here, hashedKeys are merged into pubKeys to get a combined set of keys for combined loading
   139  	for _, key := range hashedKeys {
   140  		ns := deriveHashedDataNs(key.Namespace, key.CollectionName)
   141  		// No need to check for duplicates as hashedKeys are in separate namespace
   142  		var keyHashStr string
   143  		if !s.BytesKeySupported() {
   144  			keyHashStr = base64.StdEncoding.EncodeToString([]byte(key.KeyHash))
   145  		} else {
   146  			keyHashStr = key.KeyHash
   147  		}
   148  		pubKeys = append(pubKeys, &statedb.CompositeKey{
   149  			Namespace: ns,
   150  			Key:       keyHashStr,
   151  		})
   152  	}
   153  
   154  	err := bulkOptimizable.LoadCommittedVersions(pubKeys)
   155  	if err != nil {
   156  		return err
   157  	}
   158  
   159  	return nil
   160  }
   161  
   162  // ClearCachedVersions implements corresponding function in interface DB
   163  func (s *CommonStorageDB) ClearCachedVersions() {
   164  	bulkOptimizable, ok := s.VersionedDB.(statedb.BulkOptimizable)
   165  	if ok {
   166  		bulkOptimizable.ClearCachedVersions()
   167  	}
   168  }
   169  
   170  // GetChaincodeEventListener implements corresponding function in interface DB
   171  func (s *CommonStorageDB) GetChaincodeEventListener() cceventmgmt.ChaincodeLifecycleEventListener {
   172  	_, ok := s.VersionedDB.(statedb.IndexCapable)
   173  	if ok {
   174  		return s
   175  	}
   176  	return nil
   177  }
   178  
   179  // GetPrivateData implements corresponding function in interface DB
   180  func (s *CommonStorageDB) GetPrivateData(namespace, collection, key string) (*statedb.VersionedValue, error) {
   181  	return s.GetState(derivePvtDataNs(namespace, collection), key)
   182  }
   183  
   184  // GetPrivateDataHash implements corresponding function in interface DB
   185  func (s *CommonStorageDB) GetPrivateDataHash(namespace, collection, key string) (*statedb.VersionedValue, error) {
   186  	return s.GetValueHash(namespace, collection, util.ComputeStringHash(key))
   187  }
   188  
   189  // GetValueHash implements corresponding function in interface DB
   190  func (s *CommonStorageDB) GetValueHash(namespace, collection string, keyHash []byte) (*statedb.VersionedValue, error) {
   191  	keyHashStr := string(keyHash)
   192  	if !s.BytesKeySupported() {
   193  		keyHashStr = base64.StdEncoding.EncodeToString(keyHash)
   194  	}
   195  	return s.GetState(deriveHashedDataNs(namespace, collection), keyHashStr)
   196  }
   197  
   198  // GetKeyHashVersion implements corresponding function in interface DB
   199  func (s *CommonStorageDB) GetKeyHashVersion(namespace, collection string, keyHash []byte) (*version.Height, error) {
   200  	keyHashStr := string(keyHash)
   201  	if !s.BytesKeySupported() {
   202  		keyHashStr = base64.StdEncoding.EncodeToString(keyHash)
   203  	}
   204  	return s.GetVersion(deriveHashedDataNs(namespace, collection), keyHashStr)
   205  }
   206  
   207  // GetCachedKeyHashVersion retrieves the keyhash version from cache
   208  func (s *CommonStorageDB) GetCachedKeyHashVersion(namespace, collection string, keyHash []byte) (*version.Height, bool) {
   209  	bulkOptimizable, ok := s.VersionedDB.(statedb.BulkOptimizable)
   210  	if !ok {
   211  		return nil, false
   212  	}
   213  
   214  	keyHashStr := string(keyHash)
   215  	if !s.BytesKeySupported() {
   216  		keyHashStr = base64.StdEncoding.EncodeToString(keyHash)
   217  	}
   218  	return bulkOptimizable.GetCachedVersion(deriveHashedDataNs(namespace, collection), keyHashStr)
   219  }
   220  
   221  // GetPrivateDataMultipleKeys implements corresponding function in interface DB
   222  func (s *CommonStorageDB) GetPrivateDataMultipleKeys(namespace, collection string, keys []string) ([]*statedb.VersionedValue, error) {
   223  	return s.GetStateMultipleKeys(derivePvtDataNs(namespace, collection), keys)
   224  }
   225  
   226  // GetPrivateDataRangeScanIterator implements corresponding function in interface DB
   227  func (s *CommonStorageDB) GetPrivateDataRangeScanIterator(namespace, collection, startKey, endKey string) (statedb.ResultsIterator, error) {
   228  	return s.GetStateRangeScanIterator(derivePvtDataNs(namespace, collection), startKey, endKey)
   229  }
   230  
   231  // ExecuteQueryOnPrivateData implements corresponding function in interface DB
   232  func (s CommonStorageDB) ExecuteQueryOnPrivateData(namespace, collection, query string) (statedb.ResultsIterator, error) {
   233  	return s.ExecuteQuery(derivePvtDataNs(namespace, collection), query)
   234  }
   235  
   236  // ApplyUpdates overrides the function in statedb.VersionedDB and throws appropriate error message
   237  // Otherwise, somewhere in the code, usage of this function could lead to updating only public data.
   238  func (s *CommonStorageDB) ApplyUpdates(batch *statedb.UpdateBatch, height *version.Height) error {
   239  	return errors.New("this function should not be invoked on this type. Please invoke function ApplyPrivacyAwareUpdates")
   240  }
   241  
   242  // ApplyPrivacyAwareUpdates implements corresponding function in interface DB
   243  func (s *CommonStorageDB) ApplyPrivacyAwareUpdates(updates *UpdateBatch, height *version.Height) error {
   244  	// combinedUpdates includes both updates to public db and private db, which are partitioned by a separate namespace
   245  	combinedUpdates := updates.PubUpdates
   246  	addPvtUpdates(combinedUpdates, updates.PvtUpdates)
   247  	addHashedUpdates(combinedUpdates, updates.HashUpdates, !s.BytesKeySupported())
   248  	s.metadataHint.setMetadataUsedFlag(updates)
   249  	return s.VersionedDB.ApplyUpdates(combinedUpdates.UpdateBatch, height)
   250  }
   251  
   252  // GetStateMetadata implements corresponding function in interface DB. This implementation provides
   253  // an optimization such that it keeps track if a namespaces has never stored metadata for any of
   254  // its items, the value 'nil' is returned without going to the db. This is intended to be invoked
   255  // in the validation and commit path. This saves the chaincodes from paying unnecessary performance
   256  // penalty if they do not use features that leverage metadata (such as key-level endorsement),
   257  func (s *CommonStorageDB) GetStateMetadata(namespace, key string) ([]byte, error) {
   258  	if !s.metadataHint.metadataEverUsedFor(namespace) {
   259  		return nil, nil
   260  	}
   261  	vv, err := s.GetState(namespace, key)
   262  	if err != nil || vv == nil {
   263  		return nil, err
   264  	}
   265  	return vv.Metadata, nil
   266  }
   267  
   268  // GetPrivateDataMetadataByHash implements corresponding function in interface DB. For additional details, see
   269  // description of the similar function 'GetStateMetadata'
   270  func (s *CommonStorageDB) GetPrivateDataMetadataByHash(namespace, collection string, keyHash []byte) ([]byte, error) {
   271  	if !s.metadataHint.metadataEverUsedFor(namespace) {
   272  		return nil, nil
   273  	}
   274  	vv, err := s.GetValueHash(namespace, collection, keyHash)
   275  	if err != nil || vv == nil {
   276  		return nil, err
   277  	}
   278  	return vv.Metadata, nil
   279  }
   280  
   281  // HandleChaincodeDeploy initializes database artifacts for the database associated with the namespace
   282  // This function deliberately suppresses the errors that occur during the creation of the indexes on couchdb.
   283  // This is because, in the present code, we do not differentiate between the errors because of couchdb interaction
   284  // and the errors because of bad index files - the later being unfixable by the admin. Note that the error suppression
   285  // is acceptable since peer can continue in the committing role without the indexes. However, executing chaincode queries
   286  // may be affected, until a new chaincode with fixed indexes is installed and instantiated
   287  func (s *CommonStorageDB) HandleChaincodeDeploy(chaincodeDefinition *cceventmgmt.ChaincodeDefinition, dbArtifactsTar []byte) error {
   288  	//Check to see if the interface for IndexCapable is implemented
   289  	indexCapable, ok := s.VersionedDB.(statedb.IndexCapable)
   290  	if !ok {
   291  		return nil
   292  	}
   293  	if chaincodeDefinition == nil {
   294  		return errors.New("chaincode definition not found while creating couchdb index")
   295  	}
   296  	dbArtifacts, err := ccprovider.ExtractFileEntries(dbArtifactsTar, indexCapable.GetDBType())
   297  	if err != nil {
   298  		logger.Errorf("Index creation: error extracting db artifacts from tar for chaincode [%s]: %s", chaincodeDefinition.Name, err)
   299  		return nil
   300  	}
   301  
   302  	collectionConfigMap, err := extractCollectionNames(chaincodeDefinition)
   303  	if err != nil {
   304  		logger.Errorf("Error while retrieving collection config for chaincode=[%s]: %s",
   305  			chaincodeDefinition.Name, err)
   306  		return nil
   307  	}
   308  
   309  	for directoryPath, archiveDirectoryEntries := range dbArtifacts {
   310  		// split the directory name
   311  		directoryPathArray := strings.Split(directoryPath, "/")
   312  		// process the indexes for the chain
   313  		if directoryPathArray[3] == "indexes" {
   314  			err := indexCapable.ProcessIndexesForChaincodeDeploy(chaincodeDefinition.Name, archiveDirectoryEntries)
   315  			if err != nil {
   316  				logger.Errorf("Error processing index for chaincode [%s]: %s", chaincodeDefinition.Name, err)
   317  			}
   318  			continue
   319  		}
   320  		// check for the indexes directory for the collection
   321  		if directoryPathArray[3] == "collections" && directoryPathArray[5] == "indexes" {
   322  			collectionName := directoryPathArray[4]
   323  			_, ok := collectionConfigMap[collectionName]
   324  			if !ok {
   325  				logger.Errorf("Error processing index for chaincode [%s]: cannot create an index for an undefined collection=[%s]", chaincodeDefinition.Name, collectionName)
   326  			} else {
   327  				err := indexCapable.ProcessIndexesForChaincodeDeploy(derivePvtDataNs(chaincodeDefinition.Name, collectionName),
   328  					archiveDirectoryEntries)
   329  				if err != nil {
   330  					logger.Errorf("Error processing collection index for chaincode [%s]: %s", chaincodeDefinition.Name, err)
   331  				}
   332  			}
   333  		}
   334  	}
   335  	return nil
   336  }
   337  
   338  // ChaincodeDeployDone is a noop for couchdb state impl
   339  func (s *CommonStorageDB) ChaincodeDeployDone(succeeded bool) {
   340  	// NOOP
   341  }
   342  
   343  func derivePvtDataNs(namespace, collection string) string {
   344  	return namespace + nsJoiner + pvtDataPrefix + collection
   345  }
   346  
   347  func deriveHashedDataNs(namespace, collection string) string {
   348  	return namespace + nsJoiner + hashDataPrefix + collection
   349  }
   350  
   351  func addPvtUpdates(pubUpdateBatch *PubUpdateBatch, pvtUpdateBatch *PvtUpdateBatch) {
   352  	for ns, nsBatch := range pvtUpdateBatch.UpdateMap {
   353  		for _, coll := range nsBatch.GetCollectionNames() {
   354  			for key, vv := range nsBatch.GetUpdates(coll) {
   355  				pubUpdateBatch.Update(derivePvtDataNs(ns, coll), key, vv)
   356  			}
   357  		}
   358  	}
   359  }
   360  
   361  func addHashedUpdates(pubUpdateBatch *PubUpdateBatch, hashedUpdateBatch *HashedUpdateBatch, base64Key bool) {
   362  	for ns, nsBatch := range hashedUpdateBatch.UpdateMap {
   363  		for _, coll := range nsBatch.GetCollectionNames() {
   364  			for key, vv := range nsBatch.GetUpdates(coll) {
   365  				if base64Key {
   366  					key = base64.StdEncoding.EncodeToString([]byte(key))
   367  				}
   368  				pubUpdateBatch.Update(deriveHashedDataNs(ns, coll), key, vv)
   369  			}
   370  		}
   371  	}
   372  }
   373  
   374  func extractCollectionNames(chaincodeDefinition *cceventmgmt.ChaincodeDefinition) (map[string]bool, error) {
   375  	collectionConfigs := chaincodeDefinition.CollectionConfigs
   376  	collectionConfigsMap := make(map[string]bool)
   377  	if collectionConfigs != nil {
   378  		for _, config := range collectionConfigs.Config {
   379  			sConfig := config.GetStaticCollectionConfig()
   380  			if sConfig == nil {
   381  				continue
   382  			}
   383  			collectionConfigsMap[sConfig.Name] = true
   384  		}
   385  	}
   386  	return collectionConfigsMap, nil
   387  }