github.com/yous1230/fabric@v2.0.0-beta.0.20191224111736-74345bee6ac2+incompatible/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  
   141  			sources[key] = endorsersFromEligibleOrgs(ns, col, endorsers, policy.MemberOrgs())
   142  		}
   143  	}
   144  
   145  	return &pvtdataRetrievalInfo{
   146  		sources:               sources,
   147  		txns:                  txList,
   148  		eligibleMissingKeys:   eligibleMissingKeys,
   149  		ineligibleMissingKeys: ineligibleMissingKeys,
   150  	}, nil
   151  }
   152  
   153  type PvtdataProvider struct {
   154  	selfSignedData                          protoutil.SignedData
   155  	logger                                  util.Logger
   156  	listMissingPrivateDataDurationHistogram metrics.Histogram
   157  	fetchDurationHistogram                  metrics.Histogram
   158  	purgeDurationHistogram                  metrics.Histogram
   159  	transientStore                          *transientstore.Store
   160  	pullRetryThreshold                      time.Duration
   161  	prefetchedPvtdata                       util.PvtDataCollections
   162  	transientBlockRetention                 uint64
   163  	channelID                               string
   164  	blockNum                                uint64
   165  	storePvtdataOfInvalidTx                 bool
   166  	skipPullingInvalidTransactions          bool
   167  	idDeserializerFactory                   IdentityDeserializerFactory
   168  	fetcher                                 Fetcher
   169  
   170  	sleeper sleeper
   171  }
   172  
   173  // RetrievePvtdata is passed a list of private data items from a block,
   174  // it determines which private data items this peer is eligible for, and then
   175  // retrieves the private data from local cache, local transient store, or a remote peer.
   176  func (pdp *PvtdataProvider) RetrievePvtdata(pvtdataToRetrieve []*ledger.TxPvtdataInfo) (*RetrievedPvtdata, error) {
   177  	retrievedPvtdata := &RetrievedPvtdata{
   178  		transientStore:          pdp.transientStore,
   179  		logger:                  pdp.logger,
   180  		purgeDurationHistogram:  pdp.purgeDurationHistogram,
   181  		blockNum:                pdp.blockNum,
   182  		transientBlockRetention: pdp.transientBlockRetention,
   183  	}
   184  
   185  	listMissingStart := time.Now()
   186  	eligibilityComputer := &eligibilityComputer{
   187  		logger:                  pdp.logger,
   188  		storePvtdataOfInvalidTx: pdp.storePvtdataOfInvalidTx,
   189  		channelID:               pdp.channelID,
   190  		selfSignedData:          pdp.selfSignedData,
   191  		idDeserializerFactory:   pdp.idDeserializerFactory,
   192  	}
   193  
   194  	pvtdataRetrievalInfo, err := eligibilityComputer.computeEligibility(pvtdataToRetrieve)
   195  	if err != nil {
   196  		return nil, err
   197  	}
   198  	pdp.listMissingPrivateDataDurationHistogram.Observe(time.Since(listMissingStart).Seconds())
   199  
   200  	pvtdata := make(rwsetByKeys)
   201  
   202  	// POPULATE FROM CACHE
   203  	pdp.populateFromCache(pvtdata, pvtdataRetrievalInfo, pvtdataToRetrieve)
   204  	if len(pvtdataRetrievalInfo.eligibleMissingKeys) == 0 {
   205  		pdp.logger.Debug("No missing collection private write sets to fetch from transient store")
   206  		retrievedPvtdata.pvtdataRetrievalInfo = pvtdataRetrievalInfo
   207  		retrievedPvtdata.blockPvtdata = pdp.prepareBlockPvtdata(pvtdata, pvtdataRetrievalInfo)
   208  		return retrievedPvtdata, nil
   209  	}
   210  
   211  	// POPULATE FROM TRANSIENT STORE
   212  	pdp.logger.Debugf("Could not find all collection private write sets in cache for block [%d]", pdp.blockNum)
   213  	pdp.logger.Debugf("Fetching %d collection private write sets from transient store", len(pvtdataRetrievalInfo.eligibleMissingKeys))
   214  	pdp.populateFromTransientStore(pvtdata, pvtdataRetrievalInfo)
   215  	if len(pvtdataRetrievalInfo.eligibleMissingKeys) == 0 {
   216  		pdp.logger.Debug("No missing collection private write sets to fetch from remote peers")
   217  		retrievedPvtdata.pvtdataRetrievalInfo = pvtdataRetrievalInfo
   218  		retrievedPvtdata.blockPvtdata = pdp.prepareBlockPvtdata(pvtdata, pvtdataRetrievalInfo)
   219  		return retrievedPvtdata, nil
   220  	}
   221  
   222  	// POPULATE FROM REMOTE PEERS
   223  	retryThresh := pdp.pullRetryThreshold
   224  	pdp.logger.Debugf("Could not find all collection private write sets in local peer transient store for block [%d]", pdp.blockNum)
   225  	pdp.logger.Debugf("Fetching %d collection private write sets from remote peers for a maximum duration of %s", len(pvtdataRetrievalInfo.eligibleMissingKeys), retryThresh)
   226  	startPull := time.Now()
   227  	for len(pvtdataRetrievalInfo.eligibleMissingKeys) > 0 && time.Since(startPull) < retryThresh {
   228  		pdp.populateFromRemotePeers(pvtdata, pvtdataRetrievalInfo)
   229  
   230  		// If succeeded to fetch everything, break to skip sleep
   231  		if len(pvtdataRetrievalInfo.eligibleMissingKeys) == 0 {
   232  			break
   233  		}
   234  
   235  		// If there are still missing keys, sleep before retry
   236  		pdp.sleeper.Sleep(pullRetrySleepInterval)
   237  	}
   238  	elapsedPull := int64(time.Since(startPull) / time.Millisecond) // duration in ms
   239  	pdp.fetchDurationHistogram.Observe(time.Since(startPull).Seconds())
   240  
   241  	if len(pvtdataRetrievalInfo.eligibleMissingKeys) == 0 {
   242  		pdp.logger.Debugf("Fetched all missing collection private write sets from remote peers for block [%d] (%dms)", pdp.blockNum, elapsedPull)
   243  	} else {
   244  		pdp.logger.Debugf("Could not fetch all missing collection private write sets from remote peers for block [%d]",
   245  			pdp.blockNum)
   246  	}
   247  
   248  	retrievedPvtdata.pvtdataRetrievalInfo = pvtdataRetrievalInfo
   249  	retrievedPvtdata.blockPvtdata = pdp.prepareBlockPvtdata(pvtdata, pvtdataRetrievalInfo)
   250  	return retrievedPvtdata, nil
   251  }
   252  
   253  // populateFromCache populates pvtdata with data fetched from cache and updates
   254  // pvtdataRetrievalInfo by removing missing data that was fetched from cache
   255  func (pdp *PvtdataProvider) populateFromCache(pvtdata rwsetByKeys, pvtdataRetrievalInfo *pvtdataRetrievalInfo, pvtdataToRetrieve []*ledger.TxPvtdataInfo) {
   256  	pdp.logger.Debugf("Attempting to retrieve %d private write sets from cache.", len(pvtdataRetrievalInfo.eligibleMissingKeys))
   257  
   258  	for _, txPvtdata := range pdp.prefetchedPvtdata {
   259  		txID := getTxIDBySeqInBlock(txPvtdata.SeqInBlock, pvtdataToRetrieve)
   260  		// if can't match txID from query, then the data was never requested so skip the entire tx
   261  		if txID == "" {
   262  			pdp.logger.Warningf("Found extra data in prefetched at sequence [%d]. Skipping.", txPvtdata.SeqInBlock)
   263  			continue
   264  		}
   265  		for _, ns := range txPvtdata.WriteSet.NsPvtRwset {
   266  			for _, col := range ns.CollectionPvtRwset {
   267  				key := rwSetKey{
   268  					txID:       txID,
   269  					seqInBlock: txPvtdata.SeqInBlock,
   270  					collection: col.CollectionName,
   271  					namespace:  ns.Namespace,
   272  					hash:       hex.EncodeToString(commonutil.ComputeSHA256(col.Rwset)),
   273  				}
   274  				// skip if key not originally missing
   275  				if _, missing := pvtdataRetrievalInfo.eligibleMissingKeys[key]; !missing {
   276  					pdp.logger.Warningf("Found extra data in prefetched:[%v]. Skipping.", key)
   277  					continue
   278  				}
   279  				// populate the pvtdata with the RW set from the cache
   280  				pvtdata[key] = col.Rwset
   281  				// remove key from missing
   282  				delete(pvtdataRetrievalInfo.eligibleMissingKeys, key)
   283  			} // iterate over collections in the namespace
   284  		} // iterate over the namespaces in the WSet
   285  	} // iterate over cached private data in the block
   286  }
   287  
   288  // populateFromTransientStore populates pvtdata with data fetched from transient store
   289  // and updates pvtdataRetrievalInfo by removing missing data that was fetched from transient store
   290  func (pdp *PvtdataProvider) populateFromTransientStore(pvtdata rwsetByKeys, pvtdataRetrievalInfo *pvtdataRetrievalInfo) {
   291  	pdp.logger.Debugf("Attempting to retrieve %d private write sets from transient store.", len(pvtdataRetrievalInfo.eligibleMissingKeys))
   292  
   293  	// Put into pvtdata RW sets that are missing and found in the transient store
   294  	for k := range pvtdataRetrievalInfo.eligibleMissingKeys {
   295  		filter := ledger.NewPvtNsCollFilter()
   296  		filter.Add(k.namespace, k.collection)
   297  		iterator, err := pdp.transientStore.GetTxPvtRWSetByTxid(k.txID, filter)
   298  		if err != nil {
   299  			pdp.logger.Warningf("Failed fetching private data from transient store: Failed obtaining iterator from transient store: %s", err)
   300  			return
   301  		}
   302  		defer iterator.Close()
   303  		for {
   304  			res, err := iterator.Next()
   305  			if err != nil {
   306  				pdp.logger.Warningf("Failed fetching private data from transient store: Failed iterating over transient store data: %s", err)
   307  				return
   308  			}
   309  			if res == nil {
   310  				// End of iteration
   311  				break
   312  			}
   313  			if res.PvtSimulationResultsWithConfig == nil {
   314  				pdp.logger.Warningf("Resultset's PvtSimulationResultsWithConfig for txID [%s] is nil. Skipping.", k.txID)
   315  				continue
   316  			}
   317  			simRes := res.PvtSimulationResultsWithConfig
   318  			if simRes.PvtRwset == nil {
   319  				pdp.logger.Warningf("The PvtRwset of PvtSimulationResultsWithConfig for txID [%s] is nil. Skipping.", k.txID)
   320  				continue
   321  			}
   322  			for _, ns := range simRes.PvtRwset.NsPvtRwset {
   323  				for _, col := range ns.CollectionPvtRwset {
   324  					key := rwSetKey{
   325  						txID:       k.txID,
   326  						seqInBlock: k.seqInBlock,
   327  						collection: col.CollectionName,
   328  						namespace:  ns.Namespace,
   329  						hash:       hex.EncodeToString(commonutil.ComputeSHA256(col.Rwset)),
   330  					}
   331  					// skip if not missing
   332  					if _, missing := pvtdataRetrievalInfo.eligibleMissingKeys[key]; !missing {
   333  						continue
   334  					}
   335  					// populate the pvtdata with the RW set from the transient store
   336  					pvtdata[key] = col.Rwset
   337  					// remove key from missing
   338  					delete(pvtdataRetrievalInfo.eligibleMissingKeys, key)
   339  				} // iterating over all collections
   340  			} // iterating over all namespaces
   341  		} // iterating over the TxPvtRWSet results
   342  	}
   343  }
   344  
   345  // populateFromRemotePeers populates pvtdata with data fetched from remote peers and updates
   346  // pvtdataRetrievalInfo by removing missing data that was fetched from remote peers
   347  func (pdp *PvtdataProvider) populateFromRemotePeers(pvtdata rwsetByKeys, pvtdataRetrievalInfo *pvtdataRetrievalInfo) {
   348  	pdp.logger.Debugf("Attempting to retrieve %d private write sets from remote peers.", len(pvtdataRetrievalInfo.eligibleMissingKeys))
   349  
   350  	dig2src := make(map[pvtdatacommon.DigKey][]*peer.Endorsement)
   351  	for k, v := range pvtdataRetrievalInfo.eligibleMissingKeys {
   352  		if v.invalid && pdp.skipPullingInvalidTransactions {
   353  			pdp.logger.Debugf("Skipping invalid key [%v] because peer is configured to skip pulling rwsets of invalid transactions.", k)
   354  			continue
   355  		}
   356  		pdp.logger.Debugf("Fetching [%v] from remote peers", k)
   357  		dig := pvtdatacommon.DigKey{
   358  			TxId:       k.txID,
   359  			SeqInBlock: k.seqInBlock,
   360  			Collection: k.collection,
   361  			Namespace:  k.namespace,
   362  			BlockSeq:   pdp.blockNum,
   363  		}
   364  		dig2src[dig] = pvtdataRetrievalInfo.sources[k]
   365  	}
   366  	fetchedData, err := pdp.fetcher.fetch(dig2src)
   367  	if err != nil {
   368  		pdp.logger.Warningf("Failed fetching private data from remote peers for dig2src:[%v], err: %s", dig2src, err)
   369  		return
   370  	}
   371  
   372  	// Iterate over data fetched from remote peers
   373  	for _, element := range fetchedData.AvailableElements {
   374  		dig := element.Digest
   375  		for _, rws := range element.Payload {
   376  			key := rwSetKey{
   377  				txID:       dig.TxId,
   378  				namespace:  dig.Namespace,
   379  				collection: dig.Collection,
   380  				seqInBlock: dig.SeqInBlock,
   381  				hash:       hex.EncodeToString(commonutil.ComputeSHA256(rws)),
   382  			}
   383  			// skip if not missing
   384  			if _, missing := pvtdataRetrievalInfo.eligibleMissingKeys[key]; !missing {
   385  				// key isn't missing and was never fetched earlier, log that it wasn't originally requested
   386  				if _, exists := pvtdata[key]; !exists {
   387  					pdp.logger.Debugf("Ignoring [%v] because it was never requested.", key)
   388  				}
   389  				continue
   390  			}
   391  			// populate the pvtdata with the RW set from the remote peer
   392  			pvtdata[key] = rws
   393  			// remove key from missing
   394  			delete(pvtdataRetrievalInfo.eligibleMissingKeys, key)
   395  			pdp.logger.Debugf("Fetched [%v]", key)
   396  		}
   397  	}
   398  	// Iterate over purged data
   399  	for _, dig := range fetchedData.PurgedElements {
   400  		// delete purged key from missing keys
   401  		for missingPvtRWKey := range pvtdataRetrievalInfo.eligibleMissingKeys {
   402  			if missingPvtRWKey.namespace == dig.Namespace &&
   403  				missingPvtRWKey.collection == dig.Collection &&
   404  				missingPvtRWKey.seqInBlock == dig.SeqInBlock &&
   405  				missingPvtRWKey.txID == dig.TxId {
   406  				delete(pvtdataRetrievalInfo.eligibleMissingKeys, missingPvtRWKey)
   407  				pdp.logger.Warningf("Missing key because was purged or will soon be purged, "+
   408  					"continue block commit without [%+v] in private rwset", missingPvtRWKey)
   409  			}
   410  		}
   411  	}
   412  }
   413  
   414  // prepareBlockPvtdata consolidates the fetched private data as well as ineligible and eligible
   415  // missing private data into a ledger.BlockPvtdata for the PvtdataProvider to return to the consumer
   416  func (pdp *PvtdataProvider) prepareBlockPvtdata(pvtdata rwsetByKeys, pvtdataRetrievalInfo *pvtdataRetrievalInfo) *ledger.BlockPvtdata {
   417  	blockPvtdata := &ledger.BlockPvtdata{
   418  		PvtData:        make(ledger.TxPvtDataMap),
   419  		MissingPvtData: make(ledger.TxMissingPvtDataMap),
   420  	}
   421  
   422  	if len(pvtdataRetrievalInfo.eligibleMissingKeys) == 0 {
   423  		pdp.logger.Infof("Successfully fetched all eligible collection private write sets for block [%d]", pdp.blockNum)
   424  	} else {
   425  		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]",
   426  			pdp.blockNum, pvtdataRetrievalInfo.eligibleMissingKeys)
   427  	}
   428  
   429  	for seqInBlock, nsRWS := range pvtdata.bySeqsInBlock() {
   430  		// add all found pvtdata to blockPvtDataPvtdata for seqInBlock
   431  		blockPvtdata.PvtData[seqInBlock] = &ledger.TxPvtData{
   432  			SeqInBlock: seqInBlock,
   433  			WriteSet:   nsRWS.toRWSet(),
   434  		}
   435  	}
   436  
   437  	for key := range pvtdataRetrievalInfo.eligibleMissingKeys {
   438  		blockPvtdata.MissingPvtData.Add(key.seqInBlock, key.namespace, key.collection, true)
   439  	}
   440  
   441  	for key := range pvtdataRetrievalInfo.ineligibleMissingKeys {
   442  		blockPvtdata.MissingPvtData.Add(key.seqInBlock, key.namespace, key.collection, false)
   443  	}
   444  
   445  	return blockPvtdata
   446  }
   447  
   448  type pvtdataRetrievalInfo struct {
   449  	sources               map[rwSetKey][]*peer.Endorsement
   450  	txns                  []string
   451  	eligibleMissingKeys   rwsetKeys
   452  	ineligibleMissingKeys rwsetKeys
   453  }
   454  
   455  // rwset types
   456  
   457  type readWriteSets []*readWriteSet
   458  
   459  func (s readWriteSets) toRWSet() *rwset.TxPvtReadWriteSet {
   460  	namespaces := make(map[string]*rwset.NsPvtReadWriteSet)
   461  	dataModel := rwset.TxReadWriteSet_KV
   462  	for _, rws := range s {
   463  		if _, exists := namespaces[rws.namespace]; !exists {
   464  			namespaces[rws.namespace] = &rwset.NsPvtReadWriteSet{
   465  				Namespace: rws.namespace,
   466  			}
   467  		}
   468  		col := &rwset.CollectionPvtReadWriteSet{
   469  			CollectionName: rws.collection,
   470  			Rwset:          rws.rws,
   471  		}
   472  		namespaces[rws.namespace].CollectionPvtRwset = append(namespaces[rws.namespace].CollectionPvtRwset, col)
   473  	}
   474  
   475  	var namespaceSlice []*rwset.NsPvtReadWriteSet
   476  	for _, nsRWset := range namespaces {
   477  		namespaceSlice = append(namespaceSlice, nsRWset)
   478  	}
   479  
   480  	return &rwset.TxPvtReadWriteSet{
   481  		DataModel:  dataModel,
   482  		NsPvtRwset: namespaceSlice,
   483  	}
   484  }
   485  
   486  type readWriteSet struct {
   487  	rwSetKey
   488  	rws []byte
   489  }
   490  
   491  type rwsetByKeys map[rwSetKey][]byte
   492  
   493  func (s rwsetByKeys) bySeqsInBlock() map[uint64]readWriteSets {
   494  	res := make(map[uint64]readWriteSets)
   495  	for k, rws := range s {
   496  		res[k.seqInBlock] = append(res[k.seqInBlock], &readWriteSet{
   497  			rws:      rws,
   498  			rwSetKey: k,
   499  		})
   500  	}
   501  	return res
   502  }
   503  
   504  type rwsetInfo struct {
   505  	invalid bool
   506  }
   507  
   508  type rwsetKeys map[rwSetKey]rwsetInfo
   509  
   510  // String returns a string representation of the rwsetKeys
   511  func (s rwsetKeys) String() string {
   512  	var buffer bytes.Buffer
   513  	for k := range s {
   514  		buffer.WriteString(fmt.Sprintf("%s\n", k.String()))
   515  	}
   516  	return buffer.String()
   517  }
   518  
   519  type rwSetKey struct {
   520  	txID       string
   521  	seqInBlock uint64
   522  	namespace  string
   523  	collection string
   524  	hash       string
   525  }
   526  
   527  // String returns a string representation of the rwSetKey
   528  func (k *rwSetKey) String() string {
   529  	return fmt.Sprintf("txID: %s, seq: %d, namespace: %s, collection: %s, hash: %s", k.txID, k.seqInBlock, k.namespace, k.collection, k.hash)
   530  }
   531  
   532  func getTxIDBySeqInBlock(seqInBlock uint64, pvtdataToRetrieve []*ledger.TxPvtdataInfo) string {
   533  	for _, txPvtdataItem := range pvtdataToRetrieve {
   534  		if txPvtdataItem.SeqInBlock == seqInBlock {
   535  			return txPvtdataItem.TxID
   536  		}
   537  	}
   538  
   539  	return ""
   540  }
   541  
   542  func endorsersFromEligibleOrgs(ns string, col string, endorsers []*peer.Endorsement, orgs []string) []*peer.Endorsement {
   543  	var res []*peer.Endorsement
   544  	for _, e := range endorsers {
   545  		sID := &msp.SerializedIdentity{}
   546  		err := proto.Unmarshal(e.Endorser, sID)
   547  		if err != nil {
   548  			logger.Warning("Failed unmarshalling endorser:", err)
   549  			continue
   550  		}
   551  		if !util.Contains(sID.Mspid, orgs) {
   552  			logger.Debug(sID.Mspid, "isn't among the collection's orgs:", orgs, "for namespace", ns, ",collection", col)
   553  			continue
   554  		}
   555  		res = append(res, e)
   556  	}
   557  	return res
   558  }