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