code: func computeOwnedRWsets(block *common.Block, blockPvtData util.PvtDataCollections) (rwsetByKeys, error) {
  	lastBlockSeq := len(block.Data.Data) - 1
  
  	ownedRWsets := make(map[rwSetKey][]byte)
  	for _, txPvtData := range blockPvtData {
  		if lastBlockSeq < int(txPvtData.SeqInBlock) {
  			logger.Warningf("Claimed SeqInBlock %d but block has only %d transactions", txPvtData.SeqInBlock, len(block.Data.Data))
  			continue
  		}
  		env, err := utils.GetEnvelopeFromBlock(block.Data.Data[txPvtData.SeqInBlock])
  		if err != nil {
  			return nil, err
  		}
  		payload, err := utils.GetPayload(env)
  		if err != nil {
  			return nil, err
  		}
  
  		chdr, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader)
  		if err != nil {
  			return nil, err
  		}
  		for _, ns := range txPvtData.WriteSet.NsPvtRwset {
  			for _, col := range ns.CollectionPvtRwset {
  				computedHash := hex.EncodeToString(util2.ComputeSHA256(col.Rwset))
  				ownedRWsets[rwSetKey{
  					txID:       chdr.TxId,
  					seqInBlock: txPvtData.SeqInBlock,
  					collection: col.CollectionName,
  					namespace:  ns.Namespace,
  					hash:       computedHash,
  				}] = col.Rwset
  			} // iterate over collections in the namespace
  		} // iterate over the namespaces in the WSet
  	} // iterate over the transactions in the block
  	return ownedRWsets, nil
  }
docs: // computeOwnedRWsets identifies which block private data we already have