github.com/status-im/status-go@v1.1.0/protocol/communities/community_bloom_filter.go (about) 1 package communities 2 3 import ( 4 "crypto/ecdsa" 5 "encoding/binary" 6 "errors" 7 "math/bits" 8 9 "github.com/bits-and-blooms/bloom/v3" 10 11 "github.com/status-im/status-go/eth-node/crypto" 12 "github.com/status-im/status-go/protocol/common" 13 "github.com/status-im/status-go/protocol/encryption" 14 "github.com/status-im/status-go/protocol/protobuf" 15 ) 16 17 func generateBloomFiltersForChannels(description *protobuf.CommunityDescription, privateKey *ecdsa.PrivateKey) error { 18 for channelID, channel := range description.Chats { 19 if !channelEncrypted(ChatID(description.ID, channelID), description.TokenPermissions) { 20 continue 21 } 22 23 filter, err := generateBloomFilter(channel.Members, privateKey, channelID, description.Clock) 24 if err != nil { 25 return err 26 } 27 28 marshaledFilter, err := filter.MarshalBinary() 29 if err != nil { 30 return err 31 } 32 33 channel.MembersList = &protobuf.CommunityBloomFilter{ 34 Data: marshaledFilter, 35 M: uint64(filter.Cap()), 36 K: uint64(filter.K()), 37 } 38 } 39 40 return nil 41 } 42 43 func nextPowerOfTwo(x int) uint { 44 return 1 << bits.Len(uint(x)) 45 } 46 47 func max(x, y uint) uint { 48 if x > y { 49 return x 50 } 51 return y 52 } 53 54 func generateBloomFilter(members map[string]*protobuf.CommunityMember, privateKey *ecdsa.PrivateKey, channelID string, clock uint64) (*bloom.BloomFilter, error) { 55 membersCount := len(members) 56 if membersCount == 0 { 57 return nil, errors.New("invalid members count") 58 } 59 60 const falsePositiveRate = 0.001 61 numberOfItems := max(128, nextPowerOfTwo(membersCount)) // This makes it difficult to guess the exact number of members, even with knowledge of filter size and parameters. 62 filter := bloom.NewWithEstimates(numberOfItems, falsePositiveRate) 63 64 for pk := range members { 65 publicKey, err := common.HexToPubkey(pk) 66 if err != nil { 67 return nil, err 68 } 69 70 value, err := bloomFilterValue(privateKey, publicKey, channelID, clock) 71 if err != nil { 72 return nil, err 73 } 74 75 filter.Add(value) 76 } 77 78 return filter, nil 79 } 80 81 func verifyMembershipWithBloomFilter(membersList *protobuf.CommunityBloomFilter, privateKey *ecdsa.PrivateKey, publicKey *ecdsa.PublicKey, channelID string, clock uint64) (bool, error) { 82 filter := bloom.New(uint(membersList.M), uint(membersList.K)) 83 err := filter.UnmarshalBinary(membersList.Data) 84 if err != nil { 85 return false, err 86 } 87 88 value, err := bloomFilterValue(privateKey, publicKey, channelID, clock) 89 if err != nil { 90 return false, err 91 } 92 93 return filter.Test(value), nil 94 } 95 96 func bloomFilterValue(privateKey *ecdsa.PrivateKey, publicKey *ecdsa.PublicKey, channelID string, clock uint64) ([]byte, error) { 97 sharedSecret, err := encryption.GenerateSharedKey(privateKey, publicKey) 98 if err != nil { 99 return nil, err 100 } 101 102 clockBytes := make([]byte, 8) 103 binary.LittleEndian.PutUint64(clockBytes, clock) 104 105 return crypto.Keccak256(sharedSecret, []byte(channelID), clockBytes), nil 106 }