github.com/yimialmonte/fabric@v2.1.1+incompatible/gossip/privdata/dataretriever.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package privdata
     8  
     9  import (
    10  	protosgossip "github.com/hyperledger/fabric-protos-go/gossip"
    11  	"github.com/hyperledger/fabric-protos-go/ledger/rwset"
    12  	"github.com/hyperledger/fabric/core/committer"
    13  	"github.com/hyperledger/fabric/core/ledger"
    14  	"github.com/hyperledger/fabric/core/transientstore"
    15  	"github.com/hyperledger/fabric/gossip/privdata/common"
    16  	"github.com/hyperledger/fabric/gossip/util"
    17  	"github.com/pkg/errors"
    18  )
    19  
    20  //go:generate mockery -dir . -name RWSetScanner -case underscore -output mocks/
    21  
    22  // RWSetScanner is the local interface used to generate mocks for foreign interface.
    23  type RWSetScanner interface {
    24  	transientstore.RWSetScanner
    25  }
    26  
    27  // StorageDataRetriever defines an API to retrieve private date from the storage.
    28  type StorageDataRetriever interface {
    29  	// CollectionRWSet retrieves for give digest relevant private data if
    30  	// available otherwise returns nil, bool which is true if data fetched from ledger and false if was fetched from transient store, and an error
    31  	CollectionRWSet(dig []*protosgossip.PvtDataDigest, blockNum uint64) (Dig2PvtRWSetWithConfig, bool, error)
    32  }
    33  
    34  type dataRetriever struct {
    35  	store     *transientstore.Store
    36  	committer committer.Committer
    37  }
    38  
    39  // NewDataRetriever constructing function for implementation of the
    40  // StorageDataRetriever interface
    41  func NewDataRetriever(store *transientstore.Store, committer committer.Committer) StorageDataRetriever {
    42  	return &dataRetriever{
    43  		store:     store,
    44  		committer: committer,
    45  	}
    46  }
    47  
    48  // CollectionRWSet retrieves for give digest relevant private data if
    49  // available otherwise returns nil, bool which is true if data fetched from ledger and false if was fetched from transient store, and an error
    50  func (dr *dataRetriever) CollectionRWSet(digests []*protosgossip.PvtDataDigest, blockNum uint64) (Dig2PvtRWSetWithConfig, bool, error) {
    51  	height, err := dr.committer.LedgerHeight()
    52  	if err != nil {
    53  		// if there is an error getting info from the ledger, we need to try to read from transient store
    54  		return nil, false, errors.Wrap(err, "wasn't able to read ledger height")
    55  	}
    56  	if height <= blockNum {
    57  		logger.Debug("Current ledger height ", height, "is below requested block sequence number",
    58  			blockNum, "retrieving private data from transient store")
    59  	}
    60  
    61  	if height <= blockNum { // Check whenever current ledger height is equal or below block sequence num.
    62  		results := make(Dig2PvtRWSetWithConfig)
    63  		for _, dig := range digests {
    64  			filter := map[string]ledger.PvtCollFilter{
    65  				dig.Namespace: map[string]bool{
    66  					dig.Collection: true,
    67  				},
    68  			}
    69  			pvtRWSet, err := dr.fromTransientStore(dig, filter)
    70  			if err != nil {
    71  				logger.Errorf("couldn't read from transient store private read-write set, "+
    72  					"digest %+v, because of %s", dig, err)
    73  				continue
    74  			}
    75  			results[common.DigKey{
    76  				Namespace:  dig.Namespace,
    77  				Collection: dig.Collection,
    78  				TxId:       dig.TxId,
    79  				BlockSeq:   dig.BlockSeq,
    80  				SeqInBlock: dig.SeqInBlock,
    81  			}] = pvtRWSet
    82  		}
    83  
    84  		return results, false, nil
    85  	}
    86  	// Since ledger height is above block sequence number private data is might be available in the ledger
    87  	results, err := dr.fromLedger(digests, blockNum)
    88  	return results, true, err
    89  }
    90  
    91  func (dr *dataRetriever) fromLedger(digests []*protosgossip.PvtDataDigest, blockNum uint64) (Dig2PvtRWSetWithConfig, error) {
    92  	filter := make(map[string]ledger.PvtCollFilter)
    93  	for _, dig := range digests {
    94  		if _, ok := filter[dig.Namespace]; !ok {
    95  			filter[dig.Namespace] = make(ledger.PvtCollFilter)
    96  		}
    97  		filter[dig.Namespace][dig.Collection] = true
    98  	}
    99  
   100  	pvtData, err := dr.committer.GetPvtDataByNum(blockNum, filter)
   101  	if err != nil {
   102  		return nil, errors.Errorf("wasn't able to obtain private data, block sequence number %d, due to %s", blockNum, err)
   103  	}
   104  
   105  	results := make(Dig2PvtRWSetWithConfig)
   106  	for _, dig := range digests {
   107  		dig := dig
   108  		pvtRWSetWithConfig := &util.PrivateRWSetWithConfig{}
   109  		for _, data := range pvtData {
   110  			if data.WriteSet == nil {
   111  				logger.Warning("Received nil write set for collection tx in block", data.SeqInBlock, "block number", blockNum)
   112  				continue
   113  			}
   114  
   115  			// private data doesn't hold rwsets for namespace and collection or
   116  			// belongs to different transaction
   117  			if !data.Has(dig.Namespace, dig.Collection) || data.SeqInBlock != dig.SeqInBlock {
   118  				continue
   119  			}
   120  
   121  			pvtRWSet := dr.extractPvtRWsets(data.WriteSet.NsPvtRwset, dig.Namespace, dig.Collection)
   122  			pvtRWSetWithConfig.RWSet = append(pvtRWSetWithConfig.RWSet, pvtRWSet...)
   123  		}
   124  
   125  		confHistoryRetriever, err := dr.committer.GetConfigHistoryRetriever()
   126  		if err != nil {
   127  			return nil, errors.Errorf("cannot obtain configuration history retriever, for collection <%s>"+
   128  				" txID <%s> block sequence number <%d> due to <%s>", dig.Collection, dig.TxId, dig.BlockSeq, err)
   129  		}
   130  
   131  		configInfo, err := confHistoryRetriever.MostRecentCollectionConfigBelow(dig.BlockSeq, dig.Namespace)
   132  		if err != nil {
   133  			return nil, errors.Errorf("cannot find recent collection config update below block sequence = %d,"+
   134  				" collection name = <%s> for chaincode <%s>", dig.BlockSeq, dig.Collection, dig.Namespace)
   135  		}
   136  
   137  		if configInfo == nil {
   138  			return nil, errors.Errorf("no collection config update below block sequence = <%d>"+
   139  				" collection name = <%s> for chaincode <%s> is available ", dig.BlockSeq, dig.Collection, dig.Namespace)
   140  		}
   141  		configs := extractCollectionConfig(configInfo.CollectionConfig, dig.Collection)
   142  		if configs == nil {
   143  			return nil, errors.Errorf("no collection config was found for collection <%s>"+
   144  				" namespace <%s> txID <%s>", dig.Collection, dig.Namespace, dig.TxId)
   145  		}
   146  		pvtRWSetWithConfig.CollectionConfig = configs
   147  		results[common.DigKey{
   148  			Namespace:  dig.Namespace,
   149  			Collection: dig.Collection,
   150  			TxId:       dig.TxId,
   151  			BlockSeq:   dig.BlockSeq,
   152  			SeqInBlock: dig.SeqInBlock,
   153  		}] = pvtRWSetWithConfig
   154  	}
   155  
   156  	return results, nil
   157  }
   158  
   159  func (dr *dataRetriever) fromTransientStore(dig *protosgossip.PvtDataDigest, filter map[string]ledger.PvtCollFilter) (*util.PrivateRWSetWithConfig, error) {
   160  	results := &util.PrivateRWSetWithConfig{}
   161  	it, err := dr.store.GetTxPvtRWSetByTxid(dig.TxId, filter)
   162  	if err != nil {
   163  		return nil, errors.Errorf("was not able to retrieve private data from transient store, namespace <%s>"+
   164  			", collection name %s, txID <%s>, due to <%s>", dig.Namespace, dig.Collection, dig.TxId, err)
   165  	}
   166  	defer it.Close()
   167  
   168  	maxEndorsedAt := uint64(0)
   169  	for {
   170  		res, err := it.Next()
   171  		if err != nil {
   172  			return nil, errors.Errorf("error getting next element out of private data iterator, namespace <%s>"+
   173  				", collection name <%s>, txID <%s>, due to <%s>", dig.Namespace, dig.Collection, dig.TxId, err)
   174  		}
   175  		if res == nil {
   176  			return results, nil
   177  		}
   178  		rws := res.PvtSimulationResultsWithConfig
   179  		if rws == nil {
   180  			logger.Debug("Skipping nil PvtSimulationResultsWithConfig received at block height", res.ReceivedAtBlockHeight)
   181  			continue
   182  		}
   183  		txPvtRWSet := rws.PvtRwset
   184  		if txPvtRWSet == nil {
   185  			logger.Debug("Skipping empty PvtRwset of PvtSimulationResultsWithConfig received at block height", res.ReceivedAtBlockHeight)
   186  			continue
   187  		}
   188  
   189  		colConfigs, found := rws.CollectionConfigs[dig.Namespace]
   190  		if !found {
   191  			logger.Error("No collection config was found for chaincode", dig.Namespace, "collection name",
   192  				dig.Namespace, "txID", dig.TxId)
   193  			continue
   194  		}
   195  
   196  		configs := extractCollectionConfig(colConfigs, dig.Collection)
   197  		if configs == nil {
   198  			logger.Error("No collection config was found for collection", dig.Collection,
   199  				"namespace", dig.Namespace, "txID", dig.TxId)
   200  			continue
   201  		}
   202  
   203  		pvtRWSet := dr.extractPvtRWsets(txPvtRWSet.NsPvtRwset, dig.Namespace, dig.Collection)
   204  		if rws.EndorsedAt >= maxEndorsedAt {
   205  			maxEndorsedAt = rws.EndorsedAt
   206  			results.CollectionConfig = configs
   207  		}
   208  		results.RWSet = append(results.RWSet, pvtRWSet...)
   209  	}
   210  }
   211  
   212  func (dr *dataRetriever) extractPvtRWsets(pvtRWSets []*rwset.NsPvtReadWriteSet, namespace string, collectionName string) []util.PrivateRWSet {
   213  	pRWsets := []util.PrivateRWSet{}
   214  
   215  	// Iterate over all namespaces
   216  	for _, nsws := range pvtRWSets {
   217  		// and in each namespace - iterate over all collections
   218  		if nsws.Namespace != namespace {
   219  			logger.Debug("Received private data namespace ", nsws.Namespace, " instead of ", namespace, " skipping...")
   220  			continue
   221  		}
   222  		for _, col := range nsws.CollectionPvtRwset {
   223  			// This isn't the collection we're looking for
   224  			if col.CollectionName != collectionName {
   225  				logger.Debug("Received private data collection ", col.CollectionName, " instead of ", collectionName, " skipping...")
   226  				continue
   227  			}
   228  			// Add the collection pRWset to the accumulated set
   229  			pRWsets = append(pRWsets, col.Rwset)
   230  		}
   231  	}
   232  
   233  	return pRWsets
   234  }