package protocol import ( "context" "encoding/json" "fmt" "reflect" "testing" "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/event" v1protocol "github.com/status-im/status-go/protocol/v1" "github.com/status-im/status-go/protocol/wakusync" "github.com/status-im/status-go/services/accounts/accountsevent" "github.com/stretchr/testify/suite" "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/images" "github.com/status-im/status-go/multiaccounts/accounts" "github.com/status-im/status-go/multiaccounts/settings" "github.com/status-im/status-go/protocol/protobuf" "github.com/status-im/status-go/protocol/requests" ) func TestMessengerBackupSuite(t *testing.T) { suite.Run(t, new(MessengerBackupSuite)) } type MessengerBackupSuite struct { MessengerBaseTestSuite } func (s *MessengerBackupSuite) TestBackupContacts() { bob1 := s.m // Create bob2 bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, nil) s.Require().NoError(err) defer TearDownMessenger(&s.Suite, bob2) // Create 2 contacts contact1Key, err := crypto.GenerateKey() s.Require().NoError(err) contactID1 := types.EncodeHex(crypto.FromECDSAPub(&contact1Key.PublicKey)) _, err = bob1.AddContact(context.Background(), &requests.AddContact{ID: contactID1}) s.Require().NoError(err) contact2Key, err := crypto.GenerateKey() s.Require().NoError(err) contactID2 := types.EncodeHex(crypto.FromECDSAPub(&contact2Key.PublicKey)) _, err = bob1.AddContact(context.Background(), &requests.AddContact{ID: contactID2}) s.Require().NoError(err) s.Require().Len(bob1.Contacts(), 2) actualContacts := bob1.Contacts() if actualContacts[0].ID == contactID1 { s.Require().Equal(actualContacts[0].ID, contactID1) s.Require().Equal(actualContacts[1].ID, contactID2) } else { s.Require().Equal(actualContacts[0].ID, contactID2) s.Require().Equal(actualContacts[1].ID, contactID1) } s.Require().Equal(ContactRequestStateSent, actualContacts[0].ContactRequestLocalState) s.Require().Equal(ContactRequestStateSent, actualContacts[1].ContactRequestLocalState) s.Require().True(actualContacts[0].added()) s.Require().True(actualContacts[1].added()) // Backup clock, err := bob1.BackupData(context.Background()) s.Require().NoError(err) // Safety check s.Require().Len(bob2.Contacts(), 0) // Wait for the message to reach its destination _, err = WaitOnMessengerResponse( bob2, func(r *MessengerResponse) bool { return r.BackupHandled }, "no messages", ) s.Require().NoError(err) s.Require().Len(bob2.AddedContacts(), 2) actualContacts = bob2.AddedContacts() if actualContacts[0].ID == contactID1 { s.Require().Equal(actualContacts[0].ID, contactID1) s.Require().Equal(actualContacts[1].ID, contactID2) } else { s.Require().Equal(actualContacts[0].ID, contactID2) s.Require().Equal(actualContacts[1].ID, contactID1) } s.Require().Equal(ContactRequestStateSent, actualContacts[0].ContactRequestLocalState) s.Require().Equal(ContactRequestStateSent, actualContacts[1].ContactRequestLocalState) lastBackup, err := bob1.lastBackup() s.Require().NoError(err) s.Require().NotEmpty(lastBackup) s.Require().Equal(clock, lastBackup) } func (s *MessengerBackupSuite) TestBackupProfile() { const bob1DisplayName = "bobby" // Create bob1 bob1 := s.m bobProfileKp := accounts.GetProfileKeypairForTest(true, false, false) bobProfileKp.KeyUID = bob1.account.KeyUID bobProfileKp.Accounts[0].KeyUID = bob1.account.KeyUID err := bob1.settings.SaveOrUpdateKeypair(bobProfileKp) s.Require().NoError(err) err = bob1.SetDisplayName(bob1DisplayName) s.Require().NoError(err) bob1KeyUID := bob1.account.KeyUID imagesExpected := fmt.Sprintf(`[{"keyUid":"%s","type":"large","uri":"data:image/png;base64,iVBORw0KGgoAAAANSUg=","width":240,"height":300,"fileSize":1024,"resizeTarget":240,"clock":0},{"keyUid":"%s","type":"thumbnail","uri":"data:image/jpeg;base64,/9j/2wCEAFA3PEY8MlA=","width":80,"height":80,"fileSize":256,"resizeTarget":80,"clock":0}]`, bob1KeyUID, bob1KeyUID) iis := images.SampleIdentityImages() s.Require().NoError(bob1.multiAccounts.StoreIdentityImages(bob1KeyUID, iis, false)) bob1EnsUsernameDetail, err := bob1.saveEnsUsernameDetailProto(&protobuf.SyncEnsUsernameDetail{ Clock: 1, Username: "bob1.eth", ChainId: 1, Removed: false, }) s.Require().NoError(err) profileShowcasePreferences := DummyProfileShowcasePreferences(false) err = bob1.SetProfileShowcasePreferences(profileShowcasePreferences, false) s.Require().NoError(err) // Create bob2 bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, nil) s.Require().NoError(err) defer TearDownMessenger(&s.Suite, bob2) // Check bob1 storedBob1DisplayName, err := bob1.settings.DisplayName() s.Require().NoError(err) s.Require().Equal(bob1DisplayName, storedBob1DisplayName) bob1Images, err := bob1.multiAccounts.GetIdentityImages(bob1KeyUID) s.Require().NoError(err) jBob1Images, err := json.Marshal(bob1Images) s.Require().NoError(err) s.Require().Equal(imagesExpected, string(jBob1Images)) bob1EnsUsernameDetails, err := bob1.getEnsUsernameDetails() s.Require().NoError(err) s.Require().Equal(1, len(bob1EnsUsernameDetails)) bob1ProfileShowcasePreferences, err := bob1.GetProfileShowcasePreferences() s.Require().NoError(err) s.Require().NotNil(bob1ProfileShowcasePreferences) s.Require().Greater(bob1ProfileShowcasePreferences.Clock, uint64(0)) profileShowcasePreferences.Clock = bob1ProfileShowcasePreferences.Clock // override clock for simpler comparison s.Require().True(reflect.DeepEqual(profileShowcasePreferences, bob1ProfileShowcasePreferences)) // Check bob2 storedBob2DisplayName, err := bob2.settings.DisplayName() s.Require().NoError(err) s.Require().Equal(DefaultProfileDisplayName, storedBob2DisplayName) var expectedEmpty []*images.IdentityImage bob2Images, err := bob2.multiAccounts.GetIdentityImages(bob1KeyUID) s.Require().NoError(err) s.Require().Equal(expectedEmpty, bob2Images) bob2EnsUsernameDetails, err := bob2.getEnsUsernameDetails() s.Require().NoError(err) s.Require().Equal(0, len(bob2EnsUsernameDetails)) bob2ProfileShowcasePreferences, err := bob2.GetProfileShowcasePreferences() s.Require().NoError(err) s.Require().NotNil(bob2ProfileShowcasePreferences) s.Require().Equal(uint64(0), bob2ProfileShowcasePreferences.Clock) s.Require().Len(bob2ProfileShowcasePreferences.Communities, 0) s.Require().Len(bob2ProfileShowcasePreferences.Accounts, 0) s.Require().Len(bob2ProfileShowcasePreferences.Collectibles, 0) s.Require().Len(bob2ProfileShowcasePreferences.VerifiedTokens, 0) s.Require().Len(bob2ProfileShowcasePreferences.UnverifiedTokens, 0) s.Require().Len(bob2ProfileShowcasePreferences.SocialLinks, 0) // Backup clock, err := bob1.BackupData(context.Background()) s.Require().NoError(err) // Wait for the message to reach its destination _, err = WaitOnMessengerResponse( bob2, func(r *MessengerResponse) bool { return r.BackupHandled }, "no messages", ) s.Require().NoError(err) // Check bob2 storedBob2DisplayName, err = bob2.settings.DisplayName() s.Require().NoError(err) s.Require().Equal(bob1DisplayName, storedBob2DisplayName) bob2Images, err = bob2.multiAccounts.GetIdentityImages(bob1KeyUID) s.Require().NoError(err) s.Require().Equal(2, len(bob2Images)) s.Require().Equal(bob2Images[0].Payload, bob1Images[0].Payload) s.Require().Equal(bob2Images[1].Payload, bob1Images[1].Payload) bob2EnsUsernameDetails, err = bob2.getEnsUsernameDetails() s.Require().NoError(err) s.Require().Equal(1, len(bob2EnsUsernameDetails)) s.Require().Equal(bob1EnsUsernameDetail, bob2EnsUsernameDetails[0]) bob2ProfileShowcasePreferences, err = bob1.GetProfileShowcasePreferences() s.Require().NoError(err) s.Require().True(reflect.DeepEqual(bob1ProfileShowcasePreferences, bob2ProfileShowcasePreferences)) lastBackup, err := bob1.lastBackup() s.Require().NoError(err) s.Require().NotEmpty(lastBackup) s.Require().Equal(clock, lastBackup) } func (s *MessengerBackupSuite) TestBackupProfileWithInvalidDisplayName() { // Create bob1 bob1 := s.m state := ReceivedMessageState{ Response: &MessengerResponse{}, } err := bob1.HandleBackup( &state, &protobuf.Backup{ Profile: &protobuf.BackedUpProfile{ DisplayName: "bad-display-name_eth", }, Clock: 1, }, &v1protocol.StatusMessage{}, ) // The backup will still work, but the display name will be skipped s.Require().NoError(err) storedBob1DisplayName, err := bob1.settings.DisplayName() s.Require().NoError(err) s.Require().Equal("", storedBob1DisplayName) } func (s *MessengerBackupSuite) TestBackupSettings() { s.T().Skip("flaky test") const ( bob1DisplayName = "bobby" bob1Currency = "eur" bob1MessagesFromContactsOnly = true bob1ProfilePicturesShowTo = settings.ProfilePicturesShowToEveryone bob1ProfilePicturesVisibility = settings.ProfilePicturesVisibilityEveryone bob1Bio = "bio" bob1Mnemonic = "" bob1MnemonicRemoved = true bob1PreferredName = "talent" bob1UrlUnfUnfurlingMode = settings.URLUnfurlingEnableAll ) // Create bob1 and set fields which are supposed to be backed up to/fetched from waku bob1 := s.m err := bob1.settings.SaveSettingField(settings.DisplayName, bob1DisplayName) s.Require().NoError(err) err = bob1.settings.SaveSettingField(settings.Currency, bob1Currency) s.Require().NoError(err) err = bob1.settings.SaveSettingField(settings.MessagesFromContactsOnly, bob1MessagesFromContactsOnly) s.Require().NoError(err) err = bob1.settings.SaveSettingField(settings.ProfilePicturesShowTo, bob1ProfilePicturesShowTo) s.Require().NoError(err) err = bob1.settings.SaveSettingField(settings.ProfilePicturesVisibility, bob1ProfilePicturesVisibility) s.Require().NoError(err) err = bob1.settings.SaveSettingField(settings.Bio, bob1Bio) s.Require().NoError(err) err = bob1.settings.SaveSettingField(settings.Mnemonic, bob1Mnemonic) s.Require().NoError(err) err = bob1.settings.SaveSettingField(settings.PreferredName, bob1PreferredName) s.Require().NoError(err) err = bob1.settings.SaveSettingField(settings.URLUnfurlingMode, bob1UrlUnfUnfurlingMode) s.Require().NoError(err) // Create bob2 bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, nil) s.Require().NoError(err) defer TearDownMessenger(&s.Suite, bob2) // Check bob1 storedBob1DisplayName, err := bob1.settings.DisplayName() s.Require().NoError(err) s.Require().Equal(bob1DisplayName, storedBob1DisplayName) storedBob1Currency, err := bob1.settings.GetCurrency() s.Require().NoError(err) s.Require().Equal(bob1Currency, storedBob1Currency) storedBob1MessagesFromContactsOnly, err := bob1.settings.GetMessagesFromContactsOnly() s.Require().NoError(err) s.Require().Equal(bob1MessagesFromContactsOnly, storedBob1MessagesFromContactsOnly) storedBob1ProfilePicturesShowTo, err := bob1.settings.GetProfilePicturesShowTo() s.Require().NoError(err) s.Require().Equal(int64(bob1ProfilePicturesShowTo), storedBob1ProfilePicturesShowTo) storedBob1ProfilePicturesVisibility, err := bob1.settings.GetProfilePicturesVisibility() s.Require().NoError(err) s.Require().Equal(int(bob1ProfilePicturesVisibility), storedBob1ProfilePicturesVisibility) storedBob1Bio, err := bob1.settings.Bio() s.Require().NoError(err) s.Require().Equal(bob1Bio, storedBob1Bio) storedMnemonic, err := bob1.settings.Mnemonic() s.Require().NoError(err) s.Require().Equal(bob1Mnemonic, storedMnemonic) storedMnemonicRemoved, err := bob1.settings.MnemonicRemoved() s.Require().NoError(err) s.Require().Equal(bob1MnemonicRemoved, storedMnemonicRemoved) storedPreferredName, err := bob1.settings.GetPreferredUsername() s.NoError(err) s.Require().Equal(bob1PreferredName, storedPreferredName) storedBob1UrlUnfurlingMode, err := bob1.settings.URLUnfurlingMode() s.NoError(err) s.Require().Equal(int64(bob1UrlUnfUnfurlingMode), storedBob1UrlUnfurlingMode) // Check bob2 storedBob2DisplayName, err := bob2.settings.DisplayName() s.Require().NoError(err) s.Require().NotEqual(storedBob1DisplayName, storedBob2DisplayName) storedBob2Currency, err := bob2.settings.GetCurrency() s.Require().NoError(err) s.Require().NotEqual(storedBob1Currency, storedBob2Currency) storedBob2MessagesFromContactsOnly, err := bob2.settings.GetMessagesFromContactsOnly() s.Require().NoError(err) s.Require().NotEqual(storedBob1MessagesFromContactsOnly, storedBob2MessagesFromContactsOnly) storedBob2ProfilePicturesShowTo, err := bob2.settings.GetProfilePicturesShowTo() s.Require().NoError(err) s.Require().NotEqual(storedBob1ProfilePicturesShowTo, storedBob2ProfilePicturesShowTo) storedBob2ProfilePicturesVisibility, err := bob2.settings.GetProfilePicturesVisibility() s.Require().NoError(err) s.Require().NotEqual(storedBob1ProfilePicturesVisibility, storedBob2ProfilePicturesVisibility) storedBob2Bio, err := bob2.settings.Bio() s.Require().NoError(err) s.Require().NotEqual(storedBob1Bio, storedBob2Bio) storedBob2MnemonicRemoved, err := bob2.settings.MnemonicRemoved() s.Require().NoError(err) s.Require().Equal(false, storedBob2MnemonicRemoved) storedBob2PreferredName, err := bob2.settings.GetPreferredUsername() s.NoError(err) s.Require().Equal("", storedBob2PreferredName) storedBob2UrlUnfurlingMode, err := bob2.settings.URLUnfurlingMode() s.NoError(err) s.Require().Equal(int64(settings.URLUnfurlingAlwaysAsk), storedBob2UrlUnfurlingMode) // Backup clock, err := bob1.BackupData(context.Background()) s.Require().NoError(err) // Wait for the message to reach its destination _, err = WaitOnSignaledSendWakuFetchingBackupProgress(bob2, func(r *wakusync.WakuBackedUpDataResponse) bool { detailsMap := r.FetchingBackedUpDataDetails() if settingDetails, ok := detailsMap[SyncWakuSectionKeySettings]; ok { return settingDetails.DataNumber == settingDetails.TotalNumber } return false }, "no messages") s.Require().NoError(err) // Check bob2 storedBob2DisplayName, err = bob2.settings.DisplayName() s.Require().NoError(err) s.Require().Equal(storedBob1DisplayName, storedBob2DisplayName) storedBob2Currency, err = bob2.settings.GetCurrency() s.Require().NoError(err) s.Require().Equal(storedBob1Currency, storedBob2Currency) storedBob2MessagesFromContactsOnly, err = bob2.settings.GetMessagesFromContactsOnly() s.Require().NoError(err) s.Require().Equal(storedBob1MessagesFromContactsOnly, storedBob2MessagesFromContactsOnly) storedBob2ProfilePicturesShowTo, err = bob2.settings.GetProfilePicturesShowTo() s.Require().NoError(err) s.Require().Equal(storedBob1ProfilePicturesShowTo, storedBob2ProfilePicturesShowTo) storedBob2ProfilePicturesVisibility, err = bob2.settings.GetProfilePicturesVisibility() s.Require().NoError(err) s.Require().Equal(storedBob1ProfilePicturesVisibility, storedBob2ProfilePicturesVisibility) storedBob2Bio, err = bob2.settings.Bio() s.Require().NoError(err) s.Require().Equal(storedBob1Bio, storedBob2Bio) storedBob2MnemonicRemoved, err = bob2.settings.MnemonicRemoved() s.Require().NoError(err) s.Require().Equal(bob1MnemonicRemoved, storedBob2MnemonicRemoved) storedBob2PreferredName, err = bob2.settings.GetPreferredUsername() s.NoError(err) s.Require().Equal(bob1PreferredName, storedBob2PreferredName) s.Require().Equal(bob1PreferredName, bob2.account.Name) storedBob2UrlUnfurlingMode, err = bob2.settings.URLUnfurlingMode() s.NoError(err) s.Require().Equal(storedBob1UrlUnfurlingMode, storedBob2UrlUnfurlingMode) lastBackup, err := bob1.lastBackup() s.Require().NoError(err) s.Require().NotEmpty(lastBackup) s.Require().Equal(clock, lastBackup) } func (s *MessengerBackupSuite) TestBackupContactsGreaterThanBatch() { bob1 := s.m // Create bob2 bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, nil) s.Require().NoError(err) defer TearDownMessenger(&s.Suite, bob2) // Create contacts for i := 0; i < BackupContactsPerBatch*2; i++ { contactKey, err := crypto.GenerateKey() s.Require().NoError(err) contactID := types.EncodeHex(crypto.FromECDSAPub(&contactKey.PublicKey)) _, err = bob1.AddContact(context.Background(), &requests.AddContact{ID: contactID}) s.Require().NoError(err) } // Backup _, err = bob1.BackupData(context.Background()) s.Require().NoError(err) // Safety check s.Require().Len(bob2.Contacts(), 0) // Wait for the message to reach its destination _, err = WaitOnMessengerResponse( bob2, func(r *MessengerResponse) bool { return r.BackupHandled }, "no messages", ) s.Require().NoError(err) s.Require().Equal(BackupContactsPerBatch*2, len(bob2.Contacts())) s.Require().Len(bob2.AddedContacts(), BackupContactsPerBatch*2) } func (s *MessengerBackupSuite) TestBackupRemovedContact() { bob1 := s.m // Create bob2 bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, nil) s.Require().NoError(err) defer TearDownMessenger(&s.Suite, bob2) // Create 2 contacts on bob 1 contact1Key, err := crypto.GenerateKey() s.Require().NoError(err) contactID1 := types.EncodeHex(crypto.FromECDSAPub(&contact1Key.PublicKey)) _, err = bob1.AddContact(context.Background(), &requests.AddContact{ID: contactID1}) s.Require().NoError(err) contact2Key, err := crypto.GenerateKey() s.Require().NoError(err) contactID2 := types.EncodeHex(crypto.FromECDSAPub(&contact2Key.PublicKey)) _, err = bob1.AddContact(context.Background(), &requests.AddContact{ID: contactID2}) s.Require().NoError(err) s.Require().Len(bob1.Contacts(), 2) actualContacts := bob1.Contacts() if actualContacts[0].ID == contactID1 { s.Require().Equal(actualContacts[0].ID, contactID1) s.Require().Equal(actualContacts[1].ID, contactID2) } else { s.Require().Equal(actualContacts[0].ID, contactID2) s.Require().Equal(actualContacts[1].ID, contactID1) } // Bob 2 add one of the same contacts _, err = bob2.AddContact(context.Background(), &requests.AddContact{ID: contactID2}) s.Require().NoError(err) // Bob 1 now removes one of the contact that was also on bob 2 _, err = bob1.RemoveContact(context.Background(), contactID2) s.Require().NoError(err) // Backup clock, err := bob1.BackupData(context.Background()) s.Require().NoError(err) // Safety check s.Require().Len(bob2.Contacts(), 1) // Wait for the message to reach its destination _, err = WaitOnMessengerResponse( bob2, func(r *MessengerResponse) bool { return r.BackupHandled }, "no messages", ) // Bob 2 should remove the contact s.Require().NoError(err) s.Require().Len(bob2.AddedContacts(), 1) s.Require().Equal(contactID1, bob2.AddedContacts()[0].ID) lastBackup, err := bob1.lastBackup() s.Require().NoError(err) s.Require().NotEmpty(lastBackup) s.Require().Equal(clock, lastBackup) } func (s *MessengerBackupSuite) TestBackupLocalNickname() { bob1 := s.m // Create bob2 bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, nil) nickname := "don van vliet" s.Require().NoError(err) defer TearDownMessenger(&s.Suite, bob2) // Set contact nickname contact1Key, err := crypto.GenerateKey() s.Require().NoError(err) contactID1 := types.EncodeHex(crypto.FromECDSAPub(&contact1Key.PublicKey)) _, err = bob1.SetContactLocalNickname(&requests.SetContactLocalNickname{ID: types.Hex2Bytes(contactID1), Nickname: nickname}) s.Require().NoError(err) // Backup clock, err := bob1.BackupData(context.Background()) s.Require().NoError(err) // Safety check s.Require().Len(bob2.Contacts(), 0) var actualContact *Contact // Wait for the message to reach its destination _, err = WaitOnMessengerResponse( bob2, func(r *MessengerResponse) bool { return r.BackupHandled }, "no messages", ) s.Require().NoError(err) for _, c := range bob2.Contacts() { if c.ID == contactID1 { actualContact = c break } } s.Require().Equal(actualContact.LocalNickname, nickname) lastBackup, err := bob1.lastBackup() s.Require().NoError(err) s.Require().NotEmpty(lastBackup) s.Require().Equal(clock, lastBackup) } func (s *MessengerBackupSuite) TestBackupBlockedContacts() { bob1 := s.m // Create bob2 bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, nil) s.Require().NoError(err) defer TearDownMessenger(&s.Suite, bob2) // Create contact contact1Key, err := crypto.GenerateKey() s.Require().NoError(err) contactID1 := types.EncodeHex(crypto.FromECDSAPub(&contact1Key.PublicKey)) _, err = bob1.AddContact(context.Background(), &requests.AddContact{ID: contactID1}) s.Require().NoError(err) // Backup _, err = bob1.BackupData(context.Background()) s.Require().NoError(err) // Safety check s.Require().Len(bob2.Contacts(), 0) // Wait for the message to reach its destination _, err = WaitOnMessengerResponse( bob2, func(r *MessengerResponse) bool { return r.BackupHandled }, "no messages", ) s.Require().NoError(err) s.Require().Len(bob2.AddedContacts(), 1) actualContacts := bob2.AddedContacts() s.Require().Equal(actualContacts[0].ID, contactID1) _, err = bob1.BlockContact(context.Background(), contactID1, false) s.Require().NoError(err) // Backup _, err = bob1.BackupData(context.Background()) s.Require().NoError(err) _, err = WaitOnMessengerResponse( bob2, func(r *MessengerResponse) bool { return r.BackupHandled }, "no messages", ) s.Require().NoError(err) s.Require().Len(bob2.BlockedContacts(), 1) } func (s *MessengerBackupSuite) TestBackupCommunities() { bob1 := s.m // Create bob2 bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, nil) s.Require().NoError(err) defer TearDownMessenger(&s.Suite, bob2) // Create a communitie description := &requests.CreateCommunity{ Membership: protobuf.CommunityPermissions_AUTO_ACCEPT, Name: "status", Color: "#ffffff", Description: "status community description", } // Create a community chat response, err := bob1.CreateCommunity(description, true) s.Require().NoError(err) s.Require().NotNil(response) s.Require().Len(response.Communities(), 1) // Backup clock, err := bob1.BackupData(context.Background()) s.Require().NoError(err) // Safety check communities, err := bob2.Communities() s.Require().NoError(err) s.Require().Len(communities, 0) // Wait for the message to reach its destination _, err = WaitOnMessengerResponse( bob2, func(r *MessengerResponse) bool { return r.BackupHandled }, "no messages", ) s.Require().NoError(err) communities, err = bob2.JoinedCommunities() s.Require().NoError(err) s.Require().Len(communities, 1) lastBackup, err := bob1.lastBackup() s.Require().NoError(err) s.Require().NotEmpty(lastBackup) s.Require().Equal(clock, lastBackup) } func (s *MessengerBackupSuite) TestBackupKeypairs() { // Create bob1 bob1 := s.m profileKp := accounts.GetProfileKeypairForTest(true, true, true) seedKp := accounts.GetSeedImportedKeypair1ForTest() // Create a main account on bob1 err := bob1.settings.SaveOrUpdateKeypair(profileKp) s.Require().NoError(err, "profile keypair bob1") err = bob1.settings.SaveOrUpdateKeypair(seedKp) s.Require().NoError(err, "seed keypair bob1") // Check account is present in the db for bob1 dbProfileKp1, err := bob1.settings.GetKeypairByKeyUID(profileKp.KeyUID) s.Require().NoError(err) s.Require().True(accounts.SameKeypairs(profileKp, dbProfileKp1)) dbSeedKp1, err := bob1.settings.GetKeypairByKeyUID(seedKp.KeyUID) s.Require().NoError(err) s.Require().True(accounts.SameKeypairs(seedKp, dbSeedKp1)) // Create bob2 accountsFeed := &event.Feed{} bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, []Option{WithAccountsFeed(accountsFeed)}) s.Require().NoError(err) s.Require().NotNil(bob2.config.accountsFeed) ch := make(chan accountsevent.Event, 20) sub := bob2.config.accountsFeed.Subscribe(ch) defer TearDownMessenger(&s.Suite, bob2) // Backup _, err = bob1.BackupData(context.Background()) s.Require().NoError(err) // Wait for the message to reach its destination _, err = WaitOnMessengerResponse( bob2, func(r *MessengerResponse) bool { return r.BackupHandled }, "no messages", ) s.Require().NoError(err) // Check account is present in the db for bob2 dbProfileKp2, err := bob2.settings.GetKeypairByKeyUID(profileKp.KeyUID) s.Require().NoError(err) s.Require().Equal(profileKp.Name, dbProfileKp2.Name) s.Require().Equal(accounts.SyncedFromBackup, dbProfileKp2.SyncedFrom) for _, acc := range profileKp.Accounts { s.Require().True(accounts.Contains(dbProfileKp2.Accounts, acc, accounts.SameAccounts)) } dbSeedKp2, err := bob2.settings.GetKeypairByKeyUID(seedKp.KeyUID) s.Require().NoError(err) s.Require().True(accounts.SameKeypairsWithDifferentSyncedFrom(seedKp, dbSeedKp2, false, accounts.SyncedFromBackup, accounts.AccountNonOperable)) // Check whether accounts added event is sent expectedAddresses := [][]common.Address{} profileKpWalletAddresses := []common.Address{} seedKpAddresses := []common.Address{} for _, acc := range dbProfileKp2.Accounts { if acc.Chat { continue } profileKpWalletAddresses = append(profileKpWalletAddresses, common.Address(acc.Address)) } expectedAddresses = append(expectedAddresses, profileKpWalletAddresses) for _, acc := range dbSeedKp2.Accounts { seedKpAddresses = append(seedKpAddresses, common.Address(acc.Address)) } expectedAddresses = append(expectedAddresses, seedKpAddresses) for i := 0; i < len(expectedAddresses); i++ { select { case <-time.After(1 * time.Second): s.Fail("Timed out waiting for accountsevent") case event := <-ch: switch event.Type { case accountsevent.EventTypeAdded: s.Require().Len(event.Accounts, len(expectedAddresses[i])) s.Require().True(reflect.DeepEqual(expectedAddresses[i], event.Accounts)) } } } sub.Unsubscribe() } func (s *MessengerBackupSuite) TestBackupKeycards() { // Create bob1 bob1 := s.m kp1 := accounts.GetProfileKeypairForTest(true, true, true) keycard1 := accounts.GetProfileKeycardForTest() kp2 := accounts.GetSeedImportedKeypair1ForTest() keycard2 := accounts.GetKeycardForSeedImportedKeypair1ForTest() keycard2Copy := accounts.GetKeycardForSeedImportedKeypair1ForTest() keycard2Copy.KeycardUID = keycard2Copy.KeycardUID + "C" keycard2Copy.KeycardName = keycard2Copy.KeycardName + "Copy" kp3 := accounts.GetSeedImportedKeypair2ForTest() keycard3 := accounts.GetKeycardForSeedImportedKeypair2ForTest() // Pre-condition err := bob1.settings.SaveOrUpdateKeypair(kp1) s.Require().NoError(err) err = bob1.settings.SaveOrUpdateKeypair(kp2) s.Require().NoError(err) err = bob1.settings.SaveOrUpdateKeypair(kp3) s.Require().NoError(err) dbKeypairs, err := bob1.settings.GetActiveKeypairs() s.Require().NoError(err) s.Require().Equal(3, len(dbKeypairs)) err = bob1.settings.SaveOrUpdateKeycard(*keycard1, 0, false) s.Require().NoError(err) err = bob1.settings.SaveOrUpdateKeycard(*keycard2, 0, false) s.Require().NoError(err) err = bob1.settings.SaveOrUpdateKeycard(*keycard2Copy, 0, false) s.Require().NoError(err) err = bob1.settings.SaveOrUpdateKeycard(*keycard3, 0, false) s.Require().NoError(err) // Create bob2 bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, nil) s.Require().NoError(err) defer TearDownMessenger(&s.Suite, bob2) // Backup _, err = bob1.BackupData(context.Background()) s.Require().NoError(err) // Wait for the message to reach its destination _, err = WaitOnMessengerResponse( bob2, func(r *MessengerResponse) bool { syncedKeycards, err := bob2.settings.GetAllKnownKeycards() s.Require().NoError(err) return r.BackupHandled && len(syncedKeycards) == 4 }, "no messages", ) s.Require().NoError(err) syncedKeycards, err := bob2.settings.GetAllKnownKeycards() s.Require().NoError(err) s.Require().Equal(4, len(syncedKeycards)) s.Require().True(accounts.Contains(syncedKeycards, keycard1, accounts.SameKeycards)) s.Require().True(accounts.Contains(syncedKeycards, keycard2, accounts.SameKeycards)) s.Require().True(accounts.Contains(syncedKeycards, keycard2Copy, accounts.SameKeycards)) s.Require().True(accounts.Contains(syncedKeycards, keycard3, accounts.SameKeycards)) } func (s *MessengerBackupSuite) TestBackupWatchOnlyAccounts() { // Create bob1 bob1 := s.m woAccounts := accounts.GetWatchOnlyAccountsForTest() err := bob1.settings.SaveOrUpdateAccounts(woAccounts, false) s.Require().NoError(err) dbWoAccounts1, err := bob1.settings.GetActiveWatchOnlyAccounts() s.Require().NoError(err) s.Require().Equal(len(woAccounts), len(dbWoAccounts1)) s.Require().True(haveSameElements(woAccounts, dbWoAccounts1, accounts.SameAccounts)) // Create bob2 accountsFeed := &event.Feed{} bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, []Option{WithAccountsFeed(accountsFeed)}) s.Require().NoError(err) s.Require().NotNil(bob2.config.accountsFeed) ch := make(chan accountsevent.Event, 20) sub := bob2.config.accountsFeed.Subscribe(ch) defer TearDownMessenger(&s.Suite, bob2) // Backup _, err = bob1.BackupData(context.Background()) s.Require().NoError(err) // Wait for the message to reach its destination _, err = WaitOnMessengerResponse( bob2, func(r *MessengerResponse) bool { c, err := bob2.settings.GetActiveWatchOnlyAccounts() if err != nil { return false } return r.BackupHandled && len(woAccounts) == len(c) }, "no messages", ) s.Require().NoError(err) dbWoAccounts2, err := bob2.settings.GetActiveWatchOnlyAccounts() s.Require().NoError(err) s.Require().Equal(len(woAccounts), len(dbWoAccounts2)) s.Require().True(haveSameElements(woAccounts, dbWoAccounts2, accounts.SameAccounts)) // Check whether accounts added event is sent select { case <-time.After(1 * time.Second): s.Fail("Timed out waiting for accountsevent") case event := <-ch: switch event.Type { case accountsevent.EventTypeAdded: s.Require().Len(event.Accounts, 1) s.Require().Equal(common.Address(dbWoAccounts2[0].Address), event.Accounts[0]) } } sub.Unsubscribe() } func (s *MessengerBackupSuite) TestBackupChats() { // Create bob1 bob1 := s.m response, err := bob1.CreateGroupChatWithMembers(context.Background(), "group", []string{}) s.NoError(err) s.Require().Len(response.Chats(), 1) ourGroupChat := response.Chats()[0] err = bob1.SaveChat(ourGroupChat) s.NoError(err) alice := s.newMessenger() defer TearDownMessenger(&s.Suite, alice) ourOneOneChat := CreateOneToOneChat("Our 1TO1", &alice.identity.PublicKey, alice.transport) err = bob1.SaveChat(ourOneOneChat) s.Require().NoError(err) // Create bob2 bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, nil) s.Require().NoError(err) defer TearDownMessenger(&s.Suite, bob2) // Backup _, err = bob1.BackupData(context.Background()) s.Require().NoError(err) // Wait for the message to reach its destination response, err = WaitOnMessengerResponse( bob2, func(r *MessengerResponse) bool { return r.BackupHandled }, "no messages", ) s.Require().NoError(err) // -- Group chat // Check response chat, ok := response.chats[ourGroupChat.ID] s.Require().True(ok) s.Require().Equal(ourGroupChat.Name, chat.Name) // Check stored chats chat, ok = bob2.allChats.Load(ourGroupChat.ID) s.Require().True(ok) s.Require().Equal(ourGroupChat.Name, chat.Name) // -- One to One chat // Check response chat, ok = response.chats[ourOneOneChat.ID] s.Require().True(ok) s.Require().Equal("", chat.Name) // We set 1-1 chat names to "" because the name is not good // Check stored chats chat, ok = bob2.allChats.Load(ourOneOneChat.ID) s.Require().True(ok) s.Require().Equal("", chat.Name) } func (s *MessengerBackupSuite) TestLeftCommunitiesAreBackedUp() { bob1 := s.m // Create bob2 bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, nil) s.Require().NoError(err) defer TearDownMessenger(&s.Suite, bob2) description := &requests.CreateCommunity{ Membership: protobuf.CommunityPermissions_MANUAL_ACCEPT, Name: "other-status", Color: "#fffff4", Description: "other status community description", } // Create another community chat response, err := bob1.CreateCommunity(description, true) s.Require().NoError(err) s.Require().NotNil(response) s.Require().Len(response.Communities(), 1) newCommunity := response.Communities()[0] response, err = bob1.LeaveCommunity(newCommunity.ID()) s.Require().NoError(err) s.Require().NotNil(response) // trigger artificial Backup _, err = bob1.BackupData(context.Background()) s.Require().NoError(err) communities, err := bob1.Communities() s.Require().NoError(err) s.Require().Len(communities, 1) // Safety check communities, err = bob2.Communities() s.Require().NoError(err) s.Require().Len(communities, 0) // Wait for the message to reach its destination _, err = WaitOnMessengerResponse( bob2, func(r *MessengerResponse) bool { return r.BackupHandled }, "no messages", ) s.Require().NoError(err) communities, err = bob2.JoinedCommunities() s.Require().NoError(err) s.Require().Len(communities, 0) }