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

     1  package protocol
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/golang/protobuf/proto"
    10  	"github.com/stretchr/testify/suite"
    11  	"go.uber.org/zap"
    12  
    13  	utils "github.com/status-im/status-go/common"
    14  
    15  	"github.com/status-im/status-go/protocol/common"
    16  	"github.com/status-im/status-go/protocol/communities"
    17  	"github.com/status-im/status-go/protocol/communities/token"
    18  	"github.com/status-im/status-go/protocol/protobuf"
    19  	"github.com/status-im/status-go/protocol/requests"
    20  	"github.com/status-im/status-go/services/wallet/bigint"
    21  )
    22  
    23  func TestMessengerCommunitiesSignersSuite(t *testing.T) {
    24  	suite.Run(t, new(MessengerCommunitiesSignersSuite))
    25  }
    26  
    27  type MessengerCommunitiesSignersSuite struct {
    28  	CommunitiesMessengerTestSuiteBase
    29  	john  *Messenger
    30  	bob   *Messenger
    31  	alice *Messenger
    32  }
    33  
    34  func (s *MessengerCommunitiesSignersSuite) SetupTest() {
    35  	s.CommunitiesMessengerTestSuiteBase.SetupTest()
    36  	communities.SetValidateInterval(300 * time.Millisecond)
    37  
    38  	s.john = s.newMessenger(accountPassword, []string{commonAccountAddress})
    39  	s.bob = s.newMessenger(accountPassword, []string{bobAddress})
    40  	s.alice = s.newMessenger(accountPassword, []string{aliceAddress1})
    41  	_, err := s.john.Start()
    42  	s.Require().NoError(err)
    43  	_, err = s.bob.Start()
    44  	s.Require().NoError(err)
    45  	_, err = s.alice.Start()
    46  	s.Require().NoError(err)
    47  }
    48  
    49  func (s *MessengerCommunitiesSignersSuite) TearDownTest() {
    50  	TearDownMessenger(&s.Suite, s.john)
    51  	TearDownMessenger(&s.Suite, s.bob)
    52  	TearDownMessenger(&s.Suite, s.alice)
    53  	s.CommunitiesMessengerTestSuiteBase.TearDownTest()
    54  }
    55  
    56  func (s *MessengerCommunitiesSignersSuite) newMessenger(password string, walletAddresses []string) *Messenger {
    57  	communityManagerOptions := []communities.ManagerOption{
    58  		communities.WithAllowForcingCommunityMembersReevaluation(true),
    59  	}
    60  
    61  	return s.newMessengerWithConfig(testMessengerConfig{
    62  		logger:       s.logger,
    63  		extraOptions: []Option{WithCommunityManagerOptions(communityManagerOptions)},
    64  	}, password, walletAddresses)
    65  }
    66  
    67  func (s *MessengerCommunitiesSignersSuite) createCommunity(controlNode *Messenger) *communities.Community {
    68  	community, _ := createCommunity(&s.Suite, controlNode)
    69  	return community
    70  }
    71  
    72  func (s *MessengerCommunitiesSignersSuite) advertiseCommunityTo(controlNode *Messenger, community *communities.Community, user *Messenger) {
    73  	advertiseCommunityTo(&s.Suite, community, controlNode, user)
    74  }
    75  
    76  // John crates a community
    77  // Ownership is transferred to Alice
    78  // Alice kick all members Bob and John
    79  // Bob automatically rejoin
    80  // John receive AC notification to share the address and join to the community
    81  // Bob and John accepts the changes
    82  
    83  func (s *MessengerCommunitiesSignersSuite) TestControlNodeUpdateSigner() {
    84  	// Create a community
    85  	// Transfer ownership
    86  	// Process message
    87  	community := s.createCommunity(s.john)
    88  
    89  	s.advertiseCommunityTo(s.john, community, s.bob)
    90  	s.advertiseCommunityTo(s.john, community, s.alice)
    91  
    92  	s.joinCommunity(community, s.john, s.bob)
    93  	s.joinCommunity(community, s.john, s.alice)
    94  
    95  	// john mints owner token
    96  	var chainID uint64 = 1
    97  	tokenAddress := "token-address"
    98  	tokenName := "tokenName"
    99  	tokenSymbol := "TSM"
   100  	_, err := s.john.SaveCommunityToken(&token.CommunityToken{
   101  		TokenType:       protobuf.CommunityTokenType_ERC721,
   102  		CommunityID:     community.IDString(),
   103  		Address:         tokenAddress,
   104  		ChainID:         int(chainID),
   105  		Name:            tokenName,
   106  		Supply:          &bigint.BigInt{},
   107  		Symbol:          tokenSymbol,
   108  		PrivilegesLevel: token.OwnerLevel,
   109  	}, nil)
   110  	s.Require().NoError(err)
   111  
   112  	// john adds minted owner token to community
   113  	err = s.john.AddCommunityToken(community.IDString(), int(chainID), tokenAddress)
   114  	s.Require().NoError(err)
   115  
   116  	// update mock - the signer for the community returned by the contracts should be john
   117  	s.collectiblesServiceMock.SetSignerPubkeyForCommunity(community.ID(), common.PubkeyToHex(&s.john.identity.PublicKey))
   118  	s.collectiblesServiceMock.SetMockCollectibleContractData(chainID, tokenAddress,
   119  		&communities.CollectibleContractData{TotalSupply: &bigint.BigInt{}})
   120  
   121  	// bob accepts community update
   122  	_, err = WaitOnSignaledMessengerResponse(
   123  		s.bob,
   124  		func(r *MessengerResponse) bool {
   125  			return len(r.Communities()) > 0 && len(r.Communities()[0].TokenPermissions()) == 1
   126  		},
   127  		"no communities",
   128  	)
   129  	s.Require().NoError(err)
   130  
   131  	// alice accepts community update
   132  	_, err = WaitOnSignaledMessengerResponse(
   133  		s.alice,
   134  		func(r *MessengerResponse) bool {
   135  			return len(r.Communities()) > 0 && len(r.Communities()[0].TokenPermissions()) == 1
   136  		},
   137  		"no communities",
   138  	)
   139  	s.Require().NoError(err)
   140  
   141  	// Ownership token will be transferred to Alice and she will kick all members
   142  	// and request kicked members to rejoin
   143  	// the signer for the community returned by the contracts should be alice
   144  	s.collectiblesServiceMock.SetSignerPubkeyForCommunity(community.ID(), common.PubkeyToHex(&s.alice.identity.PublicKey))
   145  
   146  	response, err := s.alice.PromoteSelfToControlNode(community.ID())
   147  	s.Require().NoError(err)
   148  	s.Require().NotNil(response)
   149  
   150  	community, err = s.alice.communitiesManager.GetByID(community.ID())
   151  	s.Require().NoError(err)
   152  	s.Require().True(community.IsControlNode())
   153  	s.Require().True(common.IsPubKeyEqual(community.ControlNode(), &s.alice.identity.PublicKey))
   154  	s.Require().True(community.IsOwner())
   155  
   156  	// check that Bob received kick event, also he will receive
   157  	// request to share RevealedAddresses and send request to join to the control node
   158  	_, err = WaitOnSignaledMessengerResponse(
   159  		s.bob,
   160  		func(r *MessengerResponse) bool {
   161  			return len(r.Communities()) > 0 && !r.Communities()[0].HasMember(&s.bob.identity.PublicKey) &&
   162  				!r.Communities()[0].Joined() && r.Communities()[0].Spectated() &&
   163  				len(r.ActivityCenterNotifications()) == 0
   164  		},
   165  		"Bob was not kicked from the community",
   166  	)
   167  	s.Require().NoError(err)
   168  
   169  	// check that John received kick event, and AC notification msg created
   170  	// John, as ex-owner must manually join the community
   171  	_, err = WaitOnSignaledMessengerResponse(
   172  		s.john,
   173  		func(r *MessengerResponse) bool {
   174  			inSpectateMode := len(r.Communities()) > 0 && !r.Communities()[0].HasMember(&s.john.identity.PublicKey) &&
   175  				!r.Communities()[0].Joined() && r.Communities()[0].Spectated()
   176  			sharedNotificationExist := false
   177  			for _, acNotification := range r.ActivityCenterNotifications() {
   178  				if acNotification.Type == ActivityCenterNotificationTypeShareAccounts {
   179  					sharedNotificationExist = true
   180  					break
   181  				}
   182  			}
   183  			return inSpectateMode && sharedNotificationExist
   184  		},
   185  		"John was not kicked from the community",
   186  	)
   187  	s.Require().NoError(err)
   188  
   189  	// Alice auto-accept requests to join with RevealedAddresses
   190  	_, err = WaitOnMessengerResponse(
   191  		s.alice,
   192  		func(r *MessengerResponse) bool {
   193  			return len(r.Communities()) > 0 && len(r.Communities()[0].Members()) == 2
   194  		},
   195  		"no community update with accepted request",
   196  	)
   197  	s.Require().NoError(err)
   198  
   199  	validateResults := func(messenger *Messenger) *communities.Community {
   200  		community, err = messenger.communitiesManager.GetByID(community.ID())
   201  		s.Require().NoError(err)
   202  		s.Require().True(common.IsPubKeyEqual(community.ControlNode(), &s.alice.identity.PublicKey))
   203  		s.Require().Len(community.Members(), 2)
   204  		s.Require().True(community.HasMember(&messenger.identity.PublicKey))
   205  
   206  		return community
   207  	}
   208  
   209  	community = validateResults(s.alice)
   210  	s.Require().True(community.IsControlNode())
   211  	s.Require().True(community.IsOwner())
   212  
   213  	// Bob is a community member again
   214  	_, err = WaitOnMessengerResponse(
   215  		s.bob,
   216  		func(r *MessengerResponse) bool {
   217  			return len(r.Communities()) > 0 && r.Communities()[0].HasMember(&s.bob.identity.PublicKey) &&
   218  				r.Communities()[0].Joined() && !r.Communities()[0].Spectated()
   219  		},
   220  		"Bob was auto-accepted",
   221  	)
   222  	s.Require().NoError(err)
   223  
   224  	community = validateResults(s.bob)
   225  	s.Require().False(community.IsControlNode())
   226  	s.Require().False(community.IsOwner())
   227  
   228  	// John manually joins the community
   229  	s.joinCommunity(community, s.alice, s.john)
   230  
   231  	// Alice change community name
   232  
   233  	expectedName := "Alice owns community"
   234  
   235  	response, err = s.alice.EditCommunity(&requests.EditCommunity{
   236  		CommunityID: community.ID(),
   237  		CreateCommunity: requests.CreateCommunity{
   238  			Membership:  protobuf.CommunityPermissions_AUTO_ACCEPT,
   239  			Name:        expectedName,
   240  			Color:       "#000000",
   241  			Description: "edited community description",
   242  		},
   243  	})
   244  
   245  	s.Require().NoError(err)
   246  	s.Require().NotNil(response)
   247  
   248  	validateNameInResponse := func(r *MessengerResponse) bool {
   249  		return len(r.Communities()) > 0 && r.Communities()[0].IDString() == community.IDString() &&
   250  			r.Communities()[0].Name() == expectedName
   251  	}
   252  
   253  	s.Require().True(validateNameInResponse(response))
   254  
   255  	validateNameInDB := func(messenger *Messenger) {
   256  		community, err = messenger.communitiesManager.GetByID(community.ID())
   257  		s.Require().NoError(err)
   258  		s.Require().Equal(expectedName, response.Communities()[0].Name())
   259  	}
   260  
   261  	validateNameInDB(s.alice)
   262  
   263  	// john accepts community update from alice (new control node)
   264  	_, err = WaitOnMessengerResponse(
   265  		s.john,
   266  		validateNameInResponse,
   267  		"john did not receive community name update",
   268  	)
   269  	s.Require().NoError(err)
   270  	validateNameInDB(s.john)
   271  
   272  	// bob accepts community update from alice (new control node)
   273  	_, err = WaitOnMessengerResponse(
   274  		s.bob,
   275  		validateNameInResponse,
   276  		"bob did not receive community name update",
   277  	)
   278  	s.Require().NoError(err)
   279  	validateNameInDB(s.bob)
   280  }
   281  
   282  func (s *MessengerCommunitiesSignersSuite) TestAutoAcceptOnOwnershipChangeRequestRequired() {
   283  	community, _ := createOnRequestCommunity(&s.Suite, s.john)
   284  
   285  	s.advertiseCommunityTo(s.john, community, s.bob)
   286  	s.advertiseCommunityTo(s.john, community, s.alice)
   287  
   288  	s.joinOnRequestCommunity(community, s.john, s.bob)
   289  	s.joinOnRequestCommunity(community, s.john, s.alice)
   290  
   291  	// john mints owner token
   292  	var chainID uint64 = 1
   293  	tokenAddress := "token-address"
   294  	tokenName := "tokenName"
   295  	tokenSymbol := "TSM"
   296  	_, err := s.john.SaveCommunityToken(&token.CommunityToken{
   297  		TokenType:       protobuf.CommunityTokenType_ERC721,
   298  		CommunityID:     community.IDString(),
   299  		Address:         tokenAddress,
   300  		ChainID:         int(chainID),
   301  		Name:            tokenName,
   302  		Supply:          &bigint.BigInt{},
   303  		Symbol:          tokenSymbol,
   304  		PrivilegesLevel: token.OwnerLevel,
   305  	}, nil)
   306  	s.Require().NoError(err)
   307  
   308  	err = s.john.AddCommunityToken(community.IDString(), int(chainID), tokenAddress)
   309  	s.Require().NoError(err)
   310  
   311  	// set john as contract owner
   312  	s.collectiblesServiceMock.SetSignerPubkeyForCommunity(community.ID(), common.PubkeyToHex(&s.john.identity.PublicKey))
   313  	s.collectiblesServiceMock.SetMockCollectibleContractData(chainID, tokenAddress,
   314  		&communities.CollectibleContractData{TotalSupply: &bigint.BigInt{}})
   315  
   316  	hasTokenPermission := func(r *MessengerResponse) bool {
   317  		return len(r.Communities()) > 0 && r.Communities()[0].HasTokenPermissions()
   318  	}
   319  
   320  	// bob received owner permissions
   321  	_, err = WaitOnSignaledMessengerResponse(
   322  		s.bob,
   323  		hasTokenPermission,
   324  		"no communities with token permission for Bob",
   325  	)
   326  	s.Require().NoError(err)
   327  
   328  	// alice received owner permissions
   329  	_, err = WaitOnSignaledMessengerResponse(
   330  		s.alice,
   331  		hasTokenPermission,
   332  		"no communities with token permission for Alice",
   333  	)
   334  	s.Require().NoError(err)
   335  
   336  	// simulate Alice received owner token
   337  	s.collectiblesServiceMock.SetSignerPubkeyForCommunity(community.ID(), common.PubkeyToHex(&s.alice.identity.PublicKey))
   338  
   339  	// after receiving owner token - set up control node, set up owner role, kick all members
   340  	// and request kicked members to rejoin
   341  	response, err := s.alice.PromoteSelfToControlNode(community.ID())
   342  	s.Require().NoError(err)
   343  	s.Require().NotNil(response)
   344  	community, err = s.alice.communitiesManager.GetByID(community.ID())
   345  	s.Require().NoError(err)
   346  	s.Require().True(community.IsControlNode())
   347  	s.Require().True(common.IsPubKeyEqual(community.ControlNode(), &s.alice.identity.PublicKey))
   348  	s.Require().True(community.IsOwner())
   349  
   350  	// check that client received kick event
   351  	// Bob will receive request to share RevealedAddresses and send request to join to the control node
   352  	_, err = WaitOnSignaledMessengerResponse(
   353  		s.bob,
   354  		func(r *MessengerResponse) bool {
   355  			return len(r.Communities()) > 0 && !r.Communities()[0].HasMember(&s.bob.identity.PublicKey)
   356  		},
   357  		"Bob was not kicked from the community",
   358  	)
   359  	s.Require().NoError(err)
   360  
   361  	// check that client received kick event
   362  	// John will receive request to share RevealedAddresses and send request to join to the control node
   363  	_, err = WaitOnSignaledMessengerResponse(
   364  		s.john,
   365  		func(r *MessengerResponse) bool {
   366  			wasKicked := len(r.Communities()) > 0 && !r.Communities()[0].HasMember(&s.john.identity.PublicKey)
   367  			sharedNotificationExist := false
   368  			for _, acNotification := range r.ActivityCenterNotifications() {
   369  				if acNotification.Type == ActivityCenterNotificationTypeShareAccounts {
   370  					sharedNotificationExist = true
   371  					break
   372  				}
   373  			}
   374  			return wasKicked && sharedNotificationExist
   375  		},
   376  		"John was not kicked from the community",
   377  	)
   378  	s.Require().NoError(err)
   379  
   380  	// Alice auto-accept requests to join with RevealedAddresses
   381  	_, err = WaitOnMessengerResponse(
   382  		s.alice,
   383  		func(r *MessengerResponse) bool {
   384  			return len(r.Communities()) > 0 && len(r.Communities()[0].Members()) == 2
   385  		},
   386  		"no community update with accepted request",
   387  	)
   388  	s.Require().NoError(err)
   389  
   390  	validateResults := func(messenger *Messenger) *communities.Community {
   391  		community, err = messenger.communitiesManager.GetByID(community.ID())
   392  		s.Require().NoError(err)
   393  		s.Require().True(common.IsPubKeyEqual(community.ControlNode(), &s.alice.identity.PublicKey))
   394  		s.Require().Len(community.Members(), 2)
   395  		s.Require().True(community.HasMember(&messenger.identity.PublicKey))
   396  
   397  		return community
   398  	}
   399  
   400  	community = validateResults(s.alice)
   401  	s.Require().True(community.IsControlNode())
   402  	s.Require().True(community.IsOwner())
   403  
   404  	_, err = WaitOnMessengerResponse(
   405  		s.bob,
   406  		func(r *MessengerResponse) bool {
   407  			return len(r.Communities()) > 0 && r.Communities()[0].HasMember(&s.bob.identity.PublicKey)
   408  		},
   409  		"Bob was auto-accepted",
   410  	)
   411  	s.Require().NoError(err)
   412  
   413  	community = validateResults(s.bob)
   414  	s.Require().False(community.IsControlNode())
   415  	s.Require().False(community.IsOwner())
   416  }
   417  
   418  func (s *MessengerCommunitiesSignersSuite) TestNewOwnerAcceptRequestToJoin() {
   419  	// Create a community
   420  	// Transfer ownership
   421  	// New owner accepts new request to join
   422  	community := s.createCommunity(s.john)
   423  
   424  	s.advertiseCommunityTo(s.john, community, s.alice)
   425  
   426  	s.joinCommunity(community, s.john, s.alice)
   427  
   428  	// john mints owner token
   429  	var chainID uint64 = 1
   430  	tokenAddress := "token-address"
   431  	tokenName := "tokenName"
   432  	tokenSymbol := "TSM"
   433  	_, err := s.john.SaveCommunityToken(&token.CommunityToken{
   434  		TokenType:       protobuf.CommunityTokenType_ERC721,
   435  		CommunityID:     community.IDString(),
   436  		Address:         tokenAddress,
   437  		ChainID:         int(chainID),
   438  		Name:            tokenName,
   439  		Supply:          &bigint.BigInt{},
   440  		Symbol:          tokenSymbol,
   441  		PrivilegesLevel: token.OwnerLevel,
   442  	}, nil)
   443  	s.Require().NoError(err)
   444  
   445  	// john adds minted owner token to community
   446  	err = s.john.AddCommunityToken(community.IDString(), int(chainID), tokenAddress)
   447  	s.Require().NoError(err)
   448  
   449  	// update mock - the signer for the community returned by the contracts should be john
   450  	s.collectiblesServiceMock.SetSignerPubkeyForCommunity(community.ID(), common.PubkeyToHex(&s.john.identity.PublicKey))
   451  	s.collectiblesServiceMock.SetMockCollectibleContractData(chainID, tokenAddress,
   452  		&communities.CollectibleContractData{TotalSupply: &bigint.BigInt{}})
   453  
   454  	// alice accepts community update
   455  	_, err = WaitOnSignaledMessengerResponse(
   456  		s.alice,
   457  		func(r *MessengerResponse) bool {
   458  			return len(r.Communities()) > 0 && len(r.Communities()[0].TokenPermissions()) == 1
   459  		},
   460  		"no communities",
   461  	)
   462  	s.Require().NoError(err)
   463  
   464  	// Ownership token will be transferred to Alice and she will kick all members
   465  	// and request kicked members to rejoin
   466  	// the signer for the community returned by the contracts should be alice
   467  	s.collectiblesServiceMock.SetSignerPubkeyForCommunity(community.ID(), common.PubkeyToHex(&s.alice.identity.PublicKey))
   468  
   469  	response, err := s.alice.PromoteSelfToControlNode(community.ID())
   470  	s.Require().NoError(err)
   471  	s.Require().NotNil(response)
   472  
   473  	community, err = s.alice.communitiesManager.GetByID(community.ID())
   474  	s.Require().NoError(err)
   475  	s.Require().True(community.IsControlNode())
   476  	s.Require().True(common.IsPubKeyEqual(community.ControlNode(), &s.alice.identity.PublicKey))
   477  	s.Require().True(community.IsOwner())
   478  
   479  	// check that John received kick event, also he will receive
   480  	// request to share RevealedAddresses and send request to join to the control node
   481  	_, err = WaitOnSignaledMessengerResponse(
   482  		s.john,
   483  		func(r *MessengerResponse) bool {
   484  			return len(r.Communities()) > 0 && !r.Communities()[0].HasMember(&s.john.identity.PublicKey)
   485  		},
   486  		"John was not kicked from the community",
   487  	)
   488  	s.Require().NoError(err)
   489  
   490  	// Alice advertises community to Bob
   491  	chat := CreateOneToOneChat(common.PubkeyToHex(&s.bob.identity.PublicKey), &s.bob.identity.PublicKey, s.bob.transport)
   492  
   493  	inputMessage := common.NewMessage()
   494  	inputMessage.ChatId = chat.ID
   495  	inputMessage.Text = "some text"
   496  	inputMessage.CommunityID = community.IDString()
   497  
   498  	err = s.alice.SaveChat(chat)
   499  	s.Require().NoError(err)
   500  	_, err = s.alice.SendChatMessage(context.Background(), inputMessage)
   501  	s.Require().NoError(err)
   502  
   503  	_, err = WaitOnSignaledMessengerResponse(
   504  		s.bob,
   505  		func(r *MessengerResponse) bool {
   506  			return len(r.Communities()) > 0
   507  		},
   508  		"Community was not advertised to Bob",
   509  	)
   510  	s.Require().NoError(err)
   511  
   512  	// Bob joins the community
   513  	s.joinCommunity(community, s.alice, s.bob)
   514  
   515  }
   516  
   517  func (s *MessengerCommunitiesSignersSuite) testDescriptionSignature(description []byte) {
   518  	var amm protobuf.ApplicationMetadataMessage
   519  	err := proto.Unmarshal(description, &amm)
   520  	s.Require().NoError(err)
   521  
   522  	signer, err := utils.RecoverKey(&amm)
   523  	s.Require().NoError(err)
   524  	s.NotNil(signer)
   525  }
   526  
   527  func (s *MessengerCommunitiesSignersSuite) forceCommunityChange(community *communities.Community, owner *Messenger, user *Messenger) {
   528  	newDescription := community.DescriptionText() + " new"
   529  	_, err := owner.EditCommunity(&requests.EditCommunity{
   530  		CommunityID: community.ID(),
   531  		CreateCommunity: requests.CreateCommunity{
   532  			Membership:  protobuf.CommunityPermissions_AUTO_ACCEPT,
   533  			Name:        community.Name(),
   534  			Color:       community.Color(),
   535  			Description: newDescription,
   536  		},
   537  	})
   538  	s.Require().NoError(err)
   539  
   540  	// alice receives new description
   541  	_, err = WaitOnMessengerResponse(user, func(r *MessengerResponse) bool {
   542  		return len(r.Communities()) > 0 && r.Communities()[0].DescriptionText() == newDescription
   543  	}, "new description not received")
   544  	s.Require().NoError(err)
   545  }
   546  
   547  func (s *MessengerCommunitiesSignersSuite) testSyncCommunity(mintOwnerToken bool) {
   548  	community := s.createCommunity(s.john)
   549  	s.advertiseCommunityTo(s.john, community, s.alice)
   550  	s.joinCommunity(community, s.john, s.alice)
   551  
   552  	// FIXME: Remove this workaround when fixed:
   553  	// https://github.com/status-im/status-go/issues/4413
   554  	s.forceCommunityChange(community, s.john, s.alice)
   555  
   556  	aliceCommunity, err := s.alice.GetCommunityByID(community.ID())
   557  	s.Require().NoError(err)
   558  	s.testDescriptionSignature(aliceCommunity.DescriptionProtocolMessage())
   559  
   560  	if mintOwnerToken {
   561  		// john mints owner token
   562  		var chainID uint64 = 1
   563  		tokenAddress := "token-address"
   564  		tokenName := "tokenName"
   565  		tokenSymbol := "TSM"
   566  		communityToken := &token.CommunityToken{
   567  			TokenType:       protobuf.CommunityTokenType_ERC721,
   568  			CommunityID:     community.IDString(),
   569  			Address:         tokenAddress,
   570  			ChainID:         int(chainID),
   571  			Name:            tokenName,
   572  			Supply:          &bigint.BigInt{},
   573  			Symbol:          tokenSymbol,
   574  			PrivilegesLevel: token.OwnerLevel,
   575  		}
   576  
   577  		_, err := s.john.SaveCommunityToken(communityToken, nil)
   578  		s.Require().NoError(err)
   579  
   580  		// john adds minted owner token to community
   581  		err = s.john.AddCommunityToken(community.IDString(), int(chainID), tokenAddress)
   582  		s.Require().NoError(err)
   583  
   584  		// update mock - the signer for the community returned by the contracts should be john
   585  		s.collectiblesServiceMock.SetSignerPubkeyForCommunity(community.ID(), common.PubkeyToHex(&s.john.identity.PublicKey))
   586  		s.collectiblesServiceMock.SetMockCommunityTokenData(communityToken)
   587  
   588  		// alice accepts community update
   589  		_, err = WaitOnSignaledMessengerResponse(
   590  			s.alice,
   591  			func(r *MessengerResponse) bool {
   592  				return len(r.Communities()) > 0 && len(r.Communities()[0].TokenPermissions()) == 1
   593  			},
   594  			"no communities",
   595  		)
   596  		s.Require().NoError(err)
   597  	}
   598  
   599  	// Create alice second instance
   600  	alice2, err := newMessengerWithKey(
   601  		s.shh,
   602  		s.alice.identity,
   603  		s.logger.With(zap.String("name", "alice-2")),
   604  		[]Option{WithCommunityTokensService(s.collectiblesServiceMock)})
   605  
   606  	s.Require().NoError(err)
   607  	defer TearDownMessenger(&s.Suite, alice2)
   608  
   609  	// Create communities backup
   610  
   611  	clock, _ := s.alice.getLastClockWithRelatedChat()
   612  	communitiesBackup, err := s.alice.backupCommunities(context.Background(), clock)
   613  	s.Require().NoError(err)
   614  
   615  	// Find wanted communities in the backup
   616  
   617  	var syncCommunityMessages []*protobuf.SyncInstallationCommunity
   618  
   619  	for _, b := range communitiesBackup {
   620  		for _, c := range b.Communities {
   621  			if bytes.Equal(c.Id, community.ID()) {
   622  				syncCommunityMessages = append(syncCommunityMessages, c)
   623  			}
   624  		}
   625  	}
   626  	s.Require().Len(syncCommunityMessages, 1)
   627  
   628  	s.testDescriptionSignature(syncCommunityMessages[0].Description)
   629  
   630  	// Push the backup into second instance
   631  
   632  	messageState := alice2.buildMessageState()
   633  	err = alice2.HandleSyncInstallationCommunity(messageState, syncCommunityMessages[0], nil)
   634  
   635  	s.Require().NoError(err)
   636  	s.Require().Len(messageState.Response.Communities(), 1)
   637  
   638  	expectedControlNode := community.PublicKey()
   639  	if mintOwnerToken {
   640  		expectedControlNode = &s.john.identity.PublicKey
   641  	}
   642  
   643  	responseCommunity := messageState.Response.Communities()[0]
   644  	s.Require().Equal(community.IDString(), responseCommunity.IDString())
   645  	s.Require().True(common.IsPubKeyEqual(expectedControlNode, responseCommunity.ControlNode()))
   646  }
   647  
   648  func (s *MessengerCommunitiesSignersSuite) TestSyncTokenGatedCommunity() {
   649  	testCases := []struct {
   650  		name           string
   651  		mintOwnerToken bool
   652  	}{
   653  		{
   654  			name:           "general community sync",
   655  			mintOwnerToken: false,
   656  		},
   657  		{
   658  			name:           "community with token ownership",
   659  			mintOwnerToken: true,
   660  		},
   661  	}
   662  
   663  	for _, tc := range testCases {
   664  		s.Run(tc.name, func() {
   665  			s.testSyncCommunity(tc.mintOwnerToken)
   666  		})
   667  	}
   668  }
   669  
   670  func (s *MessengerCommunitiesSignersSuite) TestWithMintedOwnerTokenApplyCommunityEventsUponMakingDeviceControlNode() {
   671  	community := s.createCommunity(s.john)
   672  
   673  	// john mints owner token
   674  	var chainID uint64 = 1
   675  	tokenAddress := "token-address"
   676  	tokenName := "tokenName"
   677  	tokenSymbol := "TSM"
   678  	_, err := s.john.SaveCommunityToken(&token.CommunityToken{
   679  		TokenType:       protobuf.CommunityTokenType_ERC721,
   680  		CommunityID:     community.IDString(),
   681  		Address:         tokenAddress,
   682  		ChainID:         int(chainID),
   683  		Name:            tokenName,
   684  		Supply:          &bigint.BigInt{},
   685  		Symbol:          tokenSymbol,
   686  		PrivilegesLevel: token.OwnerLevel,
   687  	}, nil)
   688  	s.Require().NoError(err)
   689  
   690  	err = s.john.AddCommunityToken(community.IDString(), int(chainID), tokenAddress)
   691  	s.Require().NoError(err)
   692  
   693  	// Make sure there is no control node
   694  	s.Require().False(common.IsPubKeyEqual(community.ControlNode(), &s.john.identity.PublicKey))
   695  
   696  	// Trick. We need to remove the community private key otherwise the events
   697  	// will be signed and Events will be approved instead of being in Pending State.
   698  	_, err = s.john.RemovePrivateKey(community.ID())
   699  	s.Require().NoError(err)
   700  
   701  	request := requests.CreateCommunityTokenPermission{
   702  		CommunityID: community.ID(),
   703  		Type:        protobuf.CommunityTokenPermission_BECOME_ADMIN,
   704  		TokenCriteria: []*protobuf.TokenCriteria{
   705  			&protobuf.TokenCriteria{
   706  				Type:              protobuf.CommunityTokenType_ERC20,
   707  				ContractAddresses: map[uint64]string{testChainID1: "0x123"},
   708  				Symbol:            "TEST",
   709  				AmountInWei:       "100000000000000000000",
   710  				Decimals:          uint64(18),
   711  			},
   712  		},
   713  	}
   714  
   715  	response, err := s.john.CreateCommunityTokenPermission(&request)
   716  	s.Require().NoError(err)
   717  	s.Require().Len(response.CommunityChanges, 1)
   718  	s.Require().Len(response.CommunityChanges[0].TokenPermissionsAdded, 1)
   719  
   720  	addedPermission := func() *communities.CommunityTokenPermission {
   721  		for _, permission := range response.CommunityChanges[0].TokenPermissionsAdded {
   722  			return permission
   723  		}
   724  		return nil
   725  	}()
   726  	s.Require().NotNil(addedPermission)
   727  	s.Require().Equal(communities.TokenPermissionAdditionPending, addedPermission.State)
   728  
   729  	messengerReponse, err := s.john.PromoteSelfToControlNode(community.ID())
   730  
   731  	s.Require().NoError(err)
   732  	s.Require().Len(messengerReponse.Communities(), 1)
   733  
   734  	tokenPermissions := messengerReponse.Communities()[0].TokenPermissions()
   735  	s.Require().Len(tokenPermissions, 2)
   736  
   737  	tokenPermissionsMap := make(map[protobuf.CommunityTokenPermission_Type]struct{}, len(tokenPermissions))
   738  	for _, t := range tokenPermissions {
   739  		tokenPermissionsMap[t.Type] = struct{}{}
   740  	}
   741  
   742  	s.Require().Len(tokenPermissionsMap, 2)
   743  	s.Require().Contains(tokenPermissionsMap, protobuf.CommunityTokenPermission_BECOME_TOKEN_OWNER)
   744  	s.Require().Contains(tokenPermissionsMap, protobuf.CommunityTokenPermission_BECOME_ADMIN)
   745  
   746  	for _, v := range tokenPermissions {
   747  		s.Require().Equal(communities.TokenPermissionApproved, v.State)
   748  	}
   749  }
   750  
   751  func (s *MessengerCommunitiesSignersSuite) TestWithoutMintedOwnerTokenMakingDeviceControlNodeIsBlocked() {
   752  	community := s.createCommunity(s.john)
   753  
   754  	// Make sure there is no control node
   755  	s.Require().False(common.IsPubKeyEqual(community.ControlNode(), &s.john.identity.PublicKey))
   756  
   757  	response, err := s.john.PromoteSelfToControlNode(community.ID())
   758  	s.Require().Nil(response)
   759  	s.Require().NotNil(err)
   760  	s.Require().Error(err, "Owner token is needed")
   761  }
   762  
   763  func (s *MessengerCommunitiesSignersSuite) TestControlNodeDeviceChanged() {
   764  	// Note: we don't have any specific check if control node device changed,
   765  	// so in this test we will just call twice 'PromoteSelfToControlNode'
   766  	community, _ := createOnRequestCommunity(&s.Suite, s.john)
   767  
   768  	// john mints owner token
   769  	ownerTokenAddress := "token-address"
   770  	_, err := s.john.SaveCommunityToken(&token.CommunityToken{
   771  		TokenType:       protobuf.CommunityTokenType_ERC721,
   772  		CommunityID:     community.IDString(),
   773  		Address:         ownerTokenAddress,
   774  		ChainID:         int(testChainID1),
   775  		Name:            "ownerToken",
   776  		Supply:          &bigint.BigInt{},
   777  		Symbol:          "OT",
   778  		PrivilegesLevel: token.OwnerLevel,
   779  	}, nil)
   780  	s.Require().NoError(err)
   781  
   782  	err = s.john.AddCommunityToken(community.IDString(), int(testChainID1), ownerTokenAddress)
   783  	s.Require().NoError(err)
   784  
   785  	// john mints TM token
   786  	tokenMasterTokenAddress := "token-master-address"
   787  	_, err = s.john.SaveCommunityToken(&token.CommunityToken{
   788  		TokenType:       protobuf.CommunityTokenType_ERC721,
   789  		CommunityID:     community.IDString(),
   790  		Address:         tokenMasterTokenAddress,
   791  		ChainID:         int(testChainID1),
   792  		Name:            "tokenMasterToken",
   793  		Supply:          &bigint.BigInt{},
   794  		Symbol:          "TMT",
   795  		PrivilegesLevel: token.MasterLevel,
   796  	}, nil)
   797  	s.Require().NoError(err)
   798  
   799  	err = s.john.AddCommunityToken(community.IDString(), int(testChainID1), tokenMasterTokenAddress)
   800  	s.Require().NoError(err)
   801  
   802  	// set john as contract owner
   803  	s.collectiblesServiceMock.SetSignerPubkeyForCommunity(community.ID(), common.PubkeyToHex(&s.john.identity.PublicKey))
   804  	s.collectiblesServiceMock.SetMockCollectibleContractData(testChainID1, ownerTokenAddress,
   805  		&communities.CollectibleContractData{TotalSupply: &bigint.BigInt{}})
   806  	s.collectiblesServiceMock.SetMockCollectibleContractData(testChainID1, tokenMasterTokenAddress,
   807  		&communities.CollectibleContractData{TotalSupply: &bigint.BigInt{}})
   808  
   809  	community, err = s.john.communitiesManager.GetByID(community.ID())
   810  	s.Require().NoError(err)
   811  	s.Require().True(common.IsPubKeyEqual(community.ControlNode(), &s.john.identity.PublicKey))
   812  
   813  	var tokenMasterTokenCriteria *protobuf.TokenCriteria
   814  	for _, permission := range community.TokenPermissions() {
   815  		if permission.Type == protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER {
   816  			s.Require().Len(permission.TokenCriteria, 1)
   817  			tokenMasterTokenCriteria = permission.TokenCriteria[0]
   818  			break
   819  		}
   820  	}
   821  	s.Require().NotNil(tokenMasterTokenCriteria)
   822  
   823  	s.makeAddressSatisfyTheCriteria(testChainID1, bobAddress, tokenMasterTokenCriteria)
   824  
   825  	waitOnAliceCommunityValidation := waitOnCommunitiesEvent(s.alice, func(sub *communities.Subscription) bool {
   826  		return sub.TokenCommunityValidated != nil
   827  	})
   828  	s.advertiseCommunityTo(s.john, community, s.alice)
   829  	err = <-waitOnAliceCommunityValidation
   830  	s.Require().NoError(err)
   831  
   832  	waitOnBobCommunityValidation := waitOnCommunitiesEvent(s.bob, func(sub *communities.Subscription) bool {
   833  		return sub.TokenCommunityValidated != nil
   834  	})
   835  	s.advertiseCommunityTo(s.john, community, s.bob)
   836  	err = <-waitOnBobCommunityValidation
   837  	s.Require().NoError(err)
   838  
   839  	s.joinOnRequestCommunity(community, s.john, s.alice)
   840  	s.joinOnRequestCommunity(community, s.john, s.bob)
   841  
   842  	community, err = s.john.communitiesManager.GetByID(community.ID())
   843  	s.Require().NoError(err)
   844  	s.Require().True(checkRoleBasedOnThePermissionType(protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER, &s.bob.identity.PublicKey, community))
   845  
   846  	// Simulate control node device changed and new control node device has all members revealed addresses
   847  	response, err := s.john.PromoteSelfToControlNode(community.ID())
   848  	s.Require().NoError(err)
   849  	s.Require().Len(response.CommunityChanges, 1)
   850  	s.Require().Len(response.CommunityChanges[0].MembersRemoved, 0)
   851  
   852  	// Simulate control node device changed and new control node device does not have bob's revealed addresses
   853  	bobRequestID := communities.CalculateRequestID(s.bob.IdentityPublicKeyString(), community.ID())
   854  	err = s.john.communitiesManager.RemoveRequestToJoinRevealedAddresses(bobRequestID)
   855  	s.Require().NoError(err)
   856  
   857  	// due to test execution is fast, we update request to join clock
   858  	clock := uint64(time.Now().Unix() - 2)
   859  	err = s.john.communitiesManager.UpdateClockInRequestToJoin(bobRequestID, clock)
   860  	s.Require().NoError(err)
   861  
   862  	_, err = s.john.PromoteSelfToControlNode(community.ID())
   863  
   864  	s.Require().NoError(err)
   865  	community, err = s.john.communitiesManager.GetByID(community.ID())
   866  	s.Require().NoError(err)
   867  	s.Require().Len(community.Members(), 2)
   868  	for _, chat := range community.Chats() {
   869  		s.Require().Len(chat.Members, 2)
   870  	}
   871  
   872  	// Bob will receive request to share RevealedAddresses and send request to join to the control node
   873  	_, err = WaitOnMessengerResponse(
   874  		s.bob,
   875  		func(r *MessengerResponse) bool {
   876  			return len(r.Communities()) == 1 && !r.Communities()[0].HasMember(&s.bob.identity.PublicKey) &&
   877  				r.Communities()[0].Spectated() && len(r.ActivityCenterNotifications()) == 0
   878  		},
   879  		"Bob was not soft kicked from the community",
   880  	)
   881  	s.Require().NoError(err)
   882  
   883  	// check that alice was not soft kicked
   884  	_, err = WaitOnMessengerResponse(
   885  		s.alice,
   886  		func(r *MessengerResponse) bool {
   887  			return len(r.Communities()) > 0 && r.Communities()[0].HasMember(&s.alice.identity.PublicKey) &&
   888  				!r.Communities()[0].Spectated() && len(r.ActivityCenterNotifications()) == 0 &&
   889  				r.Communities()[0].Joined()
   890  		},
   891  		"Alice was kicked from the community",
   892  	)
   893  	s.Require().NoError(err)
   894  
   895  	// John auto-accept requests to join with RevealedAddresses
   896  	_, err = WaitOnMessengerResponse(
   897  		s.john,
   898  		func(r *MessengerResponse) bool {
   899  			return len(r.Communities()) > 0 && len(r.Communities()[0].Members()) == 3
   900  		},
   901  		"no community update with accepted request",
   902  	)
   903  	s.Require().NoError(err)
   904  
   905  	_, err = WaitOnMessengerResponse(
   906  		s.bob,
   907  		func(r *MessengerResponse) bool {
   908  			return len(r.Communities()) > 0 && r.Communities()[0].HasMember(&s.bob.identity.PublicKey) &&
   909  				r.Communities()[0].Joined() && !r.Communities()[0].Spectated() && r.Communities()[0].IsTokenMaster()
   910  		},
   911  		"Bob was auto-accepted",
   912  	)
   913  	s.Require().NoError(err)
   914  }