github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/gossip/privdata/dataretriever.go (about)

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