fix(communities)_: ensure community sync doesn't override joined state

potentially fixes: status-im/status-desktop#15009
This commit is contained in:
Patryk Osmaczko 2024-06-18 18:18:21 +02:00 committed by osmaczko
parent 88c671fcf0
commit 0995802428
7 changed files with 90 additions and 26 deletions

View File

@ -32,6 +32,7 @@ func (o *Community) ToSyncInstallationCommunityProtobuf(clock uint64, communityS
Description: wrappedCommunity, Description: wrappedCommunity,
Joined: o.Joined(), Joined: o.Joined(),
JoinedAt: o.JoinedAt(), JoinedAt: o.JoinedAt(),
Spectated: o.Spectated(),
Verified: o.Verified(), Verified: o.Verified(),
Muted: o.Muted(), Muted: o.Muted(),
RequestsToJoin: rtjs, RequestsToJoin: rtjs,

View File

@ -1065,16 +1065,18 @@ func advertiseCommunityToUserOldWay(s *suite.Suite, community *communities.Commu
s.Require().NoError(err) s.Require().NoError(err)
// Ensure community is received // Ensure community is received
response, err := WaitOnMessengerResponse( _, err = WaitOnMessengerResponse(
user, user,
func(r *MessengerResponse) bool { func(r *MessengerResponse) bool {
return len(r.Communities()) > 0 if len(r.Communities()) != 1 {
return false
}
c := r.Communities()[0]
return c.IDString() == community.IDString() && c.Clock() >= community.Clock()
}, },
"event sender did not receive community request to join", "no community received",
) )
s.Require().NoError(err) s.Require().NoError(err)
communityInResponse := response.Communities()[0]
s.Require().Equal(community.ID(), communityInResponse.ID())
} }
func testAcceptMemberRequestToJoin(base CommunityEventsTestsInterface, community *communities.Community, user *Messenger) { func testAcceptMemberRequestToJoin(base CommunityEventsTestsInterface, community *communities.Community, user *Messenger) {

View File

@ -3088,6 +3088,56 @@ func (s *MessengerCommunitiesSuite) TestSyncCommunity_ImportCommunity() {
s.Require().NoError(err) s.Require().NoError(err)
} }
func (s *MessengerCommunitiesSuite) TestSyncCommunity_OutdatedDescription() {
community, _ := s.createCommunity()
s.advertiseCommunityTo(community, s.owner, s.alice)
// Spectate community
_, err := s.alice.SpectateCommunity(community.ID())
s.Require().NoError(err)
// Update alice's community reference
community, err = s.alice.communitiesManager.GetByID(community.ID())
s.Require().NoError(err)
s.Require().False(community.Joined())
s.Require().True(community.Spectated())
// Create sync message for later
syncCommunityMsg, err := s.alice.buildSyncInstallationCommunity(community, 1)
s.Require().NoError(err)
s.Require().False(syncCommunityMsg.Joined)
s.Require().True(syncCommunityMsg.Spectated)
// Join community
s.joinCommunity(community, s.owner, s.alice)
// Update owner's community reference
community, err = s.owner.GetCommunityByID(community.ID())
s.Require().NoError(err)
s.Require().True(community.HasMember(s.alice.IdentityPublicKey()))
// Create another device
aliceOtherDevice := s.createOtherDevice(s.alice)
defer TearDownMessenger(&s.Suite, aliceOtherDevice)
// Make other device receive community
advertiseCommunityToUserOldWay(&s.Suite, community, s.owner, aliceOtherDevice)
community, err = aliceOtherDevice.GetCommunityByID(community.ID())
s.Require().NoError(err)
s.Require().True(community.Joined())
// Then make other device handle sync message with outdated community description
messageState := aliceOtherDevice.buildMessageState()
err = aliceOtherDevice.handleSyncInstallationCommunity(messageState, syncCommunityMsg)
s.Require().NoError(err)
// Then community should not be left
community, err = aliceOtherDevice.communitiesManager.GetByID(community.ID())
s.Require().NoError(err)
s.Require().True(community.Joined())
s.Require().False(community.Spectated())
}
func (s *MessengerCommunitiesSuite) TestSetMutePropertyOnChatsByCategory() { func (s *MessengerCommunitiesSuite) TestSetMutePropertyOnChatsByCategory() {
// Create a community // Create a community
createCommunityReq := &requests.CreateCommunity{ createCommunityReq := &requests.CreateCommunity{

View File

@ -3117,32 +3117,42 @@ func (m *Messenger) propagateSyncInstallationCommunityWithHRKeys(msg *protobuf.S
return nil return nil
} }
func (m *Messenger) buildSyncInstallationCommunity(community *communities.Community, clock uint64) (*protobuf.SyncInstallationCommunity, error) {
communitySettings, err := m.communitiesManager.GetCommunitySettingsByID(community.ID())
if err != nil {
return nil, err
}
syncControlNode, err := m.communitiesManager.GetSyncControlNode(community.ID())
if err != nil {
return nil, err
}
syncMessage, err := community.ToSyncInstallationCommunityProtobuf(clock, communitySettings, syncControlNode)
if err != nil {
return nil, err
}
err = m.propagateSyncInstallationCommunityWithHRKeys(syncMessage, community)
if err != nil {
return nil, err
}
return syncMessage, nil
}
func (m *Messenger) syncCommunity(ctx context.Context, community *communities.Community, rawMessageHandler RawMessageHandler) error { func (m *Messenger) syncCommunity(ctx context.Context, community *communities.Community, rawMessageHandler RawMessageHandler) error {
logger := m.logger.Named("syncCommunity") logger := m.logger.Named("syncCommunity")
if !m.hasPairedDevices() { if !m.hasPairedDevices() {
logger.Debug("device has no paired devices") logger.Debug("device has no paired devices")
return nil return nil
} }
logger.Debug("device has paired device(s)") logger.Debug("device has paired device(s)")
clock, chat := m.getLastClockWithRelatedChat() clock, chat := m.getLastClockWithRelatedChat()
communitySettings, err := m.communitiesManager.GetCommunitySettingsByID(community.ID()) syncMessage, err := m.buildSyncInstallationCommunity(community, clock)
if err != nil {
return err
}
syncControlNode, err := m.communitiesManager.GetSyncControlNode(community.ID())
if err != nil {
return err
}
syncMessage, err := community.ToSyncInstallationCommunityProtobuf(clock, communitySettings, syncControlNode)
if err != nil {
return err
}
err = m.propagateSyncInstallationCommunityWithHRKeys(syncMessage, community)
if err != nil { if err != nil {
return err return err
} }

View File

@ -317,7 +317,7 @@ func syncInstallationCommunitiesSet(communities []*protobuf.SyncInstallationComm
func (m *Messenger) handleSyncedCommunities(state *ReceivedMessageState, message *protobuf.Backup) []error { func (m *Messenger) handleSyncedCommunities(state *ReceivedMessageState, message *protobuf.Backup) []error {
var errors []error var errors []error
for _, syncCommunity := range syncInstallationCommunitiesSet(message.Communities) { for _, syncCommunity := range syncInstallationCommunitiesSet(message.Communities) {
err := m.handleSyncInstallationCommunity(state, syncCommunity, nil) err := m.handleSyncInstallationCommunity(state, syncCommunity)
if err != nil { if err != nil {
errors = append(errors, err) errors = append(errors, err)
} }

View File

@ -3694,10 +3694,10 @@ func (m *Messenger) sendSharedAddressToControlNode(receiver *ecdsa.PublicKey, co
} }
func (m *Messenger) HandleSyncInstallationCommunity(messageState *ReceivedMessageState, syncCommunity *protobuf.SyncInstallationCommunity, statusMessage *v1protocol.StatusMessage) error { func (m *Messenger) HandleSyncInstallationCommunity(messageState *ReceivedMessageState, syncCommunity *protobuf.SyncInstallationCommunity, statusMessage *v1protocol.StatusMessage) error {
return m.handleSyncInstallationCommunity(messageState, syncCommunity, nil) return m.handleSyncInstallationCommunity(messageState, syncCommunity)
} }
func (m *Messenger) handleSyncInstallationCommunity(messageState *ReceivedMessageState, syncCommunity *protobuf.SyncInstallationCommunity, statusMessage *v1protocol.StatusMessage) error { func (m *Messenger) handleSyncInstallationCommunity(messageState *ReceivedMessageState, syncCommunity *protobuf.SyncInstallationCommunity) error {
logger := m.logger.Named("handleSyncInstallationCommunity") logger := m.logger.Named("handleSyncInstallationCommunity")
// Should handle community // Should handle community
@ -3785,6 +3785,7 @@ func (m *Messenger) handleSyncInstallationCommunity(messageState *ReceivedMessag
logger.Debug("m.handleCommunityDescription error", zap.Error(err)) logger.Debug("m.handleCommunityDescription error", zap.Error(err))
return err return err
} }
descriptionOutdated := err == communities.ErrInvalidCommunityDescriptionClockOutdated
if syncCommunity.Settings != nil { if syncCommunity.Settings != nil {
err = m.HandleSyncCommunitySettings(messageState, syncCommunity.Settings, nil) err = m.HandleSyncCommunitySettings(messageState, syncCommunity.Settings, nil)
@ -3812,7 +3813,7 @@ func (m *Messenger) handleSyncInstallationCommunity(messageState *ReceivedMessag
} }
// if we are not waiting for approval, join or leave the community // if we are not waiting for approval, join or leave the community
if !pending { if !pending && !descriptionOutdated {
var mr *MessengerResponse var mr *MessengerResponse
if syncCommunity.Joined { if syncCommunity.Joined {
mr, err = m.joinCommunity(context.Background(), syncCommunity.Id, false) mr, err = m.joinCommunity(context.Background(), syncCommunity.Id, false)

View File

@ -116,7 +116,7 @@ func (m *Messenger) HandleSyncRawMessages(rawMessages []*protobuf.RawMessage) er
if err != nil { if err != nil {
return err return err
} }
err = m.handleSyncInstallationCommunity(state, &message, nil) err = m.handleSyncInstallationCommunity(state, &message)
if err != nil { if err != nil {
m.logger.Error("failed to handleSyncCommunity when HandleSyncRawMessages", zap.Error(err)) m.logger.Error("failed to handleSyncCommunity when HandleSyncRawMessages", zap.Error(err))
continue continue