diff --git a/protocol/communities/community_events_processing.go b/protocol/communities/community_events_processing.go index 21ad716ed7..4fe962b455 100644 --- a/protocol/communities/community_events_processing.go +++ b/protocol/communities/community_events_processing.go @@ -1,14 +1,12 @@ package communities import ( - "crypto/ecdsa" "errors" "sort" "github.com/golang/protobuf/proto" "go.uber.org/zap" - utils "github.com/status-im/status-go/common" "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/protobuf" ) @@ -53,7 +51,7 @@ func (e *eventsProcessor) exec() error { } func (e *eventsProcessor) validateDescription() error { - description, err := validateAndGetEventsMessageCommunityDescription(e.message.EventsBaseCommunityDescription, e.community.ControlNode()) + description, err := unmarshalCommunityDescriptionMessage(e.message.EventsBaseCommunityDescription, e.community.ControlNode()) if err != nil { return err } @@ -285,7 +283,7 @@ func (o *Community) addNewCommunityEvent(event *CommunityEvent) error { // If there were no events before, extract CommunityDescription from CommunityDescriptionProtocolMessage // and check the signature if o.config.EventsData == nil || len(o.config.EventsData.EventsBaseCommunityDescription) == 0 { - _, err := validateAndGetEventsMessageCommunityDescription(o.config.CommunityDescriptionProtocolMessage, o.ControlNode()) + _, err := unmarshalCommunityDescriptionMessage(o.config.CommunityDescriptionProtocolMessage, o.ControlNode()) if err != nil { return err } @@ -313,38 +311,3 @@ func (o *Community) toCommunityEventsMessage() *CommunityEventsMessage { Events: o.config.EventsData.Events, } } - -func validateAndGetEventsMessageCommunityDescription(signedDescription []byte, signerPubkey *ecdsa.PublicKey) (*protobuf.CommunityDescription, error) { - metadata := &protobuf.ApplicationMetadataMessage{} - - err := proto.Unmarshal(signedDescription, metadata) - if err != nil { - return nil, err - } - - if metadata.Type != protobuf.ApplicationMetadataMessage_COMMUNITY_DESCRIPTION { - return nil, ErrInvalidMessage - } - - signer, err := utils.RecoverKey(metadata) - if err != nil { - return nil, err - } - - if signer == nil { - return nil, errors.New("CommunityDescription does not contain the control node signature") - } - - if !signer.Equal(signerPubkey) { - return nil, errors.New("CommunityDescription was not signed by an owner") - } - - description := &protobuf.CommunityDescription{} - - err = proto.Unmarshal(metadata.Payload, description) - if err != nil { - return nil, err - } - - return description, nil -} diff --git a/protocol/communities/manager.go b/protocol/communities/manager.go index c8d85bbfeb..2d5195f8e5 100644 --- a/protocol/communities/manager.go +++ b/protocol/communities/manager.go @@ -3491,6 +3491,17 @@ type CheckAllChannelsPermissionsResponse struct { } func (m *Manager) HandleCommunityRequestToJoinResponse(signer *ecdsa.PublicKey, request *protobuf.CommunityRequestToJoinResponse) (*RequestToJoin, error) { + if len(request.CommunityDescriptionMessage) > 0 { + description, err := unmarshalCommunityDescriptionMessage(request.CommunityDescriptionMessage, signer) + if err != nil { + return nil, err + } + _, err = m.HandleCommunityDescriptionMessage(signer, description, request.CommunityDescriptionMessage, nil, nil) + if err != nil { + return nil, err + } + } + m.communityLock.Lock(request.CommunityId) defer m.communityLock.Unlock(request.CommunityId) @@ -3501,46 +3512,6 @@ func (m *Manager) HandleCommunityRequestToJoinResponse(signer *ecdsa.PublicKey, return nil, err } - communityDescriptionBytes, err := proto.Marshal(request.Community) - if err != nil { - return nil, err - } - - // We need to wrap `request.Community` in an `ApplicationMetadataMessage` - // of type `CommunityDescription` because `UpdateCommunityDescription` expects this. - // - // This is merely for marsheling/unmarsheling, hence we attaching a `Signature` - // is not needed. - metadataMessage := &protobuf.ApplicationMetadataMessage{ - Payload: communityDescriptionBytes, - Type: protobuf.ApplicationMetadataMessage_COMMUNITY_DESCRIPTION, - } - - appMetadataMsg, err := proto.Marshal(metadataMessage) - if err != nil { - return nil, err - } - - isControlNodeSigner := common.IsPubKeyEqual(community.ControlNode(), signer) - if !isControlNodeSigner { - m.logger.Debug("signer is not control node", zap.String("signer", common.PubkeyToHex(signer)), zap.String("controlNode", common.PubkeyToHex(community.ControlNode()))) - return nil, ErrNotAuthorized - } - - _, processedDescription, err := m.preprocessDescription(community.ID(), request.Community) - if err != nil { - return nil, err - } - - _, err = community.UpdateCommunityDescription(processedDescription, appMetadataMsg, nil) - if err != nil { - return nil, err - } - - if err = m.handleCommunityTokensMetadata(community); err != nil { - return nil, err - } - if community.Encrypted() && len(request.Grant) > 0 { _, err = m.HandleCommunityGrant(community, request.Grant, request.Clock) if err != nil && err != ErrGrantOlder && err != ErrGrantExpired { @@ -3548,19 +3519,12 @@ func (m *Manager) HandleCommunityRequestToJoinResponse(signer *ecdsa.PublicKey, } } - err = m.persistence.SaveCommunity(community) - - if err != nil { - return nil, err - } - if request.Accepted { err = m.markRequestToJoinAsAccepted(&m.identity.PublicKey, community) if err != nil { return nil, err } } else { - err = m.persistence.SetRequestToJoinState(pkString, community.ID(), RequestToJoinStateDeclined) if err != nil { return nil, err @@ -3924,7 +3888,7 @@ func (m *Manager) dbRecordBundleToCommunity(r *CommunityRecordBundle) (*Communit community.config.CommunityDescription = description if community.config.EventsData != nil { - eventsDescription, err := validateAndGetEventsMessageCommunityDescription(community.config.EventsData.EventsBaseCommunityDescription, community.ControlNode()) + eventsDescription, err := unmarshalCommunityDescriptionMessage(community.config.EventsData.EventsBaseCommunityDescription, community.ControlNode()) if err != nil { m.logger.Error("invalid EventsBaseCommunityDescription", zap.Error(err)) } @@ -5259,3 +5223,38 @@ func (m *Manager) updateEncryptionKeysRequests(communityID types.HexBytes, chann func (m *Manager) UpdateEncryptionKeysRequests(communityID types.HexBytes, channelIDs []string) error { return m.updateEncryptionKeysRequests(communityID, channelIDs, time.Now().UnixMilli()) } + +func unmarshalCommunityDescriptionMessage(signedDescription []byte, signerPubkey *ecdsa.PublicKey) (*protobuf.CommunityDescription, error) { + metadata := &protobuf.ApplicationMetadataMessage{} + + err := proto.Unmarshal(signedDescription, metadata) + if err != nil { + return nil, err + } + + if metadata.Type != protobuf.ApplicationMetadataMessage_COMMUNITY_DESCRIPTION { + return nil, ErrInvalidMessage + } + + signer, err := utils.RecoverKey(metadata) + if err != nil { + return nil, err + } + + if signer == nil { + return nil, errors.New("CommunityDescription does not contain the control node signature") + } + + if !signer.Equal(signerPubkey) { + return nil, errors.New("CommunityDescription was not signed by an owner") + } + + description := &protobuf.CommunityDescription{} + + err = proto.Unmarshal(metadata.Payload, description) + if err != nil { + return nil, err + } + + return description, nil +} diff --git a/protocol/communities_messenger_test.go b/protocol/communities_messenger_test.go index 81e7572620..93318c4c1f 100644 --- a/protocol/communities_messenger_test.go +++ b/protocol/communities_messenger_test.go @@ -4661,17 +4661,17 @@ func (s *MessengerCommunitiesSuite) TestAliceDidNotProcessOutdatedCommunityReque s.Require().NoError(err) } - encryptedDescription, err := community.EncryptedDescription() + descriptionMessage, err := community.ToProtocolMessageBytes() s.Require().NoError(err) requestToJoinResponse := &protobuf.CommunityRequestToJoinResponse{ - Clock: community.Clock(), - Accepted: true, - CommunityId: community.ID(), - Community: encryptedDescription, - Grant: grant, - ProtectedTopicPrivateKey: crypto.FromECDSA(key), - Shard: community.Shard().Protobuffer(), + Clock: community.Clock(), + Accepted: true, + CommunityId: community.ID(), + Grant: grant, + ProtectedTopicPrivateKey: crypto.FromECDSA(key), + Shard: community.Shard().Protobuffer(), + CommunityDescriptionMessage: descriptionMessage, } // alice handle duplicated request to join response diff --git a/protocol/messenger.go b/protocol/messenger.go index 5b2df38a4c..ee0fc1c304 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -3634,7 +3634,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte err := m.dispatchToHandler(messageState, msg.ApplicationLayer.Payload, msg, filter, fromArchive) if err != nil { allMessagesProcessed = false - logger.Warn("failed to process protobuf", zap.Error(err)) + logger.Warn("failed to process protobuf", zap.String("type", msg.ApplicationLayer.Type.String()), zap.Error(err)) if m.unhandledMessagesTracker != nil { m.unhandledMessagesTracker(msg, err) } diff --git a/protocol/messenger_communities.go b/protocol/messenger_communities.go index e5c75b4515..269b143917 100644 --- a/protocol/messenger_communities.go +++ b/protocol/messenger_communities.go @@ -1989,14 +1989,20 @@ func (m *Messenger) acceptRequestToJoinCommunity(requestToJoin *communities.Requ return nil, err } + descriptionMessage, err := community.ToProtocolMessageBytes() + if err != nil { + return nil, err + } + requestToJoinResponseProto := &protobuf.CommunityRequestToJoinResponse{ - Clock: community.Clock(), - Accepted: true, - CommunityId: community.ID(), - Community: encryptedDescription, - Grant: grant, - ProtectedTopicPrivateKey: crypto.FromECDSA(key), - Shard: community.Shard().Protobuffer(), + Clock: community.Clock(), + Accepted: true, + CommunityId: community.ID(), + Community: encryptedDescription, // Deprecated but kept for backward compatibility, to be removed in future + Grant: grant, + ProtectedTopicPrivateKey: crypto.FromECDSA(key), + Shard: community.Shard().Protobuffer(), + CommunityDescriptionMessage: descriptionMessage, } // The purpose of this torrent code is to get the 'magnetlink' to populate 'requestToJoinResponseProto.MagnetUri' diff --git a/protocol/messenger_handler.go b/protocol/messenger_handler.go index 11f3312cf2..77df860771 100644 --- a/protocol/messenger_handler.go +++ b/protocol/messenger_handler.go @@ -1700,7 +1700,7 @@ func (m *Messenger) HandleCommunityRequestToJoinResponse(state *ReceivedMessageS communityShardKey := &protobuf.CommunityShardKey{ CommunityId: requestToJoinResponseProto.CommunityId, PrivateKey: requestToJoinResponseProto.ProtectedTopicPrivateKey, - Clock: requestToJoinResponseProto.Community.Clock, + Clock: community.Clock(), Shard: requestToJoinResponseProto.Shard, } @@ -1765,9 +1765,6 @@ func (m *Messenger) HandleCommunityRequestToJoinResponse(state *ReceivedMessageS m.downloadAndImportHistoryArchives(community.ID(), magnetlink, task.CancelChan) }(currentTask) - - clock := requestToJoinResponseProto.Community.ArchiveMagnetlinkClock - return m.communitiesManager.UpdateMagnetlinkMessageClock(community.ID(), clock) } } diff --git a/protocol/protobuf/communities.proto b/protocol/protobuf/communities.proto index 2074b37524..a723f1f513 100644 --- a/protocol/protobuf/communities.proto +++ b/protocol/protobuf/communities.proto @@ -200,13 +200,15 @@ message CommunityUserKicked { message CommunityRequestToJoinResponse { uint64 clock = 1; - CommunityDescription community = 2; + CommunityDescription community = 2 [deprecated = true]; bool accepted = 3; bytes grant = 4; bytes community_id = 5; string magnet_uri = 6; bytes protected_topic_private_key = 7; Shard shard = 8; + // CommunityDescription with owner signature + bytes community_description_message = 9; } message CommunityRequestToLeave {