github.com/ava-labs/avalanchego@v1.11.11/network/p2p/gossip/bloom.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package gossip 5 6 import ( 7 "crypto/rand" 8 9 "github.com/prometheus/client_golang/prometheus" 10 11 "github.com/ava-labs/avalanchego/ids" 12 "github.com/ava-labs/avalanchego/utils/bloom" 13 ) 14 15 // NewBloomFilter returns a new instance of a bloom filter with at least [minTargetElements] elements 16 // anticipated at any moment, and a false positive probability of [targetFalsePositiveProbability]. If the 17 // false positive probability exceeds [resetFalsePositiveProbability], the bloom filter will be reset. 18 // 19 // Invariant: The returned bloom filter is not safe to reset concurrently with 20 // other operations. However, it is otherwise safe to access concurrently. 21 func NewBloomFilter( 22 registerer prometheus.Registerer, 23 namespace string, 24 minTargetElements int, 25 targetFalsePositiveProbability, 26 resetFalsePositiveProbability float64, 27 ) (*BloomFilter, error) { 28 metrics, err := bloom.NewMetrics(namespace, registerer) 29 if err != nil { 30 return nil, err 31 } 32 filter := &BloomFilter{ 33 minTargetElements: minTargetElements, 34 targetFalsePositiveProbability: targetFalsePositiveProbability, 35 resetFalsePositiveProbability: resetFalsePositiveProbability, 36 37 metrics: metrics, 38 } 39 err = resetBloomFilter( 40 filter, 41 minTargetElements, 42 targetFalsePositiveProbability, 43 resetFalsePositiveProbability, 44 ) 45 return filter, err 46 } 47 48 type BloomFilter struct { 49 minTargetElements int 50 targetFalsePositiveProbability float64 51 resetFalsePositiveProbability float64 52 53 metrics *bloom.Metrics 54 55 maxCount int 56 bloom *bloom.Filter 57 // salt is provided to eventually unblock collisions in Bloom. It's possible 58 // that conflicting Gossipable items collide in the bloom filter, so a salt 59 // is generated to eventually resolve collisions. 60 salt ids.ID 61 } 62 63 func (b *BloomFilter) Add(gossipable Gossipable) { 64 h := gossipable.GossipID() 65 bloom.Add(b.bloom, h[:], b.salt[:]) 66 b.metrics.Count.Inc() 67 } 68 69 func (b *BloomFilter) Has(gossipable Gossipable) bool { 70 h := gossipable.GossipID() 71 return bloom.Contains(b.bloom, h[:], b.salt[:]) 72 } 73 74 func (b *BloomFilter) Marshal() ([]byte, []byte) { 75 bloomBytes := b.bloom.Marshal() 76 // salt must be copied here to ensure the bytes aren't overwritten if salt 77 // is later modified. 78 salt := b.salt 79 return bloomBytes, salt[:] 80 } 81 82 // ResetBloomFilterIfNeeded resets a bloom filter if it breaches [targetFalsePositiveProbability]. 83 // 84 // If [targetElements] exceeds [minTargetElements], the size of the bloom filter will grow to maintain 85 // the same [targetFalsePositiveProbability]. 86 // 87 // Returns true if the bloom filter was reset. 88 func ResetBloomFilterIfNeeded( 89 bloomFilter *BloomFilter, 90 targetElements int, 91 ) (bool, error) { 92 if bloomFilter.bloom.Count() <= bloomFilter.maxCount { 93 return false, nil 94 } 95 96 targetElements = max(bloomFilter.minTargetElements, targetElements) 97 err := resetBloomFilter( 98 bloomFilter, 99 targetElements, 100 bloomFilter.targetFalsePositiveProbability, 101 bloomFilter.resetFalsePositiveProbability, 102 ) 103 return err == nil, err 104 } 105 106 func resetBloomFilter( 107 bloomFilter *BloomFilter, 108 targetElements int, 109 targetFalsePositiveProbability, 110 resetFalsePositiveProbability float64, 111 ) error { 112 numHashes, numEntries := bloom.OptimalParameters( 113 targetElements, 114 targetFalsePositiveProbability, 115 ) 116 newBloom, err := bloom.New(numHashes, numEntries) 117 if err != nil { 118 return err 119 } 120 var newSalt ids.ID 121 if _, err := rand.Read(newSalt[:]); err != nil { 122 return err 123 } 124 125 bloomFilter.maxCount = bloom.EstimateCount(numHashes, numEntries, resetFalsePositiveProbability) 126 bloomFilter.bloom = newBloom 127 bloomFilter.salt = newSalt 128 129 bloomFilter.metrics.Reset(newBloom, bloomFilter.maxCount) 130 return nil 131 }