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