github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/gossip/privdata/pull.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 "math" 14 "math/rand" 15 "sync" 16 "time" 17 18 protosgossip "github.com/hyperledger/fabric-protos-go/gossip" 19 commonutil "github.com/osdi23p228/fabric/common/util" 20 "github.com/osdi23p228/fabric/core/common/privdata" 21 "github.com/osdi23p228/fabric/gossip/api" 22 "github.com/osdi23p228/fabric/gossip/comm" 23 "github.com/osdi23p228/fabric/gossip/common" 24 "github.com/osdi23p228/fabric/gossip/discovery" 25 "github.com/osdi23p228/fabric/gossip/filter" 26 "github.com/osdi23p228/fabric/gossip/metrics" 27 privdatacommon "github.com/osdi23p228/fabric/gossip/privdata/common" 28 "github.com/osdi23p228/fabric/gossip/protoext" 29 "github.com/osdi23p228/fabric/gossip/util" 30 "github.com/osdi23p228/fabric/protoutil" 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 peer2Digests map[remotePeer][]protosgossip.PvtDataDigest 375 type noneSelectedPeers []discovery.NetworkMember 376 377 func (p *puller) assignDigestsToPeers(members []discovery.NetworkMember, dig2Filter digestToFilterMapping) (peer2Digests, noneSelectedPeers) { 378 if p.logger.IsEnabledFor(zapcore.DebugLevel) { 379 p.logger.Debug("Matching", members, "to", dig2Filter.String()) 380 } 381 res := make(map[remotePeer][]protosgossip.PvtDataDigest) 382 // Create a mapping between peer and digests to ask for 383 for dig, collectionFilter := range dig2Filter { 384 // Find a peer that is a preferred peer 385 selectedPeer := filter.First(members, collectionFilter.preferredPeer) 386 if selectedPeer == nil { 387 p.logger.Debug("No preferred peer found for", dig) 388 // Find some peer that is in the collection 389 selectedPeer = filter.First(members, collectionFilter.anyPeer) 390 } 391 if selectedPeer == nil { 392 p.logger.Debug("No peer matches txID", dig.TxId, "collection", dig.Collection) 393 continue 394 } 395 // Add the peer to the mapping from peer to digest slice 396 peer := remotePeer{pkiID: string(selectedPeer.PKIID), endpoint: selectedPeer.Endpoint} 397 res[peer] = append(res[peer], protosgossip.PvtDataDigest{ 398 TxId: dig.TxId, 399 BlockSeq: dig.BlockSeq, 400 SeqInBlock: dig.SeqInBlock, 401 Namespace: dig.Namespace, 402 Collection: dig.Collection, 403 }) 404 } 405 406 var noneSelectedPeers []discovery.NetworkMember 407 for _, member := range members { 408 peer := remotePeer{endpoint: member.PreferredEndpoint(), pkiID: string(member.PKIid)} 409 if _, selected := res[peer]; !selected { 410 noneSelectedPeers = append(noneSelectedPeers, member) 411 } 412 } 413 414 return res, noneSelectedPeers 415 } 416 417 type collectionRoutingFilter struct { 418 anyPeer filter.RoutingFilter 419 preferredPeer filter.RoutingFilter 420 } 421 422 type digestToFilterMapping map[privdatacommon.DigKey]collectionRoutingFilter 423 424 func (dig2f digestToFilterMapping) flattenFilterValues() []filter.RoutingFilter { 425 var filters []filter.RoutingFilter 426 for _, f := range dig2f { 427 filters = append(filters, f.preferredPeer) 428 filters = append(filters, f.anyPeer) 429 } 430 return filters 431 } 432 433 func (dig2f digestToFilterMapping) digests() []protosgossip.PvtDataDigest { 434 var digs []protosgossip.PvtDataDigest 435 for d := range dig2f { 436 digs = append(digs, protosgossip.PvtDataDigest{ 437 TxId: d.TxId, 438 BlockSeq: d.BlockSeq, 439 SeqInBlock: d.SeqInBlock, 440 Namespace: d.Namespace, 441 Collection: d.Collection, 442 }) 443 } 444 return digs 445 } 446 447 // String returns a string representation of t he digestToFilterMapping 448 func (dig2f digestToFilterMapping) String() string { 449 var buffer bytes.Buffer 450 collection2TxID := make(map[string][]string) 451 for dig := range dig2f { 452 collection2TxID[dig.Collection] = append(collection2TxID[dig.Collection], dig.TxId) 453 } 454 for col, txIDs := range collection2TxID { 455 buffer.WriteString(fmt.Sprintf("{%s: %v}", col, txIDs)) 456 } 457 return buffer.String() 458 } 459 460 func (p *puller) computeFilters(dig2src dig2sources) (digestToFilterMapping, error) { 461 filters := make(map[privdatacommon.DigKey]collectionRoutingFilter) 462 for digest, sources := range dig2src { 463 anyPeerInCollection, err := p.getLatestCollectionConfigRoutingFilter(digest.Namespace, digest.Collection) 464 if err != nil { 465 return nil, errors.WithStack(err) 466 } 467 468 sources := sources 469 endorserPeer, err := p.PeerFilter(common.ChannelID(p.channel), func(peerSignature api.PeerSignature) bool { 470 for _, endorsement := range sources { 471 if bytes.Equal(endorsement.Endorser, []byte(peerSignature.PeerIdentity)) { 472 return true 473 } 474 } 475 return false 476 }) 477 if err != nil { 478 return nil, errors.WithStack(err) 479 } 480 481 filters[digest] = collectionRoutingFilter{ 482 anyPeer: anyPeerInCollection, 483 preferredPeer: endorserPeer, 484 } 485 } 486 return filters, nil 487 } 488 489 func (p *puller) computeReconciliationFilters(dig2collectionConfig privdatacommon.Dig2CollectionConfig) (digestToFilterMapping, error) { 490 filters := make(map[privdatacommon.DigKey]collectionRoutingFilter) 491 for digest, originalCollectionConfig := range dig2collectionConfig { 492 anyPeerInCollection, err := p.getLatestCollectionConfigRoutingFilter(digest.Namespace, digest.Collection) 493 if err != nil { 494 return nil, err 495 } 496 497 originalConfigFilter, err := p.cs.AccessFilter(p.channel, originalCollectionConfig.MemberOrgsPolicy) 498 if err != nil { 499 return nil, err 500 } 501 if originalConfigFilter == nil { 502 return nil, errors.Errorf("Failed obtaining original collection filter for channel %s, config %s", p.channel, digest.Collection) 503 } 504 505 // get peers that were in the collection config while the missing data was created 506 peerFromDataCreation, err := p.getMatchAllRoutingFilter(originalConfigFilter) 507 if err != nil { 508 return nil, err 509 } 510 511 // prefer peers that are in the collection from the time the data was created rather than ones that were added later. 512 // the assumption is that the longer the peer is in the collection config, the chances it has the data are bigger. 513 preferredPeer := func(member discovery.NetworkMember) bool { 514 return peerFromDataCreation(member) && anyPeerInCollection(member) 515 } 516 517 filters[digest] = collectionRoutingFilter{ 518 anyPeer: anyPeerInCollection, 519 preferredPeer: preferredPeer, 520 } 521 } 522 return filters, nil 523 } 524 525 func (p *puller) getLatestCollectionConfigRoutingFilter(chaincode string, collection string) (filter.RoutingFilter, error) { 526 cc := privdata.CollectionCriteria{ 527 Channel: p.channel, 528 Collection: collection, 529 Namespace: chaincode, 530 } 531 532 latestCollectionConfig, err := p.cs.RetrieveCollectionAccessPolicy(cc) 533 if err != nil { 534 return nil, errors.WithMessagef(err, "failed obtaining collection policy for channel %s, chaincode %s, config %s", p.channel, chaincode, collection) 535 } 536 537 filt := latestCollectionConfig.AccessFilter() 538 if filt == nil { 539 return nil, errors.Errorf("Failed obtaining collection filter for channel %s, chaincode %s, collection %s", p.channel, chaincode, collection) 540 } 541 542 anyPeerInCollection, err := p.getMatchAllRoutingFilter(filt) 543 if err != nil { 544 return nil, err 545 } 546 547 return anyPeerInCollection, nil 548 } 549 550 func (p *puller) getMatchAllRoutingFilter(filt privdata.Filter) (filter.RoutingFilter, error) { 551 routingFilter, err := p.PeerFilter(common.ChannelID(p.channel), func(peerSignature api.PeerSignature) bool { 552 return filt(protoutil.SignedData{ 553 Signature: peerSignature.Signature, 554 Identity: peerSignature.PeerIdentity, 555 Data: peerSignature.Message, 556 }) 557 }) 558 return routingFilter, err 559 } 560 561 func (p *puller) getPurgedCollections(members []discovery.NetworkMember, dig2Filter digestToFilterMapping) []privdatacommon.DigKey { 562 var res []privdatacommon.DigKey 563 for dig := range dig2Filter { 564 purged, err := p.purgedFilter(dig) 565 if err != nil { 566 p.logger.Debug("Failed to obtain purged filter for digest %v", dig, "error", err) 567 continue 568 } 569 570 membersWithPurgedData := filter.AnyMatch(members, purged) 571 // at least one peer already purged the data 572 if len(membersWithPurgedData) > 0 { 573 p.logger.Debugf("Private data on channel [%s], chaincode [%s], collection name [%s] for txID = [%s],"+ 574 "has been purged at peers [%v]", p.channel, dig.Namespace, 575 dig.Collection, dig.TxId, membersWithPurgedData) 576 res = append(res, dig) 577 } 578 } 579 return res 580 } 581 582 func (p *puller) purgedFilter(dig privdatacommon.DigKey) (filter.RoutingFilter, error) { 583 cc := privdata.CollectionCriteria{ 584 Channel: p.channel, 585 Collection: dig.Collection, 586 Namespace: dig.Namespace, 587 } 588 colPersistConfig, err := p.cs.RetrieveCollectionPersistenceConfigs(cc) 589 if err != nil { 590 return nil, errors.WithStack(err) 591 } 592 593 return func(peer discovery.NetworkMember) bool { 594 if peer.Properties == nil { 595 p.logger.Debugf("No properties provided for peer %s", peer.Endpoint) 596 return false 597 } 598 // BTL equals to zero has semantic of never expires 599 if colPersistConfig.BlockToLive() == uint64(0) { 600 return false 601 } 602 // handle overflow 603 expirationSeqNum := addWithOverflow(dig.BlockSeq, colPersistConfig.BlockToLive()) 604 peerLedgerHeightWithMargin := addWithOverflow(peer.Properties.LedgerHeight, p.btlPullMargin) 605 606 isPurged := peerLedgerHeightWithMargin >= expirationSeqNum 607 if isPurged { 608 p.logger.Debugf("skipping peer [%s], since pvt for channel [%s], txID = [%s], "+ 609 "collection [%s] has been purged or will soon be purged, BTL=[%d]", 610 peer.Endpoint, p.channel, dig.TxId, cc.Collection, colPersistConfig.BlockToLive()) 611 } 612 return isPurged 613 }, nil 614 } 615 616 func (p *puller) filterNotEligible(dig2rwSets Dig2PvtRWSetWithConfig, shouldCheckLatestConfig bool, signedData protoutil.SignedData, endpoint string) []*protosgossip.PvtDataElement { 617 var returned []*protosgossip.PvtDataElement 618 for d, rwSets := range dig2rwSets { 619 if rwSets == nil { 620 p.logger.Errorf("No private rwset for [%s] channel, chaincode [%s], collection [%s], txID = [%s] is available, skipping...", 621 p.channel, d.Namespace, d.Collection, d.TxId) 622 continue 623 } 624 p.logger.Debug("Found", len(rwSets.RWSet), "for TxID", d.TxId, ", collection", d.Collection, "for", endpoint) 625 if len(rwSets.RWSet) == 0 { 626 continue 627 } 628 629 eligibleForCollection := shouldCheckLatestConfig && p.isEligibleByLatestConfig(p.channel, d.Collection, d.Namespace, signedData) 630 631 if !eligibleForCollection { 632 colAP, err := p.AccessPolicy(rwSets.CollectionConfig, p.channel) 633 if err != nil { 634 p.logger.Debug("No policy found for channel", p.channel, ", collection", d.Collection, "txID", d.TxId, ":", err, "skipping...") 635 continue 636 } 637 colFilter := colAP.AccessFilter() 638 if colFilter == nil { 639 p.logger.Debug("Collection ", d.Collection, " has no access filter, txID", d.TxId, "skipping...") 640 continue 641 } 642 eligibleForCollection = colFilter(signedData) 643 } 644 645 if !eligibleForCollection { 646 p.logger.Debug("Peer", endpoint, "isn't eligible for txID", d.TxId, "at collection", d.Collection) 647 continue 648 } 649 650 returned = append(returned, &protosgossip.PvtDataElement{ 651 Digest: &protosgossip.PvtDataDigest{ 652 TxId: d.TxId, 653 BlockSeq: d.BlockSeq, 654 Collection: d.Collection, 655 Namespace: d.Namespace, 656 SeqInBlock: d.SeqInBlock, 657 }, 658 Payload: util.PrivateRWSets(rwSets.RWSet...), 659 }) 660 } 661 return returned 662 } 663 664 func (p *puller) isEligibleByLatestConfig(channel string, collection string, chaincode string, signedData protoutil.SignedData) bool { 665 cc := privdata.CollectionCriteria{ 666 Channel: channel, 667 Collection: collection, 668 Namespace: chaincode, 669 } 670 671 latestCollectionConfig, err := p.cs.RetrieveCollectionAccessPolicy(cc) 672 if err != nil { 673 return false 674 } 675 676 collectionFilter := latestCollectionConfig.AccessFilter() 677 return collectionFilter(signedData) 678 } 679 680 func randomizeMemberList(members []discovery.NetworkMember) []discovery.NetworkMember { 681 rand.Seed(time.Now().UnixNano()) 682 res := make([]discovery.NetworkMember, len(members)) 683 for i, j := range rand.Perm(len(members)) { 684 res[i] = members[j] 685 } 686 return res 687 } 688 689 func digestsAsPointerSlice(digests []protosgossip.PvtDataDigest) []*protosgossip.PvtDataDigest { 690 res := make([]*protosgossip.PvtDataDigest, len(digests)) 691 for i, dig := range digests { 692 // re-introduce dig variable to allocate 693 // new address for each iteration 694 dig := dig 695 res[i] = &dig 696 } 697 return res 698 } 699 700 type remotePeer struct { 701 endpoint string 702 pkiID string 703 } 704 705 // AsRemotePeer converts this remotePeer to comm.RemotePeer 706 func (rp remotePeer) AsRemotePeer() *comm.RemotePeer { 707 return &comm.RemotePeer{ 708 PKIID: common.PKIidType(rp.pkiID), 709 Endpoint: rp.endpoint, 710 } 711 } 712 713 func addWithOverflow(a uint64, b uint64) uint64 { 714 res := a + b 715 if res < a { 716 return math.MaxUint64 717 } 718 return res 719 }