github.com/status-im/status-go@v1.1.0/protocol/communities/community_encryption_key_action_test.go (about)

     1  package communities
     2  
     3  import (
     4  	"crypto/ecdsa"
     5  	"reflect"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/stretchr/testify/suite"
    10  
    11  	"github.com/status-im/status-go/eth-node/crypto"
    12  	"github.com/status-im/status-go/eth-node/types"
    13  	"github.com/status-im/status-go/protocol/common"
    14  	"github.com/status-im/status-go/protocol/protobuf"
    15  )
    16  
    17  func createTestCommunity(identity *ecdsa.PrivateKey) (*Community, error) {
    18  	config := Config{
    19  		PrivateKey: identity,
    20  		CommunityDescription: &protobuf.CommunityDescription{
    21  			Members:                 map[string]*protobuf.CommunityMember{},
    22  			Permissions:             &protobuf.CommunityPermissions{},
    23  			Identity:                &protobuf.ChatIdentity{},
    24  			Chats:                   map[string]*protobuf.CommunityChat{},
    25  			BanList:                 []string{},
    26  			Categories:              map[string]*protobuf.CommunityCategory{},
    27  			TokenPermissions:        map[string]*protobuf.CommunityTokenPermission{},
    28  			CommunityTokensMetadata: []*protobuf.CommunityTokenMetadata{},
    29  		},
    30  		ID:             &identity.PublicKey,
    31  		ControlNode:    &identity.PublicKey,
    32  		ControlDevice:  true,
    33  		Joined:         true,
    34  		MemberIdentity: identity,
    35  	}
    36  
    37  	return New(config, &TimeSourceStub{}, &DescriptionEncryptorMock{}, nil)
    38  }
    39  
    40  func TestCommunityEncryptionKeyActionSuite(t *testing.T) {
    41  	suite.Run(t, new(CommunityEncryptionKeyActionSuite))
    42  }
    43  
    44  type CommunityEncryptionKeyActionSuite struct {
    45  	suite.Suite
    46  
    47  	identity    *ecdsa.PrivateKey
    48  	communityID []byte
    49  
    50  	member1 *ecdsa.PrivateKey
    51  	member2 *ecdsa.PrivateKey
    52  	member3 *ecdsa.PrivateKey
    53  
    54  	member1Key string
    55  	member2Key string
    56  	member3Key string
    57  }
    58  
    59  func (s *CommunityEncryptionKeyActionSuite) SetupTest() {
    60  	identity, err := crypto.GenerateKey()
    61  	s.Require().NoError(err)
    62  	s.identity = identity
    63  	s.communityID = crypto.CompressPubkey(&identity.PublicKey)
    64  
    65  	member1, err := crypto.GenerateKey()
    66  	s.Require().NoError(err)
    67  	s.member1 = member1
    68  
    69  	member2, err := crypto.GenerateKey()
    70  	s.Require().NoError(err)
    71  	s.member2 = member2
    72  
    73  	member3, err := crypto.GenerateKey()
    74  	s.Require().NoError(err)
    75  	s.member3 = member3
    76  
    77  	s.member1Key = common.PubkeyToHex(&s.member1.PublicKey)
    78  	s.member2Key = common.PubkeyToHex(&s.member2.PublicKey)
    79  	s.member3Key = common.PubkeyToHex(&s.member3.PublicKey)
    80  }
    81  
    82  func (s *CommunityEncryptionKeyActionSuite) TestEncryptionKeyNone() {
    83  	origin, err := createTestCommunity(s.identity)
    84  	s.Require().NoError(err)
    85  
    86  	// if there are no changes there should be no actions
    87  	actions := EvaluateCommunityEncryptionKeyActions(origin, origin)
    88  	s.Require().Equal(actions.CommunityKeyAction.ActionType, EncryptionKeyNone)
    89  	s.Require().Len(actions.ChannelKeysActions, 0)
    90  }
    91  
    92  func (s *CommunityEncryptionKeyActionSuite) TestCommunityLevelKeyActions_PermissionsCombinations() {
    93  	testCases := []struct {
    94  		name                string
    95  		originPermissions   []*protobuf.CommunityTokenPermission
    96  		modifiedPermissions []*protobuf.CommunityTokenPermission
    97  		expectedActionType  EncryptionKeyActionType
    98  	}{
    99  		{
   100  			name:              "add member permission",
   101  			originPermissions: []*protobuf.CommunityTokenPermission{},
   102  			modifiedPermissions: []*protobuf.CommunityTokenPermission{
   103  				&protobuf.CommunityTokenPermission{
   104  					Id:            "some-id",
   105  					Type:          protobuf.CommunityTokenPermission_BECOME_MEMBER,
   106  					TokenCriteria: make([]*protobuf.TokenCriteria, 0),
   107  					ChatIds:       []string{},
   108  				},
   109  			},
   110  			expectedActionType: EncryptionKeyAdd,
   111  		},
   112  		{
   113  			name:              "add member permissions",
   114  			originPermissions: []*protobuf.CommunityTokenPermission{},
   115  			modifiedPermissions: []*protobuf.CommunityTokenPermission{
   116  				&protobuf.CommunityTokenPermission{
   117  					Id:            "some-id-1",
   118  					Type:          protobuf.CommunityTokenPermission_BECOME_MEMBER,
   119  					TokenCriteria: make([]*protobuf.TokenCriteria, 0),
   120  					ChatIds:       []string{},
   121  				},
   122  				&protobuf.CommunityTokenPermission{
   123  					Id:            "some-id-2",
   124  					Type:          protobuf.CommunityTokenPermission_BECOME_MEMBER,
   125  					TokenCriteria: make([]*protobuf.TokenCriteria, 0),
   126  					ChatIds:       []string{},
   127  				},
   128  			},
   129  			expectedActionType: EncryptionKeyAdd,
   130  		},
   131  		{
   132  			name: "add another member permission",
   133  			originPermissions: []*protobuf.CommunityTokenPermission{
   134  				&protobuf.CommunityTokenPermission{
   135  					Id:            "some-id-1",
   136  					Type:          protobuf.CommunityTokenPermission_BECOME_MEMBER,
   137  					TokenCriteria: make([]*protobuf.TokenCriteria, 0),
   138  					ChatIds:       []string{},
   139  				},
   140  			},
   141  			modifiedPermissions: []*protobuf.CommunityTokenPermission{
   142  				&protobuf.CommunityTokenPermission{
   143  					Id:            "some-id-1",
   144  					Type:          protobuf.CommunityTokenPermission_BECOME_MEMBER,
   145  					TokenCriteria: make([]*protobuf.TokenCriteria, 0),
   146  					ChatIds:       []string{},
   147  				},
   148  				&protobuf.CommunityTokenPermission{
   149  					Id:            "some-id-2",
   150  					Type:          protobuf.CommunityTokenPermission_BECOME_MEMBER,
   151  					TokenCriteria: make([]*protobuf.TokenCriteria, 0),
   152  					ChatIds:       []string{},
   153  				},
   154  			},
   155  			expectedActionType: EncryptionKeyNone,
   156  		},
   157  		{
   158  			name: "add another member permission and remove previous one",
   159  			originPermissions: []*protobuf.CommunityTokenPermission{
   160  				&protobuf.CommunityTokenPermission{
   161  					Id:            "some-id-1",
   162  					Type:          protobuf.CommunityTokenPermission_BECOME_MEMBER,
   163  					TokenCriteria: make([]*protobuf.TokenCriteria, 0),
   164  					ChatIds:       []string{},
   165  				},
   166  			},
   167  			modifiedPermissions: []*protobuf.CommunityTokenPermission{
   168  				&protobuf.CommunityTokenPermission{
   169  					Id:            "some-id-2",
   170  					Type:          protobuf.CommunityTokenPermission_BECOME_MEMBER,
   171  					TokenCriteria: make([]*protobuf.TokenCriteria, 0),
   172  					ChatIds:       []string{},
   173  				},
   174  			},
   175  			expectedActionType: EncryptionKeyNone,
   176  		},
   177  		{
   178  			name: "remove member permission",
   179  			originPermissions: []*protobuf.CommunityTokenPermission{
   180  				&protobuf.CommunityTokenPermission{
   181  					Id:            "some-id",
   182  					Type:          protobuf.CommunityTokenPermission_BECOME_MEMBER,
   183  					TokenCriteria: make([]*protobuf.TokenCriteria, 0),
   184  					ChatIds:       []string{},
   185  				},
   186  			},
   187  			modifiedPermissions: []*protobuf.CommunityTokenPermission{},
   188  			expectedActionType:  EncryptionKeyRemove,
   189  		},
   190  		{
   191  			name: "remove one of member permissions",
   192  			originPermissions: []*protobuf.CommunityTokenPermission{
   193  				&protobuf.CommunityTokenPermission{
   194  					Id:            "some-id-1",
   195  					Type:          protobuf.CommunityTokenPermission_BECOME_MEMBER,
   196  					TokenCriteria: make([]*protobuf.TokenCriteria, 0),
   197  					ChatIds:       []string{},
   198  				},
   199  				&protobuf.CommunityTokenPermission{
   200  					Id:            "some-id-2",
   201  					Type:          protobuf.CommunityTokenPermission_BECOME_MEMBER,
   202  					TokenCriteria: make([]*protobuf.TokenCriteria, 0),
   203  					ChatIds:       []string{},
   204  				},
   205  			},
   206  			modifiedPermissions: []*protobuf.CommunityTokenPermission{
   207  				&protobuf.CommunityTokenPermission{
   208  					Id:            "some-id-1",
   209  					Type:          protobuf.CommunityTokenPermission_BECOME_MEMBER,
   210  					TokenCriteria: make([]*protobuf.TokenCriteria, 0),
   211  					ChatIds:       []string{},
   212  				},
   213  			},
   214  			expectedActionType: EncryptionKeyNone,
   215  		},
   216  		{
   217  			name:              "add channel permission",
   218  			originPermissions: []*protobuf.CommunityTokenPermission{},
   219  			modifiedPermissions: []*protobuf.CommunityTokenPermission{
   220  				&protobuf.CommunityTokenPermission{
   221  					Id:            "some-id",
   222  					Type:          protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL,
   223  					TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}},
   224  					ChatIds:       []string{"some-chat-id"},
   225  				},
   226  			},
   227  			expectedActionType: EncryptionKeyNone,
   228  		},
   229  		{
   230  			name: "remove channel permission",
   231  			originPermissions: []*protobuf.CommunityTokenPermission{
   232  				&protobuf.CommunityTokenPermission{
   233  					Id:            "some-id",
   234  					Type:          protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL,
   235  					TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}},
   236  					ChatIds:       []string{"some-chat-id"},
   237  				},
   238  			},
   239  			modifiedPermissions: []*protobuf.CommunityTokenPermission{},
   240  			expectedActionType:  EncryptionKeyNone,
   241  		},
   242  		{
   243  			name: "add member permission on top of channel permission",
   244  			originPermissions: []*protobuf.CommunityTokenPermission{
   245  				&protobuf.CommunityTokenPermission{
   246  					Id:            "some-id-1",
   247  					Type:          protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL,
   248  					TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}},
   249  					ChatIds:       []string{"some-chat-id"},
   250  				},
   251  			},
   252  			modifiedPermissions: []*protobuf.CommunityTokenPermission{
   253  				&protobuf.CommunityTokenPermission{
   254  					Id:            "some-id-1",
   255  					Type:          protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL,
   256  					TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}},
   257  					ChatIds:       []string{"some-chat-id"},
   258  				},
   259  				&protobuf.CommunityTokenPermission{
   260  					Id:            "some-id-2",
   261  					Type:          protobuf.CommunityTokenPermission_BECOME_MEMBER,
   262  					TokenCriteria: make([]*protobuf.TokenCriteria, 0),
   263  					ChatIds:       []string{""},
   264  				},
   265  			},
   266  			expectedActionType: EncryptionKeyAdd,
   267  		},
   268  		{
   269  			name: "add channel permission on top of member permission",
   270  			originPermissions: []*protobuf.CommunityTokenPermission{
   271  				&protobuf.CommunityTokenPermission{
   272  					Id:            "some-id-1",
   273  					Type:          protobuf.CommunityTokenPermission_BECOME_MEMBER,
   274  					TokenCriteria: make([]*protobuf.TokenCriteria, 0),
   275  					ChatIds:       []string{""},
   276  				},
   277  			},
   278  			modifiedPermissions: []*protobuf.CommunityTokenPermission{
   279  				&protobuf.CommunityTokenPermission{
   280  					Id:            "some-id-1",
   281  					Type:          protobuf.CommunityTokenPermission_BECOME_MEMBER,
   282  					TokenCriteria: make([]*protobuf.TokenCriteria, 0),
   283  					ChatIds:       []string{""},
   284  				},
   285  				&protobuf.CommunityTokenPermission{
   286  					Id:            "some-id-2",
   287  					Type:          protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL,
   288  					TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}},
   289  					ChatIds:       []string{"some-chat-id"},
   290  				},
   291  			},
   292  			expectedActionType: EncryptionKeyNone,
   293  		},
   294  		{
   295  			name: "change member permission to channel permission",
   296  			originPermissions: []*protobuf.CommunityTokenPermission{
   297  				&protobuf.CommunityTokenPermission{
   298  					Id:            "some-id",
   299  					Type:          protobuf.CommunityTokenPermission_BECOME_MEMBER,
   300  					TokenCriteria: make([]*protobuf.TokenCriteria, 0),
   301  					ChatIds:       []string{""},
   302  				},
   303  			},
   304  			modifiedPermissions: []*protobuf.CommunityTokenPermission{
   305  				&protobuf.CommunityTokenPermission{
   306  					Id:            "some-id",
   307  					Type:          protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL,
   308  					TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}},
   309  					ChatIds:       []string{""},
   310  				},
   311  			},
   312  			expectedActionType: EncryptionKeyRemove,
   313  		},
   314  		{
   315  			name: "change channel permission to member permission",
   316  			originPermissions: []*protobuf.CommunityTokenPermission{
   317  				&protobuf.CommunityTokenPermission{
   318  					Id:            "some-id",
   319  					Type:          protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL,
   320  					TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}},
   321  					ChatIds:       []string{""},
   322  				},
   323  			},
   324  			modifiedPermissions: []*protobuf.CommunityTokenPermission{
   325  				&protobuf.CommunityTokenPermission{
   326  					Id:            "some-id",
   327  					Type:          protobuf.CommunityTokenPermission_BECOME_MEMBER,
   328  					TokenCriteria: make([]*protobuf.TokenCriteria, 0),
   329  					ChatIds:       []string{""},
   330  				},
   331  			},
   332  			expectedActionType: EncryptionKeyAdd,
   333  		},
   334  		{
   335  			name: "change channel permission to member permission on top of member permission",
   336  			originPermissions: []*protobuf.CommunityTokenPermission{
   337  				&protobuf.CommunityTokenPermission{
   338  					Id:            "some-id-1",
   339  					Type:          protobuf.CommunityTokenPermission_BECOME_MEMBER,
   340  					TokenCriteria: make([]*protobuf.TokenCriteria, 0),
   341  					ChatIds:       []string{""},
   342  				},
   343  				&protobuf.CommunityTokenPermission{
   344  					Id:            "some-id-2",
   345  					Type:          protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL,
   346  					TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}},
   347  					ChatIds:       []string{""},
   348  				},
   349  			},
   350  			modifiedPermissions: []*protobuf.CommunityTokenPermission{
   351  				&protobuf.CommunityTokenPermission{
   352  					Id:            "some-id-1",
   353  					Type:          protobuf.CommunityTokenPermission_BECOME_MEMBER,
   354  					TokenCriteria: make([]*protobuf.TokenCriteria, 0),
   355  					ChatIds:       []string{""},
   356  				},
   357  				&protobuf.CommunityTokenPermission{
   358  					Id:            "some-id-2",
   359  					Type:          protobuf.CommunityTokenPermission_BECOME_MEMBER,
   360  					TokenCriteria: make([]*protobuf.TokenCriteria, 0),
   361  					ChatIds:       []string{""},
   362  				},
   363  			},
   364  			expectedActionType: EncryptionKeyNone,
   365  		},
   366  	}
   367  
   368  	for _, tc := range testCases {
   369  		s.Run(tc.name, func() {
   370  			origin, err := createTestCommunity(s.identity)
   371  			s.Require().NoError(err)
   372  			modified := origin.CreateDeepCopy()
   373  
   374  			for _, permission := range tc.originPermissions {
   375  				_, err := origin.UpsertTokenPermission(permission)
   376  				s.Require().NoError(err)
   377  			}
   378  
   379  			for _, permission := range tc.modifiedPermissions {
   380  				_, err := modified.UpsertTokenPermission(permission)
   381  				s.Require().NoError(err)
   382  			}
   383  
   384  			actions := EvaluateCommunityEncryptionKeyActions(origin, modified)
   385  			s.Require().Equal(tc.expectedActionType, actions.CommunityKeyAction.ActionType)
   386  		})
   387  	}
   388  }
   389  
   390  func (s *CommunityEncryptionKeyActionSuite) TestCommunityLevelKeyActions_MembersCombinations() {
   391  	testCases := []struct {
   392  		name            string
   393  		permissions     []*protobuf.CommunityTokenPermission
   394  		originMembers   []*ecdsa.PublicKey
   395  		modifiedMembers []*ecdsa.PublicKey
   396  		expectedAction  EncryptionKeyAction
   397  	}{
   398  		{
   399  			name:            "add member to open community",
   400  			permissions:     []*protobuf.CommunityTokenPermission{},
   401  			originMembers:   []*ecdsa.PublicKey{},
   402  			modifiedMembers: []*ecdsa.PublicKey{&s.member1.PublicKey},
   403  			expectedAction: EncryptionKeyAction{
   404  				ActionType: EncryptionKeyNone,
   405  				Members:    map[string]*protobuf.CommunityMember{},
   406  			},
   407  		},
   408  		{
   409  			name:            "remove member from open community",
   410  			permissions:     []*protobuf.CommunityTokenPermission{},
   411  			originMembers:   []*ecdsa.PublicKey{&s.member1.PublicKey},
   412  			modifiedMembers: []*ecdsa.PublicKey{},
   413  			expectedAction: EncryptionKeyAction{
   414  				ActionType: EncryptionKeyNone,
   415  				Members:    map[string]*protobuf.CommunityMember{},
   416  			},
   417  		},
   418  		{
   419  			name: "add member to token-gated community",
   420  			permissions: []*protobuf.CommunityTokenPermission{
   421  				&protobuf.CommunityTokenPermission{
   422  					Id:            "some-id",
   423  					Type:          protobuf.CommunityTokenPermission_BECOME_MEMBER,
   424  					TokenCriteria: make([]*protobuf.TokenCriteria, 0),
   425  					ChatIds:       []string{},
   426  				},
   427  			},
   428  			originMembers:   []*ecdsa.PublicKey{},
   429  			modifiedMembers: []*ecdsa.PublicKey{&s.member1.PublicKey},
   430  			expectedAction: EncryptionKeyAction{
   431  				ActionType: EncryptionKeySendToMembers,
   432  				Members: map[string]*protobuf.CommunityMember{
   433  					s.member1Key: &protobuf.CommunityMember{},
   434  				},
   435  			},
   436  		},
   437  		{
   438  			name: "add multiple members to token-gated community",
   439  			permissions: []*protobuf.CommunityTokenPermission{
   440  				&protobuf.CommunityTokenPermission{
   441  					Id:            "some-id",
   442  					Type:          protobuf.CommunityTokenPermission_BECOME_MEMBER,
   443  					TokenCriteria: make([]*protobuf.TokenCriteria, 0),
   444  					ChatIds:       []string{},
   445  				},
   446  			},
   447  			originMembers:   []*ecdsa.PublicKey{},
   448  			modifiedMembers: []*ecdsa.PublicKey{&s.member1.PublicKey, &s.member2.PublicKey},
   449  			expectedAction: EncryptionKeyAction{
   450  				ActionType: EncryptionKeySendToMembers,
   451  				Members: map[string]*protobuf.CommunityMember{
   452  					s.member1Key: &protobuf.CommunityMember{},
   453  					s.member2Key: &protobuf.CommunityMember{},
   454  				},
   455  			},
   456  		},
   457  		{
   458  			name: "remove member from token-gated community",
   459  			permissions: []*protobuf.CommunityTokenPermission{
   460  				&protobuf.CommunityTokenPermission{
   461  					Id:            "some-id",
   462  					Type:          protobuf.CommunityTokenPermission_BECOME_MEMBER,
   463  					TokenCriteria: make([]*protobuf.TokenCriteria, 0),
   464  					ChatIds:       []string{},
   465  				},
   466  			},
   467  			originMembers:   []*ecdsa.PublicKey{&s.member1.PublicKey, &s.member2.PublicKey},
   468  			modifiedMembers: []*ecdsa.PublicKey{&s.member1.PublicKey},
   469  			expectedAction: EncryptionKeyAction{
   470  				ActionType: EncryptionKeyRekey,
   471  				Members: map[string]*protobuf.CommunityMember{
   472  					s.member1Key: &protobuf.CommunityMember{},
   473  				},
   474  			},
   475  		},
   476  		{
   477  			name: "add and remove members from token-gated community",
   478  			permissions: []*protobuf.CommunityTokenPermission{
   479  				&protobuf.CommunityTokenPermission{
   480  					Id:            "some-id",
   481  					Type:          protobuf.CommunityTokenPermission_BECOME_MEMBER,
   482  					TokenCriteria: make([]*protobuf.TokenCriteria, 0),
   483  					ChatIds:       []string{},
   484  				},
   485  			},
   486  			originMembers:   []*ecdsa.PublicKey{&s.member1.PublicKey},
   487  			modifiedMembers: []*ecdsa.PublicKey{&s.member2.PublicKey, &s.member3.PublicKey},
   488  			expectedAction: EncryptionKeyAction{
   489  				ActionType: EncryptionKeyRekey,
   490  				Members: map[string]*protobuf.CommunityMember{
   491  					s.member2Key: &protobuf.CommunityMember{},
   492  					s.member3Key: &protobuf.CommunityMember{},
   493  				},
   494  			},
   495  		},
   496  	}
   497  
   498  	for _, tc := range testCases {
   499  		s.Run(tc.name, func() {
   500  			origin, err := createTestCommunity(s.identity)
   501  			s.Require().NoError(err)
   502  
   503  			for _, permission := range tc.permissions {
   504  				_, err := origin.UpsertTokenPermission(permission)
   505  				s.Require().NoError(err)
   506  			}
   507  			modified := origin.CreateDeepCopy()
   508  
   509  			for _, member := range tc.originMembers {
   510  				_, err := origin.AddMember(member, []protobuf.CommunityMember_Roles{}, origin.Clock())
   511  				s.Require().NoError(err)
   512  			}
   513  
   514  			for _, member := range tc.modifiedMembers {
   515  				_, err := modified.AddMember(member, []protobuf.CommunityMember_Roles{}, origin.Clock())
   516  				s.Require().NoError(err)
   517  			}
   518  
   519  			actions := EvaluateCommunityEncryptionKeyActions(origin, modified)
   520  			s.Require().Equal(tc.expectedAction.ActionType, actions.CommunityKeyAction.ActionType)
   521  			s.Require().Len(tc.expectedAction.Members, len(actions.CommunityKeyAction.Members))
   522  			for memberKey := range tc.expectedAction.Members {
   523  				_, exists := actions.CommunityKeyAction.Members[memberKey]
   524  				s.Require().True(exists)
   525  			}
   526  		})
   527  	}
   528  }
   529  
   530  func (s *CommunityEncryptionKeyActionSuite) TestCommunityLevelKeyActions_PermissionsMembersCombinations() {
   531  	testCases := []struct {
   532  		name                string
   533  		originPermissions   []*protobuf.CommunityTokenPermission
   534  		modifiedPermissions []*protobuf.CommunityTokenPermission
   535  		originMembers       []*ecdsa.PublicKey
   536  		modifiedMembers     []*ecdsa.PublicKey
   537  		expectedActionType  EncryptionKeyActionType
   538  	}{
   539  		{
   540  			name:              "add member permission, add members",
   541  			originPermissions: []*protobuf.CommunityTokenPermission{},
   542  			modifiedPermissions: []*protobuf.CommunityTokenPermission{
   543  				&protobuf.CommunityTokenPermission{
   544  					Id:            "some-id",
   545  					Type:          protobuf.CommunityTokenPermission_BECOME_MEMBER,
   546  					TokenCriteria: make([]*protobuf.TokenCriteria, 0),
   547  					ChatIds:       []string{},
   548  				},
   549  			},
   550  			originMembers:      []*ecdsa.PublicKey{},
   551  			modifiedMembers:    []*ecdsa.PublicKey{&s.member1.PublicKey},
   552  			expectedActionType: EncryptionKeyAdd,
   553  		},
   554  		{
   555  			name:              "add member permission, remove members",
   556  			originPermissions: []*protobuf.CommunityTokenPermission{},
   557  			modifiedPermissions: []*protobuf.CommunityTokenPermission{
   558  				&protobuf.CommunityTokenPermission{
   559  					Id:            "some-id",
   560  					Type:          protobuf.CommunityTokenPermission_BECOME_MEMBER,
   561  					TokenCriteria: make([]*protobuf.TokenCriteria, 0),
   562  					ChatIds:       []string{},
   563  				},
   564  			},
   565  			originMembers:      []*ecdsa.PublicKey{&s.member1.PublicKey},
   566  			modifiedMembers:    []*ecdsa.PublicKey{},
   567  			expectedActionType: EncryptionKeyAdd,
   568  		},
   569  		{
   570  			name: "remove member permission, add members",
   571  			originPermissions: []*protobuf.CommunityTokenPermission{
   572  				&protobuf.CommunityTokenPermission{
   573  					Id:            "some-id",
   574  					Type:          protobuf.CommunityTokenPermission_BECOME_MEMBER,
   575  					TokenCriteria: make([]*protobuf.TokenCriteria, 0),
   576  					ChatIds:       []string{},
   577  				},
   578  			},
   579  			modifiedPermissions: []*protobuf.CommunityTokenPermission{},
   580  			originMembers:       []*ecdsa.PublicKey{},
   581  			modifiedMembers:     []*ecdsa.PublicKey{&s.member1.PublicKey},
   582  			expectedActionType:  EncryptionKeyRemove,
   583  		},
   584  		{
   585  			name: "remove member permission, remove members",
   586  			originPermissions: []*protobuf.CommunityTokenPermission{
   587  				&protobuf.CommunityTokenPermission{
   588  					Id:            "some-id",
   589  					Type:          protobuf.CommunityTokenPermission_BECOME_MEMBER,
   590  					TokenCriteria: make([]*protobuf.TokenCriteria, 0),
   591  					ChatIds:       []string{},
   592  				},
   593  			},
   594  			modifiedPermissions: []*protobuf.CommunityTokenPermission{},
   595  			originMembers:       []*ecdsa.PublicKey{&s.member1.PublicKey},
   596  			modifiedMembers:     []*ecdsa.PublicKey{},
   597  			expectedActionType:  EncryptionKeyRemove,
   598  		},
   599  	}
   600  
   601  	for _, tc := range testCases {
   602  		s.Run(tc.name, func() {
   603  			origin, err := createTestCommunity(s.identity)
   604  			s.Require().NoError(err)
   605  			modified := origin.CreateDeepCopy()
   606  
   607  			for _, permission := range tc.originPermissions {
   608  				_, err := origin.UpsertTokenPermission(permission)
   609  				s.Require().NoError(err)
   610  			}
   611  			for _, member := range tc.originMembers {
   612  				_, err := origin.AddMember(member, []protobuf.CommunityMember_Roles{}, origin.Clock())
   613  				s.Require().NoError(err)
   614  			}
   615  
   616  			for _, permission := range tc.modifiedPermissions {
   617  				_, err := modified.UpsertTokenPermission(permission)
   618  				s.Require().NoError(err)
   619  			}
   620  			for _, member := range tc.modifiedMembers {
   621  				_, err := modified.AddMember(member, []protobuf.CommunityMember_Roles{}, origin.Clock())
   622  				s.Require().NoError(err)
   623  			}
   624  
   625  			actions := EvaluateCommunityEncryptionKeyActions(origin, modified)
   626  			s.Require().Equal(tc.expectedActionType, actions.CommunityKeyAction.ActionType)
   627  		})
   628  	}
   629  }
   630  
   631  func (s *CommunityEncryptionKeyActionSuite) TestChannelLevelKeyActions() {
   632  	channelID := "1234"
   633  	chatID := types.EncodeHex(crypto.CompressPubkey(&s.identity.PublicKey)) + channelID
   634  	testCases := []struct {
   635  		name                string
   636  		originPermissions   []*protobuf.CommunityTokenPermission
   637  		modifiedPermissions []*protobuf.CommunityTokenPermission
   638  		originMembers       []*ecdsa.PublicKey
   639  		modifiedMembers     []*ecdsa.PublicKey
   640  		expectedAction      EncryptionKeyAction
   641  	}{
   642  		{
   643  			name:              "add channel permission",
   644  			originPermissions: []*protobuf.CommunityTokenPermission{},
   645  			modifiedPermissions: []*protobuf.CommunityTokenPermission{
   646  				&protobuf.CommunityTokenPermission{
   647  					Id:            "some-id",
   648  					Type:          protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL,
   649  					TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}},
   650  					ChatIds:       []string{chatID},
   651  				},
   652  			},
   653  			originMembers:   []*ecdsa.PublicKey{},
   654  			modifiedMembers: []*ecdsa.PublicKey{},
   655  			expectedAction: EncryptionKeyAction{
   656  				ActionType: EncryptionKeyAdd,
   657  				Members:    map[string]*protobuf.CommunityMember{},
   658  			},
   659  		},
   660  		{
   661  			name: "remove channel permission",
   662  			originPermissions: []*protobuf.CommunityTokenPermission{
   663  				&protobuf.CommunityTokenPermission{
   664  					Id:            "some-id",
   665  					Type:          protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL,
   666  					TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}},
   667  					ChatIds:       []string{chatID},
   668  				},
   669  			},
   670  			modifiedPermissions: []*protobuf.CommunityTokenPermission{},
   671  			originMembers:       []*ecdsa.PublicKey{},
   672  			modifiedMembers:     []*ecdsa.PublicKey{},
   673  			expectedAction: EncryptionKeyAction{
   674  				ActionType: EncryptionKeyRemove,
   675  				Members:    map[string]*protobuf.CommunityMember{},
   676  			},
   677  		},
   678  		{
   679  			name: "add members to token-gated channel",
   680  			originPermissions: []*protobuf.CommunityTokenPermission{
   681  				&protobuf.CommunityTokenPermission{
   682  					Id:            "some-id",
   683  					Type:          protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL,
   684  					TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}},
   685  					ChatIds:       []string{chatID},
   686  				},
   687  			},
   688  			modifiedPermissions: []*protobuf.CommunityTokenPermission{
   689  				&protobuf.CommunityTokenPermission{
   690  					Id:            "some-id",
   691  					Type:          protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL,
   692  					TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}},
   693  					ChatIds:       []string{chatID},
   694  				},
   695  			},
   696  			originMembers:   []*ecdsa.PublicKey{},
   697  			modifiedMembers: []*ecdsa.PublicKey{&s.member1.PublicKey, &s.member2.PublicKey},
   698  			expectedAction: EncryptionKeyAction{
   699  				ActionType: EncryptionKeySendToMembers,
   700  				Members: map[string]*protobuf.CommunityMember{
   701  					s.member1Key: &protobuf.CommunityMember{},
   702  					s.member2Key: &protobuf.CommunityMember{},
   703  				},
   704  			},
   705  		},
   706  		{
   707  			name: "remove members from token-gated channel",
   708  			originPermissions: []*protobuf.CommunityTokenPermission{
   709  				&protobuf.CommunityTokenPermission{
   710  					Id:            "some-id",
   711  					Type:          protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL,
   712  					TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}},
   713  					ChatIds:       []string{chatID},
   714  				},
   715  			},
   716  			modifiedPermissions: []*protobuf.CommunityTokenPermission{
   717  				&protobuf.CommunityTokenPermission{
   718  					Id:            "some-id",
   719  					Type:          protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL,
   720  					TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}},
   721  					ChatIds:       []string{chatID},
   722  				},
   723  			},
   724  			originMembers:   []*ecdsa.PublicKey{&s.member1.PublicKey, &s.member2.PublicKey},
   725  			modifiedMembers: []*ecdsa.PublicKey{},
   726  			expectedAction: EncryptionKeyAction{
   727  				ActionType: EncryptionKeyRekey,
   728  				Members:    map[string]*protobuf.CommunityMember{},
   729  			},
   730  		},
   731  		{
   732  			name:                "add members to open channel",
   733  			originPermissions:   []*protobuf.CommunityTokenPermission{},
   734  			modifiedPermissions: []*protobuf.CommunityTokenPermission{},
   735  			originMembers:       []*ecdsa.PublicKey{},
   736  			modifiedMembers:     []*ecdsa.PublicKey{&s.member1.PublicKey, &s.member2.PublicKey},
   737  			expectedAction: EncryptionKeyAction{
   738  				ActionType: EncryptionKeyNone,
   739  				Members:    map[string]*protobuf.CommunityMember{},
   740  			},
   741  		},
   742  	}
   743  
   744  	for _, tc := range testCases {
   745  		s.Run(tc.name, func() {
   746  			origin, err := createTestCommunity(s.identity)
   747  			s.Require().NoError(err)
   748  
   749  			_, err = origin.CreateChat(channelID, &protobuf.CommunityChat{
   750  				Members:     map[string]*protobuf.CommunityMember{},
   751  				Permissions: &protobuf.CommunityPermissions{Access: protobuf.CommunityPermissions_AUTO_ACCEPT},
   752  				Identity:    &protobuf.ChatIdentity{},
   753  			})
   754  			s.Require().NoError(err)
   755  
   756  			modified := origin.CreateDeepCopy()
   757  
   758  			for _, permission := range tc.originPermissions {
   759  				_, err := origin.UpsertTokenPermission(permission)
   760  				s.Require().NoError(err)
   761  			}
   762  			for _, member := range tc.originMembers {
   763  				_, err := origin.AddMember(member, []protobuf.CommunityMember_Roles{}, origin.Clock())
   764  				s.Require().NoError(err)
   765  				_, err = origin.AddMemberToChat(channelID, member, []protobuf.CommunityMember_Roles{}, protobuf.CommunityMember_CHANNEL_ROLE_POSTER)
   766  				s.Require().NoError(err)
   767  			}
   768  
   769  			for _, permission := range tc.modifiedPermissions {
   770  				_, err := modified.UpsertTokenPermission(permission)
   771  				s.Require().NoError(err)
   772  			}
   773  			for _, member := range tc.modifiedMembers {
   774  				_, err := modified.AddMember(member, []protobuf.CommunityMember_Roles{}, origin.Clock())
   775  				s.Require().NoError(err)
   776  				_, err = modified.AddMemberToChat(channelID, member, []protobuf.CommunityMember_Roles{}, protobuf.CommunityMember_CHANNEL_ROLE_POSTER)
   777  				s.Require().NoError(err)
   778  			}
   779  
   780  			actions := EvaluateCommunityEncryptionKeyActions(origin, modified)
   781  			channelAction, ok := actions.ChannelKeysActions[channelID]
   782  			s.Require().True(ok)
   783  			s.Require().Equal(tc.expectedAction.ActionType, channelAction.ActionType)
   784  			s.Require().Len(tc.expectedAction.Members, len(channelAction.Members))
   785  			for memberKey := range tc.expectedAction.Members {
   786  				_, exists := channelAction.Members[memberKey]
   787  				s.Require().True(exists)
   788  			}
   789  		})
   790  	}
   791  }
   792  
   793  func (s *CommunityEncryptionKeyActionSuite) TestNilOrigin() {
   794  	newCommunity, err := createTestCommunity(s.identity)
   795  	s.Require().NoError(err)
   796  
   797  	channelID := "0x1234"
   798  	chatID := types.EncodeHex(crypto.CompressPubkey(&s.identity.PublicKey)) + channelID
   799  
   800  	_, err = newCommunity.CreateChat(channelID, &protobuf.CommunityChat{
   801  		Members:     map[string]*protobuf.CommunityMember{},
   802  		Permissions: &protobuf.CommunityPermissions{Access: protobuf.CommunityPermissions_AUTO_ACCEPT},
   803  		Identity:    &protobuf.ChatIdentity{},
   804  	})
   805  	s.Require().NoError(err)
   806  
   807  	newCommunityPermissions := []*protobuf.CommunityTokenPermission{
   808  		&protobuf.CommunityTokenPermission{
   809  			Id:            "some-id-1",
   810  			Type:          protobuf.CommunityTokenPermission_BECOME_MEMBER,
   811  			TokenCriteria: make([]*protobuf.TokenCriteria, 0),
   812  			ChatIds:       []string{},
   813  		},
   814  		&protobuf.CommunityTokenPermission{
   815  			Id:            "some-id-2",
   816  			Type:          protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL,
   817  			TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}},
   818  			ChatIds:       []string{chatID},
   819  		},
   820  	}
   821  	for _, permission := range newCommunityPermissions {
   822  		_, err := newCommunity.UpsertTokenPermission(permission)
   823  		s.Require().NoError(err)
   824  	}
   825  
   826  	actions := EvaluateCommunityEncryptionKeyActions(nil, newCommunity)
   827  	s.Require().Equal(actions.CommunityKeyAction.ActionType, EncryptionKeyAdd)
   828  	s.Require().Len(actions.ChannelKeysActions, 1)
   829  	s.Require().NotNil(actions.ChannelKeysActions[channelID])
   830  	s.Require().Equal(actions.ChannelKeysActions[channelID].ActionType, EncryptionKeyAdd)
   831  }
   832  
   833  func (s *CommunityEncryptionKeyActionSuite) TestControlNodeChange() {
   834  	channelID := "1234"
   835  	chatID := types.EncodeHex(crypto.CompressPubkey(&s.identity.PublicKey)) + channelID
   836  	clock := uint64(time.Now().Unix())
   837  	testCases := []struct {
   838  		name            string
   839  		permissions     []*protobuf.CommunityTokenPermission
   840  		members         []*ecdsa.PublicKey
   841  		channelMembers  []*ecdsa.PublicKey
   842  		expectedActions EncryptionKeyActions
   843  	}{
   844  		{
   845  			name:           "change control node in open community",
   846  			permissions:    []*protobuf.CommunityTokenPermission{},
   847  			members:        []*ecdsa.PublicKey{&s.member1.PublicKey, &s.member2.PublicKey},
   848  			channelMembers: []*ecdsa.PublicKey{&s.member1.PublicKey},
   849  			expectedActions: EncryptionKeyActions{
   850  				CommunityKeyAction: EncryptionKeyAction{
   851  					ActionType: EncryptionKeyNone,
   852  					Members:    map[string]*protobuf.CommunityMember{},
   853  				},
   854  				ChannelKeysActions: map[string]EncryptionKeyAction{
   855  					channelID: EncryptionKeyAction{
   856  						ActionType: EncryptionKeyNone,
   857  						Members:    map[string]*protobuf.CommunityMember{},
   858  					},
   859  				},
   860  			},
   861  		},
   862  		{
   863  			name: "change control node in token-gated community",
   864  			permissions: []*protobuf.CommunityTokenPermission{
   865  				&protobuf.CommunityTokenPermission{
   866  					Id:            "some-id",
   867  					Type:          protobuf.CommunityTokenPermission_BECOME_MEMBER,
   868  					TokenCriteria: make([]*protobuf.TokenCriteria, 0),
   869  					ChatIds:       []string{},
   870  				},
   871  			},
   872  			members:        []*ecdsa.PublicKey{&s.member1.PublicKey, &s.member2.PublicKey},
   873  			channelMembers: []*ecdsa.PublicKey{&s.member1.PublicKey},
   874  			expectedActions: EncryptionKeyActions{
   875  				CommunityKeyAction: EncryptionKeyAction{
   876  					ActionType: EncryptionKeyRekey,
   877  					Members: map[string]*protobuf.CommunityMember{
   878  						s.member1Key: &protobuf.CommunityMember{LastUpdateClock: clock},
   879  						s.member2Key: &protobuf.CommunityMember{LastUpdateClock: clock},
   880  					},
   881  				},
   882  				ChannelKeysActions: map[string]EncryptionKeyAction{
   883  					channelID: EncryptionKeyAction{
   884  						ActionType: EncryptionKeyNone,
   885  						Members:    map[string]*protobuf.CommunityMember{},
   886  					},
   887  				},
   888  			},
   889  		},
   890  		{
   891  			name: "change control node in open community with token-gated channel",
   892  			permissions: []*protobuf.CommunityTokenPermission{
   893  				&protobuf.CommunityTokenPermission{
   894  					Id:            "some-id",
   895  					Type:          protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL,
   896  					TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}},
   897  					ChatIds:       []string{chatID},
   898  				},
   899  			},
   900  			members:        []*ecdsa.PublicKey{&s.member1.PublicKey, &s.member2.PublicKey},
   901  			channelMembers: []*ecdsa.PublicKey{&s.member1.PublicKey},
   902  			expectedActions: EncryptionKeyActions{
   903  				CommunityKeyAction: EncryptionKeyAction{
   904  					ActionType: EncryptionKeyNone,
   905  					Members:    map[string]*protobuf.CommunityMember{},
   906  				},
   907  				ChannelKeysActions: map[string]EncryptionKeyAction{
   908  					channelID: EncryptionKeyAction{
   909  						ActionType: EncryptionKeyRekey,
   910  						Members: map[string]*protobuf.CommunityMember{
   911  							s.member1Key: &protobuf.CommunityMember{},
   912  						},
   913  					},
   914  				},
   915  			},
   916  		},
   917  		{
   918  			name: "change control node in token-gated community with token-gated channel",
   919  			permissions: []*protobuf.CommunityTokenPermission{
   920  				&protobuf.CommunityTokenPermission{
   921  					Id:            "some-id-1",
   922  					Type:          protobuf.CommunityTokenPermission_BECOME_MEMBER,
   923  					TokenCriteria: make([]*protobuf.TokenCriteria, 0),
   924  					ChatIds:       []string{},
   925  				},
   926  				&protobuf.CommunityTokenPermission{
   927  					Id:            "some-id-2",
   928  					Type:          protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL,
   929  					TokenCriteria: []*protobuf.TokenCriteria{&protobuf.TokenCriteria{}},
   930  					ChatIds:       []string{chatID},
   931  				},
   932  			},
   933  			members:        []*ecdsa.PublicKey{&s.member1.PublicKey, &s.member2.PublicKey},
   934  			channelMembers: []*ecdsa.PublicKey{&s.member1.PublicKey},
   935  			expectedActions: EncryptionKeyActions{
   936  				CommunityKeyAction: EncryptionKeyAction{
   937  					ActionType: EncryptionKeyRekey,
   938  					Members: map[string]*protobuf.CommunityMember{
   939  						s.member1Key: &protobuf.CommunityMember{
   940  							LastUpdateClock: clock,
   941  						},
   942  						s.member2Key: &protobuf.CommunityMember{
   943  							LastUpdateClock: clock,
   944  						},
   945  					},
   946  				},
   947  				ChannelKeysActions: map[string]EncryptionKeyAction{
   948  					channelID: EncryptionKeyAction{
   949  						ActionType: EncryptionKeyRekey,
   950  						Members: map[string]*protobuf.CommunityMember{
   951  							s.member1Key: &protobuf.CommunityMember{},
   952  						},
   953  					},
   954  				},
   955  			},
   956  		},
   957  	}
   958  
   959  	for _, tc := range testCases {
   960  		s.Run(tc.name, func() {
   961  			origin, err := createTestCommunity(s.identity)
   962  			s.Require().NoError(err)
   963  
   964  			_, err = origin.CreateChat(channelID, &protobuf.CommunityChat{
   965  				Members:     map[string]*protobuf.CommunityMember{},
   966  				Permissions: &protobuf.CommunityPermissions{Access: protobuf.CommunityPermissions_AUTO_ACCEPT},
   967  				Identity:    &protobuf.ChatIdentity{},
   968  			})
   969  			s.Require().NoError(err)
   970  
   971  			for _, permission := range tc.permissions {
   972  				_, err := origin.UpsertTokenPermission(permission)
   973  				s.Require().NoError(err)
   974  			}
   975  			for _, member := range tc.members {
   976  				_, err := origin.AddMember(member, []protobuf.CommunityMember_Roles{}, clock)
   977  				s.Require().NoError(err)
   978  			}
   979  			for _, member := range tc.channelMembers {
   980  				_, err = origin.AddMemberToChat(channelID, member, []protobuf.CommunityMember_Roles{}, protobuf.CommunityMember_CHANNEL_ROLE_POSTER)
   981  				s.Require().NoError(err)
   982  			}
   983  
   984  			// change control node to arbitrary member
   985  			modified := origin.CreateDeepCopy()
   986  			modified.setControlNode(&s.member1.PublicKey)
   987  
   988  			actions := EvaluateCommunityEncryptionKeyActions(origin, modified)
   989  			s.Require().True(reflect.DeepEqual(tc.expectedActions, *actions))
   990  		})
   991  	}
   992  }