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

     1  package protocol
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"time"
     7  
     8  	"github.com/golang/protobuf/proto"
     9  	"go.uber.org/zap"
    10  
    11  	"github.com/status-im/status-go/eth-node/crypto"
    12  	"github.com/status-im/status-go/images"
    13  	"github.com/status-im/status-go/protocol/common"
    14  	"github.com/status-im/status-go/protocol/encryption/multidevice"
    15  	"github.com/status-im/status-go/protocol/protobuf"
    16  	"github.com/status-im/status-go/protocol/requests"
    17  )
    18  
    19  func (m *Messenger) EnableAndSyncInstallation(request *requests.EnableAndSyncInstallation) error {
    20  	if err := request.Validate(); err != nil {
    21  		return err
    22  	}
    23  	err := m.EnableInstallation(request.InstallationID)
    24  	if err != nil {
    25  		return err
    26  	}
    27  	return m.SyncDevices(context.Background(), "", "", nil)
    28  }
    29  
    30  func (m *Messenger) EnableInstallationAndPair(request *requests.EnableInstallationAndPair) (*MessengerResponse, error) {
    31  	if err := request.Validate(); err != nil {
    32  		return nil, err
    33  	}
    34  
    35  	myIdentity := crypto.CompressPubkey(&m.identity.PublicKey)
    36  	timestamp := time.Now().UnixNano()
    37  
    38  	installation := &multidevice.Installation{
    39  		ID:        request.InstallationID,
    40  		Enabled:   true,
    41  		Version:   2,
    42  		Timestamp: timestamp,
    43  	}
    44  
    45  	_, err := m.encryptor.AddInstallation(myIdentity, timestamp, installation, true)
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  	i, ok := m.allInstallations.Load(request.InstallationID)
    50  	if !ok {
    51  		i = installation
    52  	} else {
    53  		i.Enabled = true
    54  	}
    55  	m.allInstallations.Store(request.InstallationID, i)
    56  	return m.SendPairInstallation(context.Background(), nil)
    57  }
    58  
    59  // SendPairInstallation sends a pair installation message
    60  func (m *Messenger) SendPairInstallation(ctx context.Context, rawMessageHandler RawMessageHandler) (*MessengerResponse, error) {
    61  	var err error
    62  	var response MessengerResponse
    63  
    64  	installation, ok := m.allInstallations.Load(m.installationID)
    65  	if !ok {
    66  		return nil, errors.New("no installation found")
    67  	}
    68  
    69  	if installation.InstallationMetadata == nil {
    70  		return nil, errors.New("no installation metadata")
    71  	}
    72  
    73  	clock, chat := m.getLastClockWithRelatedChat()
    74  
    75  	pairMessage := &protobuf.SyncPairInstallation{
    76  		Clock:          clock,
    77  		Name:           installation.InstallationMetadata.Name,
    78  		InstallationId: installation.ID,
    79  		DeviceType:     installation.InstallationMetadata.DeviceType,
    80  		Version:        installation.Version}
    81  	encodedMessage, err := proto.Marshal(pairMessage)
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  
    86  	if rawMessageHandler == nil {
    87  		rawMessageHandler = m.dispatchPairInstallationMessage
    88  	}
    89  	_, err = rawMessageHandler(ctx, common.RawMessage{
    90  		LocalChatID: chat.ID,
    91  		Payload:     encodedMessage,
    92  		MessageType: protobuf.ApplicationMetadataMessage_SYNC_PAIR_INSTALLATION,
    93  		ResendType:  common.ResendTypeDataSync,
    94  	})
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  
    99  	response.AddChat(chat)
   100  
   101  	chat.LastClockValue = clock
   102  	err = m.saveChat(chat)
   103  	if err != nil {
   104  		return nil, err
   105  	}
   106  	return &response, nil
   107  }
   108  
   109  // SyncDevices sends all public chats and contacts to paired devices
   110  // TODO remove use of photoPath in contacts
   111  func (m *Messenger) SyncDevices(ctx context.Context, ensName, photoPath string, rawMessageHandler RawMessageHandler) (err error) {
   112  	if rawMessageHandler == nil {
   113  		rawMessageHandler = m.dispatchMessage
   114  	}
   115  
   116  	myID := contactIDFromPublicKey(&m.identity.PublicKey)
   117  
   118  	displayName, err := m.settings.DisplayName()
   119  	if err != nil {
   120  		return err
   121  	}
   122  
   123  	if _, err = m.sendContactUpdate(ctx, myID, displayName, ensName, photoPath, m.account.GetCustomizationColor(), rawMessageHandler); err != nil {
   124  		return err
   125  	}
   126  
   127  	m.allChats.Range(func(chatID string, chat *Chat) bool {
   128  		if !chat.shouldBeSynced() {
   129  			return true
   130  
   131  		}
   132  		err = m.syncChat(ctx, chat, rawMessageHandler)
   133  		return err == nil
   134  	})
   135  	if err != nil {
   136  		return err
   137  	}
   138  
   139  	m.allContacts.Range(func(contactID string, contact *Contact) bool {
   140  		if contact.ID == myID {
   141  			return true
   142  		}
   143  		if contact.LocalNickname != "" || contact.added() || contact.hasAddedUs() || contact.Blocked {
   144  			if err = m.syncContact(ctx, contact, rawMessageHandler); err != nil {
   145  				return false
   146  			}
   147  		}
   148  		return true
   149  	})
   150  
   151  	cs, err := m.communitiesManager.JoinedAndPendingCommunitiesWithRequests()
   152  	if err != nil {
   153  		return err
   154  	}
   155  	for _, c := range cs {
   156  		if err = m.syncCommunity(ctx, c, rawMessageHandler); err != nil {
   157  			return err
   158  		}
   159  	}
   160  
   161  	bookmarks, err := m.browserDatabase.GetBookmarks()
   162  	if err != nil {
   163  		return err
   164  	}
   165  	for _, b := range bookmarks {
   166  		if err = m.SyncBookmark(ctx, b, rawMessageHandler); err != nil {
   167  			return err
   168  		}
   169  	}
   170  
   171  	trustedUsers, err := m.verificationDatabase.GetAllTrustStatus()
   172  	if err != nil {
   173  		return err
   174  	}
   175  	for id, ts := range trustedUsers {
   176  		if err = m.SyncTrustedUser(ctx, id, ts, rawMessageHandler); err != nil {
   177  			return err
   178  		}
   179  	}
   180  
   181  	verificationRequests, err := m.verificationDatabase.GetVerificationRequests()
   182  	if err != nil {
   183  		return err
   184  	}
   185  	for i := range verificationRequests {
   186  		if err = m.SyncVerificationRequest(ctx, &verificationRequests[i], rawMessageHandler); err != nil {
   187  			return err
   188  		}
   189  	}
   190  
   191  	err = m.syncSettings(rawMessageHandler)
   192  	if err != nil {
   193  		return err
   194  	}
   195  
   196  	err = m.syncProfilePicturesFromDatabase(rawMessageHandler)
   197  	if err != nil {
   198  		return err
   199  	}
   200  
   201  	if err = m.syncLatestContactRequests(ctx, rawMessageHandler); err != nil {
   202  		return err
   203  	}
   204  
   205  	// we have to sync deleted keypairs as well
   206  	keypairs, err := m.settings.GetAllKeypairs()
   207  	if err != nil {
   208  		return err
   209  	}
   210  
   211  	for _, kp := range keypairs {
   212  		err = m.syncKeypair(kp, rawMessageHandler)
   213  		if err != nil {
   214  			return err
   215  		}
   216  	}
   217  
   218  	// we have to sync deleted watch only accounts as well
   219  	woAccounts, err := m.settings.GetAllWatchOnlyAccounts()
   220  	if err != nil {
   221  		return err
   222  	}
   223  
   224  	for _, woAcc := range woAccounts {
   225  		err = m.syncWalletAccount(woAcc, rawMessageHandler)
   226  		if err != nil {
   227  			return err
   228  		}
   229  	}
   230  
   231  	savedAddresses, err := m.savedAddressesManager.GetRawSavedAddresses()
   232  	if err != nil {
   233  		return err
   234  	}
   235  
   236  	for i := range savedAddresses {
   237  		sa := savedAddresses[i]
   238  
   239  		err = m.syncSavedAddress(ctx, sa, rawMessageHandler)
   240  		if err != nil {
   241  			return err
   242  		}
   243  	}
   244  
   245  	if err = m.syncEnsUsernameDetails(ctx, rawMessageHandler); err != nil {
   246  		return err
   247  	}
   248  
   249  	if err = m.syncDeleteForMeMessage(ctx, rawMessageHandler); err != nil {
   250  		return err
   251  	}
   252  
   253  	err = m.syncAccountsPositions(rawMessageHandler)
   254  	if err != nil {
   255  		return err
   256  	}
   257  
   258  	err = m.syncProfileShowcasePreferences(context.Background(), rawMessageHandler)
   259  	if err != nil {
   260  		return err
   261  	}
   262  
   263  	return nil
   264  }
   265  
   266  func (m *Messenger) syncProfilePictures(rawMessageHandler RawMessageHandler, identityImages []*images.IdentityImage) error {
   267  	if !m.hasPairedDevices() {
   268  		return nil
   269  	}
   270  
   271  	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
   272  	defer cancel()
   273  
   274  	pictures := make([]*protobuf.SyncProfilePicture, len(identityImages))
   275  	clock, chat := m.getLastClockWithRelatedChat()
   276  	for i, image := range identityImages {
   277  		p := &protobuf.SyncProfilePicture{}
   278  		p.Name = image.Name
   279  		p.Payload = image.Payload
   280  		p.Width = uint32(image.Width)
   281  		p.Height = uint32(image.Height)
   282  		p.FileSize = uint32(image.FileSize)
   283  		p.ResizeTarget = uint32(image.ResizeTarget)
   284  		if image.Clock == 0 {
   285  			p.Clock = clock
   286  		} else {
   287  			p.Clock = image.Clock
   288  		}
   289  		pictures[i] = p
   290  	}
   291  
   292  	message := &protobuf.SyncProfilePictures{}
   293  	message.KeyUid = m.account.KeyUID
   294  	message.Pictures = pictures
   295  
   296  	encodedMessage, err := proto.Marshal(message)
   297  	if err != nil {
   298  		return err
   299  	}
   300  
   301  	rawMessage := common.RawMessage{
   302  		LocalChatID: chat.ID,
   303  		Payload:     encodedMessage,
   304  		MessageType: protobuf.ApplicationMetadataMessage_SYNC_PROFILE_PICTURES,
   305  		ResendType:  common.ResendTypeDataSync,
   306  	}
   307  
   308  	_, err = rawMessageHandler(ctx, rawMessage)
   309  	if err != nil {
   310  		return err
   311  	}
   312  
   313  	chat.LastClockValue = clock
   314  	return m.saveChat(chat)
   315  }
   316  
   317  func (m *Messenger) syncLatestContactRequests(ctx context.Context, rawMessageHandler RawMessageHandler) error {
   318  	latestContactRequests, err := m.persistence.LatestContactRequests()
   319  
   320  	if err != nil {
   321  		return err
   322  	}
   323  
   324  	for _, r := range latestContactRequests {
   325  		if r.ContactRequestState == common.ContactRequestStateAccepted || r.ContactRequestState == common.ContactRequestStateDismissed {
   326  			accepted := r.ContactRequestState == common.ContactRequestStateAccepted
   327  			err = m.syncContactRequestDecision(ctx, r.MessageID, r.ContactID, accepted, rawMessageHandler)
   328  			if err != nil {
   329  				return err
   330  			}
   331  		}
   332  	}
   333  	return nil
   334  }
   335  
   336  func (m *Messenger) syncContactRequestDecision(ctx context.Context, requestID, contactId string, accepted bool, rawMessageHandler RawMessageHandler) error {
   337  	m.logger.Info("syncContactRequestDecision", zap.Any("from", requestID))
   338  	if !m.hasPairedDevices() {
   339  		return nil
   340  	}
   341  
   342  	clock, chat := m.getLastClockWithRelatedChat()
   343  
   344  	var status protobuf.SyncContactRequestDecision_DecisionStatus
   345  	if accepted {
   346  		status = protobuf.SyncContactRequestDecision_ACCEPTED
   347  	} else {
   348  		status = protobuf.SyncContactRequestDecision_DECLINED
   349  	}
   350  
   351  	message := &protobuf.SyncContactRequestDecision{
   352  		RequestId:      requestID,
   353  		ContactId:      contactId,
   354  		Clock:          clock,
   355  		DecisionStatus: status,
   356  	}
   357  
   358  	encodedMessage, err := proto.Marshal(message)
   359  	if err != nil {
   360  		return err
   361  	}
   362  
   363  	rawMessage := common.RawMessage{
   364  		LocalChatID: chat.ID,
   365  		Payload:     encodedMessage,
   366  		MessageType: protobuf.ApplicationMetadataMessage_SYNC_CONTACT_REQUEST_DECISION,
   367  		ResendType:  common.ResendTypeDataSync,
   368  	}
   369  
   370  	_, err = rawMessageHandler(ctx, rawMessage)
   371  	if err != nil {
   372  		return err
   373  	}
   374  
   375  	return nil
   376  }
   377  
   378  func (m *Messenger) getLastClockWithRelatedChat() (uint64, *Chat) {
   379  	chatID := contactIDFromPublicKey(&m.identity.PublicKey)
   380  
   381  	chat, ok := m.allChats.Load(chatID)
   382  	if !ok {
   383  		chat = OneToOneFromPublicKey(&m.identity.PublicKey, m.getTimesource())
   384  		// We don't want to show the chat to the user
   385  		chat.Active = false
   386  	}
   387  
   388  	m.allChats.Store(chat.ID, chat)
   389  	clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
   390  
   391  	return clock, chat
   392  }
   393  
   394  func (m *Messenger) syncProfilePicturesFromDatabase(rawMessageHandler RawMessageHandler) error {
   395  	keyUID := m.account.KeyUID
   396  	identityImages, err := m.multiAccounts.GetIdentityImages(keyUID)
   397  	if err != nil {
   398  		return err
   399  	}
   400  	return m.syncProfilePictures(rawMessageHandler, identityImages)
   401  }
   402  
   403  func (m *Messenger) InitInstallations() error {
   404  	installations, err := m.encryptor.GetOurInstallations(&m.identity.PublicKey)
   405  	if err != nil {
   406  		return err
   407  	}
   408  
   409  	for _, installation := range installations {
   410  		m.allInstallations.Store(installation.ID, installation)
   411  	}
   412  
   413  	err = m.setInstallationHostname()
   414  	if err != nil {
   415  		return err
   416  	}
   417  
   418  	if m.telemetryClient != nil {
   419  		installation, ok := m.allInstallations.Load(m.installationID)
   420  		if ok {
   421  			m.telemetryClient.SetDeviceType(installation.InstallationMetadata.DeviceType)
   422  		}
   423  	}
   424  
   425  	return nil
   426  }
   427  
   428  func (m *Messenger) Installations() []*multidevice.Installation {
   429  	installations := make([]*multidevice.Installation, m.allInstallations.Len())
   430  
   431  	var i = 0
   432  	m.allInstallations.Range(func(installationID string, installation *multidevice.Installation) (shouldContinue bool) {
   433  		installations[i] = installation
   434  		i++
   435  		return true
   436  	})
   437  	return installations
   438  }
   439  
   440  func (m *Messenger) setInstallationMetadata(id string, data *multidevice.InstallationMetadata) error {
   441  	installation, ok := m.allInstallations.Load(id)
   442  	if !ok {
   443  		return errors.New("no installation found")
   444  	}
   445  
   446  	installation.InstallationMetadata = data
   447  	return m.encryptor.SetInstallationMetadata(m.IdentityPublicKey(), id, data)
   448  }
   449  
   450  func (m *Messenger) SetInstallationMetadata(id string, data *multidevice.InstallationMetadata) error {
   451  	return m.setInstallationMetadata(id, data)
   452  }
   453  
   454  func (m *Messenger) SetInstallationName(id string, name string) error {
   455  	installation, ok := m.allInstallations.Load(id)
   456  	if !ok {
   457  		return errors.New("no installation found")
   458  	}
   459  
   460  	installation.InstallationMetadata.Name = name
   461  	return m.encryptor.SetInstallationName(m.IdentityPublicKey(), id, name)
   462  }
   463  
   464  func (m *Messenger) EnableInstallation(id string) error {
   465  	installation, ok := m.allInstallations.Load(id)
   466  	if !ok {
   467  		return errors.New("no installation found")
   468  	}
   469  
   470  	err := m.encryptor.EnableInstallation(&m.identity.PublicKey, id)
   471  	if err != nil {
   472  		return err
   473  	}
   474  	installation.Enabled = true
   475  	// TODO(samyoul) remove storing of an updated reference pointer?
   476  	m.allInstallations.Store(id, installation)
   477  	return nil
   478  }
   479  
   480  func (m *Messenger) DisableInstallation(id string) error {
   481  	installation, ok := m.allInstallations.Load(id)
   482  	if !ok {
   483  		return errors.New("no installation found")
   484  	}
   485  
   486  	err := m.encryptor.DisableInstallation(&m.identity.PublicKey, id)
   487  	if err != nil {
   488  		return err
   489  	}
   490  	installation.Enabled = false
   491  	// TODO(samyoul) remove storing of an updated reference pointer?
   492  	m.allInstallations.Store(id, installation)
   493  	return nil
   494  }