github.com/sykesm/fabric@v1.1.0-preview.0.20200129034918-2aa12b1a0181/gossip/privdata/pvtdataprovider.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  	"bytes"
    11  	"encoding/hex"
    12  	"fmt"
    13  	"time"
    14  
    15  	"github.com/golang/protobuf/proto"
    16  	"github.com/hyperledger/fabric-protos-go/ledger/rwset"
    17  	"github.com/hyperledger/fabric-protos-go/msp"
    18  	"github.com/hyperledger/fabric-protos-go/peer"
    19  	vsccErrors "github.com/hyperledger/fabric/common/errors"
    20  	"github.com/hyperledger/fabric/common/metrics"
    21  	commonutil "github.com/hyperledger/fabric/common/util"
    22  	pvtdatasc "github.com/hyperledger/fabric/core/common/privdata"
    23  	"github.com/hyperledger/fabric/core/ledger"
    24  	"github.com/hyperledger/fabric/core/transientstore"
    25  	pvtdatacommon "github.com/hyperledger/fabric/gossip/privdata/common"
    26  	"github.com/hyperledger/fabric/gossip/util"
    27  	"github.com/hyperledger/fabric/protoutil"
    28  )
    29  
    30  type sleeper struct {
    31  	sleep func(time.Duration)
    32  }
    33  
    34  func (s sleeper) Sleep(d time.Duration) {
    35  	if s.sleep == nil {
    36  		time.Sleep(d)
    37  		return
    38  	}
    39  	s.sleep(d)
    40  }
    41  
    42  type RetrievedPvtdata struct {
    43  	blockPvtdata            *ledger.BlockPvtdata
    44  	pvtdataRetrievalInfo    *pvtdataRetrievalInfo
    45  	transientStore          *transientstore.Store
    46  	logger                  util.Logger
    47  	purgeDurationHistogram  metrics.Histogram
    48  	blockNum                uint64
    49  	transientBlockRetention uint64
    50  }
    51  
    52  // GetBlockPvtdata returns the BlockPvtdata
    53  func (r *RetrievedPvtdata) GetBlockPvtdata() *ledger.BlockPvtdata {
    54  	return r.blockPvtdata
    55  }
    56  
    57  // Purge purges private data for transactions in the block from the transient store.
    58  // Transactions older than the retention period are considered orphaned and also purged.
    59  func (r *RetrievedPvtdata) Purge() {
    60  	purgeStart := time.Now()
    61  
    62  	if len(r.blockPvtdata.PvtData) > 0 {
    63  		// Finally, purge all transactions in block - valid or not valid.
    64  		if err := r.transientStore.PurgeByTxids(r.pvtdataRetrievalInfo.txns); err != nil {
    65  			r.logger.Errorf("Purging transactions %v failed: %s", r.pvtdataRetrievalInfo.txns, err)
    66  		}
    67  	}
    68  
    69  	blockNum := r.blockNum
    70  	if blockNum%r.transientBlockRetention == 0 && blockNum > r.transientBlockRetention {
    71  		err := r.transientStore.PurgeBelowHeight(blockNum - r.transientBlockRetention)
    72  		if err != nil {
    73  			r.logger.Errorf("Failed purging data from transient store at block [%d]: %s", blockNum, err)
    74  		}
    75  	}
    76  
    77  	r.purgeDurationHistogram.Observe(time.Since(purgeStart).Seconds())
    78  }
    79  
    80  type eligibilityComputer struct {
    81  	logger                  util.Logger
    82  	storePvtdataOfInvalidTx bool
    83  	channelID               string
    84  	selfSignedData          protoutil.SignedData
    85  	idDeserializerFactory   IdentityDeserializerFactory
    86  }
    87  
    88  // computeEligibility computes eligilibity of private data and
    89  // groups all private data as either eligibleMissing or ineligibleMissing prior to fetching
    90  func (ec *eligibilityComputer) computeEligibility(pvtdataToRetrieve []*ledger.TxPvtdataInfo) (*pvtdataRetrievalInfo, error) {
    91  	sources := make(map[rwSetKey][]*peer.Endorsement)
    92  	eligibleMissingKeys := make(rwsetKeys)
    93  	ineligibleMissingKeys := make(rwsetKeys)
    94  
    95  	var txList []string
    96  	for _, txPvtdata := range pvtdataToRetrieve {
    97  		txID := txPvtdata.TxID
    98  		seqInBlock := txPvtdata.SeqInBlock
    99  		invalid := txPvtdata.Invalid
   100  		txList = append(txList, txID)
   101  		if invalid && !ec.storePvtdataOfInvalidTx {
   102  			ec.logger.Debugf("Skipping Tx [%s] at sequence [%d] because it's invalid.", txID, seqInBlock)
   103  			continue
   104  		}
   105  		deserializer := ec.idDeserializerFactory.GetIdentityDeserializer(ec.channelID)
   106  		for _, colInfo := range txPvtdata.CollectionPvtdataInfo {
   107  			ns := colInfo.Namespace
   108  			col := colInfo.Collection
   109  			hash := colInfo.ExpectedHash
   110  			endorsers := colInfo.Endorsers
   111  			colConfig := colInfo.CollectionConfig
   112  
   113  			policy, err := pvtdatasc.NewSimpleCollection(colConfig, deserializer)
   114  			if err != nil {
   115  				ec.logger.Errorf("Failed to retrieve collection access policy for chaincode [%s], collection name [%s] for txID [%s]: %s.",
   116  					ns, col, txID, err)
   117  				return nil, &vsccErrors.VSCCExecutionFailureError{Err: err}
   118  			}
   119  
   120  			key := rwSetKey{
   121  				txID:       txID,
   122  				seqInBlock: seqInBlock,
   123  				hash:       hex.EncodeToString(hash),
   124  				namespace:  ns,
   125  				collection: col,
   126  			}
   127  
   128  			if !policy.AccessFilter()(ec.selfSignedData) {
   129  				ec.logger.Debugf("Peer is not eligible for collection: chaincode [%s], "+
   130  					"collection name [%s], txID [%s] the policy is [%#v]. Skipping.",
   131  					ns, col, txID, policy)
   132  				ineligibleMissingKeys[key] = rwsetInfo{}
   133  				continue
   134  			}
   135  
   136  			// treat all eligible keys as missing
   137  			eligibleMissingKeys[key] = rwsetInfo{
   138  				invalid: invalid,
   139  			}
   140  			sources[key] = endorsersFromEligibleOrgs(ns, col, endorsers, policy.MemberOrgs())
   141  		}
   142  	}
   143  
   144  	return &pvtdataRetrievalInfo{
   145  		sources:               sources,
   146  		txns:                  txList,
   147  		eligibleMissingKeys:   eligibleMissingKeys,
   148  		ineligibleMissingKeys: ineligibleMissingKeys,
   149  	}, nil
   150  }
   151  
   152  type PvtdataProvider struct {
   153  	selfSignedData                          protoutil.SignedData
   154  	logger                                  util.Logger
   155  	listMissingPrivateDataDurationHistogram metrics.Histogram
   156  	fetchDurationHistogram                  metrics.Histogram
   157  	purgeDurationHistogram                  metrics.Histogram
   158  	transientStore                          *transientstore.Store
   159  	pullRetryThreshold                      time.Duration
   160  	prefetchedPvtdata                       util.PvtDataCollections
   161  	transientBlockRetention                 uint64
   162  	channelID                               string
   163  	blockNum                                uint64
   164  	storePvtdataOfInvalidTx                 bool
   165  	skipPullingInvalidTransactions          bool
   166  	idDeserializerFactory                   IdentityDeserializerFactory
   167  	fetcher                                 Fetcher
   168  
   169  	sleeper sleeper
   170  }
   171  
   172  // RetrievePvtdata is passed a list of private data items from a block,
   173  // it determines which private data items this peer is eligible for, and then
   174  // retrieves the private data from local cache, local transient store, or a remote peer.
   175  func (pdp *PvtdataProvider) RetrievePvtdata(pvtdataToRetrieve []*ledger.TxPvtdataInfo) (*RetrievedPvtdata, error) {
   176  	retrievedPvtdata := &RetrievedPvtdata{
   177  		transientStore:          pdp.transientStore,
   178  		logger:                  pdp.logger,
   179  		purgeDurationHistogram:  pdp.purgeDurationHistogram,
   180  		blockNum:                pdp.blockNum,
   181  		transientBlockRetention: pdp.transientBlockRetention,
   182  	}
   183  
   184  	listMissingStart := time.Now()
   185  	eligibilityComputer := &eligibilityComputer{
   186  		logger:                  pdp.logger,
   187  		storePvtdataOfInvalidTx: pdp.storePvtdataOfInvalidTx,
   188  		channelID:               pdp.channelID,
   189  		selfSignedData:          pdp.selfSignedData,
   190  		idDeserializerFactory:   pdp.idDeserializerFactory,
   191  	}
   192  
   193  	pvtdataRetrievalInfo, err := eligibilityComputer.computeEligibility(pvtdataToRetrieve)
   194  	if err != nil {
   195  		return nil, err
   196  	}
   197  	pdp.listMissingPrivateDataDurationHistogram.Observe(time.Since(listMissingStart).Seconds())
   198  
   199  	pvtdata := make(rwsetByKeys)
   200  
   201  	// POPULATE FROM CACHE
   202  	pdp.populateFromCache(pvtdata, pvtdataRetrievalInfo, pvtdataToRetrieve)
   203  	if len(pvtdataRetrievalInfo.eligibleMissingKeys) == 0 {
   204  		pdp.logger.Debug("No missing collection private write sets to fetch from transient store")
   205  		retrievedPvtdata.pvtdataRetrievalInfo = pvtdataRetrievalInfo
   206  		retrievedPvtdata.blockPvtdata = pdp.prepareBlockPvtdata(pvtdata, pvtdataRetrievalInfo)
   207  		return retrievedPvtdata, nil
   208  	}
   209  
   210  	// POPULATE FROM TRANSIENT STORE
   211  	pdp.logger.Debugf("Could not find all collection private write sets in cache for block [%d]", pdp.blockNum)
   212  	pdp.logger.Debugf("Fetching %d collection private write sets from transient store", len(pvtdataRetrievalInfo.eligibleMissingKeys))
   213  	pdp.populateFromTransientStore(pvtdata, pvtdataRetrievalInfo)
   214  	if len(pvtdataRetrievalInfo.eligibleMissingKeys) == 0 {
   215  		pdp.logger.Debug("No missing collection private write sets to fetch from remote peers")
   216  		retrievedPvtdata.pvtdataRetrievalInfo = pvtdataRetrievalInfo
   217  		retrievedPvtdata.blockPvtdata = pdp.prepareBlockPvtdata(pvtdata, pvtdataRetrievalInfo)
   218  		return retrievedPvtdata, nil
   219  	}
   220  
   221  	// POPULATE FROM REMOTE PEERS
   222  	retryThresh := pdp.pullRetryThreshold
   223  	pdp.logger.Debugf("Could not find all collection private write sets in local peer transient store for block [%d]", pdp.blockNum)
   224  	pdp.logger.Debugf("Fetching %d collection private write sets from remote peers for a maximum duration of %s", len(pvtdataRetrievalInfo.eligibleMissingKeys), retryThresh)
   225  	startPull := time.Now()
   226  	for len(pvtdataRetrievalInfo.eligibleMissingKeys) > 0 && time.Since(startPull) < retryThresh {
   227  		if needToRetry := pdp.populateFromRemotePeers(pvtdata, pvtdataRetrievalInfo); !needToRetry {
   228  			break
   229  		}
   230  		// If there are still missing keys, sleep before retry
   231  		pdp.sleeper.Sleep(pullRetrySleepInterval)
   232  	}
   233  	elapsedPull := int64(time.Since(startPull) / time.Millisecond) // duration in ms
   234  	pdp.fetchDurationHistogram.Observe(time.Since(startPull).Seconds())
   235  
   236  	if len(pvtdataRetrievalInfo.eligibleMissingKeys) == 0 {
   237  		pdp.logger.Debugf("Fetched all missing collection private write sets from remote peers for block [%d] (%dms)", pdp.blockNum, elapsedPull)
   238  	} else {
   239  		pdp.logger.Debugf("Could not fetch all missing collection private write sets from remote peers for block [%d]",
   240  			pdp.blockNum)
   241  	}
   242  
   243  	retrievedPvtdata.pvtdataRetrievalInfo = pvtdataRetrievalInfo
   244  	retrievedPvtdata.blockPvtdata = pdp.prepareBlockPvtdata(pvtdata, pvtdataRetrievalInfo)
   245  	return retrievedPvtdata, nil
   246  }
   247  
   248  // populateFromCache populates pvtdata with data fetched from cache and updates
   249  // pvtdataRetrievalInfo by removing missing data that was fetched from cache
   250  func (pdp *PvtdataProvider) populateFromCache(pvtdata rwsetByKeys, pvtdataRetrievalInfo *pvtdataRetrievalInfo, pvtdataToRetrieve []*ledger.TxPvtdataInfo) {
   251  	pdp.logger.Debugf("Attempting to retrieve %d private write sets from cache.", len(pvtdataRetrievalInfo.eligibleMissingKeys))
   252  
   253  	for _, txPvtdata := range pdp.prefetchedPvtdata {
   254  		txID := getTxIDBySeqInBlock(txPvtdata.SeqInBlock, pvtdataToRetrieve)
   255  		// if can't match txID from query, then the data was never requested so skip the entire tx
   256  		if txID == "" {
   257  			pdp.logger.Warningf("Found extra data in prefetched at sequence [%d]. Skipping.", txPvtdata.SeqInBlock)
   258  			continue
   259  		}
   260  		for _, ns := range txPvtdata.WriteSet.NsPvtRwset {
   261  			for _, col := range ns.CollectionPvtRwset {
   262  				key := rwSetKey{
   263  					txID:       txID,
   264  					seqInBlock: txPvtdata.SeqInBlock,
   265  					collection: col.CollectionName,
   266  					namespace:  ns.Namespace,
   267  					hash:       hex.EncodeToString(commonutil.ComputeSHA256(col.Rwset)),
   268  				}
   269  				// skip if key not originally missing
   270  				if _, missing := pvtdataRetrievalInfo.eligibleMissingKeys[key]; !missing {
   271  					pdp.logger.Warningf("Found extra data in prefetched:[%v]. Skipping.", key)
   272  					continue
   273  				}
   274  				// populate the pvtdata with the RW set from the cache
   275  				pvtdata[key] = col.Rwset
   276  				// remove key from missing
   277  				delete(pvtdataRetrievalInfo.eligibleMissingKeys, key)
   278  			} // iterate over collections in the namespace
   279  		} // iterate over the namespaces in the WSet
   280  	} // iterate over cached private data in the block
   281  }
   282  
   283  // populateFromTransientStore populates pvtdata with data fetched from transient store
   284  // and updates pvtdataRetrievalInfo by removing missing data that was fetched from transient store
   285  func (pdp *PvtdataProvider) populateFromTransientStore(pvtdata rwsetByKeys, pvtdataRetrievalInfo *pvtdataRetrievalInfo) {
   286  	pdp.logger.Debugf("Attempting to retrieve %d private write sets from transient store.", len(pvtdataRetrievalInfo.eligibleMissingKeys))
   287  
   288  	// Put into pvtdata RW sets that are missing and found in the transient store
   289  	for k := range pvtdataRetrievalInfo.eligibleMissingKeys {
   290  		filter := ledger.NewPvtNsCollFilter()
   291  		filter.Add(k.namespace, k.collection)
   292  		iterator, err := pdp.transientStore.GetTxPvtRWSetByTxid(k.txID, filter)
   293  		if err != nil {
   294  			pdp.logger.Warningf("Failed fetching private data from transient store: Failed obtaining iterator from transient store: %s", err)
   295  			return
   296  		}
   297  		defer iterator.Close()
   298  		for {
   299  			res, err := iterator.Next()
   300  			if err != nil {
   301  				pdp.logger.Warningf("Failed fetching private data from transient store: Failed iterating over transient store data: %s", err)
   302  				return
   303  			}
   304  			if res == nil {
   305  				// End of iteration
   306  				break
   307  			}
   308  			if res.PvtSimulationResultsWithConfig == nil {
   309  				pdp.logger.Warningf("Resultset's PvtSimulationResultsWithConfig for txID [%s] is nil. Skipping.", k.txID)
   310  				continue
   311  			}
   312  			simRes := res.PvtSimulationResultsWithConfig
   313  			if simRes.PvtRwset == nil {
   314  				pdp.logger.Warningf("The PvtRwset of PvtSimulationResultsWithConfig for txID [%s] is nil. Skipping.", k.txID)
   315  				continue
   316  			}
   317  			for _, ns := range simRes.PvtRwset.NsPvtRwset {
   318  				for _, col := range ns.CollectionPvtRwset {
   319  					key := rwSetKey{
   320  						txID:       k.txID,
   321  						seqInBlock: k.seqInBlock,
   322  						collection: col.CollectionName,
   323  						namespace:  ns.Namespace,
   324  						hash:       hex.EncodeToString(commonutil.ComputeSHA256(col.Rwset)),
   325  					}
   326  					// skip if not missing
   327  					if _, missing := pvtdataRetrievalInfo.eligibleMissingKeys[key]; !missing {
   328  						continue
   329  					}
   330  					// populate the pvtdata with the RW set from the transient store
   331  					pvtdata[key] = col.Rwset
   332  					// remove key from missing
   333  					delete(pvtdataRetrievalInfo.eligibleMissingKeys, key)
   334  				} // iterating over all collections
   335  			} // iterating over all namespaces
   336  		} // iterating over the TxPvtRWSet results
   337  	}
   338  }
   339  
   340  // populateFromRemotePeers populates pvtdata with data fetched from remote peers and updates
   341  // pvtdataRetrievalInfo by removing missing data that was fetched from remote peers
   342  func (pdp *PvtdataProvider) populateFromRemotePeers(pvtdata rwsetByKeys, pvtdataRetrievalInfo *pvtdataRetrievalInfo) bool {
   343  	pdp.logger.Debugf("Attempting to retrieve %d private write sets from remote peers.", len(pvtdataRetrievalInfo.eligibleMissingKeys))
   344  
   345  	dig2src := make(map[pvtdatacommon.DigKey][]*peer.Endorsement)
   346  	var skipped int
   347  	for k, v := range pvtdataRetrievalInfo.eligibleMissingKeys {
   348  		if v.invalid && pdp.skipPullingInvalidTransactions {
   349  			pdp.logger.Debugf("Skipping invalid key [%v] because peer is configured to skip pulling rwsets of invalid transactions.", k)
   350  			skipped++
   351  			continue
   352  		}
   353  		pdp.logger.Debugf("Fetching [%v] from remote peers", k)
   354  		dig := pvtdatacommon.DigKey{
   355  			TxId:       k.txID,
   356  			SeqInBlock: k.seqInBlock,
   357  			Collection: k.collection,
   358  			Namespace:  k.namespace,
   359  			BlockSeq:   pdp.blockNum,
   360  		}
   361  		dig2src[dig] = pvtdataRetrievalInfo.sources[k]
   362  	}
   363  
   364  	if len(dig2src) == 0 {
   365  		return false
   366  	}
   367  
   368  	fetchedData, err := pdp.fetcher.fetch(dig2src)
   369  	if err != nil {
   370  		pdp.logger.Warningf("Failed fetching private data from remote peers for dig2src:[%v], err: %s", dig2src, err)
   371  		return true
   372  	}
   373  
   374  	// Iterate over data fetched from remote peers
   375  	for _, element := range fetchedData.AvailableElements {
   376  		dig := element.Digest
   377  		for _, rws := range element.Payload {
   378  			key := rwSetKey{
   379  				txID:       dig.TxId,
   380  				namespace:  dig.Namespace,
   381  				collection: dig.Collection,
   382  				seqInBlock: dig.SeqInBlock,
   383  				hash:       hex.EncodeToString(commonutil.ComputeSHA256(rws)),
   384  			}
   385  			// skip if not missing
   386  			if _, missing := pvtdataRetrievalInfo.eligibleMissingKeys[key]; !missing {
   387  				// key isn't missing and was never fetched earlier, log that it wasn't originally requested
   388  				if _, exists := pvtdata[key]; !exists {
   389  					pdp.logger.Debugf("Ignoring [%v] because it was never requested.", key)
   390  				}
   391  				continue
   392  			}
   393  			// populate the pvtdata with the RW set from the remote peer
   394  			pvtdata[key] = rws
   395  			// remove key from missing
   396  			delete(pvtdataRetrievalInfo.eligibleMissingKeys, key)
   397  			pdp.logger.Debugf("Fetched [%v]", key)
   398  		}
   399  	}
   400  	// Iterate over purged data
   401  	for _, dig := range fetchedData.PurgedElements {
   402  		// delete purged key from missing keys
   403  		for missingPvtRWKey := range pvtdataRetrievalInfo.eligibleMissingKeys {
   404  			if missingPvtRWKey.namespace == dig.Namespace &&
   405  				missingPvtRWKey.collection == dig.Collection &&
   406  				missingPvtRWKey.seqInBlock == dig.SeqInBlock &&
   407  				missingPvtRWKey.txID == dig.TxId {
   408  				delete(pvtdataRetrievalInfo.eligibleMissingKeys, missingPvtRWKey)
   409  				pdp.logger.Warningf("Missing key because was purged or will soon be purged, "+
   410  					"continue block commit without [%+v] in private rwset", missingPvtRWKey)
   411  			}
   412  		}
   413  	}
   414  
   415  	return len(pvtdataRetrievalInfo.eligibleMissingKeys) > skipped
   416  }
   417  
   418  // prepareBlockPvtdata consolidates the fetched private data as well as ineligible and eligible
   419  // missing private data into a ledger.BlockPvtdata for the PvtdataProvider to return to the consumer
   420  func (pdp *PvtdataProvider) prepareBlockPvtdata(pvtdata rwsetByKeys, pvtdataRetrievalInfo *pvtdataRetrievalInfo) *ledger.BlockPvtdata {
   421  	blockPvtdata := &ledger.BlockPvtdata{
   422  		PvtData:        make(ledger.TxPvtDataMap),
   423  		MissingPvtData: make(ledger.TxMissingPvtDataMap),
   424  	}
   425  
   426  	if len(pvtdataRetrievalInfo.eligibleMissingKeys) == 0 {
   427  		pdp.logger.Infof("Successfully fetched all eligible collection private write sets for block [%d]", pdp.blockNum)
   428  	} else {
   429  		pdp.logger.Warningf("Could not fetch all missing eligible collection private write sets for block [%d]. Will commit block with missing private write sets:[%v]",
   430  			pdp.blockNum, pvtdataRetrievalInfo.eligibleMissingKeys)
   431  	}
   432  
   433  	for seqInBlock, nsRWS := range pvtdata.bySeqsInBlock() {
   434  		// add all found pvtdata to blockPvtDataPvtdata for seqInBlock
   435  		blockPvtdata.PvtData[seqInBlock] = &ledger.TxPvtData{
   436  			SeqInBlock: seqInBlock,
   437  			WriteSet:   nsRWS.toRWSet(),
   438  		}
   439  	}
   440  
   441  	for key := range pvtdataRetrievalInfo.eligibleMissingKeys {
   442  		blockPvtdata.MissingPvtData.Add(key.seqInBlock, key.namespace, key.collection, true)
   443  	}
   444  
   445  	for key := range pvtdataRetrievalInfo.ineligibleMissingKeys {
   446  		blockPvtdata.MissingPvtData.Add(key.seqInBlock, key.namespace, key.collection, false)
   447  	}
   448  
   449  	return blockPvtdata
   450  }
   451  
   452  type pvtdataRetrievalInfo struct {
   453  	sources               map[rwSetKey][]*peer.Endorsement
   454  	txns                  []string
   455  	eligibleMissingKeys   rwsetKeys
   456  	ineligibleMissingKeys rwsetKeys
   457  }
   458  
   459  // rwset types
   460  
   461  type readWriteSets []*readWriteSet
   462  
   463  func (s readWriteSets) toRWSet() *rwset.TxPvtReadWriteSet {
   464  	namespaces := make(map[string]*rwset.NsPvtReadWriteSet)
   465  	dataModel := rwset.TxReadWriteSet_KV
   466  	for _, rws := range s {
   467  		if _, exists := namespaces[rws.namespace]; !exists {
   468  			namespaces[rws.namespace] = &rwset.NsPvtReadWriteSet{
   469  				Namespace: rws.namespace,
   470  			}
   471  		}
   472  		col := &rwset.CollectionPvtReadWriteSet{
   473  			CollectionName: rws.collection,
   474  			Rwset:          rws.rws,
   475  		}
   476  		namespaces[rws.namespace].CollectionPvtRwset = append(namespaces[rws.namespace].CollectionPvtRwset, col)
   477  	}
   478  
   479  	var namespaceSlice []*rwset.NsPvtReadWriteSet
   480  	for _, nsRWset := range namespaces {
   481  		namespaceSlice = append(namespaceSlice, nsRWset)
   482  	}
   483  
   484  	return &rwset.TxPvtReadWriteSet{
   485  		DataModel:  dataModel,
   486  		NsPvtRwset: namespaceSlice,
   487  	}
   488  }
   489  
   490  type readWriteSet struct {
   491  	rwSetKey
   492  	rws []byte
   493  }
   494  
   495  type rwsetByKeys map[rwSetKey][]byte
   496  
   497  func (s rwsetByKeys) bySeqsInBlock() map[uint64]readWriteSets {
   498  	res := make(map[uint64]readWriteSets)
   499  	for k, rws := range s {
   500  		res[k.seqInBlock] = append(res[k.seqInBlock], &readWriteSet{
   501  			rws:      rws,
   502  			rwSetKey: k,
   503  		})
   504  	}
   505  	return res
   506  }
   507  
   508  type rwsetInfo struct {
   509  	invalid bool
   510  }
   511  
   512  type rwsetKeys map[rwSetKey]rwsetInfo
   513  
   514  // String returns a string representation of the rwsetKeys
   515  func (s rwsetKeys) String() string {
   516  	var buffer bytes.Buffer
   517  	for k := range s {
   518  		buffer.WriteString(fmt.Sprintf("%s\n", k.String()))
   519  	}
   520  	return buffer.String()
   521  }
   522  
   523  type rwSetKey struct {
   524  	txID       string
   525  	seqInBlock uint64
   526  	namespace  string
   527  	collection string
   528  	hash       string
   529  }
   530  
   531  // String returns a string representation of the rwSetKey
   532  func (k *rwSetKey) String() string {
   533  	return fmt.Sprintf("txID: %s, seq: %d, namespace: %s, collection: %s, hash: %s", k.txID, k.seqInBlock, k.namespace, k.collection, k.hash)
   534  }
   535  
   536  func getTxIDBySeqInBlock(seqInBlock uint64, pvtdataToRetrieve []*ledger.TxPvtdataInfo) string {
   537  	for _, txPvtdataItem := range pvtdataToRetrieve {
   538  		if txPvtdataItem.SeqInBlock == seqInBlock {
   539  			return txPvtdataItem.TxID
   540  		}
   541  	}
   542  
   543  	return ""
   544  }
   545  
   546  func endorsersFromEligibleOrgs(ns string, col string, endorsers []*peer.Endorsement, orgs []string) []*peer.Endorsement {
   547  	var res []*peer.Endorsement
   548  	for _, e := range endorsers {
   549  		sID := &msp.SerializedIdentity{}
   550  		err := proto.Unmarshal(e.Endorser, sID)
   551  		if err != nil {
   552  			logger.Warning("Failed unmarshalling endorser:", err)
   553  			continue
   554  		}
   555  		if !util.Contains(sID.Mspid, orgs) {
   556  			logger.Debug(sID.Mspid, "isn't among the collection's orgs:", orgs, "for namespace", ns, ",collection", col)
   557  			continue
   558  		}
   559  		res = append(res, e)
   560  	}
   561  	return res
   562  }