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 }