github.com/kaituanwang/hyperledger@v2.0.1+incompatible/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/helper.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  SPDX-License-Identifier: Apache-2.0
     4  */
     5  
     6  package lockbasedtxmgr
     7  
     8  import (
     9  	"fmt"
    10  
    11  	"github.com/hyperledger/fabric-protos-go/ledger/queryresult"
    12  	"github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset"
    13  	commonledger "github.com/hyperledger/fabric/common/ledger"
    14  	ledger "github.com/hyperledger/fabric/core/ledger"
    15  	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/rwsetutil"
    16  	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb"
    17  	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/storageutil"
    18  	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/txmgr"
    19  	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/version"
    20  	"github.com/hyperledger/fabric/core/ledger/util"
    21  	"github.com/pkg/errors"
    22  )
    23  
    24  const (
    25  	queryReadsHashingEnabled   = true
    26  	maxDegreeQueryReadsHashing = uint32(50)
    27  )
    28  
    29  type queryHelper struct {
    30  	txmgr             *LockBasedTxMgr
    31  	collNameValidator *collNameValidator
    32  	rwsetBuilder      *rwsetutil.RWSetBuilder
    33  	itrs              []*resultsItr
    34  	err               error
    35  	doneInvoked       bool
    36  	hasher            ledger.Hasher
    37  }
    38  
    39  func newQueryHelper(txmgr *LockBasedTxMgr, rwsetBuilder *rwsetutil.RWSetBuilder, performCollCheck bool, hasher ledger.Hasher) *queryHelper {
    40  	helper := &queryHelper{
    41  		txmgr:        txmgr,
    42  		rwsetBuilder: rwsetBuilder,
    43  		hasher:       hasher,
    44  	}
    45  	validator := newCollNameValidator(txmgr.ledgerid, txmgr.ccInfoProvider, &lockBasedQueryExecutor{helper: helper}, !performCollCheck)
    46  	helper.collNameValidator = validator
    47  	return helper
    48  }
    49  
    50  func (h *queryHelper) getState(ns string, key string) ([]byte, []byte, error) {
    51  	if err := h.checkDone(); err != nil {
    52  		return nil, nil, err
    53  	}
    54  	versionedValue, err := h.txmgr.db.GetState(ns, key)
    55  	if err != nil {
    56  		return nil, nil, err
    57  	}
    58  	val, metadata, ver := decomposeVersionedValue(versionedValue)
    59  	if h.rwsetBuilder != nil {
    60  		h.rwsetBuilder.AddToReadSet(ns, key, ver)
    61  	}
    62  	return val, metadata, nil
    63  }
    64  
    65  func (h *queryHelper) getStateMultipleKeys(namespace string, keys []string) ([][]byte, error) {
    66  	if err := h.checkDone(); err != nil {
    67  		return nil, err
    68  	}
    69  	versionedValues, err := h.txmgr.db.GetStateMultipleKeys(namespace, keys)
    70  	if err != nil {
    71  		return nil, nil
    72  	}
    73  	values := make([][]byte, len(versionedValues))
    74  	for i, versionedValue := range versionedValues {
    75  		val, _, ver := decomposeVersionedValue(versionedValue)
    76  		if h.rwsetBuilder != nil {
    77  			h.rwsetBuilder.AddToReadSet(namespace, keys[i], ver)
    78  		}
    79  		values[i] = val
    80  	}
    81  	return values, nil
    82  }
    83  
    84  func (h *queryHelper) getStateRangeScanIterator(namespace string, startKey string, endKey string) (ledger.QueryResultsIterator, error) {
    85  	if err := h.checkDone(); err != nil {
    86  		return nil, err
    87  	}
    88  	itr, err := newResultsItr(
    89  		namespace,
    90  		startKey,
    91  		endKey,
    92  		nil,
    93  		h.txmgr.db,
    94  		h.rwsetBuilder,
    95  		queryReadsHashingEnabled,
    96  		maxDegreeQueryReadsHashing,
    97  		h.hasher,
    98  	)
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  	h.itrs = append(h.itrs, itr)
   103  	return itr, nil
   104  }
   105  
   106  func (h *queryHelper) getStateRangeScanIteratorWithMetadata(namespace string, startKey string, endKey string, metadata map[string]interface{}) (ledger.QueryResultsIterator, error) {
   107  	if err := h.checkDone(); err != nil {
   108  		return nil, err
   109  	}
   110  	itr, err := newResultsItr(
   111  		namespace,
   112  		startKey,
   113  		endKey,
   114  		metadata,
   115  		h.txmgr.db,
   116  		h.rwsetBuilder,
   117  		queryReadsHashingEnabled,
   118  		maxDegreeQueryReadsHashing,
   119  		h.hasher,
   120  	)
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  	h.itrs = append(h.itrs, itr)
   125  	return itr, nil
   126  }
   127  
   128  func (h *queryHelper) executeQuery(namespace, query string) (commonledger.ResultsIterator, error) {
   129  	if err := h.checkDone(); err != nil {
   130  		return nil, err
   131  	}
   132  	dbItr, err := h.txmgr.db.ExecuteQuery(namespace, query)
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  	return &queryResultsItr{DBItr: dbItr, RWSetBuilder: h.rwsetBuilder}, nil
   137  }
   138  
   139  func (h *queryHelper) executeQueryWithMetadata(namespace, query string, metadata map[string]interface{}) (ledger.QueryResultsIterator, error) {
   140  	if err := h.checkDone(); err != nil {
   141  		return nil, err
   142  	}
   143  	dbItr, err := h.txmgr.db.ExecuteQueryWithMetadata(namespace, query, metadata)
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  	return &queryResultsItr{DBItr: dbItr, RWSetBuilder: h.rwsetBuilder}, nil
   148  }
   149  
   150  func (h *queryHelper) getPrivateData(ns, coll, key string) ([]byte, error) {
   151  	if err := h.validateCollName(ns, coll); err != nil {
   152  		return nil, err
   153  	}
   154  	if err := h.checkDone(); err != nil {
   155  		return nil, err
   156  	}
   157  
   158  	var err error
   159  	var hashVersion *version.Height
   160  	var versionedValue *statedb.VersionedValue
   161  
   162  	if versionedValue, err = h.txmgr.db.GetPrivateData(ns, coll, key); err != nil {
   163  		return nil, err
   164  	}
   165  
   166  	// metadata is always nil for private data - because, the metadata is part of the hashed key (instead of raw key)
   167  	val, _, ver := decomposeVersionedValue(versionedValue)
   168  
   169  	keyHash := util.ComputeStringHash(key)
   170  	if hashVersion, err = h.txmgr.db.GetKeyHashVersion(ns, coll, keyHash); err != nil {
   171  		return nil, err
   172  	}
   173  	if !version.AreSame(hashVersion, ver) {
   174  		return nil, &txmgr.ErrPvtdataNotAvailable{Msg: fmt.Sprintf(
   175  			"private data matching public hash version is not available. Public hash version = %s, Private data version = %s",
   176  			hashVersion, ver)}
   177  	}
   178  	if h.rwsetBuilder != nil {
   179  		h.rwsetBuilder.AddToHashedReadSet(ns, coll, key, ver)
   180  	}
   181  	return val, nil
   182  }
   183  
   184  func (h *queryHelper) getPrivateDataValueHash(ns, coll, key string) (valueHash, metadataBytes []byte, err error) {
   185  	if err := h.validateCollName(ns, coll); err != nil {
   186  		return nil, nil, err
   187  	}
   188  	if err := h.checkDone(); err != nil {
   189  		return nil, nil, err
   190  	}
   191  	var versionedValue *statedb.VersionedValue
   192  	if versionedValue, err = h.txmgr.db.GetPrivateDataHash(ns, coll, key); err != nil {
   193  		return nil, nil, err
   194  	}
   195  	valHash, metadata, ver := decomposeVersionedValue(versionedValue)
   196  	if h.rwsetBuilder != nil {
   197  		h.rwsetBuilder.AddToHashedReadSet(ns, coll, key, ver)
   198  	}
   199  	return valHash, metadata, nil
   200  }
   201  
   202  func (h *queryHelper) getPrivateDataMultipleKeys(ns, coll string, keys []string) ([][]byte, error) {
   203  	if err := h.validateCollName(ns, coll); err != nil {
   204  		return nil, err
   205  	}
   206  	if err := h.checkDone(); err != nil {
   207  		return nil, err
   208  	}
   209  	versionedValues, err := h.txmgr.db.GetPrivateDataMultipleKeys(ns, coll, keys)
   210  	if err != nil {
   211  		return nil, nil
   212  	}
   213  	values := make([][]byte, len(versionedValues))
   214  	for i, versionedValue := range versionedValues {
   215  		val, _, ver := decomposeVersionedValue(versionedValue)
   216  		if h.rwsetBuilder != nil {
   217  			h.rwsetBuilder.AddToHashedReadSet(ns, coll, keys[i], ver)
   218  		}
   219  		values[i] = val
   220  	}
   221  	return values, nil
   222  }
   223  
   224  func (h *queryHelper) getPrivateDataRangeScanIterator(namespace, collection, startKey, endKey string) (commonledger.ResultsIterator, error) {
   225  	if err := h.validateCollName(namespace, collection); err != nil {
   226  		return nil, err
   227  	}
   228  	if err := h.checkDone(); err != nil {
   229  		return nil, err
   230  	}
   231  	dbItr, err := h.txmgr.db.GetPrivateDataRangeScanIterator(namespace, collection, startKey, endKey)
   232  	if err != nil {
   233  		return nil, err
   234  	}
   235  	return &pvtdataResultsItr{namespace, collection, dbItr}, nil
   236  }
   237  
   238  func (h *queryHelper) executeQueryOnPrivateData(namespace, collection, query string) (commonledger.ResultsIterator, error) {
   239  	if err := h.validateCollName(namespace, collection); err != nil {
   240  		return nil, err
   241  	}
   242  	if err := h.checkDone(); err != nil {
   243  		return nil, err
   244  	}
   245  	dbItr, err := h.txmgr.db.ExecuteQueryOnPrivateData(namespace, collection, query)
   246  	if err != nil {
   247  		return nil, err
   248  	}
   249  	return &pvtdataResultsItr{namespace, collection, dbItr}, nil
   250  }
   251  
   252  func (h *queryHelper) getStateMetadata(ns string, key string) (map[string][]byte, error) {
   253  	if err := h.checkDone(); err != nil {
   254  		return nil, err
   255  	}
   256  	var metadataBytes []byte
   257  	var err error
   258  	if h.rwsetBuilder == nil {
   259  		// reads versions are not getting recorded, retrieve metadata value via optimized path
   260  		if metadataBytes, err = h.txmgr.db.GetStateMetadata(ns, key); err != nil {
   261  			return nil, err
   262  		}
   263  	} else {
   264  		if _, metadataBytes, err = h.getState(ns, key); err != nil {
   265  			return nil, err
   266  		}
   267  	}
   268  	return storageutil.DeserializeMetadata(metadataBytes)
   269  }
   270  
   271  func (h *queryHelper) getPrivateDataMetadata(ns, coll, key string) (map[string][]byte, error) {
   272  	if h.rwsetBuilder == nil {
   273  		// reads versions are not getting recorded, retrieve metadata value via optimized path
   274  		return h.getPrivateDataMetadataByHash(ns, coll, util.ComputeStringHash(key))
   275  	}
   276  	if err := h.validateCollName(ns, coll); err != nil {
   277  		return nil, err
   278  	}
   279  	if err := h.checkDone(); err != nil {
   280  		return nil, err
   281  	}
   282  	_, metadataBytes, err := h.getPrivateDataValueHash(ns, coll, key)
   283  	if err != nil {
   284  		return nil, err
   285  	}
   286  	return storageutil.DeserializeMetadata(metadataBytes)
   287  }
   288  
   289  func (h *queryHelper) getPrivateDataMetadataByHash(ns, coll string, keyhash []byte) (map[string][]byte, error) {
   290  	if err := h.validateCollName(ns, coll); err != nil {
   291  		return nil, err
   292  	}
   293  	if err := h.checkDone(); err != nil {
   294  		return nil, err
   295  	}
   296  	if h.rwsetBuilder != nil {
   297  		// this requires to improve rwset builder to accept a keyhash
   298  		return nil, errors.New("retrieving private data metadata by keyhash is not supported in simulation. This function is only available for query as yet")
   299  	}
   300  	metadataBytes, err := h.txmgr.db.GetPrivateDataMetadataByHash(ns, coll, keyhash)
   301  	if err != nil {
   302  		return nil, err
   303  	}
   304  	return storageutil.DeserializeMetadata(metadataBytes)
   305  }
   306  
   307  func (h *queryHelper) done() {
   308  	if h.doneInvoked {
   309  		return
   310  	}
   311  
   312  	defer func() {
   313  		h.txmgr.commitRWLock.RUnlock()
   314  		h.doneInvoked = true
   315  		for _, itr := range h.itrs {
   316  			itr.Close()
   317  		}
   318  	}()
   319  }
   320  
   321  func (h *queryHelper) addRangeQueryInfo() {
   322  	for _, itr := range h.itrs {
   323  		if h.rwsetBuilder != nil {
   324  			results, hash, err := itr.rangeQueryResultsHelper.Done()
   325  			if err != nil {
   326  				h.err = err
   327  				return
   328  			}
   329  			if results != nil {
   330  				rwsetutil.SetRawReads(itr.rangeQueryInfo, results)
   331  			}
   332  			if hash != nil {
   333  				rwsetutil.SetMerkelSummary(itr.rangeQueryInfo, hash)
   334  			}
   335  			h.rwsetBuilder.AddToRangeQuerySet(itr.ns, itr.rangeQueryInfo)
   336  		}
   337  	}
   338  }
   339  
   340  func (h *queryHelper) checkDone() error {
   341  	if h.doneInvoked {
   342  		return errors.New("this instance should not be used after calling Done()")
   343  	}
   344  	return nil
   345  }
   346  
   347  func (h *queryHelper) validateCollName(ns, coll string) error {
   348  	return h.collNameValidator.validateCollName(ns, coll)
   349  }
   350  
   351  // resultsItr implements interface ledger.ResultsIterator
   352  // this wraps the actual db iterator and intercept the calls
   353  // to build rangeQueryInfo in the ReadWriteSet that is used
   354  // for performing phantom read validation during commit
   355  type resultsItr struct {
   356  	ns                      string
   357  	endKey                  string
   358  	dbItr                   statedb.ResultsIterator
   359  	rwSetBuilder            *rwsetutil.RWSetBuilder
   360  	rangeQueryInfo          *kvrwset.RangeQueryInfo
   361  	rangeQueryResultsHelper *rwsetutil.RangeQueryResultsHelper
   362  }
   363  
   364  func newResultsItr(ns string, startKey string, endKey string, metadata map[string]interface{},
   365  	db statedb.VersionedDB, rwsetBuilder *rwsetutil.RWSetBuilder, enableHashing bool, maxDegree uint32, hasher ledger.Hasher) (*resultsItr, error) {
   366  	var err error
   367  	var dbItr statedb.ResultsIterator
   368  	if metadata == nil {
   369  		dbItr, err = db.GetStateRangeScanIterator(ns, startKey, endKey)
   370  	} else {
   371  		dbItr, err = db.GetStateRangeScanIteratorWithMetadata(ns, startKey, endKey, metadata)
   372  	}
   373  	if err != nil {
   374  		return nil, err
   375  	}
   376  	itr := &resultsItr{ns: ns, dbItr: dbItr}
   377  	// it's a simulation request so, enable capture of range query info
   378  	if rwsetBuilder != nil {
   379  		itr.rwSetBuilder = rwsetBuilder
   380  		itr.endKey = endKey
   381  		// just set the StartKey... set the EndKey later below in the Next() method.
   382  		itr.rangeQueryInfo = &kvrwset.RangeQueryInfo{StartKey: startKey}
   383  		resultsHelper, err := rwsetutil.NewRangeQueryResultsHelper(enableHashing, maxDegree, hasher)
   384  		if err != nil {
   385  			return nil, err
   386  		}
   387  		itr.rangeQueryResultsHelper = resultsHelper
   388  	}
   389  	return itr, nil
   390  }
   391  
   392  // Next implements method in interface ledger.ResultsIterator
   393  // Before returning the next result, update the EndKey and ItrExhausted in rangeQueryInfo
   394  // If we set the EndKey in the constructor (as we do for the StartKey) to what is
   395  // supplied in the original query, we may be capturing the unnecessary longer range if the
   396  // caller decides to stop iterating at some intermediate point. Alternatively, we could have
   397  // set the EndKey and ItrExhausted in the Close() function but it may not be desirable to change
   398  // transactional behaviour based on whether the Close() was invoked or not
   399  func (itr *resultsItr) Next() (commonledger.QueryResult, error) {
   400  	queryResult, err := itr.dbItr.Next()
   401  	if err != nil {
   402  		return nil, err
   403  	}
   404  	itr.updateRangeQueryInfo(queryResult)
   405  	if queryResult == nil {
   406  		return nil, nil
   407  	}
   408  	versionedKV := queryResult.(*statedb.VersionedKV)
   409  	return &queryresult.KV{Namespace: versionedKV.Namespace, Key: versionedKV.Key, Value: versionedKV.Value}, nil
   410  }
   411  
   412  // GetBookmarkAndClose implements method in interface ledger.ResultsIterator
   413  func (itr *resultsItr) GetBookmarkAndClose() string {
   414  	returnBookmark := ""
   415  	if queryResultIterator, ok := itr.dbItr.(statedb.QueryResultsIterator); ok {
   416  		returnBookmark = queryResultIterator.GetBookmarkAndClose()
   417  	}
   418  	return returnBookmark
   419  }
   420  
   421  // updateRangeQueryInfo updates two attributes of the rangeQueryInfo
   422  // 1) The EndKey - set to either a) latest key that is to be returned to the caller (if the iterator is not exhausted)
   423  //                                  because, we do not know if the caller is again going to invoke Next() or not.
   424  //                            or b) the last key that was supplied in the original query (if the iterator is exhausted)
   425  // 2) The ItrExhausted - set to true if the iterator is going to return nil as a result of the Next() call
   426  func (itr *resultsItr) updateRangeQueryInfo(queryResult statedb.QueryResult) {
   427  	if itr.rwSetBuilder == nil {
   428  		return
   429  	}
   430  
   431  	if queryResult == nil {
   432  		// caller scanned till the iterator got exhausted.
   433  		// So, set the endKey to the actual endKey supplied in the query
   434  		itr.rangeQueryInfo.ItrExhausted = true
   435  		itr.rangeQueryInfo.EndKey = itr.endKey
   436  		return
   437  	}
   438  	versionedKV := queryResult.(*statedb.VersionedKV)
   439  	itr.rangeQueryResultsHelper.AddResult(rwsetutil.NewKVRead(versionedKV.Key, versionedKV.Version))
   440  	// Set the end key to the latest key retrieved by the caller.
   441  	// Because, the caller may actually not invoke the Next() function again
   442  	itr.rangeQueryInfo.EndKey = versionedKV.Key
   443  }
   444  
   445  // Close implements method in interface ledger.ResultsIterator
   446  func (itr *resultsItr) Close() {
   447  	itr.dbItr.Close()
   448  }
   449  
   450  type queryResultsItr struct {
   451  	DBItr        statedb.ResultsIterator
   452  	RWSetBuilder *rwsetutil.RWSetBuilder
   453  }
   454  
   455  // Next implements method in interface ledger.ResultsIterator
   456  func (itr *queryResultsItr) Next() (commonledger.QueryResult, error) {
   457  
   458  	queryResult, err := itr.DBItr.Next()
   459  	if err != nil {
   460  		return nil, err
   461  	}
   462  	if queryResult == nil {
   463  		return nil, nil
   464  	}
   465  	versionedQueryRecord := queryResult.(*statedb.VersionedKV)
   466  	logger.Debugf("queryResultsItr.Next() returned a record:%s", string(versionedQueryRecord.Value))
   467  
   468  	if itr.RWSetBuilder != nil {
   469  		itr.RWSetBuilder.AddToReadSet(versionedQueryRecord.Namespace, versionedQueryRecord.Key, versionedQueryRecord.Version)
   470  	}
   471  	return &queryresult.KV{Namespace: versionedQueryRecord.Namespace, Key: versionedQueryRecord.Key, Value: versionedQueryRecord.Value}, nil
   472  }
   473  
   474  // Close implements method in interface ledger.ResultsIterator
   475  func (itr *queryResultsItr) Close() {
   476  	itr.DBItr.Close()
   477  }
   478  
   479  func (itr *queryResultsItr) GetBookmarkAndClose() string {
   480  	returnBookmark := ""
   481  	if queryResultIterator, ok := itr.DBItr.(statedb.QueryResultsIterator); ok {
   482  		returnBookmark = queryResultIterator.GetBookmarkAndClose()
   483  	}
   484  	return returnBookmark
   485  }
   486  
   487  func decomposeVersionedValue(versionedValue *statedb.VersionedValue) ([]byte, []byte, *version.Height) {
   488  	var value []byte
   489  	var metadata []byte
   490  	var ver *version.Height
   491  	if versionedValue != nil {
   492  		value = versionedValue.Value
   493  		ver = versionedValue.Version
   494  		metadata = versionedValue.Metadata
   495  	}
   496  	return value, metadata, ver
   497  }
   498  
   499  // pvtdataResultsItr iterates over results of a query on pvt data
   500  type pvtdataResultsItr struct {
   501  	ns    string
   502  	coll  string
   503  	dbItr statedb.ResultsIterator
   504  }
   505  
   506  // Next implements method in interface ledger.ResultsIterator
   507  func (itr *pvtdataResultsItr) Next() (commonledger.QueryResult, error) {
   508  	queryResult, err := itr.dbItr.Next()
   509  	if err != nil {
   510  		return nil, err
   511  	}
   512  	if queryResult == nil {
   513  		return nil, nil
   514  	}
   515  	versionedQueryRecord := queryResult.(*statedb.VersionedKV)
   516  	return &queryresult.KV{
   517  		Namespace: itr.ns,
   518  		Key:       versionedQueryRecord.Key,
   519  		Value:     versionedQueryRecord.Value,
   520  	}, nil
   521  }
   522  
   523  // Close implements method in interface ledger.ResultsIterator
   524  func (itr *pvtdataResultsItr) Close() {
   525  	itr.dbItr.Close()
   526  }