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 }