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 }