package protocol import ( "context" "crypto/ecdsa" "errors" "math/big" "testing" "time" "github.com/stretchr/testify/suite" "go.uber.org/zap" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/status-im/status-go/appdatabase" gethbridge "github.com/status-im/status-go/eth-node/bridge/geth" "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/multiaccounts/accounts" "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/communities" "github.com/status-im/status-go/protocol/identity" "github.com/status-im/status-go/protocol/protobuf" "github.com/status-im/status-go/protocol/requests" "github.com/status-im/status-go/protocol/sqlite" "github.com/status-im/status-go/protocol/tt" "github.com/status-im/status-go/services/wallet/bigint" walletCommon "github.com/status-im/status-go/services/wallet/common" "github.com/status-im/status-go/services/wallet/thirdparty" "github.com/status-im/status-go/t/helpers" "github.com/status-im/status-go/waku" ) type CollectiblesManagerMock struct { response map[thirdparty.CollectibleUniqueID][]thirdparty.AccountBalance } func (m *CollectiblesManagerMock) FetchBalancesByOwnerAndContractAddress(ctx context.Context, chainID walletCommon.ChainID, ownerAddress gethcommon.Address, contractAddresses []gethcommon.Address) (thirdparty.TokenBalancesPerContractAddress, error) { return nil, errors.New("FetchBalancesByOwnerAndContractAddress is not implemented for testCollectiblesManager") } func (m *CollectiblesManagerMock) GetCollectibleOwnership(requestedID thirdparty.CollectibleUniqueID) ([]thirdparty.AccountBalance, error) { // NOTE: TokenID inside of thirdparty.CollectibleUniqueID is a pointer so m.response[id] is now working for id, balances := range m.response { if id.ContractID.Address == requestedID.ContractID.Address && id.ContractID.ChainID == requestedID.ContractID.ChainID { return balances, nil } } return []thirdparty.AccountBalance{}, nil } func (m *CollectiblesManagerMock) SetResponse(id thirdparty.CollectibleUniqueID, balances []thirdparty.AccountBalance) { if m.response == nil { m.response = map[thirdparty.CollectibleUniqueID][]thirdparty.AccountBalance{} } m.response[id] = balances } func TestMessengerProfileShowcaseSuite(t *testing.T) { // nolint: deadcode,unused suite.Run(t, new(TestMessengerProfileShowcase)) } type TestMessengerProfileShowcase struct { suite.Suite m *Messenger // main instance of Messenger privateKey *ecdsa.PrivateKey // private key for the main instance of Messenger // If one wants to send messages between different instances of Messenger, // a single waku service should be shared. shh types.Waku logger *zap.Logger collectiblesMock CollectiblesManagerMock } func (s *TestMessengerProfileShowcase) SetupTest() { s.logger = tt.MustCreateTestLogger() config := waku.DefaultConfig config.MinimumAcceptedPoW = 0 shh := waku.New(&config, s.logger) s.shh = gethbridge.NewGethWakuWrapper(shh) s.Require().NoError(shh.Start()) s.m = s.newMessengerForProfileShowcase() s.privateKey = s.m.identity } func (s *TestMessengerProfileShowcase) TearDownTest() { TearDownMessenger(&s.Suite, s.m) _ = s.logger.Sync() } func (s *TestMessengerProfileShowcase) newMessengerForProfileShowcase() *Messenger { db, err := helpers.SetupTestMemorySQLDB(appdatabase.DbInitializer{}) s.NoError(err, "creating sqlite db instance") err = sqlite.Migrate(db) s.NoError(err, "protocol migrate") privateKey, err := crypto.GenerateKey() s.Require().NoError(err) s.collectiblesMock = CollectiblesManagerMock{} options := []Option{ WithCollectiblesManager(&s.collectiblesMock), } m, err := newMessengerWithKey(s.shh, privateKey, s.logger, options) s.Require().NoError(err) return m } func (s *TestMessengerProfileShowcase) mutualContact(theirMessenger *Messenger) { messageText := "hello!" contactID := types.EncodeHex(crypto.FromECDSAPub(&theirMessenger.identity.PublicKey)) request := &requests.SendContactRequest{ ID: contactID, Message: messageText, } // Send contact request _, err := s.m.SendContactRequest(context.Background(), request) s.Require().NoError(err) // Wait for the message to reach its destination _, err = WaitOnMessengerResponse( theirMessenger, func(r *MessengerResponse) bool { return len(r.Contacts) > 0 && len(r.Messages()) > 0 }, "no messages", ) s.Require().NoError(err) // Make sure it's the pending contact requests contactRequests, _, err := theirMessenger.PendingContactRequests("", 10) s.Require().NoError(err) s.Require().Len(contactRequests, 1) s.Require().Equal(contactRequests[0].ContactRequestState, common.ContactRequestStatePending) // Accept contact request, receiver side _, err = theirMessenger.AcceptContactRequest(context.Background(), &requests.AcceptContactRequest{ID: types.Hex2Bytes(contactRequests[0].ID)}) s.Require().NoError(err) // Wait for the message to reach its destination resp, err := WaitOnMessengerResponse( s.m, func(r *MessengerResponse) bool { return len(r.Contacts) == 1 && len(r.Messages()) == 2 && len(r.ActivityCenterNotifications()) == 1 }, "no messages", ) s.Require().NoError(err) // Check the contact state is correctly set s.Require().Len(resp.Contacts, 1) s.Require().True(resp.Contacts[0].mutual()) } func (s *TestMessengerProfileShowcase) verifiedContact(theirMessenger *Messenger) { theirPk := types.EncodeHex(crypto.FromECDSAPub(&theirMessenger.identity.PublicKey)) challenge := "Want to see what I'm hiding in my profile showcase?" _, err := s.m.SendContactVerificationRequest(context.Background(), theirPk, challenge) s.Require().NoError(err) // Wait for the message to reach its destination resp, err := WaitOnMessengerResponse( theirMessenger, func(r *MessengerResponse) bool { return len(r.VerificationRequests()) == 1 && len(r.ActivityCenterNotifications()) == 1 }, "no messages", ) s.Require().NoError(err) s.Require().Len(resp.VerificationRequests(), 1) verificationRequestID := resp.VerificationRequests()[0].ID _, err = theirMessenger.AcceptContactVerificationRequest(context.Background(), verificationRequestID, "For sure!") s.Require().NoError(err) s.Require().NoError(err) // Wait for the message to reach its destination _, err = WaitOnMessengerResponse( s.m, func(r *MessengerResponse) bool { return len(r.VerificationRequests()) == 1 }, "no messages", ) s.Require().NoError(err) resp, err = s.m.VerifiedTrusted(context.Background(), &requests.VerifiedTrusted{ID: types.FromHex(verificationRequestID)}) s.Require().NoError(err) s.Require().Len(resp.Messages(), 1) s.Require().Equal(common.ContactVerificationStateTrusted, resp.Messages()[0].ContactVerificationState) } func (s *TestMessengerProfileShowcase) TestSaveAndGetProfileShowcasePreferences() { request := DummyProfileShowcasePreferences(true) // Provide collectible balances test response collectible := request.Collectibles[0] collectibleID, err := toCollectibleUniqueID(collectible.ContractAddress, collectible.TokenID, collectible.ChainID) s.Require().NoError(err) balances := []thirdparty.AccountBalance{ thirdparty.AccountBalance{ Address: gethcommon.HexToAddress(request.Accounts[0].Address), Balance: &bigint.BigInt{Int: big.NewInt(5)}, TxTimestamp: 0, }, } s.collectiblesMock.SetResponse(collectibleID, balances) err = s.m.SetProfileShowcasePreferences(request, false) s.Require().NoError(err) // Restored preferences shoulf be same as stored response, err := s.m.GetProfileShowcasePreferences() s.Require().NoError(err) s.Require().Equal(len(response.Communities), len(request.Communities)) for i := 0; i < len(response.Communities); i++ { s.Require().Equal(*response.Communities[i], *request.Communities[i]) } s.Require().Equal(len(response.Accounts), len(request.Accounts)) for i := 0; i < len(response.Accounts); i++ { s.Require().Equal(*response.Accounts[i], *request.Accounts[i]) } s.Require().Equal(len(response.Collectibles), len(request.Collectibles)) for i := 0; i < len(response.Collectibles); i++ { s.Require().Equal(*response.Collectibles[i], *request.Collectibles[i]) } s.Require().Equal(len(response.VerifiedTokens), len(request.VerifiedTokens)) for i := 0; i < len(response.VerifiedTokens); i++ { s.Require().Equal(*response.VerifiedTokens[i], *request.VerifiedTokens[i]) } s.Require().Equal(len(response.UnverifiedTokens), len(request.UnverifiedTokens)) for i := 0; i < len(response.UnverifiedTokens); i++ { s.Require().Equal(*response.UnverifiedTokens[i], *request.UnverifiedTokens[i]) } s.Require().Equal(len(response.SocialLinks), len(request.SocialLinks)) for i := 0; i < len(response.SocialLinks); i++ { s.Require().Equal(*response.SocialLinks[i], *request.SocialLinks[i]) } } func (s *TestMessengerProfileShowcase) TestFailToSaveProfileShowcasePreferencesWithWrongVisibility() { accountEntry := &identity.ProfileShowcaseAccountPreference{ Address: "0x0000000000000000000000000032433445133424", ShowcaseVisibility: identity.ProfileShowcaseVisibilityIDVerifiedContacts, Order: 17, } collectibleEntry := &identity.ProfileShowcaseCollectiblePreference{ ContractAddress: "0x12378534257568678487683576", ChainID: 11155111, TokenID: "12321389592999903", ShowcaseVisibility: identity.ProfileShowcaseVisibilityContacts, Order: 17, } request := &identity.ProfileShowcasePreferences{ Accounts: []*identity.ProfileShowcaseAccountPreference{accountEntry}, Collectibles: []*identity.ProfileShowcaseCollectiblePreference{collectibleEntry}, } // Provide collectible balances test response collectible := request.Collectibles[0] collectibleID, err := toCollectibleUniqueID(collectible.ContractAddress, collectible.TokenID, collectible.ChainID) s.Require().NoError(err) balances := []thirdparty.AccountBalance{ thirdparty.AccountBalance{ Address: gethcommon.HexToAddress(request.Accounts[0].Address), Balance: &bigint.BigInt{Int: big.NewInt(5)}, TxTimestamp: 0, }, } s.collectiblesMock.SetResponse(collectibleID, balances) err = s.m.SetProfileShowcasePreferences(request, false) s.Require().Equal(errorAccountVisibilityLowerThanCollectible, err) } func (s *TestMessengerProfileShowcase) TestEncryptAndDecryptProfileShowcaseEntries() { // Add mutual contact theirMessenger := s.newMessengerForProfileShowcase() defer TearDownMessenger(&s.Suite, theirMessenger) s.mutualContact(theirMessenger) entries := &protobuf.ProfileShowcaseEntries{ Communities: []*protobuf.ProfileShowcaseCommunity{ &protobuf.ProfileShowcaseCommunity{ CommunityId: "0x01312357798976535235432345", Order: 12, }, &protobuf.ProfileShowcaseCommunity{ CommunityId: "0x12378534257568678487683576", Order: 11, }, }, Accounts: []*protobuf.ProfileShowcaseAccount{ &protobuf.ProfileShowcaseAccount{ Address: "0x00000323245", Name: "Default", ColorId: "red", Emoji: "(=^ ◡ ^=)", Order: 1, }, }, Collectibles: []*protobuf.ProfileShowcaseCollectible{ &protobuf.ProfileShowcaseCollectible{ ContractAddress: "0x12378534257568678487683576", ChainId: 1, TokenId: "12321389592999903", AccountAddress: "0x32433445133424", CommunityId: "0x12378534257568678487683576", Order: 0, }, }, VerifiedTokens: []*protobuf.ProfileShowcaseVerifiedToken{ &protobuf.ProfileShowcaseVerifiedToken{ Symbol: "ETH", Order: 1, }, &protobuf.ProfileShowcaseVerifiedToken{ Symbol: "DAI", Order: 2, }, &protobuf.ProfileShowcaseVerifiedToken{ Symbol: "SNT", Order: 3, }, }, UnverifiedTokens: []*protobuf.ProfileShowcaseUnverifiedToken{ &protobuf.ProfileShowcaseUnverifiedToken{ ContractAddress: "0x454525452023452", ChainId: 11155111, Order: 0, }, &protobuf.ProfileShowcaseUnverifiedToken{ ContractAddress: "0x12312323323233", ChainId: 1, Order: 1, }, }, SocialLinks: []*protobuf.ProfileShowcaseSocialLink{ &protobuf.ProfileShowcaseSocialLink{ Text: identity.TwitterID, Url: "https://twitter.com/ethstatus", Order: 1, }, &protobuf.ProfileShowcaseSocialLink{ Text: identity.TwitterID, Url: "https://twitter.com/StatusIMBlog", Order: 2, }, &protobuf.ProfileShowcaseSocialLink{ Text: identity.GithubID, Url: "https://github.com/status-im", Order: 3, }, }, } data, err := s.m.EncryptProfileShowcaseEntriesWithContactPubKeys(entries, s.m.Contacts()) s.Require().NoError(err) entriesBack, err := theirMessenger.DecryptProfileShowcaseEntriesWithPubKey(&s.m.identity.PublicKey, data) s.Require().NoError(err) s.Require().Equal(len(entries.Communities), len(entriesBack.Communities)) for i := 0; i < len(entriesBack.Communities); i++ { s.Require().Equal(entries.Communities[i].CommunityId, entriesBack.Communities[i].CommunityId) s.Require().Equal(entries.Communities[i].Order, entriesBack.Communities[i].Order) } s.Require().Equal(len(entries.Accounts), len(entriesBack.Accounts)) for i := 0; i < len(entriesBack.Accounts); i++ { s.Require().Equal(entries.Accounts[i].Address, entriesBack.Accounts[i].Address) s.Require().Equal(entries.Accounts[i].Name, entriesBack.Accounts[i].Name) s.Require().Equal(entries.Accounts[i].ColorId, entriesBack.Accounts[i].ColorId) s.Require().Equal(entries.Accounts[i].Emoji, entriesBack.Accounts[i].Emoji) s.Require().Equal(entries.Accounts[i].Order, entriesBack.Accounts[i].Order) } s.Require().Equal(len(entries.Collectibles), len(entriesBack.Collectibles)) for i := 0; i < len(entriesBack.Collectibles); i++ { s.Require().Equal(entries.Collectibles[i].TokenId, entriesBack.Collectibles[i].TokenId) s.Require().Equal(entries.Collectibles[i].ChainId, entriesBack.Collectibles[i].ChainId) s.Require().Equal(entries.Collectibles[i].ContractAddress, entriesBack.Collectibles[i].ContractAddress) s.Require().Equal(entries.Collectibles[i].AccountAddress, entriesBack.Collectibles[i].AccountAddress) s.Require().Equal(entries.Collectibles[i].CommunityId, entriesBack.Collectibles[i].CommunityId) s.Require().Equal(entries.Collectibles[i].Order, entriesBack.Collectibles[i].Order) } s.Require().Equal(len(entries.VerifiedTokens), len(entriesBack.VerifiedTokens)) for i := 0; i < len(entriesBack.VerifiedTokens); i++ { s.Require().Equal(entries.VerifiedTokens[i].Symbol, entriesBack.VerifiedTokens[i].Symbol) s.Require().Equal(entries.VerifiedTokens[i].Order, entriesBack.VerifiedTokens[i].Order) } s.Require().Equal(len(entries.UnverifiedTokens), len(entriesBack.UnverifiedTokens)) for i := 0; i < len(entriesBack.UnverifiedTokens); i++ { s.Require().Equal(entries.UnverifiedTokens[i].ContractAddress, entriesBack.UnverifiedTokens[i].ContractAddress) s.Require().Equal(entries.UnverifiedTokens[i].ChainId, entriesBack.UnverifiedTokens[i].ChainId) s.Require().Equal(entries.UnverifiedTokens[i].Order, entriesBack.UnverifiedTokens[i].Order) } s.Require().Equal(len(entries.SocialLinks), len(entriesBack.SocialLinks)) for i := 0; i < len(entriesBack.SocialLinks); i++ { s.Require().Equal(entries.SocialLinks[i].Text, entriesBack.SocialLinks[i].Text) s.Require().Equal(entries.SocialLinks[i].Url, entriesBack.SocialLinks[i].Url) s.Require().Equal(entries.SocialLinks[i].Order, entriesBack.SocialLinks[i].Order) } } func (s *TestMessengerProfileShowcase) TestShareShowcasePreferences() { // Set Display name to pass shouldPublishChatIdentity check profileKp := accounts.GetProfileKeypairForTest(true, false, false) profileKp.KeyUID = s.m.account.KeyUID profileKp.Accounts[0].KeyUID = s.m.account.KeyUID err := s.m.settings.SaveOrUpdateKeypair(profileKp) s.Require().NoError(err) err = s.m.SetDisplayName("bobby") s.Require().NoError(err) // Save preferences to dispatch changes request := DummyProfileShowcasePreferences(true) // Save wallet accounts to pass the validation acc1 := &accounts.Account{ Address: types.HexToAddress(request.Accounts[0].Address), Type: accounts.AccountTypeGenerated, Name: "Test Account 1", ColorID: "", Emoji: "emoji", } acc2 := &accounts.Account{ Address: types.HexToAddress(request.Accounts[1].Address), Type: accounts.AccountTypeSeed, Name: "Test Account 2", ColorID: "", Emoji: "emoji", } err = s.m.settings.SaveOrUpdateAccounts([]*accounts.Account{acc1, acc2}, true) s.Require().NoError(err) // Provide collectible balances test response collectible := request.Collectibles[0] collectibleID, err := toCollectibleUniqueID(collectible.ContractAddress, collectible.TokenID, collectible.ChainID) s.Require().NoError(err) balances := []thirdparty.AccountBalance{ thirdparty.AccountBalance{ Address: gethcommon.HexToAddress(request.Accounts[0].Address), Balance: &bigint.BigInt{Int: big.NewInt(1)}, TxTimestamp: 32443424, }, } s.collectiblesMock.SetResponse(collectibleID, balances) err = s.m.SetProfileShowcasePreferences(request, false) s.Require().NoError(err) profileShowcasePreferences, err := s.m.GetProfileShowcasePreferences() s.Require().NoError(err) // Make sure the count for entries is correct s.Require().Len(profileShowcasePreferences.Communities, 2) s.Require().Len(profileShowcasePreferences.Accounts, 2) s.Require().Len(profileShowcasePreferences.Collectibles, 1) s.Require().Len(profileShowcasePreferences.VerifiedTokens, 3) s.Require().Len(profileShowcasePreferences.UnverifiedTokens, 2) // Add mutual contact mutualContact := s.newMessengerForProfileShowcase() defer TearDownMessenger(&s.Suite, mutualContact) s.mutualContact(mutualContact) // Add identity verified contact verifiedContact := s.newMessengerForProfileShowcase() defer TearDownMessenger(&s.Suite, verifiedContact) s.mutualContact(verifiedContact) s.verifiedContact(verifiedContact) contactID := types.EncodeHex(crypto.FromECDSAPub(&s.m.identity.PublicKey)) // Get summarised profile data for mutual contact _, err = WaitOnMessengerResponse( mutualContact, func(r *MessengerResponse) bool { return r.updatedProfileShowcaseContactIDs[contactID] == true }, "no messages", ) s.Require().NoError(err) profileShowcase, err := mutualContact.GetProfileShowcaseForContact(contactID, false) s.Require().NoError(err) s.Require().Len(profileShowcase.Communities, 2) s.Require().Equal(profileShowcase.Communities[0].CommunityID, request.Communities[0].CommunityID) s.Require().Equal(profileShowcase.Communities[0].Order, request.Communities[0].Order) s.Require().Equal(profileShowcase.Communities[1].CommunityID, request.Communities[1].CommunityID) s.Require().Equal(profileShowcase.Communities[1].Order, request.Communities[1].Order) s.Require().Len(profileShowcase.Accounts, 2) s.Require().Equal(profileShowcase.Accounts[0].Address, request.Accounts[0].Address) s.Require().Equal(profileShowcase.Accounts[0].Order, request.Accounts[0].Order) s.Require().Equal(profileShowcase.Accounts[1].Address, request.Accounts[1].Address) s.Require().Equal(profileShowcase.Accounts[1].Order, request.Accounts[1].Order) s.Require().Len(profileShowcase.Collectibles, 1) s.Require().Equal(profileShowcase.Collectibles[0].TokenID, request.Collectibles[0].TokenID) s.Require().Equal(profileShowcase.Collectibles[0].ChainID, request.Collectibles[0].ChainID) s.Require().Equal(profileShowcase.Collectibles[0].ContractAddress, request.Collectibles[0].ContractAddress) s.Require().Equal(profileShowcase.Collectibles[0].Order, request.Collectibles[0].Order) s.Require().Len(profileShowcase.VerifiedTokens, 1) s.Require().Equal(profileShowcase.VerifiedTokens[0].Symbol, request.VerifiedTokens[0].Symbol) s.Require().Equal(profileShowcase.VerifiedTokens[0].Order, request.VerifiedTokens[0].Order) s.Require().Len(profileShowcase.UnverifiedTokens, 2) s.Require().Equal(profileShowcase.UnverifiedTokens[0].ContractAddress, request.UnverifiedTokens[0].ContractAddress) s.Require().Equal(profileShowcase.UnverifiedTokens[0].ChainID, request.UnverifiedTokens[0].ChainID) s.Require().Equal(profileShowcase.UnverifiedTokens[0].Order, request.UnverifiedTokens[0].Order) s.Require().Equal(profileShowcase.UnverifiedTokens[1].ContractAddress, request.UnverifiedTokens[1].ContractAddress) s.Require().Equal(profileShowcase.UnverifiedTokens[1].ChainID, request.UnverifiedTokens[1].ChainID) s.Require().Equal(profileShowcase.UnverifiedTokens[1].Order, request.UnverifiedTokens[1].Order) s.Require().Len(profileShowcase.SocialLinks, 2) s.Require().Equal(profileShowcase.SocialLinks[0].Text, request.SocialLinks[0].Text) s.Require().Equal(profileShowcase.SocialLinks[0].URL, request.SocialLinks[0].URL) s.Require().Equal(profileShowcase.SocialLinks[0].Order, request.SocialLinks[0].Order) s.Require().Equal(profileShowcase.SocialLinks[1].Text, request.SocialLinks[2].Text) s.Require().Equal(profileShowcase.SocialLinks[1].URL, request.SocialLinks[2].URL) s.Require().Equal(profileShowcase.SocialLinks[1].Order, request.SocialLinks[2].Order) // Get summarised profile data for verified contact _, err = WaitOnMessengerResponse( verifiedContact, func(r *MessengerResponse) bool { return r.updatedProfileShowcaseContactIDs[contactID] == true }, "no messages", ) s.Require().NoError(err) profileShowcase, err = verifiedContact.GetProfileShowcaseForContact(contactID, false) s.Require().NoError(err) s.Require().Len(profileShowcase.Accounts, 2) s.Require().Equal(profileShowcase.Accounts[0].Address, request.Accounts[0].Address) s.Require().Equal(profileShowcase.Accounts[0].Order, request.Accounts[0].Order) s.Require().Equal(profileShowcase.Accounts[1].Address, request.Accounts[1].Address) s.Require().Equal(profileShowcase.Accounts[1].Order, request.Accounts[1].Order) s.Require().Len(profileShowcase.Collectibles, 1) s.Require().Equal(profileShowcase.Collectibles[0].ContractAddress, request.Collectibles[0].ContractAddress) s.Require().Equal(profileShowcase.Collectibles[0].ChainID, request.Collectibles[0].ChainID) s.Require().Equal(profileShowcase.Collectibles[0].TokenID, request.Collectibles[0].TokenID) s.Require().Equal(profileShowcase.Collectibles[0].Order, request.Collectibles[0].Order) s.Require().Len(profileShowcase.VerifiedTokens, 2) s.Require().Equal(profileShowcase.VerifiedTokens[0].Symbol, request.VerifiedTokens[0].Symbol) s.Require().Equal(profileShowcase.VerifiedTokens[0].Order, request.VerifiedTokens[0].Order) s.Require().Equal(profileShowcase.VerifiedTokens[1].Symbol, request.VerifiedTokens[1].Symbol) s.Require().Equal(profileShowcase.VerifiedTokens[1].Order, request.VerifiedTokens[1].Order) s.Require().Len(profileShowcase.UnverifiedTokens, 2) s.Require().Equal(profileShowcase.UnverifiedTokens[0].ContractAddress, request.UnverifiedTokens[0].ContractAddress) s.Require().Equal(profileShowcase.UnverifiedTokens[0].ChainID, request.UnverifiedTokens[0].ChainID) s.Require().Equal(profileShowcase.UnverifiedTokens[0].Order, request.UnverifiedTokens[0].Order) s.Require().Equal(profileShowcase.UnverifiedTokens[1].ContractAddress, request.UnverifiedTokens[1].ContractAddress) s.Require().Equal(profileShowcase.UnverifiedTokens[1].ChainID, request.UnverifiedTokens[1].ChainID) s.Require().Equal(profileShowcase.UnverifiedTokens[1].Order, request.UnverifiedTokens[1].Order) s.Require().Len(profileShowcase.SocialLinks, 3) s.Require().Equal(profileShowcase.SocialLinks[0].Text, request.SocialLinks[0].Text) s.Require().Equal(profileShowcase.SocialLinks[0].URL, request.SocialLinks[0].URL) s.Require().Equal(profileShowcase.SocialLinks[0].Order, request.SocialLinks[0].Order) s.Require().Equal(profileShowcase.SocialLinks[1].Text, request.SocialLinks[1].Text) s.Require().Equal(profileShowcase.SocialLinks[1].URL, request.SocialLinks[1].URL) s.Require().Equal(profileShowcase.SocialLinks[1].Order, request.SocialLinks[1].Order) s.Require().Equal(profileShowcase.SocialLinks[2].Text, request.SocialLinks[2].Text) s.Require().Equal(profileShowcase.SocialLinks[2].URL, request.SocialLinks[2].URL) s.Require().Equal(profileShowcase.SocialLinks[2].Order, request.SocialLinks[2].Order) } func (s *TestMessengerProfileShowcase) TestProfileShowcaseProofOfMembershipUnencryptedCommunities() { alice := s.m // Set Display name to pass shouldPublishChatIdentity check profileKp := accounts.GetProfileKeypairForTest(true, false, false) profileKp.KeyUID = alice.account.KeyUID profileKp.Accounts[0].KeyUID = alice.account.KeyUID err := alice.settings.SaveOrUpdateKeypair(profileKp) s.Require().NoError(err) err = alice.SetDisplayName("Alice") s.Require().NoError(err) // Add bob as a mutual contact bob := s.newMessengerForProfileShowcase() defer TearDownMessenger(&s.Suite, bob) s.mutualContact(bob) // Alice creates a community aliceCommunity, _ := createCommunityConfigurable(&s.Suite, alice, protobuf.CommunityPermissions_MANUAL_ACCEPT) advertiseCommunityTo(&s.Suite, aliceCommunity, alice, bob) // Bobs creates an another community bobCommunity, _ := createCommunityConfigurable(&s.Suite, bob, protobuf.CommunityPermissions_AUTO_ACCEPT) // Add community to the Alice's profile showcase & get it on the Bob's side err = alice.SetProfileShowcasePreferences(&identity.ProfileShowcasePreferences{ Communities: []*identity.ProfileShowcaseCommunityPreference{ &identity.ProfileShowcaseCommunityPreference{ CommunityID: aliceCommunity.IDString(), ShowcaseVisibility: identity.ProfileShowcaseVisibilityContacts, Order: 0, }, &identity.ProfileShowcaseCommunityPreference{ CommunityID: bobCommunity.IDString(), ShowcaseVisibility: identity.ProfileShowcaseVisibilityEveryone, Order: 2, }, }, }, false) s.Require().NoError(err) contactID := types.EncodeHex(crypto.FromECDSAPub(&alice.identity.PublicKey)) _, err = WaitOnMessengerResponse( bob, func(r *MessengerResponse) bool { return r.updatedProfileShowcaseContactIDs[contactID] == true }, "no messages", ) s.Require().NoError(err) profileShowcase, err := bob.GetProfileShowcaseForContact(contactID, true) s.Require().NoError(err) // Verify community's data s.Require().Len(profileShowcase.Communities, 2) s.Require().Equal(profileShowcase.Communities[0].CommunityID, aliceCommunity.IDString()) s.Require().Equal(profileShowcase.Communities[0].MembershipStatus, identity.ProfileShowcaseMembershipStatusProvenMember) s.Require().Equal(profileShowcase.Communities[1].CommunityID, bobCommunity.IDString()) s.Require().Equal(profileShowcase.Communities[1].MembershipStatus, identity.ProfileShowcaseMembershipStatusNotAMember) } func (s *TestMessengerProfileShowcase) TestProfileShowcaseProofOfMembershipEncryptedCommunity() { alice := s.m // Set Display name to pass shouldPublishChatIdentity check profileKp := accounts.GetProfileKeypairForTest(true, false, false) profileKp.KeyUID = alice.account.KeyUID profileKp.Accounts[0].KeyUID = alice.account.KeyUID err := alice.settings.SaveOrUpdateKeypair(profileKp) s.Require().NoError(err) err = alice.SetDisplayName("Alice") s.Require().NoError(err) // Add bob as a mutual contact bob := s.newMessengerForProfileShowcase() defer TearDownMessenger(&s.Suite, bob) s.mutualContact(bob) // Alice creates an ecrypted community aliceCommunity, _ := createEncryptedCommunity(&s.Suite, alice) s.Require().True(aliceCommunity.Encrypted()) advertiseCommunityTo(&s.Suite, aliceCommunity, alice, bob) // Bob creates an another encryped community bobCommunity, _ := createEncryptedCommunity(&s.Suite, bob) s.Require().True(bobCommunity.Encrypted()) advertiseCommunityTo(&s.Suite, bobCommunity, bob, alice) // Add community to the Alice's profile showcase & get it on the Bob's side err = alice.SetProfileShowcasePreferences(&identity.ProfileShowcasePreferences{ Communities: []*identity.ProfileShowcaseCommunityPreference{ &identity.ProfileShowcaseCommunityPreference{ CommunityID: aliceCommunity.IDString(), ShowcaseVisibility: identity.ProfileShowcaseVisibilityContacts, Order: 0, }, &identity.ProfileShowcaseCommunityPreference{ CommunityID: bobCommunity.IDString(), ShowcaseVisibility: identity.ProfileShowcaseVisibilityEveryone, Order: 1, }, }, }, false) s.Require().NoError(err) contactID := types.EncodeHex(crypto.FromECDSAPub(&alice.identity.PublicKey)) _, err = WaitOnMessengerResponse( bob, func(r *MessengerResponse) bool { return r.updatedProfileShowcaseContactIDs[contactID] == true }, "no messages", ) s.Require().NoError(err) profileShowcase, err := bob.GetProfileShowcaseForContact(contactID, true) s.Require().NoError(err) // Verify community's data s.Require().Len(profileShowcase.Communities, 2) s.Require().Equal(profileShowcase.Communities[0].CommunityID, aliceCommunity.IDString()) s.Require().Equal(profileShowcase.Communities[0].MembershipStatus, identity.ProfileShowcaseMembershipStatusProvenMember) s.Require().Equal(profileShowcase.Communities[1].CommunityID, bobCommunity.IDString()) s.Require().Equal(profileShowcase.Communities[1].MembershipStatus, identity.ProfileShowcaseMembershipStatusUnproven) } // The scenario tested is as follow: // 1) Alice creates an encrypted community // 2) Bob add Alice become a mutual contacts // 4) Alice presents the community in her profile showcase // 5) Bob gets the community from Alice's profile showcase and tries to validate community's membership with expired grant func (s *TestMessengerProfileShowcase) TestProfileShowcaseCommuniesGrantExpires() { grantInvokesProfileDispatchIntervalBackup := grantInvokesProfileDispatchInterval grantInvokesProfileDispatchInterval = 1 * time.Millisecond communitiesGrantExpirationTimeBackup := communities.GrantExpirationTime communities.GrantExpirationTime = 1 * time.Millisecond alice := s.m // Set Display name to pass shouldPublishChatIdentity check profileKp := accounts.GetProfileKeypairForTest(true, false, false) profileKp.KeyUID = alice.account.KeyUID profileKp.Accounts[0].KeyUID = alice.account.KeyUID err := alice.settings.SaveOrUpdateKeypair(profileKp) s.Require().NoError(err) err = alice.SetDisplayName("Alice") s.Require().NoError(err) // 1) Alice creates an encrypted community alice.communitiesManager.PermissionChecker = &testPermissionChecker{} community, _ := createEncryptedCommunity(&s.Suite, alice) s.Require().True(community.Encrypted()) // 2) Bob add Alice become a mutual contacts bob := s.newMessengerForProfileShowcase() defer TearDownMessenger(&s.Suite, bob) s.mutualContact(bob) advertiseCommunityTo(&s.Suite, community, alice, bob) // 3) Alice presents the community in her profile showcase err = alice.SetProfileShowcasePreferences(&identity.ProfileShowcasePreferences{ Communities: []*identity.ProfileShowcaseCommunityPreference{ &identity.ProfileShowcaseCommunityPreference{ CommunityID: community.IDString(), ShowcaseVisibility: identity.ProfileShowcaseVisibilityEveryone, Order: 0, }, }, }, false) s.Require().NoError(err) // 5) Bob gets the community from Alice's profile showcase and tries to validate community's membership with expired grant contactID := types.EncodeHex(crypto.FromECDSAPub(&alice.identity.PublicKey)) _, err = WaitOnMessengerResponse( bob, func(r *MessengerResponse) bool { return r.updatedProfileShowcaseContactIDs[contactID] == true }, "no messages", ) s.Require().NoError(err) profileShowcase, err := bob.GetProfileShowcaseForContact(contactID, true) s.Require().NoError(err) s.Require().Len(profileShowcase.Communities, 1) s.Require().Equal(community.IDString(), profileShowcase.Communities[0].CommunityID) s.Require().Equal(identity.ProfileShowcaseMembershipStatusUnproven, profileShowcase.Communities[0].MembershipStatus) // Return values back because they can affect other tests grantInvokesProfileDispatchInterval = grantInvokesProfileDispatchIntervalBackup communities.GrantExpirationTime = communitiesGrantExpirationTimeBackup } // The scenario tested is as follow: // 1) Owner creates an encrypted community // 2) Bob add Alice become a mutual contacts // 3) Alice joins the community // 4) Alice presents the community in her profile showcase // 5) Bob gets the community from Alice's profile showcase and validates community's membership with grant // 6) Owner updates the grant // 7) Bob should be able to validate the membership again func (s *TestMessengerProfileShowcase) TestProfileShowcaseCommuniesDispatchOnGrantUpdate() { grantInvokesProfileDispatchIntervalBackup := grantInvokesProfileDispatchInterval grantInvokesProfileDispatchInterval = 1 * time.Millisecond alice := s.m // Set Display name to pass shouldPublishChatIdentity check profileKp := accounts.GetProfileKeypairForTest(true, false, false) profileKp.KeyUID = alice.account.KeyUID profileKp.Accounts[0].KeyUID = alice.account.KeyUID err := alice.settings.SaveOrUpdateKeypair(profileKp) s.Require().NoError(err) err = alice.SetDisplayName("Alice") s.Require().NoError(err) // 1) Owner creates an encrypted community owner := s.newMessengerForProfileShowcase() defer TearDownMessenger(&s.Suite, owner) owner.communitiesManager.PermissionChecker = &testPermissionChecker{} community, _ := createEncryptedCommunity(&s.Suite, owner) s.Require().True(community.Encrypted()) // 2) Bob add Alice become a mutual contacts bob := s.newMessengerForProfileShowcase() defer TearDownMessenger(&s.Suite, bob) s.mutualContact(bob) // 3) Alice joins the community advertiseCommunityTo(&s.Suite, community, owner, alice) advertiseCommunityTo(&s.Suite, community, owner, bob) request := &requests.RequestToJoinCommunity{CommunityID: community.ID()} joinCommunity(&s.Suite, community, owner, alice, request, "") joinedCommunities, err := alice.communitiesManager.Joined() s.Require().NoError(err) s.Require().Len(joinedCommunities, 1) s.Require().Equal(joinedCommunities[0].IDString(), community.IDString()) s.Require().True(joinedCommunities[0].Encrypted()) grant, clock, err := alice.communitiesManager.GetCommunityGrant(community.IDString()) s.Require().NoError(err) s.Require().NotEqual(grant, []byte{}) s.Require().True(clock > 0) // 4) Alice presents the community in her profile showcase err = alice.SetProfileShowcasePreferences(&identity.ProfileShowcasePreferences{ Communities: []*identity.ProfileShowcaseCommunityPreference{ &identity.ProfileShowcaseCommunityPreference{ CommunityID: community.IDString(), ShowcaseVisibility: identity.ProfileShowcaseVisibilityEveryone, Order: 0, }, }, }, false) s.Require().NoError(err) // 5) Bob gets the community from Alice's profile showcase and validates community's membership with grant contactID := types.EncodeHex(crypto.FromECDSAPub(&alice.identity.PublicKey)) _, err = WaitOnMessengerResponse( bob, func(r *MessengerResponse) bool { return r.updatedProfileShowcaseContactIDs[contactID] == true }, "no messages", ) s.Require().NoError(err) profileShowcase, err := bob.GetProfileShowcaseForContact(contactID, true) s.Require().NoError(err) s.Require().Len(profileShowcase.Communities, 1) s.Require().Equal(community.IDString(), profileShowcase.Communities[0].CommunityID) s.Require().Equal(identity.ProfileShowcaseMembershipStatusProvenMember, profileShowcase.Communities[0].MembershipStatus) // 6) Owner updates the grant owner.updateGrantsForControlledCommunities() // Retrieve for grant clock update err = tt.RetryWithBackOff(func() error { _, err = alice.RetrieveAll() if err != nil { return err } _, updatedClock, err := alice.communitiesManager.GetCommunityGrant(community.IDString()) if err != nil { return err } if clock == updatedClock { return errors.New("can't recive an updated grant") } return nil }) s.Require().NoError(err) // 7) Bob should be able to validate the membership again _, err = WaitOnMessengerResponse( bob, func(r *MessengerResponse) bool { return r.updatedProfileShowcaseContactIDs[contactID] == true }, "no messages", ) s.Require().NoError(err) profileShowcase, err = bob.GetProfileShowcaseForContact(contactID, true) s.Require().NoError(err) s.Require().Len(profileShowcase.Communities, 1) s.Require().Equal(profileShowcase.Communities[0].CommunityID, community.IDString()) s.Require().Equal(profileShowcase.Communities[0].MembershipStatus, identity.ProfileShowcaseMembershipStatusProvenMember) // Return values back because they can affect other tests grantInvokesProfileDispatchInterval = grantInvokesProfileDispatchIntervalBackup }