diff --git a/protocol/communities/community.go b/protocol/communities/community.go index aa6bbfafd..c053b887f 100644 --- a/protocol/communities/community.go +++ b/protocol/communities/community.go @@ -104,26 +104,26 @@ func (o *Community) MarshalPublicAPIJSON() ([]byte, error) { return nil, errors.New("member identity not set") } communityItem := struct { - ID types.HexBytes `json:"id"` - Verified bool `json:"verified"` - Chats map[string]CommunityChat `json:"chats"` - Categories map[string]CommunityCategory `json:"categories"` - Name string `json:"name"` - Description string `json:"description"` - IntroMessage string `json:"introMessage"` - OutroMessage string `json:"outroMessage"` - Tags []CommunityTag `json:"tags"` - Images map[string]images.IdentityImage `json:"images"` - Color string `json:"color"` - MembersCount int `json:"membersCount"` - EnsName string `json:"ensName"` - Link string `json:"link"` - CommunityAdminSettings CommunityAdminSettings `json:"adminSettings"` - Encrypted bool `json:"encrypted"` - BanList []string `json:"banList"` - TokenPermissions map[string]*protobuf.CommunityTokenPermission `json:"tokenPermissions"` - CommunityTokensMetadata []*protobuf.CommunityTokenMetadata `json:"communityTokensMetadata"` - ActiveMembersCount uint64 `json:"activeMembersCount"` + ID types.HexBytes `json:"id"` + Verified bool `json:"verified"` + Chats map[string]CommunityChat `json:"chats"` + Categories map[string]CommunityCategory `json:"categories"` + Name string `json:"name"` + Description string `json:"description"` + IntroMessage string `json:"introMessage"` + OutroMessage string `json:"outroMessage"` + Tags []CommunityTag `json:"tags"` + Images map[string]images.IdentityImage `json:"images"` + Color string `json:"color"` + MembersCount int `json:"membersCount"` + EnsName string `json:"ensName"` + Link string `json:"link"` + CommunityAdminSettings CommunityAdminSettings `json:"adminSettings"` + Encrypted bool `json:"encrypted"` + BanList []string `json:"banList"` + TokenPermissions map[string]*CommunityTokenPermission `json:"tokenPermissions"` + CommunityTokensMetadata []*protobuf.CommunityTokenMetadata `json:"communityTokensMetadata"` + ActiveMembersCount uint64 `json:"activeMembersCount"` }{ ID: o.ID(), Verified: o.config.Verified, @@ -161,7 +161,7 @@ func (o *Community) MarshalPublicAPIJSON() ([]byte, error) { communityItem.Chats[id] = chat } - communityItem.TokenPermissions = o.config.CommunityDescription.TokenPermissions + communityItem.TokenPermissions = o.tokenPermissions() communityItem.MembersCount = len(o.config.CommunityDescription.Members) communityItem.Link = fmt.Sprintf("https://join.status.im/c/0x%x", o.ID()) communityItem.IntroMessage = o.config.CommunityDescription.IntroMessage @@ -199,38 +199,38 @@ func (o *Community) MarshalJSON() ([]byte, error) { return nil, errors.New("member identity not set") } communityItem := struct { - ID types.HexBytes `json:"id"` - MemberRole protobuf.CommunityMember_Roles `json:"memberRole"` - IsControlNode bool `json:"isControlNode"` - Verified bool `json:"verified"` - Joined bool `json:"joined"` - Spectated bool `json:"spectated"` - RequestedAccessAt int `json:"requestedAccessAt"` - Name string `json:"name"` - Description string `json:"description"` - IntroMessage string `json:"introMessage"` - OutroMessage string `json:"outroMessage"` - Tags []CommunityTag `json:"tags"` - Chats map[string]CommunityChat `json:"chats"` - Categories map[string]CommunityCategory `json:"categories"` - Images map[string]images.IdentityImage `json:"images"` - Permissions *protobuf.CommunityPermissions `json:"permissions"` - Members map[string]*protobuf.CommunityMember `json:"members"` - CanRequestAccess bool `json:"canRequestAccess"` - CanManageUsers bool `json:"canManageUsers"` //TODO: we can remove this - CanDeleteMessageForEveryone bool `json:"canDeleteMessageForEveryone"` //TODO: we can remove this - CanJoin bool `json:"canJoin"` - Color string `json:"color"` - RequestedToJoinAt uint64 `json:"requestedToJoinAt,omitempty"` - IsMember bool `json:"isMember"` - Muted bool `json:"muted"` - MuteTill time.Time `json:"muteTill,omitempty"` - CommunityAdminSettings CommunityAdminSettings `json:"adminSettings"` - Encrypted bool `json:"encrypted"` - BanList []string `json:"banList"` - TokenPermissions map[string]*protobuf.CommunityTokenPermission `json:"tokenPermissions"` - CommunityTokensMetadata []*protobuf.CommunityTokenMetadata `json:"communityTokensMetadata"` - ActiveMembersCount uint64 `json:"activeMembersCount"` + ID types.HexBytes `json:"id"` + MemberRole protobuf.CommunityMember_Roles `json:"memberRole"` + IsControlNode bool `json:"isControlNode"` + Verified bool `json:"verified"` + Joined bool `json:"joined"` + Spectated bool `json:"spectated"` + RequestedAccessAt int `json:"requestedAccessAt"` + Name string `json:"name"` + Description string `json:"description"` + IntroMessage string `json:"introMessage"` + OutroMessage string `json:"outroMessage"` + Tags []CommunityTag `json:"tags"` + Chats map[string]CommunityChat `json:"chats"` + Categories map[string]CommunityCategory `json:"categories"` + Images map[string]images.IdentityImage `json:"images"` + Permissions *protobuf.CommunityPermissions `json:"permissions"` + Members map[string]*protobuf.CommunityMember `json:"members"` + CanRequestAccess bool `json:"canRequestAccess"` + CanManageUsers bool `json:"canManageUsers"` //TODO: we can remove this + CanDeleteMessageForEveryone bool `json:"canDeleteMessageForEveryone"` //TODO: we can remove this + CanJoin bool `json:"canJoin"` + Color string `json:"color"` + RequestedToJoinAt uint64 `json:"requestedToJoinAt,omitempty"` + IsMember bool `json:"isMember"` + Muted bool `json:"muted"` + MuteTill time.Time `json:"muteTill,omitempty"` + CommunityAdminSettings CommunityAdminSettings `json:"adminSettings"` + Encrypted bool `json:"encrypted"` + BanList []string `json:"banList"` + TokenPermissions map[string]*CommunityTokenPermission `json:"tokenPermissions"` + CommunityTokensMetadata []*protobuf.CommunityTokenMetadata `json:"communityTokensMetadata"` + ActiveMembersCount uint64 `json:"activeMembersCount"` }{ ID: o.ID(), MemberRole: o.MemberRole(o.MemberIdentity()), @@ -280,7 +280,7 @@ func (o *Community) MarshalJSON() ([]byte, error) { } communityItem.Chats[id] = chat } - communityItem.TokenPermissions = o.config.CommunityDescription.TokenPermissions + communityItem.TokenPermissions = o.tokenPermissions() communityItem.Members = o.config.CommunityDescription.Members communityItem.Permissions = o.config.CommunityDescription.Permissions communityItem.IntroMessage = o.config.CommunityDescription.IntroMessage @@ -949,15 +949,16 @@ func (o *Community) UpdateCommunityDescription(description *protobuf.CommunityDe return response, nil } - // We only calculate changes if we joined/spectated the community or we requested access, otherwise not interested - if o.config.Joined || o.config.Spectated || o.config.RequestedToJoinAt > 0 { - response = EvaluateCommunityChanges(o.config.CommunityDescription, description) - response.Community = o - } + originCommunity := o.CreateDeepCopy() o.config.CommunityDescription = description o.config.CommunityDescriptionProtocolMessage = rawMessage + // We only calculate changes if we joined/spectated the community or we requested access, otherwise not interested + if o.config.Joined || o.config.Spectated || o.config.RequestedToJoinAt > 0 { + response = EvaluateCommunityChanges(originCommunity, o) + } + return response, nil } @@ -1315,20 +1316,64 @@ func (o *Community) Categories() map[string]*protobuf.CommunityCategory { return response } -func (o *Community) TokenPermissions() map[string]*protobuf.CommunityTokenPermission { - return o.config.CommunityDescription.TokenPermissions +func (o *Community) tokenPermissions() map[string]*CommunityTokenPermission { + result := make(map[string]*CommunityTokenPermission, len(o.config.CommunityDescription.TokenPermissions)) + for _, tokenPermission := range o.config.CommunityDescription.TokenPermissions { + result[tokenPermission.Id] = NewCommunityTokenPermission(tokenPermission) + } + + // Non-privileged members should not see pending permissions + if o.config.EventsData == nil || !o.IsPrivilegedMember(o.MemberIdentity()) { + return result + } + + processedPermissions := make(map[string]*struct{}) + for _, event := range o.config.EventsData.Events { + if event.TokenPermission == nil || processedPermissions[event.TokenPermission.Id] != nil { + continue + } + processedPermissions[event.TokenPermission.Id] = &struct{}{} // first permission event wins + + switch event.Type { + case protobuf.CommunityEvent_COMMUNITY_MEMBER_TOKEN_PERMISSION_CHANGE: + tokenPermission := result[event.TokenPermission.Id] + if tokenPermission != nil { + tokenPermission.State = TokenPermissionUpdatePending + } else { + tokenPermission := NewCommunityTokenPermission(event.TokenPermission) + tokenPermission.State = TokenPermissionAdditionPending + result[event.TokenPermission.Id] = tokenPermission + } + + case protobuf.CommunityEvent_COMMUNITY_MEMBER_TOKEN_PERMISSION_DELETE: + tokenPermission := result[event.TokenPermission.Id] + if tokenPermission != nil { + tokenPermission.State = TokenPermissionRemovalPending + } + default: + } + } + + return result +} + +func (o *Community) TokenPermissions() map[string]*CommunityTokenPermission { + o.mutex.Lock() + defer o.mutex.Unlock() + return o.tokenPermissions() } func (o *Community) HasTokenPermissions() bool { - return o.config.CommunityDescription.TokenPermissions != nil && len(o.config.CommunityDescription.TokenPermissions) > 0 + o.mutex.Lock() + defer o.mutex.Unlock() + return len(o.tokenPermissions()) > 0 } func (o *Community) ChannelHasTokenPermissions(chatID string) bool { - if !o.HasTokenPermissions() { - return false - } + o.mutex.Lock() + defer o.mutex.Unlock() - for _, tokenPermission := range o.TokenPermissions() { + for _, tokenPermission := range o.tokenPermissions() { if includes(tokenPermission.ChatIds, chatID) { return true } @@ -1337,8 +1382,8 @@ func (o *Community) ChannelHasTokenPermissions(chatID string) bool { return false } -func TokenPermissionsByType(permissions map[string]*protobuf.CommunityTokenPermission, permissionType protobuf.CommunityTokenPermission_Type) []*protobuf.CommunityTokenPermission { - result := make([]*protobuf.CommunityTokenPermission, 0) +func TokenPermissionsByType(permissions map[string]*CommunityTokenPermission, permissionType protobuf.CommunityTokenPermission_Type) []*CommunityTokenPermission { + result := make([]*CommunityTokenPermission, 0) for _, tokenPermission := range permissions { if tokenPermission.Type == permissionType { result = append(result, tokenPermission) @@ -1347,22 +1392,24 @@ func TokenPermissionsByType(permissions map[string]*protobuf.CommunityTokenPermi return result } -func (o *Community) tokenPermissionByID(ID string) *protobuf.CommunityTokenPermission { - permissions := o.config.CommunityDescription.TokenPermissions - if permissions == nil { - return nil - } - - return permissions[ID] +func (o *Community) tokenPermissionByID(ID string) *CommunityTokenPermission { + return o.tokenPermissions()[ID] } -func (o *Community) TokenPermissionsByType(permissionType protobuf.CommunityTokenPermission_Type) []*protobuf.CommunityTokenPermission { - return TokenPermissionsByType(o.TokenPermissions(), permissionType) +func (o *Community) TokenPermissionByID(ID string) *CommunityTokenPermission { + o.mutex.Lock() + defer o.mutex.Unlock() + + return o.tokenPermissionByID(ID) } -func (o *Community) ChannelTokenPermissionsByType(channelID string, permissionType protobuf.CommunityTokenPermission_Type) []*protobuf.CommunityTokenPermission { - permissions := make([]*protobuf.CommunityTokenPermission, 0) - for _, tokenPermission := range o.TokenPermissions() { +func (o *Community) TokenPermissionsByType(permissionType protobuf.CommunityTokenPermission_Type) []*CommunityTokenPermission { + return TokenPermissionsByType(o.tokenPermissions(), permissionType) +} + +func (o *Community) ChannelTokenPermissionsByType(channelID string, permissionType protobuf.CommunityTokenPermission_Type) []*CommunityTokenPermission { + permissions := make([]*CommunityTokenPermission, 0) + for _, tokenPermission := range o.tokenPermissions() { if tokenPermission.Type == permissionType && includes(tokenPermission.ChatIds, channelID) { permissions = append(permissions, tokenPermission) } @@ -1387,57 +1434,80 @@ func (o *Community) UpsertTokenPermission(tokenPermission *protobuf.CommunityTok o.mutex.Lock() defer o.mutex.Unlock() - if !(o.IsControlNode() || o.hasPermissionToSendTokenPermissionCommunityEvent(protobuf.CommunityEvent_COMMUNITY_MEMBER_TOKEN_PERMISSION_CHANGE, tokenPermission.Type)) { - return nil, ErrNotAuthorized - } - - changes, err := o.upsertTokenPermission(tokenPermission) - if err != nil { - return nil, err - } - if o.IsControlNode() { + changes, err := o.upsertTokenPermission(tokenPermission) + if err != nil { + return nil, err + } + o.updateEncrypted() o.increaseClock() - } else { + + return changes, nil + } + + if o.hasPermissionToSendTokenPermissionCommunityEvent(protobuf.CommunityEvent_COMMUNITY_MEMBER_TOKEN_PERMISSION_CHANGE, tokenPermission.Type) { + existed := o.tokenPermissionByID(tokenPermission.Id) != nil + err := o.addNewCommunityEvent(o.ToCommunityTokenPermissionChangeCommunityEvent(tokenPermission)) if err != nil { return nil, err } + + permission := NewCommunityTokenPermission(tokenPermission) + + changes := o.emptyCommunityChanges() + if existed { + permission.State = TokenPermissionUpdatePending + changes.TokenPermissionsModified[tokenPermission.Id] = permission + } else { + permission.State = TokenPermissionAdditionPending + changes.TokenPermissionsAdded[tokenPermission.Id] = permission + } + + return changes, nil } - return changes, nil + return nil, ErrNotAuthorized } func (o *Community) DeleteTokenPermission(permissionID string) (*CommunityChanges, error) { o.mutex.Lock() defer o.mutex.Unlock() - permission, exists := o.config.CommunityDescription.TokenPermissions[permissionID] + tokenPermission, exists := o.config.CommunityDescription.TokenPermissions[permissionID] if !exists { return nil, ErrTokenPermissionNotFound } - if !(o.IsControlNode() || o.hasPermissionToSendTokenPermissionCommunityEvent(protobuf.CommunityEvent_COMMUNITY_MEMBER_TOKEN_PERMISSION_DELETE, permission.Type)) { - return nil, ErrNotAuthorized - } - - changes, err := o.deleteTokenPermission(permissionID) - if err != nil { - return nil, err - } - if o.IsControlNode() { - o.updateEncrypted() - o.increaseClock() - } else { - err := o.addNewCommunityEvent(o.ToCommunityTokenPermissionDeleteCommunityEvent(permission)) + changes, err := o.deleteTokenPermission(permissionID) if err != nil { return nil, err } + + o.updateEncrypted() + o.increaseClock() + + return changes, nil } - return changes, nil + if o.hasPermissionToSendTokenPermissionCommunityEvent(protobuf.CommunityEvent_COMMUNITY_MEMBER_TOKEN_PERMISSION_DELETE, tokenPermission.Type) { + err := o.addNewCommunityEvent(o.ToCommunityTokenPermissionDeleteCommunityEvent(tokenPermission)) + if err != nil { + return nil, err + } + + permission := NewCommunityTokenPermission(tokenPermission) + permission.State = TokenPermissionRemovalPending + + changes := o.emptyCommunityChanges() + changes.TokenPermissionsModified[permission.Id] = permission + + return changes, nil + } + + return nil, ErrNotAuthorized } func (o *Community) VerifyGrantSignature(data []byte) (*protobuf.Grant, error) { @@ -1989,9 +2059,9 @@ func (o *Community) upsertTokenPermission(permission *protobuf.CommunityTokenPer changes := o.emptyCommunityChanges() if existed { - changes.TokenPermissionsModified[permission.Id] = permission + changes.TokenPermissionsModified[permission.Id] = NewCommunityTokenPermission(permission) } else { - changes.TokenPermissionsAdded[permission.Id] = permission + changes.TokenPermissionsAdded[permission.Id] = NewCommunityTokenPermission(permission) } return changes, nil @@ -2007,7 +2077,7 @@ func (o *Community) deleteTokenPermission(permissionID string) (*CommunityChange changes := o.emptyCommunityChanges() - changes.TokenPermissionsRemoved[permissionID] = permission + changes.TokenPermissionsRemoved[permissionID] = NewCommunityTokenPermission(permission) return changes, nil } diff --git a/protocol/communities/community_changes.go b/protocol/communities/community_changes.go index f3f8dbbf7..cebaef6ed 100644 --- a/protocol/communities/community_changes.go +++ b/protocol/communities/community_changes.go @@ -17,9 +17,9 @@ type CommunityChanges struct { MembersAdded map[string]*protobuf.CommunityMember `json:"membersAdded"` MembersRemoved map[string]*protobuf.CommunityMember `json:"membersRemoved"` - TokenPermissionsAdded map[string]*protobuf.CommunityTokenPermission `json:"tokenPermissionsAdded"` - TokenPermissionsModified map[string]*protobuf.CommunityTokenPermission `json:"tokenPermissionsModified"` - TokenPermissionsRemoved map[string]*protobuf.CommunityTokenPermission `json:"tokenPermissionsRemoved"` + TokenPermissionsAdded map[string]*CommunityTokenPermission `json:"tokenPermissionsAdded"` + TokenPermissionsModified map[string]*CommunityTokenPermission `json:"tokenPermissionsModified"` + TokenPermissionsRemoved map[string]*CommunityTokenPermission `json:"tokenPermissionsRemoved"` ChatsRemoved map[string]*protobuf.CommunityChat `json:"chatsRemoved"` ChatsAdded map[string]*protobuf.CommunityChat `json:"chatsAdded"` @@ -46,9 +46,9 @@ func EmptyCommunityChanges() *CommunityChanges { MembersAdded: make(map[string]*protobuf.CommunityMember), MembersRemoved: make(map[string]*protobuf.CommunityMember), - TokenPermissionsAdded: make(map[string]*protobuf.CommunityTokenPermission), - TokenPermissionsModified: make(map[string]*protobuf.CommunityTokenPermission), - TokenPermissionsRemoved: make(map[string]*protobuf.CommunityTokenPermission), + TokenPermissionsAdded: make(map[string]*CommunityTokenPermission), + TokenPermissionsModified: make(map[string]*CommunityTokenPermission), + TokenPermissionsRemoved: make(map[string]*CommunityTokenPermission), ChatsRemoved: make(map[string]*protobuf.CommunityChat), ChatsAdded: make(map[string]*protobuf.CommunityChat), @@ -79,7 +79,35 @@ func (c *CommunityChanges) HasMemberLeft(identity string) bool { return ok } -func EvaluateCommunityChanges(origin, modified *protobuf.CommunityDescription) *CommunityChanges { +func EvaluateCommunityChanges(origin, modified *Community) *CommunityChanges { + changes := evaluateCommunityChangesByDescription(origin.Description(), modified.Description()) + + originTokenPermissions := origin.tokenPermissions() + modifiedTokenPermissions := modified.tokenPermissions() + + // Check for modified or removed token permissions + for id, originPermission := range originTokenPermissions { + if modifiedPermission := modifiedTokenPermissions[id]; modifiedPermission != nil { + if !modifiedPermission.Equals(originPermission) { + changes.TokenPermissionsModified[id] = modifiedPermission + } + } else { + changes.TokenPermissionsRemoved[id] = originPermission + } + } + + // Check for added token permissions + for id, permission := range modifiedTokenPermissions { + if _, ok := originTokenPermissions[id]; !ok { + changes.TokenPermissionsAdded[id] = permission + } + } + + changes.Community = modified + return changes +} + +func evaluateCommunityChangesByDescription(origin, modified *protobuf.CommunityDescription) *CommunityChanges { changes := EmptyCommunityChanges() // Check for new members at the org level @@ -229,19 +257,5 @@ func EvaluateCommunityChanges(origin, modified *protobuf.CommunityDescription) * } } - // Check for removed token permissions - for id, permission := range origin.TokenPermissions { - if _, ok := modified.TokenPermissions[id]; !ok { - changes.TokenPermissionsRemoved[id] = permission - } - } - - // Check for added token permissions - for id, permission := range modified.TokenPermissions { - if _, ok := origin.TokenPermissions[id]; !ok { - changes.TokenPermissionsAdded[id] = permission - } - } - return changes } diff --git a/protocol/communities/community_encryption_key_action.go b/protocol/communities/community_encryption_key_action.go index 1796d01f5..5dfc43cc0 100644 --- a/protocol/communities/community_encryption_key_action.go +++ b/protocol/communities/community_encryption_key_action.go @@ -44,7 +44,7 @@ func EvaluateCommunityEncryptionKeyActions(origin, modified *Community) *Encrypt } } - changes := EvaluateCommunityChanges(origin.Description(), modified.Description()) + changes := EvaluateCommunityChanges(origin, modified) result := &EncryptionKeyActions{ CommunityKeyAction: *evaluateCommunityLevelEncryptionKeyAction(origin, modified, changes), @@ -89,7 +89,7 @@ func evaluateChannelLevelEncryptionKeyActions(origin, modified *Community, chang return &result } -func evaluateEncryptionKeyAction(originPermissions, modifiedPermissions []*protobuf.CommunityTokenPermission, allMembers, membersAdded, membersRemoved map[string]*protobuf.CommunityMember) *EncryptionKeyAction { +func evaluateEncryptionKeyAction(originPermissions, modifiedPermissions []*CommunityTokenPermission, allMembers, membersAdded, membersRemoved map[string]*protobuf.CommunityMember) *EncryptionKeyAction { result := &EncryptionKeyAction{ ActionType: EncryptionKeyNone, Members: map[string]*protobuf.CommunityMember{}, diff --git a/protocol/communities/community_event.go b/protocol/communities/community_event.go index 6eccffb53..0603f9e45 100644 --- a/protocol/communities/community_event.go +++ b/protocol/communities/community_event.go @@ -181,43 +181,34 @@ func (o *Community) ToAddTokenMetadataCommunityEvent(tokenMetadata *protobuf.Com } } -func (o *Community) UpdateCommunityByEvents(communityEventMessage *CommunityEventsMessage) (*CommunityChanges, error) { +func (o *Community) UpdateCommunityByEvents(communityEventMessage *CommunityEventsMessage) error { o.mutex.Lock() defer o.mutex.Unlock() // Validate that EventsBaseCommunityDescription was signed by the control node description, err := validateAndGetEventsMessageCommunityDescription(communityEventMessage.EventsBaseCommunityDescription, o.config.ID) if err != nil { - return nil, err + return err } if description.Clock != o.config.CommunityDescription.Clock { - return nil, ErrInvalidCommunityEventClock + return ErrInvalidCommunityEventClock } - // Create a deep copy of current community so we can update CommunityDescription by new admin events - copy := o.CreateDeepCopy() - - // Merge community admin events to existing community. Admin events must be stored to the db + // Merge community events to existing community. Community events must be stored to the db // during saving the community o.mergeCommunityEvents(communityEventMessage) - copy.config.CommunityDescription = description - copy.config.CommunityDescriptionProtocolMessage = communityEventMessage.EventsBaseCommunityDescription - copy.config.EventsData = o.config.EventsData + o.config.CommunityDescription = description + o.config.CommunityDescriptionProtocolMessage = communityEventMessage.EventsBaseCommunityDescription // Update the copy of the CommunityDescription by community events - err = copy.updateCommunityDescriptionByEvents() + err = o.updateCommunityDescriptionByEvents() if err != nil { - return nil, err + return err } - // Evaluate `CommunityChanges` data by searching a difference between `CommunityDescription` - // from the DB and `CommunityDescription` patched by community events - changes := EvaluateCommunityChanges(o.config.CommunityDescription, copy.config.CommunityDescription) - changes.Community = copy - - return changes, nil + return nil } func (o *Community) updateCommunityDescriptionByEvents() error { @@ -246,15 +237,19 @@ func (o *Community) updateCommunityDescriptionByCommunityEvent(communityEvent Co o.config.CommunityDescription.Tags = communityEvent.CommunityConfig.Tags case protobuf.CommunityEvent_COMMUNITY_MEMBER_TOKEN_PERMISSION_CHANGE: - _, err := o.upsertTokenPermission(communityEvent.TokenPermission) - if err != nil { - return err + if o.IsControlNode() { + _, err := o.upsertTokenPermission(communityEvent.TokenPermission) + if err != nil { + return err + } } case protobuf.CommunityEvent_COMMUNITY_MEMBER_TOKEN_PERMISSION_DELETE: - _, err := o.deleteTokenPermission(communityEvent.TokenPermission.Id) - if err != nil { - return err + if o.IsControlNode() { + _, err := o.deleteTokenPermission(communityEvent.TokenPermission.Id) + if err != nil { + return err + } } case protobuf.CommunityEvent_COMMUNITY_CATEGORY_CREATE: diff --git a/protocol/communities/community_token_permission.go b/protocol/communities/community_token_permission.go new file mode 100644 index 000000000..fc34f6aa1 --- /dev/null +++ b/protocol/communities/community_token_permission.go @@ -0,0 +1,65 @@ +package communities + +import ( + "reflect" + + "github.com/status-im/status-go/protocol/protobuf" +) + +type TokenPermissionState uint8 + +const ( + TokenPermissionApproved TokenPermissionState = iota + TokenPermissionAdditionPending + TokenPermissionUpdatePending + TokenPermissionRemovalPending +) + +type CommunityTokenPermission struct { + *protobuf.CommunityTokenPermission + State TokenPermissionState `json:"state,omitempty"` +} + +func NewCommunityTokenPermission(base *protobuf.CommunityTokenPermission) *CommunityTokenPermission { + return &CommunityTokenPermission{ + CommunityTokenPermission: base, + State: TokenPermissionApproved, + } +} + +func (p *CommunityTokenPermission) Equals(other *CommunityTokenPermission) bool { + if p.Id != other.Id || + p.Type != other.Type || + len(p.TokenCriteria) != len(other.TokenCriteria) || + len(p.ChatIds) != len(other.ChatIds) || + p.IsPrivate != other.IsPrivate || + p.State != other.State { + return false + } + + for i := range p.TokenCriteria { + if !compareTokenCriteria(p.TokenCriteria[i], other.TokenCriteria[i]) { + return false + } + } + + return reflect.DeepEqual(p.ChatIds, other.ChatIds) +} + +func compareTokenCriteria(a, b *protobuf.TokenCriteria) bool { + if a == nil && b == nil { + return true + } + if a == nil || b == nil { + return false + } + + return a.Type == b.Type && + a.Symbol == b.Symbol && + a.Name == b.Name && + a.Amount == b.Amount && + a.EnsPattern == b.EnsPattern && + a.Decimals == b.Decimals && + reflect.DeepEqual(a.ContractAddresses, b.ContractAddresses) && + reflect.DeepEqual(a.TokenIds, b.TokenIds) +} diff --git a/protocol/communities/manager.go b/protocol/communities/manager.go index e0207fa37..7dccfb2d4 100644 --- a/protocol/communities/manager.go +++ b/protocol/communities/manager.go @@ -1350,6 +1350,7 @@ func (m *Manager) handleCommunityDescriptionMessageCommon(community *Community, if err != nil { return nil, err } + community.config.EventsData = nil err = m.persistence.SaveCommunity(community) if err != nil { @@ -1433,9 +1434,11 @@ func (m *Manager) HandleCommunityEventsMessage(signer *ecdsa.PublicKey, message return nil, errors.New("user has not permissions to send events") } + originCommunity := community.CreateDeepCopy() + eventsMessage.Events = m.validateAndFilterEvents(community, eventsMessage.Events) - changes, err := community.UpdateCommunityByEvents(eventsMessage) + err = community.UpdateCommunityByEvents(eventsMessage) if err != nil { if err == ErrInvalidCommunityEventClock && community.IsControlNode() { m.publish(&Subscription{ @@ -1447,7 +1450,7 @@ func (m *Manager) HandleCommunityEventsMessage(signer *ecdsa.PublicKey, message return nil, err } - additionalCommunityResponse, err := m.handleAdditionalAdminChanges(changes.Community) + additionalCommunityResponse, err := m.handleAdditionalAdminChanges(community) if err != nil { return nil, err } @@ -1456,30 +1459,30 @@ func (m *Manager) HandleCommunityEventsMessage(signer *ecdsa.PublicKey, message return nil, err } - // Control node cerifies community events and publish changes - // all other nodes only apply changes to the community - if changes.Community.IsControlNode() { - changes.Community.increaseClock() - err = m.persistence.SaveCommunity(changes.Community) + // Control node applies events and publish updated CommunityDescription + if community.IsControlNode() { + community.config.EventsData = nil // clear events, they are already applied + community.increaseClock() + err = m.persistence.SaveCommunity(community) if err != nil { return nil, err } - m.publish(&Subscription{Community: changes.Community}) + m.publish(&Subscription{Community: community}) } else { - err = m.persistence.SaveCommunity(changes.Community) + err = m.persistence.SaveCommunity(community) if err != nil { return nil, err } - err := m.persistence.SaveCommunityEvents(changes.Community) + err := m.persistence.SaveCommunityEvents(community) if err != nil { return nil, err } } return &CommunityResponse{ - Community: changes.Community, - Changes: changes, + Community: community, + Changes: EvaluateCommunityChanges(originCommunity, community), RequestsToJoin: additionalCommunityResponse.RequestsToJoin, }, nil } @@ -2320,7 +2323,7 @@ func calculateChainIDsSet(accountsAndChainIDs []*AccountChainIDsCombination, req // checkPermissions will retrieve balances and check whether the user has // permission to join the community, if shortcircuit is true, it will stop as soon // as we know the answer -func (m *Manager) checkPermissions(permissions []*protobuf.CommunityTokenPermission, accountsAndChainIDs []*AccountChainIDsCombination, shortcircuit bool) (*CheckPermissionsResponse, error) { +func (m *Manager) checkPermissions(permissions []*CommunityTokenPermission, accountsAndChainIDs []*AccountChainIDsCombination, shortcircuit bool) (*CheckPermissionsResponse, error) { response := &CheckPermissionsResponse{ Satisfied: false, @@ -2661,7 +2664,7 @@ type CheckChannelViewAndPostPermissionsResult struct { Permissions map[string]*PermissionTokenCriteriaResult `json:"permissions"` } -func (m *Manager) checkChannelPermissions(viewOnlyPermissions []*protobuf.CommunityTokenPermission, viewAndPostPermissions []*protobuf.CommunityTokenPermission, accountsAndChainIDs []*AccountChainIDsCombination, shortcircuit bool) (*CheckChannelPermissionsResponse, error) { +func (m *Manager) checkChannelPermissions(viewOnlyPermissions []*CommunityTokenPermission, viewAndPostPermissions []*CommunityTokenPermission, accountsAndChainIDs []*AccountChainIDsCombination, shortcircuit bool) (*CheckChannelPermissionsResponse, error) { response := &CheckChannelPermissionsResponse{ ViewOnlyPermissions: &CheckChannelViewOnlyPermissionsResult{ @@ -4477,7 +4480,7 @@ func revealedAccountsToAccountsAndChainIDsCombination(revealedAccounts []*protob return accountsAndChainIDs } -func (m *Manager) accountsHasPrivilegedPermission(privilegedPermissions []*protobuf.CommunityTokenPermission, accounts []*AccountChainIDsCombination) bool { +func (m *Manager) accountsHasPrivilegedPermission(privilegedPermissions []*CommunityTokenPermission, accounts []*AccountChainIDsCombination) bool { if len(privilegedPermissions) > 0 { permissionResponse, err := m.checkPermissions(privilegedPermissions, accounts, true) if err != nil { @@ -4551,7 +4554,7 @@ func (m *Manager) GetRevealedAddresses(communityID types.HexBytes, memberPk stri return m.persistence.GetRequestToJoinRevealedAddresses(requestID) } -func (m *Manager) ReevaluatePrivelegedMember(community *Community, tokenPermissions []*protobuf.CommunityTokenPermission, +func (m *Manager) ReevaluatePrivelegedMember(community *Community, tokenPermissions []*CommunityTokenPermission, accountsAndChainIDs []*AccountChainIDsCombination, memberPubKey *ecdsa.PublicKey, privilegedRole protobuf.CommunityMember_Roles, alreadyHasPrivilegedRole bool) (bool, error) { @@ -4672,7 +4675,7 @@ func (m *Manager) HandleCommunityTokensMetadata(community *Community) error { return nil } -func getPrivilegesLevel(chainID uint64, tokenAddress string, tokenPermissions map[string]*protobuf.CommunityTokenPermission) community_token.PrivilegesLevel { +func getPrivilegesLevel(chainID uint64, tokenAddress string, tokenPermissions map[string]*CommunityTokenPermission) community_token.PrivilegesLevel { for _, permission := range tokenPermissions { if permission.Type == protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER || permission.Type == protobuf.CommunityTokenPermission_BECOME_TOKEN_OWNER { for _, tokenCriteria := range permission.TokenCriteria { diff --git a/protocol/communities/manager_test.go b/protocol/communities/manager_test.go index 26f1eedce..ccacec879 100644 --- a/protocol/communities/manager_test.go +++ b/protocol/communities/manager_test.go @@ -187,11 +187,13 @@ func (s *ManagerSuite) TestRetrieveTokens() { }, } - var permissions = []*protobuf.CommunityTokenPermission{ - &protobuf.CommunityTokenPermission{ - Id: "some-id", - Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, - TokenCriteria: tokenCriteria, + var permissions = []*CommunityTokenPermission{ + &CommunityTokenPermission{ + CommunityTokenPermission: &protobuf.CommunityTokenPermission{ + Id: "some-id", + Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, + TokenCriteria: tokenCriteria, + }, }, } @@ -234,11 +236,13 @@ func (s *ManagerSuite) TestRetrieveCollectibles() { }, } - var permissions = []*protobuf.CommunityTokenPermission{ - &protobuf.CommunityTokenPermission{ - Id: "some-id", - Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, - TokenCriteria: tokenCriteria, + var permissions = []*CommunityTokenPermission{ + &CommunityTokenPermission{ + CommunityTokenPermission: &protobuf.CommunityTokenPermission{ + Id: "some-id", + Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, + TokenCriteria: tokenCriteria, + }, }, } @@ -905,8 +909,8 @@ func (s *ManagerSuite) TestCheckChannelPermissions_NoPermissions() { }, } - var viewOnlyPermissions = make([]*protobuf.CommunityTokenPermission, 0) - var viewAndPostPermissions = make([]*protobuf.CommunityTokenPermission, 0) + var viewOnlyPermissions = make([]*CommunityTokenPermission, 0) + var viewAndPostPermissions = make([]*CommunityTokenPermission, 0) tm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), 0) resp, err := m.checkChannelPermissions(viewOnlyPermissions, viewAndPostPermissions, accountChainIDsCombination, false) @@ -946,16 +950,18 @@ func (s *ManagerSuite) TestCheckChannelPermissions_ViewOnlyPermissions() { }, } - var viewOnlyPermissions = []*protobuf.CommunityTokenPermission{ - &protobuf.CommunityTokenPermission{ - Id: "some-id", - Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL, - TokenCriteria: tokenCriteria, - ChatIds: []string{"test-channel-id", "test-channel-id-2"}, + var viewOnlyPermissions = []*CommunityTokenPermission{ + &CommunityTokenPermission{ + CommunityTokenPermission: &protobuf.CommunityTokenPermission{ + Id: "some-id", + Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL, + TokenCriteria: tokenCriteria, + ChatIds: []string{"test-channel-id", "test-channel-id-2"}, + }, }, } - var viewAndPostPermissions = make([]*protobuf.CommunityTokenPermission, 0) + var viewAndPostPermissions = make([]*CommunityTokenPermission, 0) tm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), 0) resp, err := m.checkChannelPermissions(viewOnlyPermissions, viewAndPostPermissions, accountChainIDsCombination, false) @@ -1004,16 +1010,18 @@ func (s *ManagerSuite) TestCheckChannelPermissions_ViewAndPostPermissions() { }, } - var viewAndPostPermissions = []*protobuf.CommunityTokenPermission{ - &protobuf.CommunityTokenPermission{ - Id: "some-id", - Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL, - TokenCriteria: tokenCriteria, - ChatIds: []string{"test-channel-id", "test-channel-id-2"}, + var viewAndPostPermissions = []*CommunityTokenPermission{ + &CommunityTokenPermission{ + CommunityTokenPermission: &protobuf.CommunityTokenPermission{ + Id: "some-id", + Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL, + TokenCriteria: tokenCriteria, + ChatIds: []string{"test-channel-id", "test-channel-id-2"}, + }, }, } - var viewOnlyPermissions = make([]*protobuf.CommunityTokenPermission, 0) + var viewOnlyPermissions = make([]*CommunityTokenPermission, 0) tm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), 0) resp, err := m.checkChannelPermissions(viewOnlyPermissions, viewAndPostPermissions, accountChainIDsCombination, false) @@ -1063,12 +1071,14 @@ func (s *ManagerSuite) TestCheckChannelPermissions_ViewAndPostPermissionsCombina }, } - var viewOnlyPermissions = []*protobuf.CommunityTokenPermission{ - &protobuf.CommunityTokenPermission{ - Id: "some-id", - Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL, - TokenCriteria: viewOnlyTokenCriteria, - ChatIds: []string{"test-channel-id", "test-channel-id-2"}, + var viewOnlyPermissions = []*CommunityTokenPermission{ + &CommunityTokenPermission{ + CommunityTokenPermission: &protobuf.CommunityTokenPermission{ + Id: "some-id", + Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL, + TokenCriteria: viewOnlyTokenCriteria, + ChatIds: []string{"test-channel-id", "test-channel-id-2"}, + }, }, } @@ -1087,12 +1097,14 @@ func (s *ManagerSuite) TestCheckChannelPermissions_ViewAndPostPermissionsCombina }, } - var viewAndPostPermissions = []*protobuf.CommunityTokenPermission{ - &protobuf.CommunityTokenPermission{ - Id: "some-id", - Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL, - TokenCriteria: viewAndPostTokenCriteria, - ChatIds: []string{"test-channel-id", "test-channel-id-2"}, + var viewAndPostPermissions = []*CommunityTokenPermission{ + &CommunityTokenPermission{ + CommunityTokenPermission: &protobuf.CommunityTokenPermission{ + Id: "some-id", + Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL, + TokenCriteria: viewAndPostTokenCriteria, + ChatIds: []string{"test-channel-id", "test-channel-id-2"}, + }, }, } diff --git a/protocol/communities/utils.go b/protocol/communities/utils.go index bd9df755b..25ddfcc18 100644 --- a/protocol/communities/utils.go +++ b/protocol/communities/utils.go @@ -14,7 +14,7 @@ func CalculateRequestID(publicKey string, communityID types.HexBytes) types.HexB return crypto.Keccak256([]byte(idString)) } -func ExtractTokenCriteria(permissions []*protobuf.CommunityTokenPermission) (erc20TokenCriteria map[uint64]map[string]*protobuf.TokenCriteria, erc721TokenCriteria map[uint64]map[string]*protobuf.TokenCriteria, ensTokenCriteria []string) { +func ExtractTokenCriteria(permissions []*CommunityTokenPermission) (erc20TokenCriteria map[uint64]map[string]*protobuf.TokenCriteria, erc721TokenCriteria map[uint64]map[string]*protobuf.TokenCriteria, ensTokenCriteria []string) { erc20TokenCriteria = make(map[uint64]map[string]*protobuf.TokenCriteria) erc721TokenCriteria = make(map[uint64]map[string]*protobuf.TokenCriteria) ensTokenCriteria = make([]string, 0) diff --git a/protocol/communities_events_utils_test.go b/protocol/communities_events_utils_test.go index 5e2648586..1b33d85dc 100644 --- a/protocol/communities_events_utils_test.go +++ b/protocol/communities_events_utils_test.go @@ -277,34 +277,44 @@ func createTestPermissionRequest(community *communities.Community, pType protobu } func createTokenPermission(base CommunityEventsTestsInterface, community *communities.Community, request *requests.CreateCommunityTokenPermission) (string, *requests.CreateCommunityTokenPermission) { - checkTokenPermissionCreation := func(response *MessengerResponse) error { - modifiedCommmunity, err := getModifiedCommunity(response, community.IDString()) - if err != nil { - return err - } - - if len(modifiedCommmunity.TokenPermissionsByType(request.Type)) == 0 { - return errors.New("new token permission was not found") - } - - return nil - } - response, err := base.GetEventSender().CreateCommunityTokenPermission(request) s := base.GetSuite() s.Require().NoError(err) - s.Require().Nil(checkTokenPermissionCreation(response)) + s.Require().Len(response.CommunityChanges, 1) + s.Require().Len(response.CommunityChanges[0].TokenPermissionsAdded, 1) - checkClientsReceivedAdminEvent(base, WaitCommunityCondition, checkTokenPermissionCreation) + addedPermission := func() *communities.CommunityTokenPermission { + for _, permission := range response.CommunityChanges[0].TokenPermissionsAdded { + return permission + } + return nil + }() + s.Require().NotNil(addedPermission) + // Permission added by event must be in pending state + s.Require().Equal(communities.TokenPermissionAdditionPending, addedPermission.State) - var tokenPermissionID string - for tokenPermissionID = range response.CommunityChanges[0].TokenPermissionsAdded { - break + responseHasApprovedTokenPermission := func(r *MessengerResponse) bool { + if len(r.Communities()) == 0 { + return false + } + + receivedPermission := r.Communities()[0].TokenPermissionByID(addedPermission.Id) + return receivedPermission != nil && receivedPermission.State == communities.TokenPermissionApproved } - s.Require().NotEqual(tokenPermissionID, "") + // Control node receives community event & approves it + _, err = WaitOnMessengerResponse(base.GetControlNode(), responseHasApprovedTokenPermission, "community with approved permission not found") + s.Require().NoError(err) - return tokenPermissionID, request + // Member receives updated community description + _, err = WaitOnMessengerResponse(base.GetMember(), responseHasApprovedTokenPermission, "community with approved permission not found") + s.Require().NoError(err) + + // EventSender receives updated community description + _, err = WaitOnMessengerResponse(base.GetEventSender(), responseHasApprovedTokenPermission, "community with approved permission not found") + s.Require().NoError(err) + + return addedPermission.Id, request } func createTestTokenPermission(base CommunityEventsTestsInterface, community *communities.Community, pType protobuf.CommunityTokenPermission_Type) (string, *requests.CreateCommunityTokenPermission) { @@ -314,54 +324,79 @@ func createTestTokenPermission(base CommunityEventsTestsInterface, community *co func editTokenPermission(base CommunityEventsTestsInterface, community *communities.Community, request *requests.EditCommunityTokenPermission) { s := base.GetSuite() - checkTokenPermissionEdit := func(response *MessengerResponse) error { - modifiedCommmunity, err := getModifiedCommunity(response, community.IDString()) - if err != nil { - return err - } - - assertCheckTokenPermissionEdited(s, modifiedCommmunity, request.CreateCommunityTokenPermission.Type) - - return nil - } response, err := base.GetEventSender().EditCommunityTokenPermission(request) s.Require().NoError(err) - s.Require().Nil(checkTokenPermissionEdit(response)) + s.Require().Len(response.CommunityChanges, 1) + s.Require().Len(response.CommunityChanges[0].TokenPermissionsModified, 1) - checkClientsReceivedAdminEvent(base, WaitCommunityCondition, checkTokenPermissionEdit) -} + editedPermission := response.CommunityChanges[0].TokenPermissionsModified[request.PermissionID] + s.Require().NotNil(editedPermission) + // Permission edited by event must be in pending state + s.Require().Equal(communities.TokenPermissionUpdatePending, editedPermission.State) -func assertCheckTokenPermissionEdited(s *suite.Suite, community *communities.Community, pType protobuf.CommunityTokenPermission_Type) { - permissions := community.TokenPermissionsByType(pType) - s.Require().Len(permissions, 1) - s.Require().Len(permissions[0].TokenCriteria, 1) - s.Require().Equal(permissions[0].TokenCriteria[0].Type, protobuf.CommunityTokenType_ERC20) - s.Require().Equal(permissions[0].TokenCriteria[0].Symbol, "UPDATED") - s.Require().Equal(permissions[0].TokenCriteria[0].Amount, "200") - s.Require().Equal(permissions[0].TokenCriteria[0].Decimals, uint64(18)) + permissionSatisfyRequest := func(p *communities.CommunityTokenPermission) bool { + return request.Type == p.Type && + request.TokenCriteria[0].Symbol == p.TokenCriteria[0].Symbol && + request.TokenCriteria[0].Amount == p.TokenCriteria[0].Amount && + request.TokenCriteria[0].Decimals == p.TokenCriteria[0].Decimals + } + s.Require().True(permissionSatisfyRequest(editedPermission)) + + responseHasApprovedEditedTokenPermission := func(r *MessengerResponse) bool { + if len(r.Communities()) == 0 { + return false + } + + receivedPermission := r.Communities()[0].TokenPermissionByID(editedPermission.Id) + return receivedPermission != nil && receivedPermission.State == communities.TokenPermissionApproved && + permissionSatisfyRequest(receivedPermission) + } + + // Control node receives community event & approves it + _, err = WaitOnMessengerResponse(base.GetControlNode(), responseHasApprovedEditedTokenPermission, "community with approved permission not found") + s.Require().NoError(err) + + // Member receives updated community description + _, err = WaitOnMessengerResponse(base.GetMember(), responseHasApprovedEditedTokenPermission, "community with approved permission not found") + s.Require().NoError(err) + + // EventSender receives updated community description + _, err = WaitOnMessengerResponse(base.GetEventSender(), responseHasApprovedEditedTokenPermission, "community with approved permission not found") + s.Require().NoError(err) } func deleteTokenPermission(base CommunityEventsTestsInterface, community *communities.Community, request *requests.DeleteCommunityTokenPermission) { - checkTokenPermissionDeleted := func(response *MessengerResponse) error { - modifiedCommmunity, err := getModifiedCommunity(response, community.IDString()) - if err != nil { - return err - } - - if modifiedCommmunity.HasTokenPermissions() { - return errors.New("token permission was not deleted") - } - - return nil - } - response, err := base.GetEventSender().DeleteCommunityTokenPermission(request) s := base.GetSuite() s.Require().NoError(err) - s.Require().Nil(checkTokenPermissionDeleted(response)) + s.Require().Len(response.CommunityChanges, 1) + s.Require().Len(response.CommunityChanges[0].TokenPermissionsModified, 1) - checkClientsReceivedAdminEvent(base, WaitCommunityCondition, checkTokenPermissionDeleted) + removedPermission := response.CommunityChanges[0].TokenPermissionsModified[request.PermissionID] + s.Require().NotNil(removedPermission) + // Permission removed by event must be in pending state + s.Require().Equal(communities.TokenPermissionRemovalPending, removedPermission.State) + + responseHasNoTokenPermission := func(r *MessengerResponse) bool { + if len(r.Communities()) == 0 { + return false + } + + return r.Communities()[0].TokenPermissionByID(removedPermission.Id) == nil + } + + // Control node receives community event & approves it + _, err = WaitOnMessengerResponse(base.GetControlNode(), responseHasNoTokenPermission, "community with approved permission not found") + s.Require().NoError(err) + + // Member receives updated community description + _, err = WaitOnMessengerResponse(base.GetMember(), responseHasNoTokenPermission, "community with approved permission not found") + s.Require().NoError(err) + + // EventSender receives updated community description + _, err = WaitOnMessengerResponse(base.GetEventSender(), responseHasNoTokenPermission, "community with approved permission not found") + s.Require().NoError(err) } func assertCheckTokenPermissionCreated(s *suite.Suite, community *communities.Community, pType protobuf.CommunityTokenPermission_Type) { diff --git a/protocol/communities_messenger_token_permissions_test.go b/protocol/communities_messenger_token_permissions_test.go index 81fb9b169..d26315234 100644 --- a/protocol/communities_messenger_token_permissions_test.go +++ b/protocol/communities_messenger_token_permissions_test.go @@ -1001,7 +1001,7 @@ func (s *MessengerCommunitiesTokenPermissionsSuite) testReevaluateMemberPrivileg s.advertiseCommunityTo(community, s.alice) - var tokenPermission *protobuf.CommunityTokenPermission + var tokenPermission *communities.CommunityTokenPermission for _, tokenPermission = range community.TokenPermissions() { break } @@ -1110,8 +1110,8 @@ func (s *MessengerCommunitiesTokenPermissionsSuite) testReevaluateMemberPrivileg s.advertiseCommunityTo(community, s.alice) - var tokenPermission *protobuf.CommunityTokenPermission - var tokenMemberPermission *protobuf.CommunityTokenPermission + var tokenPermission *communities.CommunityTokenPermission + var tokenMemberPermission *communities.CommunityTokenPermission for _, permission := range community.TokenPermissions() { if permission.Type == protobuf.CommunityTokenPermission_BECOME_MEMBER { tokenMemberPermission = permission