chore(community)_: reevaluateMembers optimization (#5169)

* chore(community)_: reevaluateMembers optinizations
This commit is contained in:
Mykhailo Prakhov 2024-05-17 18:15:39 +02:00 committed by GitHub
parent 948e09af03
commit 77541725aa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 542 additions and 133 deletions

View File

@ -2186,6 +2186,14 @@ func (o *Community) AddMemberToChat(chatID string, publicKey *ecdsa.PublicKey,
return changes, nil
}
func (o *Community) PopulateChannelsWithAllMembers() {
members := o.Members()
for _, channel := range o.Chats() {
channel.Members = members
}
o.increaseClock()
}
func (o *Community) PopulateChatWithAllMembers(chatID string) (*CommunityChanges, error) {
o.mutex.Lock()
defer o.mutex.Unlock()
@ -2479,6 +2487,7 @@ func (o *Community) deleteTokenPermission(permissionID string) (*CommunityChange
changes := o.emptyCommunityChanges()
changes.TokenPermissionsRemoved[permissionID] = NewCommunityTokenPermission(permission)
return changes, nil
}

View File

@ -991,6 +991,11 @@ func (m *Manager) EditCommunityTokenPermission(request *requests.EditCommunityTo
return community, changes, nil
}
// use it only for testing purposes
func (m *Manager) ReevaluateMembers(communityID types.HexBytes) (*Community, map[protobuf.CommunityMember_Roles][]*ecdsa.PublicKey, error) {
return m.reevaluateMembers(communityID)
}
func (m *Manager) reevaluateMembers(communityID types.HexBytes) (*Community, map[protobuf.CommunityMember_Roles][]*ecdsa.PublicKey, error) {
m.communityLock.Lock(communityID)
defer m.communityLock.Unlock(communityID)
@ -1006,16 +1011,23 @@ func (m *Manager) reevaluateMembers(communityID types.HexBytes) (*Community, map
return nil, nil, ErrNotEnoughPermissions
}
becomeMemberPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_MEMBER)
becomeAdminPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_ADMIN)
becomeTokenMasterPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER)
communityPermissionsPreParsedData, channelPermissionsPreParsedData := PreParsePermissionsData(community.tokenPermissions())
hasMemberPermissions := len(becomeMemberPermissions) > 0
hasMemberPermissions := communityPermissionsPreParsedData[protobuf.CommunityTokenPermission_BECOME_MEMBER] != nil
if len(channelPermissionsPreParsedData) == 0 {
community.PopulateChannelsWithAllMembers()
}
newPrivilegedRoles := make(map[protobuf.CommunityMember_Roles][]*ecdsa.PublicKey)
newPrivilegedRoles[protobuf.CommunityMember_ROLE_TOKEN_MASTER] = []*ecdsa.PublicKey{}
newPrivilegedRoles[protobuf.CommunityMember_ROLE_ADMIN] = []*ecdsa.PublicKey{}
membersAccounts, err := m.persistence.GetCommunityRequestsToJoinRevealedAddresses(community.ID())
if err != nil {
return nil, nil, err
}
for memberKey := range community.Members() {
memberPubKey, err := common.HexToPubkey(memberKey)
if err != nil {
@ -1028,13 +1040,9 @@ func (m *Manager) reevaluateMembers(communityID types.HexBytes) (*Community, map
isCurrentRoleTokenMaster := community.IsMemberTokenMaster(memberPubKey)
isCurrentRoleAdmin := community.IsMemberAdmin(memberPubKey)
requestID := CalculateRequestID(memberKey, community.ID())
revealedAccounts, err := m.persistence.GetRequestToJoinRevealedAddresses(requestID)
if err != nil {
return nil, nil, err
}
memberHasWallet := len(revealedAccounts) > 0
revealedAccount, exists := membersAccounts[memberKey]
memberHasWallet := exists
// Check if user has privilege role without sharing the account to controlNode
// or user treated as a member without wallet in closed community
@ -1046,9 +1054,13 @@ func (m *Manager) reevaluateMembers(communityID types.HexBytes) (*Community, map
continue
}
accountsAndChainIDs := revealedAccountsToAccountsAndChainIDsCombination(revealedAccounts)
accountsAndChainIDs := revealedAccountsToAccountsAndChainIDsCombination(revealedAccount)
isNewRoleTokenMaster, err := m.ReevaluatePrivilegedMember(community, becomeTokenMasterPermissions, accountsAndChainIDs, memberPubKey,
isNewRoleTokenMaster, err := m.ReevaluatePrivilegedMember(
community,
communityPermissionsPreParsedData[protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER],
accountsAndChainIDs,
memberPubKey,
protobuf.CommunityMember_ROLE_TOKEN_MASTER, isCurrentRoleTokenMaster)
if err != nil {
@ -1064,7 +1076,11 @@ func (m *Manager) reevaluateMembers(communityID types.HexBytes) (*Community, map
continue
}
isNewRoleAdmin, err := m.ReevaluatePrivilegedMember(community, becomeAdminPermissions, accountsAndChainIDs, memberPubKey,
isNewRoleAdmin, err := m.ReevaluatePrivilegedMember(
community,
communityPermissionsPreParsedData[protobuf.CommunityTokenPermission_BECOME_ADMIN],
accountsAndChainIDs,
memberPubKey,
protobuf.CommunityMember_ROLE_ADMIN, isCurrentRoleAdmin)
if err != nil {
@ -1081,7 +1097,10 @@ func (m *Manager) reevaluateMembers(communityID types.HexBytes) (*Community, map
}
if hasMemberPermissions {
permissionResponse, err := m.PermissionChecker.CheckPermissions(becomeMemberPermissions, accountsAndChainIDs, true)
permissionResponse, err := m.PermissionChecker.CheckPermissions(
communityPermissionsPreParsedData[protobuf.CommunityTokenPermission_BECOME_MEMBER],
accountsAndChainIDs,
true)
if err != nil {
return nil, nil, err
}
@ -1096,52 +1115,96 @@ func (m *Manager) reevaluateMembers(communityID types.HexBytes) (*Community, map
}
}
// Validate channel permissions
for channelID := range community.Chats() {
chatID := community.ChatID(channelID)
viewOnlyPermissions := community.ChannelTokenPermissionsByType(chatID, protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL)
viewAndPostPermissions := community.ChannelTokenPermissionsByType(chatID, protobuf.CommunityTokenPermission_CAN_VIEW_AND_POST_CHANNEL)
if len(viewOnlyPermissions) == 0 && len(viewAndPostPermissions) == 0 {
// ensure all members are added back if channel permissions were removed
_, err = community.PopulateChatWithAllMembers(channelID)
if err != nil {
return nil, nil, err
}
continue
}
response, err := m.checkChannelPermissions(viewOnlyPermissions, viewAndPostPermissions, accountsAndChainIDs, true)
if err != nil {
return nil, nil, err
}
isMemberAlreadyInChannel := community.IsMemberInChat(memberPubKey, channelID)
if response.ViewOnlyPermissions.Satisfied || response.ViewAndPostPermissions.Satisfied {
channelRole := protobuf.CommunityMember_CHANNEL_ROLE_VIEWER
if response.ViewAndPostPermissions.Satisfied {
channelRole = protobuf.CommunityMember_CHANNEL_ROLE_POSTER
}
// Add the member back to the chat member list in case the role changed (it replaces the previous values)
_, err := community.AddMemberToChat(channelID, memberPubKey, []protobuf.CommunityMember_Roles{}, channelRole)
if err != nil {
return nil, nil, err
}
} else if isMemberAlreadyInChannel {
_, err := community.RemoveUserFromChat(memberPubKey, channelID)
if err != nil {
return nil, nil, err
}
}
err = m.reevaluateMemberChannelsPermissions(community, memberPubKey, channelPermissionsPreParsedData, accountsAndChainIDs)
if err != nil {
return nil, nil, err
}
}
return community, newPrivilegedRoles, m.saveAndPublish(community)
}
func (m *Manager) reevaluateMemberChannelsPermissions(community *Community, memberPubKey *ecdsa.PublicKey,
channelPermissionsPreParsedData map[string]*PreParsedCommunityPermissionsData, accountsAndChainIDs []*AccountChainIDsCombination) error {
if len(channelPermissionsPreParsedData) == 0 {
return nil
}
// check which permissions we satisfy and which not
channelPermissionsCheckResult, err := m.checkChannelsPermissions(channelPermissionsPreParsedData, accountsAndChainIDs, true)
if err != nil {
return err
}
for channelID := range community.Chats() {
chatID := community.ChatID(channelID)
isMemberAlreadyInChannel := community.IsMemberInChat(memberPubKey, channelID)
channelPermissionsCheckResult, exists := channelPermissionsCheckResult[chatID]
// if channel permissions were removed member must be added back
if !exists {
if !isMemberAlreadyInChannel {
_, err := community.AddMemberToChat(channelID, memberPubKey, []protobuf.CommunityMember_Roles{}, protobuf.CommunityMember_CHANNEL_ROLE_POSTER)
if err != nil {
return err
}
}
continue
}
viewAndPostSatisfied, viewAndPosPermissionExists := channelPermissionsCheckResult[protobuf.CommunityTokenPermission_CAN_VIEW_AND_POST_CHANNEL]
viewOnlySatisfied, viewOnlyPermissionExists := channelPermissionsCheckResult[protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL]
satisfied := false
channelRole := protobuf.CommunityMember_CHANNEL_ROLE_VIEWER
if viewAndPosPermissionExists && viewAndPostSatisfied {
satisfied = viewAndPostSatisfied
channelRole = protobuf.CommunityMember_CHANNEL_ROLE_POSTER
} else if !satisfied && viewOnlyPermissionExists {
satisfied = viewOnlySatisfied
}
if satisfied {
// Add the member back to the chat member list in case the role changed (it replaces the previous values)
_, err := community.AddMemberToChat(channelID, memberPubKey, []protobuf.CommunityMember_Roles{}, channelRole)
if err != nil {
return err
}
} else if !satisfied && isMemberAlreadyInChannel {
_, err := community.RemoveUserFromChat(memberPubKey, channelID)
if err != nil {
return err
}
}
}
return nil
}
func (m *Manager) checkChannelsPermissions(channelsPermissionsPreParsedData map[string]*PreParsedCommunityPermissionsData, accountsAndChainIDs []*AccountChainIDsCombination, shortcircuit bool) (map[string]map[protobuf.CommunityTokenPermission_Type]bool, error) {
channelPermissionsCheckResult := make(map[string]map[protobuf.CommunityTokenPermission_Type]bool)
for _, channelsPermissionPreParsedData := range channelsPermissionsPreParsedData {
permissionResponse, err := m.PermissionChecker.CheckPermissions(channelsPermissionPreParsedData, accountsAndChainIDs, true)
if err != nil {
return channelPermissionsCheckResult, err
}
// Note: in `PreParsedCommunityPermissionsData` for channels there will be only one permission
// no need to iterate over `Permissions`
for _, chatId := range channelsPermissionPreParsedData.Permissions[0].ChatIds {
if _, exists := channelPermissionsCheckResult[chatId]; !exists {
channelPermissionsCheckResult[chatId] = make(map[protobuf.CommunityTokenPermission_Type]bool)
}
satisfied, exists := channelPermissionsCheckResult[chatId][channelsPermissionPreParsedData.Permissions[0].Type]
if exists && satisfied {
continue
}
channelPermissionsCheckResult[chatId][channelsPermissionPreParsedData.Permissions[0].Type] = permissionResponse.Satisfied
}
}
return channelPermissionsCheckResult, nil
}
func (m *Manager) StartMembersReevaluationLoop(communityID types.HexBytes, reevaluateOnStart bool) {
go m.reevaluateMembersLoop(communityID, reevaluateOnStart)
}
@ -2462,24 +2525,22 @@ func (m *Manager) CheckPermissionToJoin(id []byte, addresses []gethcommon.Addres
}
return m.PermissionChecker.CheckPermissionToJoin(community, addresses)
}
func (m *Manager) accountsSatisfyPermissionsToJoin(community *Community, accounts []*protobuf.RevealedAccount) (bool, protobuf.CommunityMember_Roles, error) {
accountsAndChainIDs := revealedAccountsToAccountsAndChainIDsCombination(accounts)
becomeAdminPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_ADMIN)
becomeMemberPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_MEMBER)
becomeTokenMasterPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER)
func (m *Manager) accountsSatisfyPermissionsToJoin(
communityPermissionsPreParsedData map[protobuf.CommunityTokenPermission_Type]*PreParsedCommunityPermissionsData,
accountsAndChainIDs []*AccountChainIDsCombination) (bool, protobuf.CommunityMember_Roles, error) {
if m.accountsHasPrivilegedPermission(becomeTokenMasterPermissions, accountsAndChainIDs) {
if m.accountsHasPrivilegedPermission(communityPermissionsPreParsedData[protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER], accountsAndChainIDs) {
return true, protobuf.CommunityMember_ROLE_TOKEN_MASTER, nil
}
if m.accountsHasPrivilegedPermission(becomeAdminPermissions, accountsAndChainIDs) {
if m.accountsHasPrivilegedPermission(communityPermissionsPreParsedData[protobuf.CommunityTokenPermission_BECOME_ADMIN], accountsAndChainIDs) {
return true, protobuf.CommunityMember_ROLE_ADMIN, nil
}
if len(becomeMemberPermissions) > 0 {
permissionResponse, err := m.PermissionChecker.CheckPermissions(becomeMemberPermissions, accountsAndChainIDs, true)
preParsedBecomeMemberPermissions := communityPermissionsPreParsedData[protobuf.CommunityTokenPermission_BECOME_MEMBER]
if preParsedBecomeMemberPermissions != nil {
permissionResponse, err := m.PermissionChecker.CheckPermissions(preParsedBecomeMemberPermissions, accountsAndChainIDs, true)
if err != nil {
return false, protobuf.CommunityMember_ROLE_NONE, err
}
@ -2490,37 +2551,49 @@ func (m *Manager) accountsSatisfyPermissionsToJoin(community *Community, account
return true, protobuf.CommunityMember_ROLE_NONE, nil
}
func (m *Manager) accountsSatisfyPermissionsToJoinChannels(community *Community, accounts []*protobuf.RevealedAccount) (map[string]*protobuf.CommunityChat, map[string]*protobuf.CommunityChat, error) {
func (m *Manager) accountsSatisfyPermissionsToJoinChannels(
community *Community,
channelPermissionsPreParsedData map[string]*PreParsedCommunityPermissionsData,
accountsAndChainIDs []*AccountChainIDsCombination) (map[string]*protobuf.CommunityChat, map[string]*protobuf.CommunityChat, error) {
viewChats := make(map[string]*protobuf.CommunityChat)
viewAndPostChats := make(map[string]*protobuf.CommunityChat)
accountsAndChainIDs := revealedAccountsToAccountsAndChainIDsCombination(accounts)
if len(channelPermissionsPreParsedData) == 0 {
for channelID, channel := range community.config.CommunityDescription.Chats {
viewAndPostChats[channelID] = channel
}
return viewChats, viewAndPostChats, nil
}
// check which permissions we satisfy and which not
channelPermissionsCheckResult, err := m.checkChannelsPermissions(channelPermissionsPreParsedData, accountsAndChainIDs, true)
if err != nil {
m.logger.Warn("check channel permission failed: %v", zap.Error(err))
return viewChats, viewAndPostChats, err
}
for channelID, channel := range community.config.CommunityDescription.Chats {
channelViewOnlyPermissions := community.ChannelTokenPermissionsByType(community.IDString()+channelID, protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL)
channelViewAndPostPermissions := community.ChannelTokenPermissionsByType(community.IDString()+channelID, protobuf.CommunityTokenPermission_CAN_VIEW_AND_POST_CHANNEL)
channelPermissions := append(channelViewOnlyPermissions, channelViewAndPostPermissions...)
chatID := community.ChatID(channelID)
channelPermissionsCheckResult, exists := channelPermissionsCheckResult[chatID]
if len(channelPermissions) == 0 {
if !exists {
viewAndPostChats[channelID] = channel
continue
}
permissionResponse, err := m.PermissionChecker.CheckPermissions(channelPermissions, accountsAndChainIDs, true)
if err != nil {
return nil, nil, err
viewAndPostSatisfied, exists := channelPermissionsCheckResult[protobuf.CommunityTokenPermission_CAN_VIEW_AND_POST_CHANNEL]
if exists && viewAndPostSatisfied {
delete(viewChats, channelID)
viewAndPostChats[channelID] = channel
continue
}
if permissionResponse.Satisfied {
highestRole := calculateRolesAndHighestRole(permissionResponse.Permissions).HighestRole
if highestRole == nil {
return nil, nil, errors.New("failed to calculate highest role")
}
switch highestRole.Role {
case protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL:
viewOnlySatisfied, exists := channelPermissionsCheckResult[protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL]
if exists && viewOnlySatisfied {
if _, exists := viewAndPostChats[channelID]; !exists {
viewChats[channelID] = channel
case protobuf.CommunityTokenPermission_CAN_VIEW_AND_POST_CHANNEL:
viewAndPostChats[channelID] = channel
}
}
}
@ -2548,7 +2621,11 @@ func (m *Manager) AcceptRequestToJoin(dbRequest *RequestToJoin) (*Community, err
return nil, err
}
permissionsSatisfied, role, err := m.accountsSatisfyPermissionsToJoin(community, revealedAccounts)
accountsAndChainIDs := revealedAccountsToAccountsAndChainIDsCombination(revealedAccounts)
communityPermissionsPreParsedData, channelPermissionsPreParsedData := PreParsePermissionsData(community.tokenPermissions())
permissionsSatisfied, role, err := m.accountsSatisfyPermissionsToJoin(communityPermissionsPreParsedData, accountsAndChainIDs)
if err != nil {
return nil, err
}
@ -2567,7 +2644,7 @@ func (m *Manager) AcceptRequestToJoin(dbRequest *RequestToJoin) (*Community, err
return nil, err
}
viewChannels, postChannels, err := m.accountsSatisfyPermissionsToJoinChannels(community, revealedAccounts)
viewChannels, postChannels, err := m.accountsSatisfyPermissionsToJoinChannels(community, channelPermissionsPreParsedData, accountsAndChainIDs)
if err != nil {
return nil, err
}
@ -3001,6 +3078,8 @@ func (m *Manager) CheckChannelPermissions(communityID types.HexBytes, chatID str
viewOnlyPermissions := community.ChannelTokenPermissionsByType(chatID, protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL)
viewAndPostPermissions := community.ChannelTokenPermissionsByType(chatID, protobuf.CommunityTokenPermission_CAN_VIEW_AND_POST_CHANNEL)
viewOnlyPreParsedPermissions := preParsedCommunityPermissionsData(viewOnlyPermissions)
viewAndPostPreParsedPermissions := preParsedCommunityPermissionsData(viewAndPostPermissions)
allChainIDs, err := m.tokenManager.GetAllChainIDs()
if err != nil {
@ -3008,7 +3087,7 @@ func (m *Manager) CheckChannelPermissions(communityID types.HexBytes, chatID str
}
accountsAndChainIDs := combineAddressesAndChainIDs(addresses, allChainIDs)
response, err := m.checkChannelPermissions(viewOnlyPermissions, viewAndPostPermissions, accountsAndChainIDs, false)
response, err := m.checkChannelPermissions(viewOnlyPreParsedPermissions, viewAndPostPreParsedPermissions, accountsAndChainIDs, false)
if err != nil {
return nil, err
}
@ -3035,7 +3114,7 @@ type CheckChannelViewAndPostPermissionsResult struct {
Permissions map[string]*PermissionTokenCriteriaResult `json:"permissions"`
}
func (m *Manager) checkChannelPermissions(viewOnlyPermissions []*CommunityTokenPermission, viewAndPostPermissions []*CommunityTokenPermission, accountsAndChainIDs []*AccountChainIDsCombination, shortcircuit bool) (*CheckChannelPermissionsResponse, error) {
func (m *Manager) checkChannelPermissions(viewOnlyPreParsedPermissions *PreParsedCommunityPermissionsData, viewAndPostPreParsedPermissions *PreParsedCommunityPermissionsData, accountsAndChainIDs []*AccountChainIDsCombination, shortcircuit bool) (*CheckChannelPermissionsResponse, error) {
response := &CheckChannelPermissionsResponse{
ViewOnlyPermissions: &CheckChannelViewOnlyPermissionsResult{
@ -3048,18 +3127,18 @@ func (m *Manager) checkChannelPermissions(viewOnlyPermissions []*CommunityTokenP
},
}
viewOnlyPermissionsResponse, err := m.PermissionChecker.CheckPermissions(viewOnlyPermissions, accountsAndChainIDs, shortcircuit)
viewOnlyPermissionsResponse, err := m.PermissionChecker.CheckPermissions(viewOnlyPreParsedPermissions, accountsAndChainIDs, shortcircuit)
if err != nil {
return nil, err
}
viewAndPostPermissionsResponse, err := m.PermissionChecker.CheckPermissions(viewAndPostPermissions, accountsAndChainIDs, shortcircuit)
viewAndPostPermissionsResponse, err := m.PermissionChecker.CheckPermissions(viewAndPostPreParsedPermissions, accountsAndChainIDs, shortcircuit)
if err != nil {
return nil, err
}
hasViewOnlyPermissions := len(viewOnlyPermissions) > 0
hasViewAndPostPermissions := len(viewAndPostPermissions) > 0
hasViewOnlyPermissions := viewOnlyPreParsedPermissions != nil
hasViewAndPostPermissions := viewAndPostPreParsedPermissions != nil
if (hasViewAndPostPermissions && !hasViewOnlyPermissions) || (hasViewOnlyPermissions && hasViewAndPostPermissions && viewAndPostPermissionsResponse.Satisfied) {
response.ViewOnlyPermissions.Satisfied = viewAndPostPermissionsResponse.Satisfied
@ -3096,11 +3175,13 @@ func (m *Manager) CheckAllChannelsPermissions(communityID types.HexBytes, addres
Channels: make(map[string]*CheckChannelPermissionsResponse),
}
// TODO: optimize
for channelID := range channels {
viewOnlyPermissions := community.ChannelTokenPermissionsByType(community.IDString()+channelID, protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL)
viewAndPostPermissions := community.ChannelTokenPermissionsByType(community.IDString()+channelID, protobuf.CommunityTokenPermission_CAN_VIEW_AND_POST_CHANNEL)
checkChannelPermissionsResponse, err := m.checkChannelPermissions(viewOnlyPermissions, viewAndPostPermissions, accountsAndChainIDs, false)
viewOnlyPreParsedPermissions := preParsedCommunityPermissionsData(viewOnlyPermissions)
viewAndPostPreParsedPermissions := preParsedCommunityPermissionsData(viewAndPostPermissions)
checkChannelPermissionsResponse, err := m.checkChannelPermissions(viewOnlyPreParsedPermissions, viewAndPostPreParsedPermissions, accountsAndChainIDs, false)
if err != nil {
return nil, err
}
@ -4992,9 +5073,9 @@ func revealedAccountsToAccountsAndChainIDsCombination(revealedAccounts []*protob
return accountsAndChainIDs
}
func (m *Manager) accountsHasPrivilegedPermission(privilegedPermissions []*CommunityTokenPermission, accounts []*AccountChainIDsCombination) bool {
if len(privilegedPermissions) > 0 {
permissionResponse, err := m.PermissionChecker.CheckPermissions(privilegedPermissions, accounts, true)
func (m *Manager) accountsHasPrivilegedPermission(preParsedCommunityPermissionData *PreParsedCommunityPermissionsData, accounts []*AccountChainIDsCombination) bool {
if preParsedCommunityPermissionData != nil {
permissionResponse, err := m.PermissionChecker.CheckPermissions(preParsedCommunityPermissionData, accounts, true)
if err != nil {
m.logger.Warn("check privileged permission failed: %v", zap.Error(err))
return false
@ -5047,16 +5128,17 @@ func (m *Manager) GetRevealedAddresses(communityID types.HexBytes, memberPk stri
return response, err
}
func (m *Manager) ReevaluatePrivilegedMember(community *Community, tokenPermissions []*CommunityTokenPermission,
func (m *Manager) ReevaluatePrivilegedMember(community *Community, permissionsData *PreParsedCommunityPermissionsData,
accountsAndChainIDs []*AccountChainIDsCombination, memberPubKey *ecdsa.PublicKey,
privilegedRole protobuf.CommunityMember_Roles, alreadyHasPrivilegedRole bool) (bool, error) {
hasPrivilegedRolePermissions := len(tokenPermissions) > 0
hasPrivilegedRolePermissions := permissionsData != nil
removeCurrentRole := false
if hasPrivilegedRolePermissions {
permissionResponse, err := m.PermissionChecker.CheckPermissions(tokenPermissions, accountsAndChainIDs, true)
permissionResponse, err := m.PermissionChecker.CheckPermissions(permissionsData, accountsAndChainIDs, true)
if err != nil {
m.logger.Warn("check privileged permission failed: %v", zap.Error(err))
return alreadyHasPrivilegedRole, err
} else if permissionResponse.Satisfied && !alreadyHasPrivilegedRole {
_, err = community.AddRoleToMember(memberPubKey, privilegedRole)

View File

@ -210,6 +210,8 @@ func (s *ManagerSuite) TestRetrieveTokens() {
},
}
preParsedPermissions := preParsedCommunityPermissionsData(permissions)
accountChainIDsCombination := []*AccountChainIDsCombination{
&AccountChainIDsCombination{
Address: gethcommon.HexToAddress("0xD6b912e09E797D291E8D0eA3D3D17F8000e01c32"),
@ -218,14 +220,14 @@ func (s *ManagerSuite) TestRetrieveTokens() {
}
// Set response to exactly the right one
tm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), int64(1*math.Pow(10, float64(decimals))))
resp, err := m.PermissionChecker.CheckPermissions(permissions, accountChainIDsCombination, false)
resp, err := m.PermissionChecker.CheckPermissions(preParsedPermissions, accountChainIDsCombination, false)
s.Require().NoError(err)
s.Require().NotNil(resp)
s.Require().True(resp.Satisfied)
// Set response to 0
tm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), 0)
resp, err = m.PermissionChecker.CheckPermissions(permissions, accountChainIDsCombination, false)
resp, err = m.PermissionChecker.CheckPermissions(preParsedPermissions, accountChainIDsCombination, false)
s.Require().NoError(err)
s.Require().NotNil(resp)
s.Require().False(resp.Satisfied)
@ -259,6 +261,8 @@ func (s *ManagerSuite) TestRetrieveCollectibles() {
},
}
preParsedPermissions := preParsedCommunityPermissionsData(permissions)
accountChainIDsCombination := []*AccountChainIDsCombination{
&AccountChainIDsCombination{
Address: gethcommon.HexToAddress("0xD6b912e09E797D291E8D0eA3D3D17F8000e01c32"),
@ -269,7 +273,7 @@ func (s *ManagerSuite) TestRetrieveCollectibles() {
// Set response to exactly the right one
tokenBalances = []thirdparty.TokenBalance{tokenBalance(tokenID, 1)}
cm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), tokenBalances)
resp, err := m.PermissionChecker.CheckPermissions(permissions, accountChainIDsCombination, false)
resp, err := m.PermissionChecker.CheckPermissions(preParsedPermissions, accountChainIDsCombination, false)
s.Require().NoError(err)
s.Require().NotNil(resp)
s.Require().True(resp.Satisfied)
@ -277,7 +281,7 @@ func (s *ManagerSuite) TestRetrieveCollectibles() {
// Set balances to 0
tokenBalances = []thirdparty.TokenBalance{}
cm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), tokenBalances)
resp, err = m.PermissionChecker.CheckPermissions(permissions, accountChainIDsCombination, false)
resp, err = m.PermissionChecker.CheckPermissions(preParsedPermissions, accountChainIDsCombination, false)
s.Require().NoError(err)
s.Require().NotNil(resp)
s.Require().False(resp.Satisfied)
@ -932,9 +936,11 @@ func (s *ManagerSuite) TestCheckChannelPermissions_NoPermissions() {
var viewOnlyPermissions = make([]*CommunityTokenPermission, 0)
var viewAndPostPermissions = make([]*CommunityTokenPermission, 0)
viewOnlyPreParsedPermissions := preParsedCommunityPermissionsData(viewOnlyPermissions)
viewAndPostPreParsedPermissions := preParsedCommunityPermissionsData(viewAndPostPermissions)
tm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), 0)
resp, err := m.checkChannelPermissions(viewOnlyPermissions, viewAndPostPermissions, accountChainIDsCombination, false)
resp, err := m.checkChannelPermissions(viewOnlyPreParsedPermissions, viewAndPostPreParsedPermissions, accountChainIDsCombination, false)
s.Require().NoError(err)
s.Require().NotNil(resp)
@ -984,8 +990,11 @@ func (s *ManagerSuite) TestCheckChannelPermissions_ViewOnlyPermissions() {
var viewAndPostPermissions = make([]*CommunityTokenPermission, 0)
viewOnlyPreParsedPermissions := preParsedCommunityPermissionsData(viewOnlyPermissions)
viewAndPostPreParsedPermissions := preParsedCommunityPermissionsData(viewAndPostPermissions)
tm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), 0)
resp, err := m.checkChannelPermissions(viewOnlyPermissions, viewAndPostPermissions, accountChainIDsCombination, false)
resp, err := m.checkChannelPermissions(viewOnlyPreParsedPermissions, viewAndPostPreParsedPermissions, accountChainIDsCombination, false)
s.Require().NoError(err)
s.Require().NotNil(resp)
@ -996,7 +1005,7 @@ func (s *ManagerSuite) TestCheckChannelPermissions_ViewOnlyPermissions() {
// Set response to exactly the right one
tm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), int64(1*math.Pow(10, float64(decimals))))
resp, err = m.checkChannelPermissions(viewOnlyPermissions, viewAndPostPermissions, accountChainIDsCombination, false)
resp, err = m.checkChannelPermissions(viewOnlyPreParsedPermissions, viewAndPostPreParsedPermissions, accountChainIDsCombination, false)
s.Require().NoError(err)
s.Require().NotNil(resp)
@ -1044,8 +1053,11 @@ func (s *ManagerSuite) TestCheckChannelPermissions_ViewAndPostPermissions() {
var viewOnlyPermissions = make([]*CommunityTokenPermission, 0)
viewOnlyPreParsedPermissions := preParsedCommunityPermissionsData(viewOnlyPermissions)
viewAndPostPreParsedPermissions := preParsedCommunityPermissionsData(viewAndPostPermissions)
tm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), 0)
resp, err := m.checkChannelPermissions(viewOnlyPermissions, viewAndPostPermissions, accountChainIDsCombination, false)
resp, err := m.checkChannelPermissions(viewOnlyPreParsedPermissions, viewAndPostPreParsedPermissions, accountChainIDsCombination, false)
s.Require().NoError(err)
s.Require().NotNil(resp)
@ -1056,7 +1068,7 @@ func (s *ManagerSuite) TestCheckChannelPermissions_ViewAndPostPermissions() {
// Set response to exactly the right one
tm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), int64(1*math.Pow(10, float64(decimals))))
resp, err = m.checkChannelPermissions(viewOnlyPermissions, viewAndPostPermissions, accountChainIDsCombination, false)
resp, err = m.checkChannelPermissions(viewOnlyPreParsedPermissions, viewAndPostPreParsedPermissions, accountChainIDsCombination, false)
s.Require().NoError(err)
s.Require().NotNil(resp)
@ -1134,7 +1146,10 @@ func (s *ManagerSuite) TestCheckChannelPermissions_ViewAndPostPermissionsCombina
// Set resopnse for viewAndPost permissions
tm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(testContractAddresses[chainID]), 0)
resp, err := m.checkChannelPermissions(viewOnlyPermissions, viewAndPostPermissions, accountChainIDsCombination, false)
viewOnlyPreParsedPermissions := preParsedCommunityPermissionsData(viewOnlyPermissions)
viewAndPostPreParsedPermissions := preParsedCommunityPermissionsData(viewAndPostPermissions)
resp, err := m.checkChannelPermissions(viewOnlyPreParsedPermissions, viewAndPostPreParsedPermissions, accountChainIDsCombination, false)
s.Require().NoError(err)
s.Require().NotNil(resp)
@ -1213,7 +1228,10 @@ func (s *ManagerSuite) TestCheckChannelPermissions_ViewAndPostPermissionsCombina
// Set resopnse for viewAndPost permissions
tm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(testContractAddresses[chainID]), int64(1*math.Pow(10, float64(decimals))))
resp, err := m.checkChannelPermissions(viewOnlyPermissions, viewAndPostPermissions, accountChainIDsCombination, false)
viewOnlyPreParsedPermissions := preParsedCommunityPermissionsData(viewOnlyPermissions)
viewAndPostPreParsedPermissions := preParsedCommunityPermissionsData(viewAndPostPermissions)
resp, err := m.checkChannelPermissions(viewOnlyPreParsedPermissions, viewAndPostPreParsedPermissions, accountChainIDsCombination, false)
s.Require().NoError(err)
s.Require().NotNil(resp)

View File

@ -22,7 +22,7 @@ import (
type PermissionChecker interface {
CheckPermissionToJoin(*Community, []gethcommon.Address) (*CheckPermissionToJoinResponse, error)
CheckPermissions(permissions []*CommunityTokenPermission, accountsAndChainIDs []*AccountChainIDsCombination, shortcircuit bool) (*CheckPermissionsResponse, error)
CheckPermissions(permissionsParsedData *PreParsedCommunityPermissionsData, accountsAndChainIDs []*AccountChainIDsCombination, shortcircuit bool) (*CheckPermissionsResponse, error)
}
type DefaultPermissionChecker struct {
@ -33,6 +33,18 @@ type DefaultPermissionChecker struct {
logger *zap.Logger
}
type PreParsedPermissionsData struct {
Erc721TokenRequirements map[uint64]map[string]*protobuf.TokenCriteria
Erc20TokenAddresses []gethcommon.Address
Erc20ChainIDsMap map[uint64]bool
Erc721ChainIDsMap map[uint64]bool
}
type PreParsedCommunityPermissionsData struct {
*PreParsedPermissionsData
Permissions []*CommunityTokenPermission
}
func (p *DefaultPermissionChecker) getOwnedENS(addresses []gethcommon.Address) ([]string, error) {
ownedENS := make([]string, 0)
if p.ensVerifier == nil {
@ -159,8 +171,8 @@ func (p *DefaultPermissionChecker) CheckPermissionToJoin(community *Community, a
return becomeMemberPermissionsResponse, nil
}
// If there are any admin or token master permissions, combine result.
adminOrTokenPermissionsResponse, err := p.CheckPermissions(adminOrTokenMasterPermissionsToJoin, accountsAndChainIDs, false)
preParsedPermissions := preParsedCommunityPermissionsData(adminOrTokenMasterPermissionsToJoin)
adminOrTokenPermissionsResponse, err := p.CheckPermissions(preParsedPermissions, accountsAndChainIDs, false)
if err != nil {
return nil, err
}
@ -191,13 +203,15 @@ func (p *DefaultPermissionChecker) checkPermissionsOrDefault(permissions []*Comm
}
return response, nil
}
return p.CheckPermissions(permissions, accountsAndChainIDs, false)
preParsedPermissions := preParsedCommunityPermissionsData(permissions)
return p.CheckPermissions(preParsedPermissions, accountsAndChainIDs, false)
}
// 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 (p *DefaultPermissionChecker) CheckPermissions(permissions []*CommunityTokenPermission, accountsAndChainIDs []*AccountChainIDsCombination, shortcircuit bool) (*CheckPermissionsResponse, error) {
func (p *DefaultPermissionChecker) CheckPermissions(permissionsParsedData *PreParsedCommunityPermissionsData, accountsAndChainIDs []*AccountChainIDsCombination, shortcircuit bool) (*CheckPermissionsResponse, error) {
response := &CheckPermissionsResponse{
Satisfied: false,
@ -205,30 +219,25 @@ func (p *DefaultPermissionChecker) CheckPermissions(permissions []*CommunityToke
ValidCombinations: make([]*AccountChainIDsCombination, 0),
}
erc20TokenRequirements, erc721TokenRequirements, _ := ExtractTokenCriteria(permissions)
if permissionsParsedData == nil {
response.Satisfied = true
return response, nil
}
erc20ChainIDsMap := make(map[uint64]bool)
erc721ChainIDsMap := make(map[uint64]bool)
erc721TokenRequirements := permissionsParsedData.Erc721TokenRequirements
erc20ChainIDsMap := permissionsParsedData.Erc20ChainIDsMap
erc721ChainIDsMap := permissionsParsedData.Erc721ChainIDsMap
erc20TokenAddresses := permissionsParsedData.Erc20TokenAddresses
erc20TokenAddresses := make([]gethcommon.Address, 0)
accounts := make([]gethcommon.Address, 0)
// TODO: move outside in order not to convert it
for _, accountAndChainIDs := range accountsAndChainIDs {
accounts = append(accounts, accountAndChainIDs.Address)
}
// figure out chain IDs we're interested in
for chainID, tokens := range erc20TokenRequirements {
erc20ChainIDsMap[chainID] = true
for contractAddress := range tokens {
erc20TokenAddresses = append(erc20TokenAddresses, gethcommon.HexToAddress(contractAddress))
}
}
for chainID := range erc721TokenRequirements {
erc721ChainIDsMap[chainID] = true
}
chainIDsForERC20 := calculateChainIDsSet(accountsAndChainIDs, erc20ChainIDsMap)
chainIDsForERC721 := calculateChainIDsSet(accountsAndChainIDs, erc721ChainIDsMap)
@ -260,7 +269,7 @@ func (p *DefaultPermissionChecker) CheckPermissions(permissions []*CommunityToke
accountsChainIDsCombinations := make(map[gethcommon.Address]map[uint64]bool)
for _, tokenPermission := range permissions {
for _, tokenPermission := range permissionsParsedData.Permissions {
permissionRequirementsMet := true
response.Permissions[tokenPermission.Id] = &PermissionTokenCriteriaResult{Role: tokenPermission.Type}
@ -435,3 +444,64 @@ func (p *DefaultPermissionChecker) CheckPermissions(permissions []*CommunityToke
return response, nil
}
func preParsedPermissionsData(permissions []*CommunityTokenPermission) *PreParsedPermissionsData {
erc20TokenRequirements, erc721TokenRequirements, _ := ExtractTokenCriteria(permissions)
erc20ChainIDsMap := make(map[uint64]bool)
erc721ChainIDsMap := make(map[uint64]bool)
erc20TokenAddresses := make([]gethcommon.Address, 0)
// figure out chain IDs we're interested in
for chainID, tokens := range erc20TokenRequirements {
erc20ChainIDsMap[chainID] = true
for contractAddress := range tokens {
erc20TokenAddresses = append(erc20TokenAddresses, gethcommon.HexToAddress(contractAddress))
}
}
for chainID := range erc721TokenRequirements {
erc721ChainIDsMap[chainID] = true
}
return &PreParsedPermissionsData{
Erc721TokenRequirements: erc721TokenRequirements,
Erc20TokenAddresses: erc20TokenAddresses,
Erc20ChainIDsMap: erc20ChainIDsMap,
Erc721ChainIDsMap: erc721ChainIDsMap,
}
}
func preParsedCommunityPermissionsData(permissions []*CommunityTokenPermission) *PreParsedCommunityPermissionsData {
if len(permissions) == 0 {
return nil
}
return &PreParsedCommunityPermissionsData{
Permissions: permissions,
PreParsedPermissionsData: preParsedPermissionsData(permissions),
}
}
func PreParsePermissionsData(permissions map[string]*CommunityTokenPermission) (map[protobuf.CommunityTokenPermission_Type]*PreParsedCommunityPermissionsData, map[string]*PreParsedCommunityPermissionsData) {
becomeMemberPermissions := TokenPermissionsByType(permissions, protobuf.CommunityTokenPermission_BECOME_MEMBER)
becomeAdminPermissions := TokenPermissionsByType(permissions, protobuf.CommunityTokenPermission_BECOME_ADMIN)
becomeTokenMasterPermissions := TokenPermissionsByType(permissions, protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER)
viewOnlyPermissions := TokenPermissionsByType(permissions, protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL)
viewAndPostPermissions := TokenPermissionsByType(permissions, protobuf.CommunityTokenPermission_CAN_VIEW_AND_POST_CHANNEL)
channelPermissions := append(viewAndPostPermissions, viewOnlyPermissions...)
communityPermissionsPreParsedData := make(map[protobuf.CommunityTokenPermission_Type]*PreParsedCommunityPermissionsData)
communityPermissionsPreParsedData[protobuf.CommunityTokenPermission_BECOME_MEMBER] = preParsedCommunityPermissionsData(becomeMemberPermissions)
communityPermissionsPreParsedData[protobuf.CommunityTokenPermission_BECOME_ADMIN] = preParsedCommunityPermissionsData(becomeAdminPermissions)
communityPermissionsPreParsedData[protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER] = preParsedCommunityPermissionsData(becomeTokenMasterPermissions)
channelPermissionsPreParsedData := make(map[string]*PreParsedCommunityPermissionsData)
for _, channelPermission := range channelPermissions {
channelPermissionsPreParsedData[channelPermission.Id] = preParsedCommunityPermissionsData([]*CommunityTokenPermission{channelPermission})
}
return communityPermissionsPreParsedData, channelPermissionsPreParsedData
}

View File

@ -2055,3 +2055,59 @@ func (p *Persistence) getDecryptedCommunityDescriptionByID(tx *sql.Tx, community
return nil, err
}
}
func (p *Persistence) GetCommunityRequestsToJoinRevealedAddresses(communityID []byte) (map[string][]*protobuf.RevealedAccount, error) {
accounts := make(map[string][]*protobuf.RevealedAccount)
rows, err := p.db.Query(`
SELECT r.public_key,
a.address, a.chain_ids, a.is_airdrop_address, a.signature
FROM communities_requests_to_join r
LEFT JOIN communities_requests_to_join_revealed_addresses a ON r.id = a.request_id
WHERE r.community_id = ? AND r.state = ?`, communityID, RequestToJoinStateAccepted)
if err != nil {
if err == sql.ErrNoRows {
return accounts, nil
}
return nil, err
}
defer rows.Close()
for rows.Next() {
var rawPublicKey sql.NullString
var address sql.NullString
var chainIDsStr sql.NullString
var isAirdropAddress sql.NullBool
var signature sql.RawBytes
err = rows.Scan(&rawPublicKey, &address, &chainIDsStr, &isAirdropAddress, &signature)
if err != nil {
return nil, err
}
if !rawPublicKey.Valid {
return nil, errors.New("GetCommunityRequestsToJoinRevealedAddresses: invalid public key")
}
publicKey := rawPublicKey.String
revealedAccount, err := toRevealedAccount(address, chainIDsStr, isAirdropAddress, signature)
if err != nil {
return nil, err
}
if revealedAccount == nil {
continue
}
if _, exists := accounts[publicKey]; !exists {
accounts[publicKey] = []*protobuf.RevealedAccount{revealedAccount}
} else {
accounts[publicKey] = append(accounts[publicKey], revealedAccount)
}
}
return accounts, nil
}

View File

@ -1051,3 +1051,55 @@ func (s *PersistenceSuite) TestDecryptedCommunityCacheClock() {
s.Require().NoError(err)
s.Require().Equal(count, 1)
}
func (s *PersistenceSuite) TestGetCommunityRequestsToJoinRevealedAddresses() {
clock := uint64(time.Now().Unix())
communityID := types.HexBytes{7, 7, 7, 7, 7, 7, 7, 7}
revealedAddress := "address1"
chainIds := []uint64{1, 2}
publicKey := common.PubkeyToHex(&s.identity.PublicKey)
signature := []byte("test")
// No data in database
accounts, err := s.db.GetCommunityRequestsToJoinRevealedAddresses(communityID)
s.Require().NoError(err)
_, exists := accounts[publicKey]
s.Require().False(exists)
expectedRtj := &RequestToJoin{
ID: types.HexBytes{1, 2, 3, 4, 5, 6, 7, 8},
PublicKey: publicKey,
Clock: clock,
CommunityID: communityID,
State: RequestToJoinStateAccepted,
RevealedAccounts: []*protobuf.RevealedAccount{
{
Address: revealedAddress,
ChainIds: chainIds,
IsAirdropAddress: true,
Signature: signature,
},
},
}
// Request to join was stored without revealed account
err = s.db.SaveRequestToJoin(expectedRtj)
s.Require().NoError(err, "SaveRequestToJoin shouldn't give any error")
// revealed account is absent
accounts, err = s.db.GetCommunityRequestsToJoinRevealedAddresses(communityID)
s.Require().NoError(err, "RevealedAccounts empty, shouldn't give any error")
_, exists = accounts[publicKey]
s.Require().False(exists)
// save revealed accounts for the previous request to join
err = s.db.SaveRequestToJoinRevealedAddresses(expectedRtj.ID, expectedRtj.RevealedAccounts)
s.Require().NoError(err)
accounts, err = s.db.GetCommunityRequestsToJoinRevealedAddresses(communityID)
s.Require().NoError(err)
memberAccounts, exists := accounts[publicKey]
s.Require().True(exists)
s.Require().Len(memberAccounts, 1)
}

View File

@ -38,7 +38,7 @@ func ExtractTokenCriteria(permissions []*CommunityTokenPermission) (erc20TokenCr
if isERC20 && !existsERC20 {
erc20TokenCriteria[chainID] = make(map[string]*protobuf.TokenCriteria)
}
// TODO: check if we do not duplicate this due to ToLower case
_, existsERC721 = erc721TokenCriteria[chainID][contractAddress]
if isERC721 && !existsERC721 {
erc721TokenCriteria[chainID][strings.ToLower(contractAddress)] = tokenRequirement

View File

@ -3619,7 +3619,8 @@ func (t *testPermissionChecker) CheckPermissionToJoin(*communities.Community, []
return &communities.CheckPermissionsResponse{Satisfied: true}, nil
}
func (t *testPermissionChecker) CheckPermissions(permissions []*communities.CommunityTokenPermission, accountsAndChainIDs []*communities.AccountChainIDsCombination, shortcircuit bool) (*communities.CheckPermissionsResponse, error) {
func (t *testPermissionChecker) CheckPermissions(permissionsParsedData *communities.PreParsedCommunityPermissionsData, accountsAndChainIDs []*communities.AccountChainIDsCombination, shortcircuit bool) (*communities.CheckPermissionsResponse, error) {
return &communities.CheckPermissionsResponse{Satisfied: true}, nil
}

View File

@ -7,11 +7,13 @@ import (
"errors"
"fmt"
"math/big"
"strconv"
"strings"
"sync"
"testing"
"time"
"github.com/google/uuid"
"github.com/stretchr/testify/suite"
"go.uber.org/zap"
@ -2011,3 +2013,122 @@ func (s *MessengerCommunitiesTokenPermissionsSuite) TestResendEncryptionKeyOnBac
s.Require().NoError(err)
s.Require().Len(response.Messages(), 1)
}
func (s *MessengerCommunitiesTokenPermissionsSuite) TestReevaluateMemberPermissionsPerformance() {
// This test is created for a performance degradation tracking for reevaluateMember permissions
// current scenario mostly track channels permissions reevaluating, but feel free to expand it to
// other scenarios or test you performance improvements
// in average, it took nearly 100-105 ms to check one permission for a current scenario:
// - 10 members
// - 10 channels
// - one permission (channel permission for all 10 channels is set up)
// currently, adding any new permission to test must twice the current test average time
community, chat := s.createCommunity()
community, err := s.owner.communitiesManager.GetByID(community.ID())
s.Require().NoError(err)
s.Require().Len(community.Chats(), 2)
requestToJoin := &communities.RequestToJoin{
Clock: uint64(time.Now().Unix()),
CommunityID: community.ID(),
State: communities.RequestToJoinStateAccepted,
RevealedAccounts: []*protobuf.RevealedAccount{
{
Address: bobAddress,
ChainIds: []uint64{testChainID1},
IsAirdropAddress: true,
Signature: []byte("test"),
},
},
}
communityRole := []protobuf.CommunityMember_Roles{}
keysCount := 10
for i := 0; i < keysCount; i++ {
privateKey, err := crypto.GenerateKey()
s.Require().NoError(err)
memberPubKeyStr := common.PubkeyToHex(&privateKey.PublicKey)
requestId := communities.CalculateRequestID(memberPubKeyStr, community.ID())
requestToJoin.ID = requestId
requestToJoin.PublicKey = memberPubKeyStr
err = s.owner.communitiesManager.SaveRequestToJoin(requestToJoin)
s.Require().NoError(err)
err = s.owner.communitiesManager.SaveRequestToJoinRevealedAddresses(requestId, requestToJoin.RevealedAccounts)
s.Require().NoError(err)
_, err = community.AddMember(&privateKey.PublicKey, communityRole)
s.Require().NoError(err)
_, err = community.AddMemberToChat(chat.CommunityChatID(), &privateKey.PublicKey, communityRole, protobuf.CommunityMember_CHANNEL_ROLE_POSTER)
s.Require().NoError(err)
}
s.Require().Equal(community.MembersCount(), keysCount+1) // 1 is owner
chatsCount := 8 // in total will be 10, 2 channels were created during creating the community
for i := 0; i < chatsCount; i++ {
newChat := &protobuf.CommunityChat{
Permissions: &protobuf.CommunityPermissions{
Access: protobuf.CommunityPermissions_AUTO_ACCEPT,
},
Identity: &protobuf.ChatIdentity{
DisplayName: "name-" + strconv.Itoa(i),
Description: "",
},
}
chatID := uuid.New().String()
_, err = community.CreateChat(chatID, newChat)
s.Require().NoError(err)
}
s.Require().Len(community.Chats(), chatsCount+2) // 2 chats were created during community creation
err = s.owner.communitiesManager.SaveCommunity(community)
s.Require().NoError(err)
// setup view channel permission
channelPermissionRequest := requests.CreateCommunityTokenPermission{
CommunityID: community.ID(),
Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL,
TokenCriteria: []*protobuf.TokenCriteria{
&protobuf.TokenCriteria{
Type: protobuf.CommunityTokenType_ERC20,
ContractAddresses: map[uint64]string{testChainID1: "0x123"},
Symbol: "TEST",
AmountInWei: "100000000000000000000",
Decimals: uint64(18),
},
},
ChatIds: community.ChatIDs(),
}
s.makeAddressSatisfyTheCriteria(testChainID1, bobAddress, channelPermissionRequest.TokenCriteria[0])
defer s.resetMockedBalances() // reset mocked balances, this test in run with different test cases
// create permission using communitiesManager in order not to launch blocking reevaluation loop
community, _, err = s.owner.communitiesManager.CreateCommunityTokenPermission(&channelPermissionRequest)
s.Require().NoError(err)
s.Require().Len(community.TokenPermissions(), 1)
for _, ids := range community.ChatIDs() {
s.Require().True(s.owner.communitiesManager.IsChannelEncrypted(community.IDString(), ids))
}
// force owner to reevaluate channel members
// in production it will happen automatically, by periodic check
start := time.Now()
_, _, err = s.owner.communitiesManager.ReevaluateMembers(community.ID())
s.Require().NoError(err)
elapsed := time.Since(start)
fmt.Println("ReevaluateMembers Time: ", elapsed)
s.Require().Less(elapsed.Seconds(), 2.0)
}