github.com/status-im/status-go@v1.1.0/protocol/communities_messenger_helpers_test.go (about) 1 package protocol 2 3 import ( 4 "context" 5 "crypto/ecdsa" 6 "encoding/json" 7 "errors" 8 "math/big" 9 "strconv" 10 "sync" 11 "time" 12 13 "github.com/stretchr/testify/suite" 14 15 gethcommon "github.com/ethereum/go-ethereum/common" 16 hexutil "github.com/ethereum/go-ethereum/common/hexutil" 17 18 "github.com/status-im/status-go/account" 19 "github.com/status-im/status-go/eth-node/crypto" 20 "github.com/status-im/status-go/eth-node/types" 21 "github.com/status-im/status-go/multiaccounts/accounts" 22 "github.com/status-im/status-go/multiaccounts/settings" 23 "github.com/status-im/status-go/params" 24 "github.com/status-im/status-go/protocol/common" 25 "github.com/status-im/status-go/protocol/communities" 26 "github.com/status-im/status-go/protocol/communities/token" 27 "github.com/status-im/status-go/protocol/protobuf" 28 "github.com/status-im/status-go/protocol/requests" 29 "github.com/status-im/status-go/services/wallet/bigint" 30 walletCommon "github.com/status-im/status-go/services/wallet/common" 31 "github.com/status-im/status-go/services/wallet/thirdparty" 32 walletToken "github.com/status-im/status-go/services/wallet/token" 33 "github.com/status-im/status-go/transactions" 34 ) 35 36 type AccountManagerMock struct { 37 AccountsMap map[string]string 38 } 39 40 func (m *AccountManagerMock) GetVerifiedWalletAccount(db *accounts.Database, address, password string) (*account.SelectedExtKey, error) { 41 return &account.SelectedExtKey{ 42 Address: types.HexToAddress(address), 43 }, nil 44 } 45 46 func (m *AccountManagerMock) CanRecover(rpcParams account.RecoverParams, revealedAddress types.Address) (bool, error) { 47 return true, nil 48 } 49 50 func (m *AccountManagerMock) Sign(rpcParams account.SignParams, verifiedAccount *account.SelectedExtKey) (result types.HexBytes, err error) { 51 // mock signature 52 bytesArray := []byte(rpcParams.Address) 53 bytesArray = append(bytesArray, []byte(rpcParams.Password)...) 54 bytesArray = common.Shake256(bytesArray) 55 return append([]byte{0}, bytesArray...), nil 56 } 57 58 func (m *AccountManagerMock) DeleteAccount(address types.Address) error { 59 return nil 60 } 61 62 type TokenManagerMock struct { 63 Balances *communities.BalancesByChain 64 } 65 66 func (m *TokenManagerMock) GetAllChainIDs() ([]uint64, error) { 67 chainIDs := make([]uint64, 0, len(*m.Balances)) 68 for key := range *m.Balances { 69 chainIDs = append(chainIDs, key) 70 } 71 return chainIDs, nil 72 } 73 74 func (m *TokenManagerMock) getBalanceBasedOnParams(accounts, tokenAddresses []gethcommon.Address, chainIDs []uint64) map[uint64]map[gethcommon.Address]map[gethcommon.Address]*hexutil.Big { 75 retBalances := make(map[uint64]map[gethcommon.Address]map[gethcommon.Address]*hexutil.Big) 76 77 for _, chainId := range chainIDs { 78 if _, exists := retBalances[chainId]; !exists { 79 retBalances[chainId] = make(map[gethcommon.Address]map[gethcommon.Address]*hexutil.Big) 80 } 81 if storedAccounts, exists := (*m.Balances)[chainId]; exists { 82 for _, account := range accounts { 83 if _, exists := retBalances[chainId][account]; !exists { 84 retBalances[chainId][account] = make(map[gethcommon.Address]*hexutil.Big) 85 } 86 if storedTokenAddresses, exists := storedAccounts[account]; exists { 87 for _, tokenAddress := range tokenAddresses { 88 if _, exists := retBalances[chainId][account][tokenAddress]; !exists { 89 retBalances[chainId][account] = make(map[gethcommon.Address]*hexutil.Big) 90 } 91 92 if balance, exists := storedTokenAddresses[tokenAddress]; exists { 93 retBalances[chainId][account][tokenAddress] = balance 94 } 95 } 96 } 97 } 98 } 99 } 100 101 return retBalances 102 } 103 104 func (m *TokenManagerMock) GetBalancesByChain(ctx context.Context, accounts, tokenAddresses []gethcommon.Address, chainIDs []uint64) (map[uint64]map[gethcommon.Address]map[gethcommon.Address]*hexutil.Big, error) { 105 time.Sleep(100 * time.Millisecond) // simulate response time 106 return m.getBalanceBasedOnParams(accounts, tokenAddresses, chainIDs), nil 107 } 108 109 func (m *TokenManagerMock) GetCachedBalancesByChain(ctx context.Context, accounts, tokenAddresses []gethcommon.Address, chainIDs []uint64) (map[uint64]map[gethcommon.Address]map[gethcommon.Address]*hexutil.Big, error) { 110 time.Sleep(100 * time.Millisecond) // simulate response time 111 return m.getBalanceBasedOnParams(accounts, tokenAddresses, chainIDs), nil 112 } 113 114 func (m *TokenManagerMock) FindOrCreateTokenByAddress(ctx context.Context, chainID uint64, address gethcommon.Address) *walletToken.Token { 115 time.Sleep(100 * time.Millisecond) // simulate response time 116 return nil 117 } 118 119 type CollectiblesManagerMock struct { 120 Collectibles *communities.CollectiblesByChain 121 collectibleOwnershipResponse map[string][]thirdparty.AccountBalance 122 } 123 124 func (m *CollectiblesManagerMock) FetchCachedBalancesByOwnerAndContractAddress(ctx context.Context, chainID walletCommon.ChainID, 125 ownerAddress gethcommon.Address, contractAddresses []gethcommon.Address) (thirdparty.TokenBalancesPerContractAddress, error) { 126 return m.FetchBalancesByOwnerAndContractAddress(ctx, chainID, ownerAddress, contractAddresses) 127 } 128 129 func (m *CollectiblesManagerMock) FetchBalancesByOwnerAndContractAddress(ctx context.Context, chainID walletCommon.ChainID, 130 ownerAddress gethcommon.Address, contractAddresses []gethcommon.Address) (thirdparty.TokenBalancesPerContractAddress, error) { 131 ret := make(thirdparty.TokenBalancesPerContractAddress) 132 accountsBalances, ok := (*m.Collectibles)[uint64(chainID)] 133 if !ok { 134 return ret, nil 135 } 136 137 balances, ok := accountsBalances[ownerAddress] 138 if !ok { 139 return ret, nil 140 } 141 142 for _, contractAddress := range contractAddresses { 143 balance, ok := balances[contractAddress] 144 if ok { 145 ret[contractAddress] = balance 146 } 147 } 148 149 return ret, nil 150 } 151 152 func (m *CollectiblesManagerMock) GetCollectibleOwnership(requestedID thirdparty.CollectibleUniqueID) ([]thirdparty.AccountBalance, error) { 153 for id, balances := range m.collectibleOwnershipResponse { 154 if id == requestedID.HashKey() { 155 return balances, nil 156 } 157 } 158 return []thirdparty.AccountBalance{}, nil 159 } 160 161 func (m *CollectiblesManagerMock) FetchCollectibleOwnersByContractAddress(ctx context.Context, chainID walletCommon.ChainID, contractAddress gethcommon.Address) (*thirdparty.CollectibleContractOwnership, error) { 162 ret := &thirdparty.CollectibleContractOwnership{ 163 ContractAddress: contractAddress, 164 Owners: []thirdparty.CollectibleOwner{}, 165 } 166 accountsBalances, ok := (*m.Collectibles)[uint64(chainID)] 167 if !ok { 168 return ret, nil 169 } 170 171 for wallet, balances := range accountsBalances { 172 balance, ok := balances[contractAddress] 173 if ok { 174 ret.Owners = append(ret.Owners, thirdparty.CollectibleOwner{ 175 OwnerAddress: wallet, 176 TokenBalances: balance, 177 }) 178 } 179 } 180 181 return ret, nil 182 } 183 184 func (m *CollectiblesManagerMock) SetCollectibleOwnershipResponse(id thirdparty.CollectibleUniqueID, balances []thirdparty.AccountBalance) { 185 if m.collectibleOwnershipResponse == nil { 186 m.collectibleOwnershipResponse = map[string][]thirdparty.AccountBalance{} 187 } 188 m.collectibleOwnershipResponse[id.HashKey()] = balances 189 } 190 191 type CollectiblesServiceMock struct { 192 Collectibles map[uint64]map[string]*communities.CollectibleContractData 193 Assets map[uint64]map[string]*communities.AssetContractData 194 Signers map[string]string 195 } 196 197 func (c *CollectiblesServiceMock) SetSignerPubkeyForCommunity(communityID []byte, signerPubKey string) { 198 if c.Signers == nil { 199 c.Signers = make(map[string]string) 200 } 201 c.Signers[types.EncodeHex(communityID)] = signerPubKey 202 } 203 204 func (c *CollectiblesServiceMock) SetSignerPubKey(ctx context.Context, chainID uint64, contractAddress string, txArgs transactions.SendTxArgs, password string, newSignerPubKey string) (string, error) { 205 return "", nil 206 } 207 208 func (c *CollectiblesServiceMock) GetCollectibleContractData(chainID uint64, contractAddress string) (*communities.CollectibleContractData, error) { 209 collectibleContractData, dataExists := c.Collectibles[chainID][contractAddress] 210 if dataExists { 211 return collectibleContractData, nil 212 } 213 return nil, nil 214 } 215 216 func (c *CollectiblesServiceMock) GetAssetContractData(chainID uint64, contractAddress string) (*communities.AssetContractData, error) { 217 assetsContractData, dataExists := c.Assets[chainID][contractAddress] 218 if dataExists { 219 return assetsContractData, nil 220 } 221 return nil, nil 222 } 223 224 func (c *CollectiblesServiceMock) SetMockCollectibleContractData(chainID uint64, contractAddress string, collectible *communities.CollectibleContractData) { 225 if c.Collectibles == nil { 226 c.Collectibles = make(map[uint64]map[string]*communities.CollectibleContractData) 227 } 228 if _, ok := c.Collectibles[chainID]; !ok { 229 c.Collectibles[chainID] = make(map[string]*communities.CollectibleContractData) 230 } 231 c.Collectibles[chainID][contractAddress] = collectible 232 } 233 234 func (c *CollectiblesServiceMock) SetMockCommunityTokenData(token *token.CommunityToken) { 235 if c.Collectibles == nil { 236 c.Collectibles = make(map[uint64]map[string]*communities.CollectibleContractData) 237 } 238 239 data := &communities.CollectibleContractData{ 240 TotalSupply: token.Supply, 241 Transferable: token.Transferable, 242 RemoteBurnable: token.RemoteSelfDestruct, 243 InfiniteSupply: token.InfiniteSupply, 244 } 245 246 c.SetMockCollectibleContractData(uint64(token.ChainID), token.Address, data) 247 } 248 249 func (c *CollectiblesServiceMock) SafeGetSignerPubKey(ctx context.Context, chainID uint64, communityID string) (string, error) { 250 if c.Signers == nil { 251 c.Signers = make(map[string]string) 252 } 253 return c.Signers[communityID], nil 254 } 255 256 func (c *CollectiblesServiceMock) SetMockAssetContractData(chainID uint64, contractAddress string, assetData *communities.AssetContractData) { 257 if c.Assets == nil { 258 c.Assets = make(map[uint64]map[string]*communities.AssetContractData) 259 } 260 c.Assets[chainID] = make(map[string]*communities.AssetContractData) 261 c.Assets[chainID][contractAddress] = assetData 262 } 263 264 func (c *CollectiblesServiceMock) DeploymentSignatureDigest(chainID uint64, addressFrom string, communityID string) ([]byte, error) { 265 return gethcommon.Hex2Bytes("ccbb375343347491706cf4b43796f7b96ccc89c9e191a8b78679daeba1684ec7"), nil 266 } 267 268 func (s *CollectiblesServiceMock) ProcessCommunityTokenAction(message *protobuf.CommunityTokenAction) error { 269 return nil 270 } 271 272 type testCommunitiesMessengerConfig struct { 273 testMessengerConfig 274 275 nodeConfig *params.NodeConfig 276 appSettings *settings.Settings 277 278 password string 279 walletAddresses []string 280 mockedBalances *communities.BalancesByChain 281 collectiblesService communities.CommunityTokensServiceInterface 282 collectiblesManager communities.CollectiblesManager 283 } 284 285 func (tcmc *testCommunitiesMessengerConfig) complete() error { 286 err := tcmc.testMessengerConfig.complete() 287 if err != nil { 288 return err 289 } 290 291 if tcmc.nodeConfig == nil { 292 tcmc.nodeConfig = defaultTestCommunitiesMessengerNodeConfig() 293 } 294 if tcmc.appSettings == nil { 295 tcmc.appSettings = defaultTestCommunitiesMessengerSettings() 296 } 297 298 return nil 299 } 300 301 func defaultTestCommunitiesMessengerNodeConfig() *params.NodeConfig { 302 return ¶ms.NodeConfig{ 303 NetworkID: 10, 304 DataDir: "test", 305 } 306 } 307 func defaultTestCommunitiesMessengerSettings() *settings.Settings { 308 networks := json.RawMessage("{}") 309 return &settings.Settings{ 310 Address: types.HexToAddress("0x1122334455667788990011223344556677889900"), 311 AnonMetricsShouldSend: false, 312 CurrentNetwork: "mainnet_rpc", 313 DappsAddress: types.HexToAddress("0x1122334455667788990011223344556677889900"), 314 InstallationID: "d3efcff6-cffa-560e-a547-21d3858cbc51", 315 KeyUID: "0x1122334455667788990011223344556677889900", 316 Name: "Test", 317 Networks: &networks, 318 LatestDerivedPath: 0, 319 PhotoPath: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAIAAACRXR/mAAAAjklEQVR4nOzXwQmFMBAAUZXUYh32ZB32ZB02sxYQQSZGsod55/91WFgSS0RM+SyjA56ZRZhFmEWYRRT6h+M6G16zrxv6fdJpmUWYRbxsYr13dKfanpN0WmYRZhGzXz6AWYRZRIfbaX26fT9Jk07LLMIsosPt9I/dTDotswizCG+nhFmEWYRZhFnEHQAA///z1CFkYamgfQAAAABJRU5ErkJggg==", 320 PreviewPrivacy: false, 321 PublicKey: "0x04112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900", 322 SigningPhrase: "yurt joey vibe", 323 SendPushNotifications: true, 324 ProfilePicturesVisibility: 1, 325 DefaultSyncPeriod: 777600, 326 UseMailservers: true, 327 LinkPreviewRequestEnabled: true, 328 SendStatusUpdates: true, 329 WalletRootAddress: types.HexToAddress("0x1122334455667788990011223344556677889900")} 330 } 331 332 func newTestCommunitiesMessenger(s *suite.Suite, waku types.Waku, config testCommunitiesMessengerConfig) *Messenger { 333 err := config.complete() 334 s.Require().NoError(err) 335 336 accountsManagerMock := &AccountManagerMock{} 337 accountsManagerMock.AccountsMap = make(map[string]string) 338 for _, walletAddress := range config.walletAddresses { 339 accountsManagerMock.AccountsMap[walletAddress] = types.EncodeHex(crypto.Keccak256([]byte(config.password))) 340 } 341 342 tokenManagerMock := &TokenManagerMock{ 343 Balances: config.mockedBalances, 344 } 345 346 options := []Option{ 347 WithAccountManager(accountsManagerMock), 348 WithTokenManager(tokenManagerMock), 349 WithCollectiblesManager(config.collectiblesManager), 350 WithCommunityTokensService(config.collectiblesService), 351 WithAppSettings(*config.appSettings, *config.nodeConfig), 352 } 353 354 config.extraOptions = append(config.extraOptions, options...) 355 356 messenger, err := newTestMessenger(waku, config.testMessengerConfig) 357 s.Require().NoError(err) 358 359 currentDistributorObj, ok := messenger.communitiesKeyDistributor.(*CommunitiesKeyDistributorImpl) 360 s.Require().True(ok) 361 messenger.communitiesKeyDistributor = &TestCommunitiesKeyDistributor{ 362 CommunitiesKeyDistributorImpl: *currentDistributorObj, 363 subscriptions: map[chan *CommunityAndKeyActions]bool{}, 364 mutex: sync.RWMutex{}, 365 } 366 367 // add wallet account with keypair 368 for _, walletAddress := range config.walletAddresses { 369 kp := accounts.GetProfileKeypairForTest(false, true, false) 370 kp.Accounts[0].Address = types.HexToAddress(walletAddress) 371 err := messenger.settings.SaveOrUpdateKeypair(kp) 372 s.Require().NoError(err) 373 } 374 375 walletAccounts, err := messenger.settings.GetActiveAccounts() 376 s.Require().NoError(err) 377 s.Require().Len(walletAccounts, len(config.walletAddresses)) 378 for i := range config.walletAddresses { 379 s.Require().Equal(walletAccounts[i].Type, accounts.AccountTypeGenerated) 380 } 381 return messenger 382 } 383 384 func createEncryptedCommunity(s *suite.Suite, owner *Messenger) (*communities.Community, *Chat) { 385 community, chat := createCommunityConfigurable(s, owner, protobuf.CommunityPermissions_AUTO_ACCEPT) 386 // Add community permission 387 _, err := owner.CreateCommunityTokenPermission(&requests.CreateCommunityTokenPermission{ 388 CommunityID: community.ID(), 389 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 390 TokenCriteria: []*protobuf.TokenCriteria{{ 391 ContractAddresses: map[uint64]string{3: "0x933"}, 392 Type: protobuf.CommunityTokenType_ERC20, 393 Symbol: "STT", 394 Name: "Status Test Token", 395 AmountInWei: "10000000000000000000", 396 Decimals: 18, 397 }}, 398 }) 399 s.Require().NoError(err) 400 401 // Add channel permission 402 response, err := owner.CreateCommunityTokenPermission(&requests.CreateCommunityTokenPermission{ 403 CommunityID: community.ID(), 404 Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL, 405 TokenCriteria: []*protobuf.TokenCriteria{ 406 &protobuf.TokenCriteria{ 407 ContractAddresses: map[uint64]string{3: "0x933"}, 408 Type: protobuf.CommunityTokenType_ERC20, 409 Symbol: "STT", 410 Name: "Status Test Token", 411 AmountInWei: "10000000000000000000", 412 Decimals: 18, 413 }, 414 }, 415 ChatIds: []string{chat.ID}, 416 }) 417 s.Require().NoError(err) 418 s.Require().Len(response.Communities(), 1) 419 community = response.Communities()[0] 420 s.Require().True(community.Encrypted()) 421 s.Require().True(community.ChannelEncrypted(chat.CommunityChatID())) 422 423 return community, chat 424 425 } 426 427 func createCommunity(s *suite.Suite, owner *Messenger) (*communities.Community, *Chat) { 428 return createCommunityConfigurable(s, owner, protobuf.CommunityPermissions_AUTO_ACCEPT) 429 } 430 431 func createOnRequestCommunity(s *suite.Suite, owner *Messenger) (*communities.Community, *Chat) { 432 return createCommunityConfigurable(s, owner, protobuf.CommunityPermissions_MANUAL_ACCEPT) 433 } 434 435 func createCommunityConfigurable(s *suite.Suite, owner *Messenger, permission protobuf.CommunityPermissions_Access) (*communities.Community, *Chat) { 436 description := &requests.CreateCommunity{ 437 Membership: permission, 438 Name: "status", 439 Color: "#ffffff", 440 Description: "status community description", 441 } 442 443 // Create an community chat 444 response, err := owner.CreateCommunity(description, true) 445 s.Require().NoError(err) 446 s.Require().NotNil(response) 447 448 s.Require().Len(response.Communities(), 1) 449 community := response.Communities()[0] 450 s.Require().True(community.Joined()) 451 s.Require().True(community.IsControlNode()) 452 s.Require().Len(community.Chats(), 1) 453 454 s.Require().Len(response.CommunitiesSettings(), 1) 455 communitySettings := response.CommunitiesSettings()[0] 456 s.Require().Equal(communitySettings.CommunityID, community.IDString()) 457 s.Require().Equal(communitySettings.HistoryArchiveSupportEnabled, false) 458 459 return community, response.Chats()[0] 460 } 461 462 func advertiseCommunityTo(s *suite.Suite, community *communities.Community, owner *Messenger, user *Messenger) { 463 // Create wrapped (Signed) community data. 464 wrappedCommunity, err := community.ToProtocolMessageBytes() 465 s.Require().NoError(err) 466 467 // Unwrap signer (Admin) data at user side. 468 signer, description, err := communities.UnwrapCommunityDescriptionMessage(wrappedCommunity) 469 s.Require().NoError(err) 470 471 // Handle community data state at receiver side 472 messageState := user.buildMessageState() 473 messageState.CurrentMessageState = &CurrentMessageState{} 474 messageState.CurrentMessageState.PublicKey = &user.identity.PublicKey 475 err = user.handleCommunityDescription(messageState, signer, description, wrappedCommunity, nil, community.Shard().Protobuffer()) 476 s.Require().NoError(err) 477 } 478 479 func createRequestToJoinCommunity(s *suite.Suite, communityID types.HexBytes, user *Messenger, password string, addresses []string) *requests.RequestToJoinCommunity { 480 airdropAddress := "" 481 if len(addresses) > 0 { 482 airdropAddress = addresses[0] 483 } 484 485 request := &requests.RequestToJoinCommunity{ 486 CommunityID: communityID, 487 AddressesToReveal: addresses, 488 AirdropAddress: airdropAddress} 489 490 if password != "" { 491 signingParams, err := user.GenerateJoiningCommunityRequestsForSigning(common.PubkeyToHex(&user.identity.PublicKey), communityID, request.AddressesToReveal) 492 s.Require().NoError(err) 493 494 for i := range signingParams { 495 signingParams[i].Password = password 496 } 497 signatures, err := user.SignData(signingParams) 498 s.Require().NoError(err) 499 500 updateAddresses := len(request.AddressesToReveal) == 0 501 if updateAddresses { 502 request.AddressesToReveal = make([]string, len(signingParams)) 503 } 504 for i := range signingParams { 505 request.AddressesToReveal[i] = signingParams[i].Address 506 request.Signatures = append(request.Signatures, types.FromHex(signatures[i])) 507 } 508 if updateAddresses { 509 request.AirdropAddress = request.AddressesToReveal[0] 510 } 511 } 512 513 return request 514 } 515 516 func joinCommunity(s *suite.Suite, communityID types.HexBytes, controlNode *Messenger, user *Messenger, password string, addresses []string) { 517 requestToJoin := createRequestToJoinCommunity(s, communityID, user, password, addresses) 518 response, err := user.RequestToJoinCommunity(requestToJoin) 519 s.Require().NoError(err) 520 s.Require().NotNil(response) 521 s.Require().Len(response.RequestsToJoinCommunity(), 1) 522 s.Require().Len(response.ActivityCenterNotifications(), 1) 523 524 notification := response.ActivityCenterNotifications()[0] 525 s.Require().NotNil(notification) 526 s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityRequest) 527 s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusPending) 528 529 // Retrieve and accept join request 530 _, err = WaitOnMessengerResponse(controlNode, func(r *MessengerResponse) bool { 531 return len(r.Communities()) > 0 && r.Communities()[0].HasMember(&user.identity.PublicKey) 532 }, "control node did accept user request to join") 533 s.Require().NoError(err) 534 535 // Retrieve join request response 536 _, err = WaitOnMessengerResponse(user, func(r *MessengerResponse) bool { 537 return len(r.Communities()) > 0 && r.Communities()[0].HasMember(&user.identity.PublicKey) && 538 // Note: 'handleCommunityRequestToJoinResponse' does not return RequestToJoin with revealed addresses 539 checkRequestToJoinInResponse(r, user, communities.RequestToJoinStateAccepted, 0) 540 }, "user not accepted") 541 s.Require().NoError(err) 542 } 543 544 func requestToJoinCommunity(s *suite.Suite, controlNode *Messenger, user *Messenger, request *requests.RequestToJoinCommunity) types.HexBytes { 545 response, err := user.RequestToJoinCommunity(request) 546 s.Require().NoError(err) 547 s.Require().NotNil(response) 548 s.Require().Len(response.RequestsToJoinCommunity(), 1) 549 550 requestToJoin := response.RequestsToJoinCommunity()[0] 551 s.Require().Equal(requestToJoin.PublicKey, common.PubkeyToHex(&user.identity.PublicKey)) 552 553 _, err = WaitOnMessengerResponse( 554 controlNode, 555 func(r *MessengerResponse) bool { 556 return checkRequestToJoinInResponse(r, user, communities.RequestToJoinStatePending, 1) 557 }, 558 "control node did not receive community request to join", 559 ) 560 s.Require().NoError(err) 561 562 return requestToJoin.ID 563 } 564 565 func joinOnRequestCommunity(s *suite.Suite, communityID types.HexBytes, controlNode *Messenger, user *Messenger, password string, addresses []string) { 566 s.Require().NotEmpty(password) 567 s.Require().NotEmpty(addresses) 568 s.Require().NotEmpty(communityID) 569 request := createRequestToJoinCommunity(s, communityID, user, password, addresses) 570 // Request to join the community 571 requestToJoinID := requestToJoinCommunity(s, controlNode, user, request) 572 573 // accept join request 574 acceptRequestToJoin := &requests.AcceptRequestToJoinCommunity{ID: requestToJoinID} 575 response, err := controlNode.AcceptRequestToJoinCommunity(acceptRequestToJoin) 576 s.Require().NoError(err) 577 s.Require().NotNil(response) 578 579 updatedCommunity := response.Communities()[0] 580 s.Require().NotNil(updatedCommunity) 581 s.Require().True(updatedCommunity.HasMember(&user.identity.PublicKey)) 582 583 // receive request to join response 584 _, err = WaitOnMessengerResponse( 585 user, 586 func(r *MessengerResponse) bool { 587 return len(r.Communities()) > 0 && r.Communities()[0].HasMember(&user.identity.PublicKey) 588 }, 589 "user did not receive request to join response", 590 ) 591 s.Require().NoError(err) 592 593 userCommunity, err := user.GetCommunityByID(communityID) 594 s.Require().NoError(err) 595 s.Require().True(userCommunity.HasMember(&user.identity.PublicKey)) 596 597 _, err = WaitOnMessengerResponse( 598 controlNode, 599 func(r *MessengerResponse) bool { 600 return len(r.Communities()) > 0 && r.Communities()[0].HasMember(&user.identity.PublicKey) 601 }, 602 "control node did not receive request to join response", 603 ) 604 s.Require().NoError(err) 605 } 606 607 func sendChatMessage(s *suite.Suite, sender *Messenger, chatID string, text string) *common.Message { 608 msg := &common.Message{ 609 ChatMessage: &protobuf.ChatMessage{ 610 ChatId: chatID, 611 ContentType: protobuf.ChatMessage_TEXT_PLAIN, 612 Text: text, 613 }, 614 } 615 616 _, err := sender.SendChatMessage(context.Background(), msg) 617 s.Require().NoError(err) 618 619 return msg 620 } 621 622 func grantPermission(s *suite.Suite, community *communities.Community, controlNode *Messenger, target *Messenger, role protobuf.CommunityMember_Roles) { 623 responseAddRole, err := controlNode.AddRoleToMember(&requests.AddRoleToMember{ 624 CommunityID: community.ID(), 625 User: common.PubkeyToHexBytes(target.IdentityPublicKey()), 626 Role: role, 627 }) 628 s.Require().NoError(err) 629 s.Require().NoError(checkRolePermissionInResponse(responseAddRole, target.IdentityPublicKey(), role)) 630 631 response, err := WaitOnMessengerResponse(target, func(response *MessengerResponse) bool { 632 if len(response.Communities()) == 0 { 633 return false 634 } 635 636 err := checkRolePermissionInResponse(response, target.IdentityPublicKey(), role) 637 638 return err == nil 639 }, "community description changed message not received") 640 s.Require().NoError(err) 641 s.Require().NoError(checkRolePermissionInResponse(response, target.IdentityPublicKey(), role)) 642 } 643 644 func checkRolePermissionInResponse(response *MessengerResponse, member *ecdsa.PublicKey, role protobuf.CommunityMember_Roles) error { 645 if len(response.Communities()) == 0 { 646 return errors.New("Response does not contain communities") 647 } 648 rCommunities := response.Communities() 649 switch role { 650 case protobuf.CommunityMember_ROLE_OWNER: 651 if !rCommunities[0].IsMemberOwner(member) { 652 return errors.New("Member without owner role") 653 } 654 case protobuf.CommunityMember_ROLE_ADMIN: 655 if !rCommunities[0].IsMemberAdmin(member) { 656 return errors.New("Member without admin role") 657 } 658 case protobuf.CommunityMember_ROLE_TOKEN_MASTER: 659 if !rCommunities[0].IsMemberTokenMaster(member) { 660 return errors.New("Member without token master role") 661 } 662 default: 663 return errors.New("Can't check unknonw member role") 664 } 665 666 return nil 667 } 668 669 func checkMemberJoinedToTheCommunity(response *MessengerResponse, member *ecdsa.PublicKey) error { 670 if len(response.Communities()) == 0 { 671 return errors.New("No communities in the response") 672 } 673 674 if !response.Communities()[0].HasMember(member) { 675 return errors.New("Member was not added to the community") 676 } 677 678 return nil 679 } 680 681 func waitOnCommunitiesEvent(user *Messenger, condition func(*communities.Subscription) bool) <-chan error { 682 errCh := make(chan error, 1) 683 684 go func() { 685 defer close(errCh) 686 subscription := user.communitiesManager.Subscribe() 687 688 for { 689 select { 690 case sub, more := <-subscription: 691 if !more { 692 errCh <- errors.New("channel closed when waiting for communities event") 693 return 694 } 695 if condition(sub) { 696 return 697 } 698 699 case <-time.After(5 * time.Second): 700 errCh <- errors.New("timed out when waiting for communities event") 701 return 702 } 703 } 704 }() 705 706 return errCh 707 } 708 709 func makeAddressSatisfyTheCriteria(s *suite.Suite, mockedBalances communities.BalancesByChain, mockedCollectibles communities.CollectiblesByChain, 710 chainID uint64, address string, criteria *protobuf.TokenCriteria) { 711 712 walletAddress := gethcommon.HexToAddress(address) 713 contractAddress := gethcommon.HexToAddress(criteria.ContractAddresses[chainID]) 714 715 switch criteria.Type { 716 case protobuf.CommunityTokenType_ERC20: 717 balance, ok := new(big.Int).SetString(criteria.AmountInWei, 10) 718 s.Require().True(ok) 719 720 if _, exists := mockedBalances[chainID]; !exists { 721 mockedBalances[chainID] = make(map[gethcommon.Address]map[gethcommon.Address]*hexutil.Big) 722 } 723 724 if _, exists := mockedBalances[chainID][walletAddress]; !exists { 725 mockedBalances[chainID][walletAddress] = make(map[gethcommon.Address]*hexutil.Big) 726 } 727 728 mockedBalances[chainID][walletAddress][contractAddress] = (*hexutil.Big)(balance) 729 730 case protobuf.CommunityTokenType_ERC721: 731 amount, err := strconv.ParseUint(criteria.AmountInWei, 10, 32) 732 s.Require().NoError(err) 733 734 balances := []thirdparty.TokenBalance{} 735 for i := uint64(0); i < amount; i++ { 736 balances = append(balances, thirdparty.TokenBalance{ 737 TokenID: &bigint.BigInt{ 738 Int: new(big.Int).SetUint64(i + 1), 739 }, 740 Balance: &bigint.BigInt{ 741 Int: new(big.Int).SetUint64(1), 742 }, 743 }) 744 } 745 746 if _, exists := mockedCollectibles[chainID]; !exists { 747 mockedCollectibles[chainID] = make(map[gethcommon.Address]thirdparty.TokenBalancesPerContractAddress) 748 } 749 750 if _, exists := mockedCollectibles[chainID][walletAddress]; !exists { 751 mockedCollectibles[chainID][walletAddress] = make(thirdparty.TokenBalancesPerContractAddress) 752 } 753 754 mockedCollectibles[chainID][walletAddress][contractAddress] = balances 755 756 case protobuf.CommunityTokenType_ENS: 757 // not implemented 758 } 759 } 760 761 func checkRequestToJoinInResponse(r *MessengerResponse, member *Messenger, state communities.RequestToJoinState, accountsCount int) bool { 762 for _, request := range r.RequestsToJoinCommunity() { 763 if request.PublicKey == member.IdentityPublicKeyString() && 764 request.State == state && 765 accountsCount == len(request.RevealedAccounts) { 766 return true 767 } 768 } 769 return false 770 }