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

     1  package protocol
     2  
     3  import (
     4  	"database/sql"
     5  
     6  	"go.uber.org/zap"
     7  
     8  	"github.com/golang/protobuf/proto"
     9  
    10  	utils "github.com/status-im/status-go/common"
    11  	"github.com/status-im/status-go/images"
    12  	"github.com/status-im/status-go/multiaccounts/errors"
    13  	"github.com/status-im/status-go/multiaccounts/settings"
    14  	"github.com/status-im/status-go/protocol/common"
    15  	"github.com/status-im/status-go/protocol/communities"
    16  	"github.com/status-im/status-go/protocol/protobuf"
    17  	v1protocol "github.com/status-im/status-go/protocol/v1"
    18  	"github.com/status-im/status-go/protocol/wakusync"
    19  	ensservice "github.com/status-im/status-go/services/ens"
    20  )
    21  
    22  const (
    23  	SyncWakuSectionKeyProfile           = "profile"
    24  	SyncWakuSectionKeyContacts          = "contacts"
    25  	SyncWakuSectionKeyCommunities       = "communities"
    26  	SyncWakuSectionKeySettings          = "settings"
    27  	SyncWakuSectionKeyKeypairs          = "keypairs"
    28  	SyncWakuSectionKeyWatchOnlyAccounts = "watchOnlyAccounts"
    29  )
    30  
    31  func (m *Messenger) HandleBackup(state *ReceivedMessageState, message *protobuf.Backup, statusMessage *v1protocol.StatusMessage) error {
    32  	if !m.processBackedupMessages {
    33  		return nil
    34  	}
    35  
    36  	errors := m.handleBackup(state, message)
    37  	if len(errors) > 0 {
    38  		for _, err := range errors {
    39  			m.logger.Warn("failed to handle Backup", zap.Error(err))
    40  		}
    41  		return errors[0]
    42  	}
    43  	return nil
    44  }
    45  
    46  func (m *Messenger) handleBackup(state *ReceivedMessageState, message *protobuf.Backup) []error {
    47  	var errors []error
    48  
    49  	err := m.handleBackedUpProfile(message.Profile, message.Clock)
    50  	if err != nil {
    51  		errors = append(errors, err)
    52  	}
    53  
    54  	for _, contact := range message.Contacts {
    55  		err = m.HandleSyncInstallationContactV2(state, contact, nil)
    56  		if err != nil {
    57  			errors = append(errors, err)
    58  		}
    59  	}
    60  
    61  	err = m.handleSyncChats(state, message.Chats)
    62  	if err != nil {
    63  		errors = append(errors, err)
    64  	}
    65  
    66  	communityErrors := m.handleSyncedCommunities(state, message)
    67  	if len(communityErrors) > 0 {
    68  		errors = append(errors, communityErrors...)
    69  	}
    70  
    71  	err = m.handleBackedUpSettings(message.Setting)
    72  	if err != nil {
    73  		errors = append(errors, err)
    74  	}
    75  
    76  	err = m.handleKeypair(message.Keypair)
    77  	if err != nil {
    78  		errors = append(errors, err)
    79  	}
    80  
    81  	err = m.handleWatchOnlyAccount(message.WatchOnlyAccount)
    82  	if err != nil {
    83  		errors = append(errors, err)
    84  	}
    85  
    86  	// Send signal about applied backup progress
    87  	if m.config.messengerSignalsHandler != nil {
    88  		response := wakusync.WakuBackedUpDataResponse{
    89  			Clock: message.Clock,
    90  		}
    91  
    92  		response.AddFetchingBackedUpDataDetails(SyncWakuSectionKeyProfile, message.ProfileDetails)
    93  		response.AddFetchingBackedUpDataDetails(SyncWakuSectionKeyContacts, message.ContactsDetails)
    94  		response.AddFetchingBackedUpDataDetails(SyncWakuSectionKeyCommunities, message.CommunitiesDetails)
    95  		response.AddFetchingBackedUpDataDetails(SyncWakuSectionKeySettings, message.SettingsDetails)
    96  		response.AddFetchingBackedUpDataDetails(SyncWakuSectionKeyKeypairs, message.KeypairDetails)
    97  		response.AddFetchingBackedUpDataDetails(SyncWakuSectionKeyWatchOnlyAccounts, message.WatchOnlyAccountDetails)
    98  
    99  		m.config.messengerSignalsHandler.SendWakuFetchingBackupProgress(&response)
   100  	}
   101  
   102  	state.Response.BackupHandled = true
   103  
   104  	return errors
   105  }
   106  
   107  func (m *Messenger) handleBackedUpProfile(message *protobuf.BackedUpProfile, backupTime uint64) error {
   108  	if message == nil {
   109  		return nil
   110  	}
   111  
   112  	response := wakusync.WakuBackedUpDataResponse{
   113  		Profile: &wakusync.BackedUpProfile{},
   114  	}
   115  
   116  	err := utils.ValidateDisplayName(&message.DisplayName)
   117  	if err != nil {
   118  		// Print a warning and set the display name to the account name, but don't stop the recovery
   119  		m.logger.Warn("invalid display name found", zap.Error(err))
   120  		response.SetDisplayName(m.account.Name)
   121  	} else {
   122  		err = m.SaveSyncDisplayName(message.DisplayName, message.DisplayNameClock)
   123  		if err != nil && err != errors.ErrNewClockOlderThanCurrent {
   124  			return err
   125  		}
   126  
   127  		response.SetDisplayName(message.DisplayName)
   128  
   129  		// if we already have a newer clock, then we don't need to update the display name
   130  		if err == errors.ErrNewClockOlderThanCurrent {
   131  			response.SetDisplayName(m.account.Name)
   132  		}
   133  	}
   134  
   135  	syncWithBackedUpImages := false
   136  	dbImages, err := m.multiAccounts.GetIdentityImages(message.KeyUid)
   137  	if err != nil {
   138  		if err != sql.ErrNoRows {
   139  			return err
   140  		}
   141  		// if images are deleted and no images were backed up, then we need to delete them on other devices,
   142  		// that's why we don't return in case of `sql.ErrNoRows`
   143  		syncWithBackedUpImages = true
   144  	}
   145  	if len(dbImages) == 0 {
   146  		if len(message.Pictures) > 0 {
   147  			syncWithBackedUpImages = true
   148  		}
   149  	} else {
   150  		// since both images (large and thumbnail) are always stored in the same time, we're free to use either of those two clocks for comparison
   151  		lastImageStoredClock := dbImages[0].Clock
   152  		syncWithBackedUpImages = lastImageStoredClock < backupTime
   153  	}
   154  
   155  	if syncWithBackedUpImages {
   156  		if len(message.Pictures) == 0 {
   157  			err = m.multiAccounts.DeleteIdentityImage(message.KeyUid)
   158  			if err != nil {
   159  				return err
   160  			}
   161  			response.SetImages(nil)
   162  		} else {
   163  			idImages := make([]images.IdentityImage, len(message.Pictures))
   164  			for i, pic := range message.Pictures {
   165  				img := images.IdentityImage{
   166  					Name:         pic.Name,
   167  					Payload:      pic.Payload,
   168  					Width:        int(pic.Width),
   169  					Height:       int(pic.Height),
   170  					FileSize:     int(pic.FileSize),
   171  					ResizeTarget: int(pic.ResizeTarget),
   172  					Clock:        pic.Clock,
   173  				}
   174  				idImages[i] = img
   175  			}
   176  			err = m.multiAccounts.StoreIdentityImages(message.KeyUid, idImages, false)
   177  			if err != nil {
   178  				return err
   179  			}
   180  			response.SetImages(idImages)
   181  		}
   182  	}
   183  
   184  	profileShowcasePreferences, err := m.saveProfileShowcasePreferencesProto(message.ProfileShowcasePreferences, false)
   185  	if err != nil {
   186  		return err
   187  	}
   188  	if profileShowcasePreferences != nil {
   189  		response.SetProfileShowcasePreferences(profileShowcasePreferences)
   190  	}
   191  
   192  	var ensUsernameDetails []*ensservice.UsernameDetail
   193  	for _, d := range message.EnsUsernameDetails {
   194  		dd, err := m.saveEnsUsernameDetailProto(d)
   195  		if err != nil {
   196  			return err
   197  		}
   198  		ensUsernameDetails = append(ensUsernameDetails, dd)
   199  	}
   200  	response.SetEnsUsernameDetails(ensUsernameDetails)
   201  
   202  	if m.config.messengerSignalsHandler != nil {
   203  		m.config.messengerSignalsHandler.SendWakuBackedUpProfile(&response)
   204  	}
   205  
   206  	return err
   207  }
   208  
   209  func (m *Messenger) handleBackedUpSettings(message *protobuf.SyncSetting) error {
   210  	if message == nil {
   211  		return nil
   212  	}
   213  
   214  	// DisplayName is recovered via `protobuf.BackedUpProfile` message
   215  	if message.GetType() == protobuf.SyncSetting_DISPLAY_NAME {
   216  		return nil
   217  	}
   218  
   219  	settingField, err := m.extractAndSaveSyncSetting(message)
   220  	if err != nil {
   221  		m.logger.Warn("failed to handle SyncSetting from backed up message", zap.Error(err))
   222  		return nil
   223  	}
   224  
   225  	if settingField != nil {
   226  		if message.GetType() == protobuf.SyncSetting_PREFERRED_NAME && message.GetValueString() != "" {
   227  			displayNameClock, err := m.settings.GetSettingLastSynced(settings.DisplayName)
   228  			if err != nil {
   229  				m.logger.Warn("failed to get last synced clock for display name", zap.Error(err))
   230  				return nil
   231  			}
   232  			// there is a race condition between display name and preferred name on updating m.account.Name, so we need to check the clock
   233  			// there is also a similar check within SaveSyncDisplayName
   234  			if displayNameClock < message.GetClock() {
   235  				m.account.Name = message.GetValueString()
   236  				err = m.multiAccounts.SaveAccount(*m.account)
   237  				if err != nil {
   238  					m.logger.Warn("[handleBackedUpSettings] failed to save account", zap.Error(err))
   239  					return nil
   240  				}
   241  			}
   242  		}
   243  
   244  		if m.config.messengerSignalsHandler != nil {
   245  			response := wakusync.WakuBackedUpDataResponse{
   246  				Setting: settingField,
   247  			}
   248  			m.config.messengerSignalsHandler.SendWakuBackedUpSettings(&response)
   249  		}
   250  	}
   251  
   252  	return nil
   253  }
   254  
   255  func (m *Messenger) handleKeypair(message *protobuf.SyncKeypair) error {
   256  	if message == nil {
   257  		return nil
   258  	}
   259  
   260  	multiAcc, err := m.multiAccounts.GetAccount(message.KeyUid)
   261  	if err != nil {
   262  		return err
   263  	}
   264  	// If user is recovering his account via seed phrase, but the backed up messages indicate that the profile keypair
   265  	// is a keycard related profile, then we need to remove related profile keycards (only profile, other keycards should remain).
   266  	if multiAcc != nil && multiAcc.KeyUID == message.KeyUid && !multiAcc.RefersToKeycard() && len(message.Keycards) > 0 {
   267  		message.Keycards = []*protobuf.SyncKeycard{}
   268  	}
   269  
   270  	keypair, err := m.handleSyncKeypair(message, false, nil)
   271  	if err != nil {
   272  		if err == ErrTryingToStoreOldKeypair {
   273  			return nil
   274  		}
   275  		return err
   276  	}
   277  
   278  	if m.config.messengerSignalsHandler != nil {
   279  		kpResponse := wakusync.WakuBackedUpDataResponse{
   280  			Keypair: keypair.CopyKeypair(),
   281  		}
   282  
   283  		m.config.messengerSignalsHandler.SendWakuBackedUpKeypair(&kpResponse)
   284  	}
   285  
   286  	return nil
   287  }
   288  
   289  func (m *Messenger) handleWatchOnlyAccount(message *protobuf.SyncAccount) error {
   290  	if message == nil {
   291  		return nil
   292  	}
   293  
   294  	acc, err := m.handleSyncWatchOnlyAccount(message, true)
   295  	if err != nil {
   296  		if err == ErrTryingToStoreOldWalletAccount {
   297  			return nil
   298  		}
   299  		return err
   300  	}
   301  
   302  	if m.config.messengerSignalsHandler != nil {
   303  		response := wakusync.WakuBackedUpDataResponse{
   304  			WatchOnlyAccount: acc,
   305  		}
   306  
   307  		m.config.messengerSignalsHandler.SendWakuBackedUpWatchOnlyAccount(&response)
   308  	}
   309  
   310  	return nil
   311  }
   312  
   313  func syncInstallationCommunitiesSet(communities []*protobuf.SyncInstallationCommunity) map[string]*protobuf.SyncInstallationCommunity {
   314  	ret := map[string]*protobuf.SyncInstallationCommunity{}
   315  	for _, c := range communities {
   316  		id := string(c.GetId())
   317  		prevC, ok := ret[id]
   318  		if !ok || prevC.Clock < c.Clock {
   319  			ret[id] = c
   320  		}
   321  	}
   322  	return ret
   323  }
   324  
   325  func (m *Messenger) handleSyncedCommunities(state *ReceivedMessageState, message *protobuf.Backup) []error {
   326  	var errors []error
   327  	for _, syncCommunity := range syncInstallationCommunitiesSet(message.Communities) {
   328  		err := m.handleSyncInstallationCommunity(state, syncCommunity)
   329  		if err != nil {
   330  			errors = append(errors, err)
   331  		}
   332  
   333  		err = m.requestCommunityKeysAndSharedAddresses(state, syncCommunity)
   334  		if err != nil {
   335  			errors = append(errors, err)
   336  		}
   337  	}
   338  
   339  	return errors
   340  }
   341  
   342  func (m *Messenger) requestCommunityKeysAndSharedAddresses(state *ReceivedMessageState, syncCommunity *protobuf.SyncInstallationCommunity) error {
   343  	if !syncCommunity.Joined {
   344  		return nil
   345  	}
   346  
   347  	community, err := m.GetCommunityByID(syncCommunity.Id)
   348  	if err != nil {
   349  		return err
   350  	}
   351  
   352  	if community == nil {
   353  		return communities.ErrOrgNotFound
   354  	}
   355  
   356  	// Send a request to get back our previous shared addresses
   357  	request := &protobuf.CommunitySharedAddressesRequest{
   358  		CommunityId: syncCommunity.Id,
   359  	}
   360  
   361  	payload, err := proto.Marshal(request)
   362  	if err != nil {
   363  		return err
   364  	}
   365  
   366  	rawMessage := &common.RawMessage{
   367  		Payload:             payload,
   368  		Sender:              m.identity,
   369  		CommunityID:         community.ID(),
   370  		SkipEncryptionLayer: true,
   371  		MessageType:         protobuf.ApplicationMetadataMessage_COMMUNITY_SHARED_ADDRESSES_REQUEST,
   372  	}
   373  
   374  	_, err = m.SendMessageToControlNode(community, rawMessage)
   375  
   376  	if err != nil {
   377  		m.logger.Error("failed to request shared addresses", zap.String("communityId", community.IDString()), zap.Error(err))
   378  		return err
   379  	}
   380  
   381  	// If the community is encrypted or one channel is, ask for the encryption keys back
   382  	isEncrypted := syncCommunity.Encrypted || len(syncCommunity.EncryptionKeysV2) > 0
   383  	if !isEncrypted {
   384  		// check if we have encrypted channels
   385  		myPk := m.IdentityPublicKeyString()
   386  		for channelID, channel := range community.Chats() {
   387  			_, exists := channel.GetMembers()[myPk]
   388  			if exists && community.ChannelEncrypted(channelID) {
   389  				isEncrypted = true
   390  				break
   391  			}
   392  		}
   393  	}
   394  
   395  	if isEncrypted {
   396  		err = m.requestCommunityEncryptionKeys(community, nil)
   397  		if err != nil {
   398  			m.logger.Error("failed to request community encryption keys", zap.String("communityId", community.IDString()), zap.Error(err))
   399  			return err
   400  		}
   401  	}
   402  
   403  	return nil
   404  }