github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/gossip/privdata/pull.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  	"bytes"
    11  	"encoding/hex"
    12  	"fmt"
    13  	"math"
    14  	"math/rand"
    15  	"sync"
    16  	"time"
    17  
    18  	commonutil "github.com/hechain20/hechain/common/util"
    19  	"github.com/hechain20/hechain/core/common/privdata"
    20  	"github.com/hechain20/hechain/gossip/api"
    21  	"github.com/hechain20/hechain/gossip/comm"
    22  	"github.com/hechain20/hechain/gossip/common"
    23  	"github.com/hechain20/hechain/gossip/discovery"
    24  	"github.com/hechain20/hechain/gossip/filter"
    25  	"github.com/hechain20/hechain/gossip/metrics"
    26  	privdatacommon "github.com/hechain20/hechain/gossip/privdata/common"
    27  	"github.com/hechain20/hechain/gossip/protoext"
    28  	"github.com/hechain20/hechain/gossip/util"
    29  	"github.com/hechain20/hechain/protoutil"
    30  	protosgossip "github.com/hyperledger/fabric-protos-go/gossip"
    31  	"github.com/pkg/errors"
    32  	"go.uber.org/zap/zapcore"
    33  )
    34  
    35  const (
    36  	membershipPollingBackoff    = time.Second
    37  	responseWaitTime            = time.Second * 5
    38  	maxMembershipPollIterations = 5
    39  )
    40  
    41  // Dig2PvtRWSetWithConfig
    42  type Dig2PvtRWSetWithConfig map[privdatacommon.DigKey]*util.PrivateRWSetWithConfig
    43  
    44  // PrivateDataRetriever interface which defines API capable
    45  // of retrieving required private data
    46  type PrivateDataRetriever interface {
    47  	// CollectionRWSet returns the bytes of CollectionPvtReadWriteSet for a given txID and collection from the transient store
    48  	CollectionRWSet(dig []*protosgossip.PvtDataDigest, blockNum uint64) (Dig2PvtRWSetWithConfig, bool, error)
    49  }
    50  
    51  // gossip defines capabilities that the gossip module gives the Coordinator
    52  type gossip interface {
    53  	// Send sends a message to remote peers
    54  	Send(msg *protosgossip.GossipMessage, peers ...*comm.RemotePeer)
    55  
    56  	// PeersOfChannel returns the NetworkMembers considered alive
    57  	// and also subscribed to the channel given
    58  	PeersOfChannel(common.ChannelID) []discovery.NetworkMember
    59  
    60  	// PeerFilter receives a SubChannelSelectionCriteria and returns a RoutingFilter that selects
    61  	// only peer identities that match the given criteria, and that they published their channel participation
    62  	PeerFilter(channel common.ChannelID, messagePredicate api.SubChannelSelectionCriteria) (filter.RoutingFilter, error)
    63  
    64  	// Accept returns a dedicated read-only channel for messages sent by other nodes that match a certain predicate.
    65  	// If passThrough is false, the messages are processed by the gossip layer beforehand.
    66  	// If passThrough is true, the gossip layer doesn't intervene and the messages
    67  	// can be used to send a reply back to the sender
    68  	Accept(acceptor common.MessageAcceptor, passThrough bool) (<-chan *protosgossip.GossipMessage, <-chan protoext.ReceivedMessage)
    69  }
    70  
    71  type puller struct {
    72  	logger        util.Logger
    73  	metrics       *metrics.PrivdataMetrics
    74  	pubSub        *util.PubSub
    75  	stopChan      chan struct{}
    76  	msgChan       <-chan protoext.ReceivedMessage
    77  	channel       string
    78  	cs            privdata.CollectionStore
    79  	btlPullMargin uint64
    80  	gossip
    81  	PrivateDataRetriever
    82  	CollectionAccessFactory
    83  }
    84  
    85  // NewPuller creates new private data puller
    86  func NewPuller(metrics *metrics.PrivdataMetrics, cs privdata.CollectionStore, g gossip,
    87  	dataRetriever PrivateDataRetriever, factory CollectionAccessFactory, channel string, btlPullMargin uint64) *puller {
    88  	p := &puller{
    89  		logger:                  logger.With("channel", channel),
    90  		metrics:                 metrics,
    91  		pubSub:                  util.NewPubSub(),
    92  		stopChan:                make(chan struct{}),
    93  		channel:                 channel,
    94  		cs:                      cs,
    95  		btlPullMargin:           btlPullMargin,
    96  		gossip:                  g,
    97  		PrivateDataRetriever:    dataRetriever,
    98  		CollectionAccessFactory: factory,
    99  	}
   100  	_, p.msgChan = p.Accept(func(o interface{}) bool {
   101  		msg := o.(protoext.ReceivedMessage).GetGossipMessage()
   102  		if !bytes.Equal(msg.Channel, []byte(p.channel)) {
   103  			return false
   104  		}
   105  		return protoext.IsPrivateDataMsg(msg.GossipMessage)
   106  	}, true)
   107  	go p.listen()
   108  	return p
   109  }
   110  
   111  func (p *puller) listen() {
   112  	for {
   113  		select {
   114  		case <-p.stopChan:
   115  			return
   116  		case msg := <-p.msgChan:
   117  			if msg == nil {
   118  				// comm module stopped, hence this channel
   119  				// closed
   120  				return
   121  			}
   122  			if msg.GetGossipMessage().GetPrivateRes() != nil {
   123  				p.handleResponse(msg)
   124  			}
   125  			if msg.GetGossipMessage().GetPrivateReq() != nil {
   126  				p.handleRequest(msg)
   127  			}
   128  		}
   129  	}
   130  }
   131  
   132  func (p *puller) handleRequest(message protoext.ReceivedMessage) {
   133  	p.logger.Debug("Got", message.GetGossipMessage(), "from", message.GetConnectionInfo().Endpoint)
   134  	message.Respond(&protosgossip.GossipMessage{
   135  		Channel: []byte(p.channel),
   136  		Tag:     protosgossip.GossipMessage_CHAN_ONLY,
   137  		Nonce:   message.GetGossipMessage().Nonce,
   138  		Content: &protosgossip.GossipMessage_PrivateRes{
   139  			PrivateRes: &protosgossip.RemotePvtDataResponse{
   140  				Elements: p.createResponse(message),
   141  			},
   142  		},
   143  	})
   144  }
   145  
   146  func (p *puller) createResponse(message protoext.ReceivedMessage) []*protosgossip.PvtDataElement {
   147  	authInfo := message.GetConnectionInfo().Auth
   148  	var returned []*protosgossip.PvtDataElement
   149  	connectionEndpoint := message.GetConnectionInfo().Endpoint
   150  
   151  	defer func() {
   152  		p.logger.Debug("Returning", connectionEndpoint, len(returned), "elements")
   153  	}()
   154  
   155  	msg := message.GetGossipMessage()
   156  	// group all digest by block number
   157  	block2dig := groupDigestsByBlockNum(msg.GetPrivateReq().Digests)
   158  
   159  	for blockNum, digests := range block2dig {
   160  		start := time.Now()
   161  		dig2rwSets, wasFetchedFromLedger, err := p.CollectionRWSet(digests, blockNum)
   162  		p.metrics.RetrieveDuration.With("channel", p.channel).Observe(time.Since(start).Seconds())
   163  		if err != nil {
   164  			p.logger.Warningf("could not obtain private collection rwset for block %d, because of %s, continue...", blockNum, err)
   165  			continue
   166  		}
   167  		returned = append(returned, p.filterNotEligible(dig2rwSets, wasFetchedFromLedger, protoutil.SignedData{
   168  			Identity:  message.GetConnectionInfo().Identity,
   169  			Data:      authInfo.SignedData,
   170  			Signature: authInfo.Signature,
   171  		}, connectionEndpoint)...)
   172  	}
   173  	return returned
   174  }
   175  
   176  // groupDigestsByBlockNum group all digest by block sequence number
   177  func groupDigestsByBlockNum(digests []*protosgossip.PvtDataDigest) map[uint64][]*protosgossip.PvtDataDigest {
   178  	results := make(map[uint64][]*protosgossip.PvtDataDigest)
   179  	for _, dig := range digests {
   180  		results[dig.BlockSeq] = append(results[dig.BlockSeq], dig)
   181  	}
   182  	return results
   183  }
   184  
   185  func (p *puller) handleResponse(message protoext.ReceivedMessage) {
   186  	msg := message.GetGossipMessage().GetPrivateRes()
   187  	p.logger.Debug("Got", msg, "from", message.GetConnectionInfo().Endpoint)
   188  	for _, el := range msg.Elements {
   189  		if el.Digest == nil {
   190  			p.logger.Warning("Got nil digest from", message.GetConnectionInfo().Endpoint, "aborting")
   191  			return
   192  		}
   193  		hash, err := hashDigest(el.Digest)
   194  		if err != nil {
   195  			p.logger.Warning("Failed hashing digest from", message.GetConnectionInfo().Endpoint, "aborting")
   196  			return
   197  		}
   198  		p.pubSub.Publish(hash, el)
   199  	}
   200  }
   201  
   202  // hashDigest returns the SHA256 representation of the PvtDataDigest's bytes
   203  func hashDigest(dig *protosgossip.PvtDataDigest) (string, error) {
   204  	b, err := protoutil.Marshal(dig)
   205  	if err != nil {
   206  		return "", err
   207  	}
   208  	return hex.EncodeToString(commonutil.ComputeSHA256(b)), nil
   209  }
   210  
   211  func (p *puller) waitForMembership() []discovery.NetworkMember {
   212  	polIteration := 0
   213  	for {
   214  		members := p.PeersOfChannel(common.ChannelID(p.channel))
   215  		if len(members) != 0 {
   216  			return members
   217  		}
   218  		polIteration++
   219  		if polIteration == maxMembershipPollIterations {
   220  			return nil
   221  		}
   222  		time.Sleep(membershipPollingBackoff)
   223  	}
   224  }
   225  
   226  func (p *puller) fetch(dig2src dig2sources) (*privdatacommon.FetchedPvtDataContainer, error) {
   227  	// computeFilters returns a map from a digest to a routing filter
   228  	dig2Filter, err := p.computeFilters(dig2src)
   229  	if err != nil {
   230  		return nil, errors.WithStack(err)
   231  	}
   232  	return p.fetchPrivateData(dig2Filter)
   233  }
   234  
   235  func (p *puller) FetchReconciledItems(dig2collectionConfig privdatacommon.Dig2CollectionConfig) (*privdatacommon.FetchedPvtDataContainer, error) {
   236  	// computeFilters returns a map from a digest to a routing filter
   237  	dig2Filter, err := p.computeReconciliationFilters(dig2collectionConfig)
   238  	if err != nil {
   239  		return nil, errors.WithStack(err)
   240  	}
   241  	return p.fetchPrivateData(dig2Filter)
   242  }
   243  
   244  func (p *puller) fetchPrivateData(dig2Filter digestToFilterMapping) (*privdatacommon.FetchedPvtDataContainer, error) {
   245  	// Get a list of peers per channel
   246  	allFilters := dig2Filter.flattenFilterValues()
   247  	members := p.waitForMembership()
   248  	p.logger.Debug("Total members in channel:", members)
   249  	members = filter.AnyMatch(members, allFilters...)
   250  	p.logger.Debug("Total members that fit some digest:", members)
   251  	if len(members) == 0 {
   252  		p.logger.Warning("Do not know any peer in the channel(", p.channel, ") that matches the policies , aborting")
   253  		return nil, errors.New("Empty membership")
   254  	}
   255  	members = randomizeMemberList(members)
   256  	res := &privdatacommon.FetchedPvtDataContainer{}
   257  	// Distribute requests to peers, and obtain subscriptions for all their messages
   258  	// matchDigestToPeer returns a map from a peer to the digests which we would ask it for
   259  	var peer2digests peer2Digests
   260  	// We expect all private RWSets represented as digests to be collected
   261  	itemsLeftToCollect := len(dig2Filter)
   262  	// As long as we still have some data to collect and new members to ask the data for:
   263  	for itemsLeftToCollect > 0 && len(members) > 0 {
   264  		purgedPvt := p.getPurgedCollections(members, dig2Filter)
   265  		// Need to remove purged digest from mapping
   266  		for _, dig := range purgedPvt {
   267  			res.PurgedElements = append(res.PurgedElements, &protosgossip.PvtDataDigest{
   268  				TxId:       dig.TxId,
   269  				BlockSeq:   dig.BlockSeq,
   270  				SeqInBlock: dig.SeqInBlock,
   271  				Namespace:  dig.Namespace,
   272  				Collection: dig.Collection,
   273  			})
   274  			// remove digest so we won't even try to pull purged data
   275  			delete(dig2Filter, dig)
   276  			itemsLeftToCollect--
   277  		}
   278  
   279  		if itemsLeftToCollect == 0 {
   280  			p.logger.Debug("No items left to collect")
   281  			return res, nil
   282  		}
   283  
   284  		peer2digests, members = p.assignDigestsToPeers(members, dig2Filter)
   285  		if len(peer2digests) == 0 {
   286  			p.logger.Warning("No available peers for digests request, "+
   287  				"cannot pull missing private data for following digests [%+v], peer membership: [%+v]",
   288  				dig2Filter.digests(), members)
   289  			return res, nil
   290  		}
   291  
   292  		p.logger.Debug("Matched", len(dig2Filter), "digests to", len(peer2digests), "peer(s)")
   293  		subscriptions := p.scatterRequests(peer2digests)
   294  		responses := p.gatherResponses(subscriptions)
   295  		for _, resp := range responses {
   296  			if len(resp.Payload) == 0 {
   297  				p.logger.Debug("Got empty response for", resp.Digest)
   298  				continue
   299  			}
   300  			delete(dig2Filter, privdatacommon.DigKey{
   301  				TxId:       resp.Digest.TxId,
   302  				BlockSeq:   resp.Digest.BlockSeq,
   303  				SeqInBlock: resp.Digest.SeqInBlock,
   304  				Namespace:  resp.Digest.Namespace,
   305  				Collection: resp.Digest.Collection,
   306  			})
   307  			itemsLeftToCollect--
   308  		}
   309  		res.AvailableElements = append(res.AvailableElements, responses...)
   310  	}
   311  	return res, nil
   312  }
   313  
   314  func (p *puller) gatherResponses(subscriptions []util.Subscription) []*protosgossip.PvtDataElement {
   315  	var res []*protosgossip.PvtDataElement
   316  	privateElements := make(chan *protosgossip.PvtDataElement, len(subscriptions))
   317  	var wg sync.WaitGroup
   318  	wg.Add(len(subscriptions))
   319  	start := time.Now()
   320  	// Listen for all subscriptions, and add then into a single channel
   321  	for _, sub := range subscriptions {
   322  		go func(sub util.Subscription) {
   323  			defer wg.Done()
   324  			el, err := sub.Listen()
   325  			if err != nil {
   326  				return
   327  			}
   328  			privateElements <- el.(*protosgossip.PvtDataElement)
   329  			p.metrics.PullDuration.With("channel", p.channel).Observe(time.Since(start).Seconds())
   330  		}(sub)
   331  	}
   332  	// Wait for all subscriptions to either return, or time out
   333  	wg.Wait()
   334  	// Close the channel, to not block when we iterate it.
   335  	close(privateElements)
   336  	// Aggregate elements to return them as a slice
   337  	for el := range privateElements {
   338  		res = append(res, el)
   339  	}
   340  	return res
   341  }
   342  
   343  func (p *puller) scatterRequests(peersDigestMapping peer2Digests) []util.Subscription {
   344  	var subscriptions []util.Subscription
   345  	for peer, digests := range peersDigestMapping {
   346  		msg := &protosgossip.GossipMessage{
   347  			Tag:     protosgossip.GossipMessage_CHAN_ONLY,
   348  			Channel: []byte(p.channel),
   349  			Nonce:   util.RandomUInt64(),
   350  			Content: &protosgossip.GossipMessage_PrivateReq{
   351  				PrivateReq: &protosgossip.RemotePvtDataRequest{
   352  					Digests: digestsAsPointerSlice(digests),
   353  				},
   354  			},
   355  		}
   356  
   357  		// Subscribe to all digests prior to sending them
   358  		for _, dig := range msg.GetPrivateReq().Digests {
   359  			hash, err := hashDigest(dig)
   360  			if err != nil {
   361  				// Shouldn't happen as we just built this message ourselves
   362  				p.logger.Warning("Failed creating digest", err)
   363  				continue
   364  			}
   365  			sub := p.pubSub.Subscribe(hash, responseWaitTime)
   366  			subscriptions = append(subscriptions, sub)
   367  		}
   368  		p.logger.Debug("Sending", peer.endpoint, "request", msg.GetPrivateReq().Digests)
   369  		p.Send(msg, peer.AsRemotePeer())
   370  	}
   371  	return subscriptions
   372  }
   373  
   374  type (
   375  	peer2Digests      map[remotePeer][]protosgossip.PvtDataDigest
   376  	noneSelectedPeers []discovery.NetworkMember
   377  )
   378  
   379  func (p *puller) assignDigestsToPeers(members []discovery.NetworkMember, dig2Filter digestToFilterMapping) (peer2Digests, noneSelectedPeers) {
   380  	if p.logger.IsEnabledFor(zapcore.DebugLevel) {
   381  		p.logger.Debug("Matching", members, "to", dig2Filter.String())
   382  	}
   383  	res := make(map[remotePeer][]protosgossip.PvtDataDigest)
   384  	// Create a mapping between peer and digests to ask for
   385  	for dig, collectionFilter := range dig2Filter {
   386  		// Find a peer that is a preferred peer
   387  		selectedPeer := filter.First(members, collectionFilter.preferredPeer)
   388  		if selectedPeer == nil {
   389  			p.logger.Debug("No preferred peer found for", dig)
   390  			// Find some peer that is in the collection
   391  			selectedPeer = filter.First(members, collectionFilter.anyPeer)
   392  		}
   393  		if selectedPeer == nil {
   394  			p.logger.Debug("No peer matches txID", dig.TxId, "collection", dig.Collection)
   395  			continue
   396  		}
   397  		// Add the peer to the mapping from peer to digest slice
   398  		peer := remotePeer{pkiID: string(selectedPeer.PKIID), endpoint: selectedPeer.Endpoint}
   399  		res[peer] = append(res[peer], protosgossip.PvtDataDigest{
   400  			TxId:       dig.TxId,
   401  			BlockSeq:   dig.BlockSeq,
   402  			SeqInBlock: dig.SeqInBlock,
   403  			Namespace:  dig.Namespace,
   404  			Collection: dig.Collection,
   405  		})
   406  	}
   407  
   408  	var noneSelectedPeers []discovery.NetworkMember
   409  	for _, member := range members {
   410  		peer := remotePeer{endpoint: member.PreferredEndpoint(), pkiID: string(member.PKIid)}
   411  		if _, selected := res[peer]; !selected {
   412  			noneSelectedPeers = append(noneSelectedPeers, member)
   413  		}
   414  	}
   415  
   416  	return res, noneSelectedPeers
   417  }
   418  
   419  type collectionRoutingFilter struct {
   420  	anyPeer       filter.RoutingFilter
   421  	preferredPeer filter.RoutingFilter
   422  }
   423  
   424  type digestToFilterMapping map[privdatacommon.DigKey]collectionRoutingFilter
   425  
   426  func (dig2f digestToFilterMapping) flattenFilterValues() []filter.RoutingFilter {
   427  	var filters []filter.RoutingFilter
   428  	for _, f := range dig2f {
   429  		filters = append(filters, f.preferredPeer)
   430  		filters = append(filters, f.anyPeer)
   431  	}
   432  	return filters
   433  }
   434  
   435  func (dig2f digestToFilterMapping) digests() []protosgossip.PvtDataDigest {
   436  	var digs []protosgossip.PvtDataDigest
   437  	for d := range dig2f {
   438  		digs = append(digs, protosgossip.PvtDataDigest{
   439  			TxId:       d.TxId,
   440  			BlockSeq:   d.BlockSeq,
   441  			SeqInBlock: d.SeqInBlock,
   442  			Namespace:  d.Namespace,
   443  			Collection: d.Collection,
   444  		})
   445  	}
   446  	return digs
   447  }
   448  
   449  // String returns a string representation of t he digestToFilterMapping
   450  func (dig2f digestToFilterMapping) String() string {
   451  	var buffer bytes.Buffer
   452  	collection2TxID := make(map[string][]string)
   453  	for dig := range dig2f {
   454  		collection2TxID[dig.Collection] = append(collection2TxID[dig.Collection], dig.TxId)
   455  	}
   456  	for col, txIDs := range collection2TxID {
   457  		buffer.WriteString(fmt.Sprintf("{%s: %v}", col, txIDs))
   458  	}
   459  	return buffer.String()
   460  }
   461  
   462  func (p *puller) computeFilters(dig2src dig2sources) (digestToFilterMapping, error) {
   463  	filters := make(map[privdatacommon.DigKey]collectionRoutingFilter)
   464  	for digest, sources := range dig2src {
   465  		anyPeerInCollection, err := p.getLatestCollectionConfigRoutingFilter(digest.Namespace, digest.Collection)
   466  		if err != nil {
   467  			return nil, errors.WithStack(err)
   468  		}
   469  
   470  		sources := sources
   471  		endorserPeer, err := p.PeerFilter(common.ChannelID(p.channel), func(peerSignature api.PeerSignature) bool {
   472  			for _, endorsement := range sources {
   473  				if bytes.Equal(endorsement.Endorser, []byte(peerSignature.PeerIdentity)) {
   474  					return true
   475  				}
   476  			}
   477  			return false
   478  		})
   479  		if err != nil {
   480  			return nil, errors.WithStack(err)
   481  		}
   482  
   483  		filters[digest] = collectionRoutingFilter{
   484  			anyPeer:       anyPeerInCollection,
   485  			preferredPeer: endorserPeer,
   486  		}
   487  	}
   488  	return filters, nil
   489  }
   490  
   491  func (p *puller) computeReconciliationFilters(dig2collectionConfig privdatacommon.Dig2CollectionConfig) (digestToFilterMapping, error) {
   492  	filters := make(map[privdatacommon.DigKey]collectionRoutingFilter)
   493  	for digest, originalCollectionConfig := range dig2collectionConfig {
   494  		anyPeerInCollection, err := p.getLatestCollectionConfigRoutingFilter(digest.Namespace, digest.Collection)
   495  		if err != nil {
   496  			return nil, err
   497  		}
   498  
   499  		originalConfigFilter, err := p.cs.AccessFilter(p.channel, originalCollectionConfig.MemberOrgsPolicy)
   500  		if err != nil {
   501  			return nil, err
   502  		}
   503  		if originalConfigFilter == nil {
   504  			return nil, errors.Errorf("Failed obtaining original collection filter for channel %s, config %s", p.channel, digest.Collection)
   505  		}
   506  
   507  		// get peers that were in the collection config while the missing data was created
   508  		peerFromDataCreation, err := p.getMatchAllRoutingFilter(originalConfigFilter)
   509  		if err != nil {
   510  			return nil, err
   511  		}
   512  
   513  		// prefer peers that are in the collection from the time the data was created rather than ones that were added later.
   514  		// the assumption is that the longer the peer is in the collection config, the chances it has the data are bigger.
   515  		preferredPeer := func(member discovery.NetworkMember) bool {
   516  			return peerFromDataCreation(member) && anyPeerInCollection(member)
   517  		}
   518  
   519  		filters[digest] = collectionRoutingFilter{
   520  			anyPeer:       anyPeerInCollection,
   521  			preferredPeer: preferredPeer,
   522  		}
   523  	}
   524  	return filters, nil
   525  }
   526  
   527  func (p *puller) getLatestCollectionConfigRoutingFilter(chaincode string, collection string) (filter.RoutingFilter, error) {
   528  	cc := privdata.CollectionCriteria{
   529  		Channel:    p.channel,
   530  		Collection: collection,
   531  		Namespace:  chaincode,
   532  	}
   533  
   534  	latestCollectionConfig, err := p.cs.RetrieveCollectionAccessPolicy(cc)
   535  	if err != nil {
   536  		return nil, errors.WithMessagef(err, "failed obtaining collection policy for channel %s, chaincode %s, config %s", p.channel, chaincode, collection)
   537  	}
   538  
   539  	filt := latestCollectionConfig.AccessFilter()
   540  	if filt == nil {
   541  		return nil, errors.Errorf("Failed obtaining collection filter for channel %s, chaincode %s, collection %s", p.channel, chaincode, collection)
   542  	}
   543  
   544  	anyPeerInCollection, err := p.getMatchAllRoutingFilter(filt)
   545  	if err != nil {
   546  		return nil, err
   547  	}
   548  
   549  	return anyPeerInCollection, nil
   550  }
   551  
   552  func (p *puller) getMatchAllRoutingFilter(filt privdata.Filter) (filter.RoutingFilter, error) {
   553  	routingFilter, err := p.PeerFilter(common.ChannelID(p.channel), func(peerSignature api.PeerSignature) bool {
   554  		return filt(protoutil.SignedData{
   555  			Signature: peerSignature.Signature,
   556  			Identity:  peerSignature.PeerIdentity,
   557  			Data:      peerSignature.Message,
   558  		})
   559  	})
   560  	return routingFilter, err
   561  }
   562  
   563  func (p *puller) getPurgedCollections(members []discovery.NetworkMember, dig2Filter digestToFilterMapping) []privdatacommon.DigKey {
   564  	var res []privdatacommon.DigKey
   565  	for dig := range dig2Filter {
   566  		purged, err := p.purgedFilter(dig)
   567  		if err != nil {
   568  			p.logger.Debugf("Failed to obtain purged filter for digest %v error %v", dig, err)
   569  			continue
   570  		}
   571  
   572  		membersWithPurgedData := filter.AnyMatch(members, purged)
   573  		// at least one peer already purged the data
   574  		if len(membersWithPurgedData) > 0 {
   575  			p.logger.Debugf("Private data on channel [%s], chaincode [%s], collection name [%s] for txID = [%s],"+
   576  				"has been purged at peers [%v]", p.channel, dig.Namespace,
   577  				dig.Collection, dig.TxId, membersWithPurgedData)
   578  			res = append(res, dig)
   579  		}
   580  	}
   581  	return res
   582  }
   583  
   584  func (p *puller) purgedFilter(dig privdatacommon.DigKey) (filter.RoutingFilter, error) {
   585  	cc := privdata.CollectionCriteria{
   586  		Channel:    p.channel,
   587  		Collection: dig.Collection,
   588  		Namespace:  dig.Namespace,
   589  	}
   590  	colPersistConfig, err := p.cs.RetrieveCollectionPersistenceConfigs(cc)
   591  	if err != nil {
   592  		return nil, errors.WithStack(err)
   593  	}
   594  
   595  	return func(peer discovery.NetworkMember) bool {
   596  		if peer.Properties == nil {
   597  			p.logger.Debugf("No properties provided for peer %s", peer.Endpoint)
   598  			return false
   599  		}
   600  		// BTL equals to zero has semantic of never expires
   601  		if colPersistConfig.BlockToLive() == uint64(0) {
   602  			return false
   603  		}
   604  		// handle overflow
   605  		expirationSeqNum := addWithOverflow(dig.BlockSeq, colPersistConfig.BlockToLive())
   606  		peerLedgerHeightWithMargin := addWithOverflow(peer.Properties.LedgerHeight, p.btlPullMargin)
   607  
   608  		isPurged := peerLedgerHeightWithMargin >= expirationSeqNum
   609  		if isPurged {
   610  			p.logger.Debugf("skipping peer [%s], since pvt for channel [%s], txID = [%s], "+
   611  				"collection [%s] has been purged or will soon be purged, BTL=[%d]",
   612  				peer.Endpoint, p.channel, dig.TxId, cc.Collection, colPersistConfig.BlockToLive())
   613  		}
   614  		return isPurged
   615  	}, nil
   616  }
   617  
   618  func (p *puller) filterNotEligible(dig2rwSets Dig2PvtRWSetWithConfig, shouldCheckLatestConfig bool, signedData protoutil.SignedData, endpoint string) []*protosgossip.PvtDataElement {
   619  	var returned []*protosgossip.PvtDataElement
   620  	for d, rwSets := range dig2rwSets {
   621  		if rwSets == nil {
   622  			p.logger.Errorf("No private rwset for [%s] channel, chaincode [%s], collection [%s], txID = [%s] is available, skipping...",
   623  				p.channel, d.Namespace, d.Collection, d.TxId)
   624  			continue
   625  		}
   626  		p.logger.Debug("Found", len(rwSets.RWSet), "for TxID", d.TxId, ", collection", d.Collection, "for", endpoint)
   627  		if len(rwSets.RWSet) == 0 {
   628  			continue
   629  		}
   630  
   631  		eligibleForCollection := shouldCheckLatestConfig && p.isEligibleByLatestConfig(p.channel, d.Collection, d.Namespace, signedData)
   632  
   633  		if !eligibleForCollection {
   634  			colAP, err := p.AccessPolicy(rwSets.CollectionConfig, p.channel)
   635  			if err != nil {
   636  				p.logger.Debug("No policy found for channel", p.channel, ", collection", d.Collection, "txID", d.TxId, ":", err, "skipping...")
   637  				continue
   638  			}
   639  			colFilter := colAP.AccessFilter()
   640  			if colFilter == nil {
   641  				p.logger.Debug("Collection ", d.Collection, " has no access filter, txID", d.TxId, "skipping...")
   642  				continue
   643  			}
   644  			eligibleForCollection = colFilter(signedData)
   645  		}
   646  
   647  		if !eligibleForCollection {
   648  			p.logger.Debug("Peer", endpoint, "isn't eligible for txID", d.TxId, "at collection", d.Collection)
   649  			continue
   650  		}
   651  
   652  		returned = append(returned, &protosgossip.PvtDataElement{
   653  			Digest: &protosgossip.PvtDataDigest{
   654  				TxId:       d.TxId,
   655  				BlockSeq:   d.BlockSeq,
   656  				Collection: d.Collection,
   657  				Namespace:  d.Namespace,
   658  				SeqInBlock: d.SeqInBlock,
   659  			},
   660  			Payload: util.PrivateRWSets(rwSets.RWSet...),
   661  		})
   662  	}
   663  	return returned
   664  }
   665  
   666  func (p *puller) isEligibleByLatestConfig(channel string, collection string, chaincode string, signedData protoutil.SignedData) bool {
   667  	cc := privdata.CollectionCriteria{
   668  		Channel:    channel,
   669  		Collection: collection,
   670  		Namespace:  chaincode,
   671  	}
   672  
   673  	latestCollectionConfig, err := p.cs.RetrieveCollectionAccessPolicy(cc)
   674  	if err != nil {
   675  		return false
   676  	}
   677  
   678  	collectionFilter := latestCollectionConfig.AccessFilter()
   679  	return collectionFilter(signedData)
   680  }
   681  
   682  func randomizeMemberList(members []discovery.NetworkMember) []discovery.NetworkMember {
   683  	rand.Seed(time.Now().UnixNano())
   684  	res := make([]discovery.NetworkMember, len(members))
   685  	for i, j := range rand.Perm(len(members)) {
   686  		res[i] = members[j]
   687  	}
   688  	return res
   689  }
   690  
   691  func digestsAsPointerSlice(digests []protosgossip.PvtDataDigest) []*protosgossip.PvtDataDigest {
   692  	res := make([]*protosgossip.PvtDataDigest, len(digests))
   693  	for i, dig := range digests {
   694  		// re-introduce dig variable to allocate
   695  		// new address for each iteration
   696  		dig := dig
   697  		res[i] = &dig
   698  	}
   699  	return res
   700  }
   701  
   702  type remotePeer struct {
   703  	endpoint string
   704  	pkiID    string
   705  }
   706  
   707  // AsRemotePeer converts this remotePeer to comm.RemotePeer
   708  func (rp remotePeer) AsRemotePeer() *comm.RemotePeer {
   709  	return &comm.RemotePeer{
   710  		PKIID:    common.PKIidType(rp.pkiID),
   711  		Endpoint: rp.endpoint,
   712  	}
   713  }
   714  
   715  func addWithOverflow(a uint64, b uint64) uint64 {
   716  	res := a + b
   717  	if res < a {
   718  		return math.MaxUint64
   719  	}
   720  	return res
   721  }