github.com/defanghe/fabric@v2.1.1+incompatible/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/helper.go (about)

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