feat_: phase-1 of use single content-topic to send messages for all community chats

This commit is contained in:
Prem Chaitanya Prathi 2024-11-01 17:13:40 +05:30
parent 768cda8de9
commit 103b415144
No known key found for this signature in database
11 changed files with 103 additions and 36 deletions

View File

@ -19,6 +19,7 @@ type NewMessage struct {
TargetPeer string `json:"targetPeer"` TargetPeer string `json:"targetPeer"`
Ephemeral bool `json:"ephemeral"` Ephemeral bool `json:"ephemeral"`
Priority *int `json:"priority"` Priority *int `json:"priority"`
ContentTopicOverride string `json:"contentTopicOverride"`
} }
// Message is the RPC representation of a whisper message. // Message is the RPC representation of a whisper message.

View File

@ -665,6 +665,7 @@ func (s *MessageSender) dispatchCommunityChatMessage(ctx context.Context, rawMes
PowTarget: calculatePoW(payload), PowTarget: calculatePoW(payload),
PowTime: whisperPoWTime, PowTime: whisperPoWTime,
PubsubTopic: rawMessage.PubsubTopic, PubsubTopic: rawMessage.PubsubTopic,
ContentTopicOverride: rawMessage.ContentTopicOverride,
} }
if rawMessage.BeforeDispatch != nil { if rawMessage.BeforeDispatch != nil {
@ -765,6 +766,7 @@ func (s *MessageSender) SendPublic(
newMessage.Ephemeral = rawMessage.Ephemeral newMessage.Ephemeral = rawMessage.Ephemeral
newMessage.PubsubTopic = rawMessage.PubsubTopic newMessage.PubsubTopic = rawMessage.PubsubTopic
newMessage.Priority = rawMessage.Priority newMessage.Priority = rawMessage.Priority
newMessage.ContentTopicOverride = rawMessage.ContentTopicOverride
messageID := v1protocol.MessageID(&rawMessage.Sender.PublicKey, wrappedMessage) messageID := v1protocol.MessageID(&rawMessage.Sender.PublicKey, wrappedMessage)

View File

@ -84,4 +84,5 @@ type RawMessage struct {
ResendType ResendType ResendType ResendType
ResendMethod ResendMethod ResendMethod ResendMethod
Priority *MessagePriority Priority *MessagePriority
ContentTopicOverride string
} }

View File

@ -1567,6 +1567,12 @@ func (o *Community) setPrivateKey(pk *ecdsa.PrivateKey) {
o.config.PrivateKey = pk o.config.PrivateKey = pk
} }
} }
func (o *Community) UniversalChatID() string {
// Using Member updates channelID as chatID to act as a universal content-topic for all chats in the community as explained here https://forum.vac.dev/t/status-communities-review-and-proposed-usage-of-waku-content-topics/335
// This is to match filter criteria of community with the content-topic usage.
// This specific topic is chosen as existing users before the change are already subscribed to this and will not get affected by it.
return o.MemberUpdateChannelID()
}
func (o *Community) SetResendAccountsClock(clock uint64) { func (o *Community) SetResendAccountsClock(clock uint64) {
o.config.CommunityDescription.ResendAccountsClock = clock o.config.CommunityDescription.ResendAccountsClock = clock

View File

@ -4194,6 +4194,22 @@ func (m *Manager) GetOwnedCommunitiesChatIDs() (map[string]bool, error) {
return chatIDs, nil return chatIDs, nil
} }
func (m *Manager) GetOwnedCommunitiesUniversalChatIDs() (map[string]bool, error) {
ownedCommunities, err := m.Controlled()
if err != nil {
return nil, err
}
chatIDs := make(map[string]bool)
for _, c := range ownedCommunities {
if c.Joined() {
chatIDs[c.UniversalChatID()] = true
}
}
return chatIDs, nil
}
func (m *Manager) StoreWakuMessage(message *types.Message) error { func (m *Manager) StoreWakuMessage(message *types.Message) error {
return m.persistence.SaveWakuMessage(message) return m.persistence.SaveWakuMessage(message)
} }

View File

@ -354,6 +354,10 @@ func (m *ArchiveManager) StartHistoryArchiveTasksInterval(community *Community,
m.logger.Error("failed to get community chat topics ", zap.Error(err)) m.logger.Error("failed to get community chat topics ", zap.Error(err))
continue continue
} }
// adding the content-topic used for all community chats as all chat messages will be published on this topic.
// since member updates would not be too frequent i.e only addition/deletion would add a new message,
// this shouldn't cause too much increase in size of archive generated.
topics = append(topics, m.transport.FilterByChatID(community.UniversalChatID()).ContentTopic)
ts := time.Now().Unix() ts := time.Now().Unix()
to := time.Unix(ts, 0) to := time.Unix(ts, 0)

View File

@ -2152,7 +2152,8 @@ func (s *MessengerCommunitiesTokenPermissionsSuite) TestImportDecryptedArchiveMe
startDate := messageDate.Add(-time.Minute) startDate := messageDate.Add(-time.Minute)
endDate := messageDate.Add(time.Minute) endDate := messageDate.Add(time.Minute)
topic := types.BytesToTopic(transport.ToTopic(chat.ID)) topic := types.BytesToTopic(transport.ToTopic(chat.ID))
topics := []types.TopicType{topic} communityCommonTopic := types.BytesToTopic(transport.ToTopic(community.UniversalChatID()))
topics := []types.TopicType{topic, communityCommonTopic}
torrentConfig := params.TorrentConfig{ torrentConfig := params.TorrentConfig{
Enabled: true, Enabled: true,

View File

@ -2268,7 +2268,8 @@ func (m *Messenger) dispatchMessage(ctx context.Context, rawMessage common.RawMe
) )
return rawMessage, fmt.Errorf("can't post message type '%d' on chat '%s'", rawMessage.MessageType, chat.ID) return rawMessage, fmt.Errorf("can't post message type '%d' on chat '%s'", rawMessage.MessageType, chat.ID)
} }
//setting content-topic over-ride for community messages to use memberUpdatesChannelID
rawMessage.ContentTopicOverride = community.UniversalChatID()
logger.Debug("sending community chat message", zap.String("chatName", chat.Name)) logger.Debug("sending community chat message", zap.String("chatName", chat.Name))
isCommunityEncrypted, err := m.communitiesManager.IsEncrypted(chat.CommunityID) isCommunityEncrypted, err := m.communitiesManager.IsEncrypted(chat.CommunityID)
if err != nil { if err != nil {
@ -3589,6 +3590,15 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
if err != nil { if err != nil {
logger.Info("failed to retrieve admin communities", zap.Error(err)) logger.Info("failed to retrieve admin communities", zap.Error(err))
} }
//fetch universal chatIDs as well.
controlledCommunitiesUniversalChatIDs, err := m.communitiesManager.GetOwnedCommunitiesUniversalChatIDs()
if err != nil {
logger.Info("failed to retrieve admin communities", zap.Error(err))
}
for chatID, flag := range controlledCommunitiesUniversalChatIDs {
controlledCommunitiesChatIDs[chatID] = flag
}
iterator := m.retrievedMessagesIteratorFactory(chatWithMessages) iterator := m.retrievedMessagesIteratorFactory(chatWithMessages)
for iterator.HasNext() { for iterator.HasNext() {

View File

@ -2714,6 +2714,13 @@ func (m *Messenger) UpdateCommunityFilters(community *communities.Community) err
publicFiltersToInit = append(publicFiltersToInit, defaultFilters...) publicFiltersToInit = append(publicFiltersToInit, defaultFilters...)
for _, filter := range defaultFilters {
_, err := m.transport.RemoveFilterByChatID(filter.ChatID)
if err != nil {
return err
}
}
for chatID := range community.Chats() { for chatID := range community.Chats() {
communityChatID := community.IDString() + chatID communityChatID := community.IDString() + chatID
_, err := m.transport.RemoveFilterByChatID(communityChatID) _, err := m.transport.RemoveFilterByChatID(communityChatID)
@ -3949,6 +3956,10 @@ func (m *Messenger) InitHistoryArchiveTasks(communities []*communities.Community
for _, filter := range filters { for _, filter := range filters {
topics = append(topics, filter.ContentTopic) topics = append(topics, filter.ContentTopic)
} }
// adding the content-topic used for member updates.
// since member updates would not be too frequent i.e only addition/deletion would add a new message,
// this shouldn't cause too much increase in size of archive generated.
filters = append(filters, m.transport.FilterByChatID(c.UniversalChatID()))
// First we need to know the timestamp of the latest waku message // First we need to know the timestamp of the latest waku message
// we've received for this community, so we can request messages we've // we've received for this community, so we can request messages we've

View File

@ -99,7 +99,7 @@ func (f *FiltersManager) Init(
// Add public, one-to-one and negotiated filters. // Add public, one-to-one and negotiated filters.
for _, fi := range filtersToInit { for _, fi := range filtersToInit {
_, err := f.LoadPublic(fi.ChatID, fi.PubsubTopic) _, err := f.LoadPublic(fi.ChatID, fi.PubsubTopic, fi.ContentTopicOverrideID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -125,13 +125,14 @@ func (f *FiltersManager) Init(
type FiltersToInitialize struct { type FiltersToInitialize struct {
ChatID string ChatID string
PubsubTopic string PubsubTopic string
ContentTopicOverrideID string //litte hacky but this is used to override content-topic in filtersManager.
} }
func (f *FiltersManager) InitPublicFilters(publicFiltersToInit []FiltersToInitialize) ([]*Filter, error) { func (f *FiltersManager) InitPublicFilters(publicFiltersToInit []FiltersToInitialize) ([]*Filter, error) {
var filters []*Filter var filters []*Filter
// Add public, one-to-one and negotiated filters. // Add public, one-to-one and negotiated filters.
for _, pf := range publicFiltersToInit { for _, pf := range publicFiltersToInit {
f, err := f.LoadPublic(pf.ChatID, pf.PubsubTopic) f, err := f.LoadPublic(pf.ChatID, pf.PubsubTopic, pf.ContentTopicOverrideID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -455,7 +456,7 @@ func (f *FiltersManager) LoadNegotiated(secret types.NegotiatedSecret) (*Filter,
} }
keyString := hex.EncodeToString(secret.Key) keyString := hex.EncodeToString(secret.Key)
filter, err := f.addSymmetric(keyString, "") filter, err := f.addSymmetric(keyString, "", "")
if err != nil { if err != nil {
f.logger.Debug("could not register negotiated topic", zap.Error(err)) f.logger.Debug("could not register negotiated topic", zap.Error(err))
return nil, err return nil, err
@ -534,11 +535,16 @@ func (f *FiltersManager) PersonalTopicFilter() *Filter {
} }
// LoadPublic adds a filter for a public chat. // LoadPublic adds a filter for a public chat.
func (f *FiltersManager) LoadPublic(chatID string, pubsubTopic string) (*Filter, error) { func (f *FiltersManager) LoadPublic(chatID string, pubsubTopic string, contentTopicID string) (*Filter, error) {
f.mutex.Lock() f.mutex.Lock()
defer f.mutex.Unlock() defer f.mutex.Unlock()
if chat, ok := f.filters[chatID]; ok { chatIDToLoad := chatID
if contentTopicID != "" {
chatIDToLoad = contentTopicID
}
if chat, ok := f.filters[chatIDToLoad]; ok {
if chat.PubsubTopic != pubsubTopic { if chat.PubsubTopic != pubsubTopic {
f.logger.Debug("updating pubsub topic for filter", f.logger.Debug("updating pubsub topic for filter",
zap.String("chatID", chatID), zap.String("chatID", chatID),
@ -547,13 +553,13 @@ func (f *FiltersManager) LoadPublic(chatID string, pubsubTopic string) (*Filter,
zap.String("newTopic", pubsubTopic), zap.String("newTopic", pubsubTopic),
) )
chat.PubsubTopic = pubsubTopic chat.PubsubTopic = pubsubTopic
f.filters[chatID] = chat f.filters[chatIDToLoad] = chat //TODO: Do we need to update watchers as well on modification?
} }
return chat, nil return chat, nil
} }
filterAndTopic, err := f.addSymmetric(chatID, pubsubTopic) filterAndTopic, err := f.addSymmetric(chatID, pubsubTopic, contentTopicID)
if err != nil { if err != nil {
f.logger.Debug("could not register public chat topic", zap.String("chatID", chatID), zap.Error(err)) f.logger.Debug("could not register public chat topic", zap.String("chatID", chatID), zap.Error(err))
return nil, err return nil, err
@ -592,7 +598,7 @@ func (f *FiltersManager) LoadContactCode(pubKey *ecdsa.PublicKey) (*Filter, erro
return f.filters[chatID], nil return f.filters[chatID], nil
} }
contactCodeFilter, err := f.addSymmetric(chatID, "") contactCodeFilter, err := f.addSymmetric(chatID, "", "")
if err != nil { if err != nil {
f.logger.Debug("could not register contact code topic", zap.String("chatID", chatID), zap.Error(err)) f.logger.Debug("could not register contact code topic", zap.String("chatID", chatID), zap.Error(err))
return nil, err return nil, err
@ -615,7 +621,7 @@ func (f *FiltersManager) LoadContactCode(pubKey *ecdsa.PublicKey) (*Filter, erro
} }
// addSymmetric adds a symmetric key filter // addSymmetric adds a symmetric key filter
func (f *FiltersManager) addSymmetric(chatID string, pubsubTopic string) (*RawFilter, error) { func (f *FiltersManager) addSymmetric(chatID string, pubsubTopic string, contentTopicID string) (*RawFilter, error) {
var symKeyID string var symKeyID string
var err error var err error
@ -644,6 +650,12 @@ func (f *FiltersManager) addSymmetric(chatID string, pubsubTopic string) (*RawFi
} }
} }
if contentTopicID != "" {
//add receive filter for the single default contentTopic for all community chats
topic = ToTopic(contentTopicID)
topics = append(topics, topic)
}
id, err := f.service.Subscribe(&types.SubscriptionOptions{ id, err := f.service.Subscribe(&types.SubscriptionOptions{
SymKeyID: symKeyID, SymKeyID: symKeyID,
PoW: minPow, PoW: minPow,

View File

@ -191,7 +191,7 @@ func (t *Transport) ProcessNegotiatedSecret(secret types.NegotiatedSecret) (*Fil
} }
func (t *Transport) JoinPublic(chatID string) (*Filter, error) { func (t *Transport) JoinPublic(chatID string) (*Filter, error) {
return t.filters.LoadPublic(chatID, "") return t.filters.LoadPublic(chatID, "", "")
} }
func (t *Transport) LeavePublic(chatID string) error { func (t *Transport) LeavePublic(chatID string) error {
@ -223,6 +223,8 @@ func (t *Transport) GetStats() types.StatsSummary {
return t.waku.GetStats() return t.waku.GetStats()
} }
// With change in filter used for communities, messages are indexed here with common filter and not their own chatID filter.
// The caller should not use chatID from the filter to determine chatID of the message, rather should dervice it from messaage itself.
func (t *Transport) RetrieveRawAll() (map[Filter][]*types.Message, error) { func (t *Transport) RetrieveRawAll() (map[Filter][]*types.Message, error) {
result := make(map[Filter][]*types.Message) result := make(map[Filter][]*types.Message)
logger := t.logger.With(zap.String("site", "retrieveRawAll")) logger := t.logger.With(zap.String("site", "retrieveRawAll"))
@ -276,16 +278,16 @@ func (t *Transport) RetrieveRawAll() (map[Filter][]*types.Message, error) {
// SendPublic sends a new message using the Whisper service. // SendPublic sends a new message using the Whisper service.
// For public filters, chat name is used as an ID as well as // For public filters, chat name is used as an ID as well as
// a topic. // a topic.
// In case of communities a single topic is used to send all messages.
func (t *Transport) SendPublic(ctx context.Context, newMessage *types.NewMessage, chatName string) ([]byte, error) { func (t *Transport) SendPublic(ctx context.Context, newMessage *types.NewMessage, chatName string) ([]byte, error) {
if err := t.addSig(newMessage); err != nil { if err := t.addSig(newMessage); err != nil {
return nil, err return nil, err
} }
//passing content-topic override, it will be used if set. otherwise chatName will be used to load filter.
filter, err := t.filters.LoadPublic(chatName, newMessage.PubsubTopic) filter, err := t.filters.LoadPublic(chatName, newMessage.PubsubTopic, newMessage.ContentTopicOverride)
if err != nil { if err != nil {
return nil, err return nil, err
} }
newMessage.SymKeyID = filter.SymKeyID newMessage.SymKeyID = filter.SymKeyID
newMessage.Topic = filter.ContentTopic newMessage.Topic = filter.ContentTopic
newMessage.PubsubTopic = filter.PubsubTopic newMessage.PubsubTopic = filter.PubsubTopic
@ -362,7 +364,8 @@ func (t *Transport) SendCommunityMessage(ctx context.Context, newMessage *types.
} }
// We load the filter to make sure we can post on it // We load the filter to make sure we can post on it
filter, err := t.filters.LoadPublic(PubkeyToHex(publicKey)[2:], newMessage.PubsubTopic) //passing content-topic override, it will be used if set. otherwise chatName will be used to load filter.
filter, err := t.filters.LoadPublic(PubkeyToHex(publicKey)[2:], newMessage.PubsubTopic, newMessage.ContentTopicOverride)
if err != nil { if err != nil {
return nil, err return nil, err
} }