github.com/status-im/status-go@v1.1.0/server/pairing/sync_device_test.go (about) 1 package pairing 2 3 import ( 4 "context" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "os" 9 "path/filepath" 10 "reflect" 11 "strings" 12 "testing" 13 "time" 14 15 "go.uber.org/zap" 16 17 "github.com/status-im/status-go/common/dbsetup" 18 "github.com/status-im/status-go/eth-node/crypto" 19 "github.com/status-im/status-go/protocol" 20 "github.com/status-im/status-go/protocol/encryption/multidevice" 21 "github.com/status-im/status-go/protocol/tt" 22 23 "github.com/stretchr/testify/require" 24 "github.com/stretchr/testify/suite" 25 26 "github.com/status-im/status-go/api" 27 "github.com/status-im/status-go/eth-node/types" 28 "github.com/status-im/status-go/multiaccounts/accounts" 29 "github.com/status-im/status-go/protocol/common" 30 "github.com/status-im/status-go/protocol/protobuf" 31 "github.com/status-im/status-go/protocol/requests" 32 accservice "github.com/status-im/status-go/services/accounts" 33 "github.com/status-im/status-go/services/browsers" 34 ) 35 36 const ( 37 pathWalletRoot = "m/44'/60'/0'/0" 38 pathEIP1581 = "m/43'/60'/1581'" 39 pathDefaultChat = pathEIP1581 + "/0'/0" 40 pathDefaultWallet = pathWalletRoot + "/0" 41 currentNetwork = "mainnet_rpc" 42 socialLinkURL = "https://github.com/status-im" 43 ensUsername = "bob.stateofus.eth" 44 ensChainID = 1 45 publicChatID = "localpairtest" 46 profileKeypairMnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon" 47 seedKeypairMnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" 48 profileKeypairMnemonic1 = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about about" 49 seedKeypairMnemonic1 = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about abandon" 50 path0 = "m/44'/60'/0'/0/0" 51 path1 = "m/44'/60'/0'/0/1" 52 expectedKDFIterations = 1024 53 ) 54 55 func TestSyncDeviceSuite(t *testing.T) { 56 suite.Run(t, new(SyncDeviceSuite)) 57 } 58 59 type SyncDeviceSuite struct { 60 suite.Suite 61 logger *zap.Logger 62 password string 63 tmpdir string 64 } 65 66 func (s *SyncDeviceSuite) SetupTest() { 67 s.logger = tt.MustCreateTestLogger() 68 s.password = "password" 69 s.tmpdir = s.T().TempDir() 70 } 71 72 func (s *SyncDeviceSuite) prepareBackendWithAccount(mnemonic, tmpdir string) *api.GethStatusBackend { 73 backend := s.prepareBackendWithoutAccount(tmpdir) 74 75 displayName, err := common.RandomAlphabeticalString(8) 76 s.Require().NoError(err) 77 78 deviceName, err := common.RandomAlphanumericString(8) 79 s.Require().NoError(err) 80 81 createAccount := requests.CreateAccount{ 82 RootDataDir: tmpdir, 83 KdfIterations: dbsetup.ReducedKDFIterationsNumber, 84 DisplayName: displayName, 85 DeviceName: deviceName, 86 Password: s.password, 87 CustomizationColor: "primary", 88 } 89 90 if mnemonic == "" { 91 _, err = backend.CreateAccountAndLogin(&createAccount) 92 } else { 93 _, err = backend.RestoreAccountAndLogin(&requests.RestoreAccount{ 94 Mnemonic: mnemonic, 95 FetchBackup: false, 96 CreateAccount: createAccount, 97 }) 98 } 99 100 s.Require().NoError(err) 101 102 accs, err := backend.GetAccounts() 103 s.Require().NoError(err) 104 s.Require().NotEmpty(accs[0].ColorHash) 105 106 return backend 107 } 108 109 func (s *SyncDeviceSuite) prepareBackendWithoutAccount(tmpdir string) *api.GethStatusBackend { 110 backend := api.NewGethStatusBackend() 111 backend.UpdateRootDataDir(tmpdir) 112 return backend 113 } 114 115 func (s *SyncDeviceSuite) pairAccounts(serverBackend *api.GethStatusBackend, serverDir string, 116 clientBackend *api.GethStatusBackend, clientDir string) { 117 118 // Start sender server 119 120 serverActiveAccount, err := serverBackend.GetActiveAccount() 121 require.NoError(s.T(), err) 122 123 serverKeystorePath := filepath.Join(serverDir, api.DefaultKeystoreRelativePath, serverActiveAccount.KeyUID) 124 serverConfig := &SenderServerConfig{ 125 SenderConfig: &SenderConfig{ 126 KeystorePath: serverKeystorePath, 127 DeviceType: "desktop", 128 KeyUID: serverActiveAccount.KeyUID, 129 Password: s.password, 130 }, 131 ServerConfig: new(ServerConfig), 132 } 133 134 configBytes, err := json.Marshal(serverConfig) 135 require.NoError(s.T(), err) 136 137 connectionString, err := StartUpSenderServer(serverBackend, string(configBytes)) 138 require.NoError(s.T(), err) 139 140 // Start receiving client 141 142 err = clientBackend.AccountManager().InitKeystore(filepath.Join(clientDir, api.DefaultKeystoreRelativePath)) 143 require.NoError(s.T(), err) 144 145 err = clientBackend.OpenAccounts() 146 require.NoError(s.T(), err) 147 148 clientPayloadSourceConfig := ReceiverClientConfig{ 149 ReceiverConfig: &ReceiverConfig{ 150 CreateAccount: &requests.CreateAccount{ 151 RootDataDir: clientDir, 152 KdfIterations: expectedKDFIterations, 153 }, 154 }, 155 ClientConfig: new(ClientConfig), 156 } 157 158 clientConfigBytes, err := json.Marshal(clientPayloadSourceConfig) 159 require.NoError(s.T(), err) 160 161 err = StartUpReceivingClient(clientBackend, connectionString, string(clientConfigBytes)) 162 require.NoError(s.T(), err) 163 164 require.True(s.T(), serverBackend.Messenger().HasPairedDevices()) 165 require.True(s.T(), clientBackend.Messenger().HasPairedDevices()) 166 } 167 168 func (s *SyncDeviceSuite) sendContactRequest(request *requests.SendContactRequest, messenger *protocol.Messenger) { 169 senderPublicKey := common.PubkeyToHex(messenger.IdentityPublicKey()) 170 s.logger.Info("sendContactRequest", zap.String("sender", senderPublicKey), zap.String("receiver", request.ID)) 171 172 resp, err := messenger.SendContactRequest(context.Background(), request) 173 s.Require().NoError(err) 174 s.Require().NotNil(resp) 175 } 176 177 func (s *SyncDeviceSuite) receiveContactRequest(messageText string, messenger *protocol.Messenger) *common.Message { 178 receiverPublicKey := types.EncodeHex(crypto.FromECDSAPub(messenger.IdentityPublicKey())) 179 s.logger.Info("receiveContactRequest", zap.String("receiver", receiverPublicKey)) 180 181 // Wait for the message to reach its destination 182 resp, err := protocol.WaitOnMessengerResponse( 183 messenger, 184 func(r *protocol.MessengerResponse) bool { 185 return len(r.Contacts) == 1 && len(r.Messages()) == 2 && len(r.ActivityCenterNotifications()) == 1 186 }, 187 "no messages", 188 ) 189 190 s.Require().NoError(err) 191 s.Require().NotNil(resp) 192 193 contactRequest := protocol.FindFirstByContentType(resp.Messages(), protobuf.ChatMessage_CONTACT_REQUEST) 194 s.Require().NotNil(contactRequest) 195 196 return contactRequest 197 } 198 199 func (s *SyncDeviceSuite) acceptContactRequest(contactRequest *common.Message, sender *protocol.Messenger, receiver *protocol.Messenger) { 200 senderPublicKey := types.EncodeHex(crypto.FromECDSAPub(sender.IdentityPublicKey())) 201 receiverPublicKey := types.EncodeHex(crypto.FromECDSAPub(receiver.IdentityPublicKey())) 202 s.logger.Info("acceptContactRequest", zap.String("sender", senderPublicKey), zap.String("receiver", receiverPublicKey)) 203 204 _, err := receiver.AcceptContactRequest(context.Background(), &requests.AcceptContactRequest{ID: types.Hex2Bytes(contactRequest.ID)}) 205 s.Require().NoError(err) 206 207 // Wait for the message to reach its destination 208 resp, err := protocol.WaitOnMessengerResponse( 209 sender, 210 func(r *protocol.MessengerResponse) bool { 211 return len(r.Contacts) == 1 && len(r.Messages()) == 2 && len(r.ActivityCenterNotifications()) == 1 212 }, 213 "no messages", 214 ) 215 s.Require().NoError(err) 216 s.Require().NotNil(resp) 217 } 218 219 func (s *SyncDeviceSuite) checkMutualContact(backend *api.GethStatusBackend, contactPublicKey string) { 220 messenger := backend.Messenger() 221 contacts := messenger.MutualContacts() 222 s.Require().Len(contacts, 1) 223 contact := contacts[0] 224 s.Require().Equal(contactPublicKey, contact.ID) 225 s.Require().Equal(protocol.ContactRequestStateSent, contact.ContactRequestLocalState) 226 s.Require().Equal(protocol.ContactRequestStateReceived, contact.ContactRequestRemoteState) 227 s.Require().NotNil(contact.DisplayName) 228 } 229 230 func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsSender() { 231 clientTmpDir := filepath.Join(s.tmpdir, "client") 232 clientBackend := s.prepareBackendWithAccount("", clientTmpDir) 233 serverTmpDir := filepath.Join(s.tmpdir, "server") 234 serverBackend := s.prepareBackendWithoutAccount(serverTmpDir) 235 defer func() { 236 require.NoError(s.T(), serverBackend.Logout()) 237 require.NoError(s.T(), clientBackend.Logout()) 238 }() 239 ctx := context.TODO() 240 241 err := serverBackend.AccountManager().InitKeystore(filepath.Join(serverTmpDir, api.DefaultKeystoreRelativePath)) 242 require.NoError(s.T(), err) 243 err = serverBackend.OpenAccounts() 244 require.NoError(s.T(), err) 245 246 serverPayloadSourceConfig := &ReceiverServerConfig{ 247 ReceiverConfig: &ReceiverConfig{ 248 CreateAccount: &requests.CreateAccount{ 249 RootDataDir: serverTmpDir, 250 KdfIterations: expectedKDFIterations, 251 }, 252 }, 253 ServerConfig: new(ServerConfig), 254 } 255 256 serverConfigBytes, err := json.Marshal(serverPayloadSourceConfig) 257 require.NoError(s.T(), err) 258 cs, err := StartUpReceiverServer(serverBackend, string(serverConfigBytes)) 259 require.NoError(s.T(), err) 260 261 // generate some data for the client 262 // generate bookmark 263 clientBrowserAPI := clientBackend.StatusNode().BrowserService().APIs()[0].Service.(*browsers.API) 264 _, err = clientBrowserAPI.StoreBookmark(ctx, browsers.Bookmark{ 265 Name: "status.im", 266 URL: "https://status.im", 267 }) 268 require.NoError(s.T(), err) 269 270 // generate ens username 271 err = clientBackend.StatusNode().EnsService().API().Add(ctx, ensChainID, ensUsername) 272 require.NoError(s.T(), err) 273 // generate profile showcase preferences 274 profileShowcasePreferences := protocol.DummyProfileShowcasePreferences(false) 275 err = clientBackend.Messenger().SetProfileShowcasePreferences(profileShowcasePreferences, false) 276 require.NoError(s.T(), err) 277 278 // startup sending client 279 clientActiveAccount, err := clientBackend.GetActiveAccount() 280 require.NoError(s.T(), err) 281 clientKeystorePath := filepath.Join(clientTmpDir, api.DefaultKeystoreRelativePath, clientActiveAccount.KeyUID) 282 clientPayloadSourceConfig := SenderClientConfig{ 283 SenderConfig: &SenderConfig{ 284 KeystorePath: clientKeystorePath, 285 DeviceType: "android", 286 KeyUID: clientActiveAccount.KeyUID, 287 Password: s.password, 288 }, 289 ClientConfig: new(ClientConfig), 290 } 291 clientConfigBytes, err := json.Marshal(clientPayloadSourceConfig) 292 require.NoError(s.T(), err) 293 err = StartUpSendingClient(clientBackend, cs, string(clientConfigBytes)) 294 require.NoError(s.T(), err) 295 296 // check that the server has the same data as the client 297 serverBrowserAPI := serverBackend.StatusNode().BrowserService().APIs()[0].Service.(*browsers.API) 298 299 bookmarks, err := serverBrowserAPI.GetBookmarks(ctx) 300 require.NoError(s.T(), err) 301 require.Equal(s.T(), 1, len(bookmarks)) 302 require.Equal(s.T(), "status.im", bookmarks[0].Name) 303 304 uds, err := serverBackend.StatusNode().EnsService().API().GetEnsUsernames(ctx) 305 require.NoError(s.T(), err) 306 require.Equal(s.T(), 1, len(uds)) 307 require.Equal(s.T(), ensUsername, uds[0].Username) 308 require.Equal(s.T(), uint64(ensChainID), uds[0].ChainID) 309 require.False(s.T(), uds[0].Removed) 310 require.Greater(s.T(), uds[0].Clock, uint64(0)) 311 312 serverProfileShowcasePreferences, err := serverBackend.Messenger().GetProfileShowcasePreferences() 313 require.NoError(s.T(), err) 314 require.True(s.T(), reflect.DeepEqual(profileShowcasePreferences, serverProfileShowcasePreferences)) 315 316 serverActiveAccount, err := serverBackend.GetActiveAccount() 317 require.NoError(s.T(), err) 318 require.Equal(s.T(), clientActiveAccount.Name, serverActiveAccount.Name) 319 require.Equal(s.T(), expectedKDFIterations, serverActiveAccount.KDFIterations) 320 321 serverMessenger := serverBackend.Messenger() 322 clientMessenger := clientBackend.Messenger() 323 require.True(s.T(), serverMessenger.HasPairedDevices()) 324 require.True(s.T(), clientMessenger.HasPairedDevices()) 325 326 serverNodeConfig, err := serverBackend.GetNodeConfig() 327 s.Require().NoError(err) 328 329 err = clientMessenger.DisableInstallation(serverNodeConfig.ShhextConfig.InstallationID) 330 require.NoError(s.T(), err) 331 require.False(s.T(), clientMessenger.HasPairedDevices()) 332 333 clientNodeConfig, err := clientBackend.GetNodeConfig() 334 require.NoError(s.T(), err) 335 err = serverMessenger.DisableInstallation(clientNodeConfig.ShhextConfig.InstallationID) 336 require.NoError(s.T(), err) 337 require.False(s.T(), serverMessenger.HasPairedDevices()) 338 339 // repeat local pairing, we should expect no error after receiver logged in 340 cs, err = StartUpReceiverServer(serverBackend, string(serverConfigBytes)) 341 require.NoError(s.T(), err) 342 err = StartUpSendingClient(clientBackend, cs, string(clientConfigBytes)) 343 require.NoError(s.T(), err) 344 require.True(s.T(), clientMessenger.HasPairedDevices()) 345 require.True(s.T(), serverMessenger.HasPairedDevices()) 346 347 // test if it's okay when account already exist but not logged in 348 require.NoError(s.T(), serverBackend.Logout()) 349 cs, err = StartUpReceiverServer(serverBackend, string(serverConfigBytes)) 350 require.NoError(s.T(), err) 351 err = StartUpSendingClient(clientBackend, cs, string(clientConfigBytes)) 352 require.NoError(s.T(), err) 353 } 354 355 func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsReceiver() { 356 clientTmpDir := filepath.Join(s.tmpdir, "client") 357 clientBackend := s.prepareBackendWithoutAccount(clientTmpDir) 358 ctx := context.TODO() 359 360 serverTmpDir := filepath.Join(s.tmpdir, "server") 361 serverBackend := s.prepareBackendWithAccount("", serverTmpDir) 362 defer func() { 363 require.NoError(s.T(), clientBackend.Logout()) 364 require.NoError(s.T(), serverBackend.Logout()) 365 }() 366 367 serverActiveAccount, err := serverBackend.GetActiveAccount() 368 require.NoError(s.T(), err) 369 serverKeystorePath := filepath.Join(serverTmpDir, api.DefaultKeystoreRelativePath, serverActiveAccount.KeyUID) 370 var config = &SenderServerConfig{ 371 SenderConfig: &SenderConfig{ 372 KeystorePath: serverKeystorePath, 373 DeviceType: "desktop", 374 KeyUID: serverActiveAccount.KeyUID, 375 Password: s.password, 376 }, 377 ServerConfig: new(ServerConfig), 378 } 379 configBytes, err := json.Marshal(config) 380 require.NoError(s.T(), err) 381 cs, err := StartUpSenderServer(serverBackend, string(configBytes)) 382 require.NoError(s.T(), err) 383 384 // generate some data for the server 385 // generate bookmark 386 serverBrowserAPI := serverBackend.StatusNode().BrowserService().APIs()[0].Service.(*browsers.API) 387 _, err = serverBrowserAPI.StoreBookmark(ctx, browsers.Bookmark{ 388 Name: "status.im", 389 URL: "https://status.im", 390 }) 391 require.NoError(s.T(), err) 392 393 serverMessenger := serverBackend.Messenger() 394 395 // generate ens username 396 err = serverBackend.StatusNode().EnsService().API().Add(ctx, ensChainID, ensUsername) 397 require.NoError(s.T(), err) 398 399 // generate profile showcase preferences 400 profileShowcasePreferences := protocol.DummyProfileShowcasePreferences(false) 401 err = serverMessenger.SetProfileShowcasePreferences(profileShowcasePreferences, false) 402 require.NoError(s.T(), err) 403 404 // generate local deleted message 405 _, err = serverMessenger.CreatePublicChat(&requests.CreatePublicChat{ID: publicChatID}) 406 require.NoError(s.T(), err) 407 serverChat := serverMessenger.Chat(publicChatID) 408 serverMessage := buildTestMessage(serverChat) 409 serverMessengerResponse, err := serverMessenger.SendChatMessage(ctx, serverMessage) 410 require.NoError(s.T(), err) 411 require.Equal(s.T(), 1, len(serverMessengerResponse.Messages())) 412 serverMessageID := serverMessengerResponse.Messages()[0].ID 413 _, err = serverMessenger.DeleteMessageForMeAndSync(ctx, publicChatID, serverMessageID) 414 require.NoError(s.T(), err) 415 416 err = clientBackend.AccountManager().InitKeystore(filepath.Join(clientTmpDir, api.DefaultKeystoreRelativePath)) 417 require.NoError(s.T(), err) 418 err = clientBackend.OpenAccounts() 419 require.NoError(s.T(), err) 420 421 clientPayloadSourceConfig := ReceiverClientConfig{ 422 ReceiverConfig: &ReceiverConfig{ 423 CreateAccount: &requests.CreateAccount{ 424 RootDataDir: clientTmpDir, 425 KdfIterations: expectedKDFIterations, 426 DeviceName: "device-1", 427 }, 428 }, 429 ClientConfig: new(ClientConfig), 430 } 431 clientConfigBytes, err := json.Marshal(clientPayloadSourceConfig) 432 require.NoError(s.T(), err) 433 err = StartUpReceivingClient(clientBackend, cs, string(clientConfigBytes)) 434 require.NoError(s.T(), err) 435 436 // check that the client has the same data as the server 437 clientMessenger := clientBackend.Messenger() 438 clientBrowserAPI := clientBackend.StatusNode().BrowserService().APIs()[0].Service.(*browsers.API) 439 bookmarks, err := clientBrowserAPI.GetBookmarks(ctx) 440 require.NoError(s.T(), err) 441 require.Equal(s.T(), 1, len(bookmarks)) 442 require.Equal(s.T(), "status.im", bookmarks[0].Name) 443 444 clientProfileShowcasePreferences, err := clientMessenger.GetProfileShowcasePreferences() 445 require.NoError(s.T(), err) 446 require.True(s.T(), reflect.DeepEqual(profileShowcasePreferences, clientProfileShowcasePreferences)) 447 448 uds, err := clientBackend.StatusNode().EnsService().API().GetEnsUsernames(ctx) 449 require.NoError(s.T(), err) 450 require.Equal(s.T(), 1, len(uds)) 451 require.Equal(s.T(), ensUsername, uds[0].Username) 452 require.Equal(s.T(), uint64(ensChainID), uds[0].ChainID) 453 deleteForMeMessages, err := clientMessenger.GetDeleteForMeMessages() 454 require.NoError(s.T(), err) 455 require.Equal(s.T(), 1, len(deleteForMeMessages)) 456 457 clientActiveAccount, err := clientBackend.GetActiveAccount() 458 require.NoError(s.T(), err) 459 require.Equal(s.T(), serverActiveAccount.Name, clientActiveAccount.Name) 460 require.Equal(s.T(), clientActiveAccount.KDFIterations, expectedKDFIterations) 461 462 require.True(s.T(), serverMessenger.HasPairedDevices()) 463 require.True(s.T(), clientMessenger.HasPairedDevices()) 464 465 clientNodeConfig, err := clientBackend.GetNodeConfig() 466 s.Require().NoError(err) 467 468 err = serverMessenger.DisableInstallation(clientNodeConfig.ShhextConfig.InstallationID) 469 require.NoError(s.T(), err) 470 require.False(s.T(), serverMessenger.HasPairedDevices()) 471 472 serverNodeConfig, err := serverBackend.GetNodeConfig() 473 require.NoError(s.T(), err) 474 err = clientMessenger.DisableInstallation(serverNodeConfig.ShhextConfig.InstallationID) 475 require.NoError(s.T(), err) 476 require.False(s.T(), clientMessenger.HasPairedDevices()) 477 478 // repeat local pairing, we should expect no error after receiver logged in 479 cs, err = StartUpSenderServer(serverBackend, string(configBytes)) 480 require.NoError(s.T(), err) 481 err = StartUpReceivingClient(clientBackend, cs, string(clientConfigBytes)) 482 require.NoError(s.T(), err) 483 require.True(s.T(), serverMessenger.HasPairedDevices()) 484 require.True(s.T(), clientMessenger.HasPairedDevices()) 485 486 // test if it's okay when account already exist but not logged in 487 require.NoError(s.T(), clientBackend.Logout()) 488 cs, err = StartUpSenderServer(serverBackend, string(configBytes)) 489 require.NoError(s.T(), err) 490 err = StartUpReceivingClient(clientBackend, cs, string(clientConfigBytes)) 491 require.NoError(s.T(), err) 492 } 493 494 func (s *SyncDeviceSuite) TestPairingThreeDevices() { 495 bobTmpDir := filepath.Join(s.tmpdir, "bob") 496 bobBackend := s.prepareBackendWithAccount("", bobTmpDir) 497 bobMessenger := bobBackend.Messenger() 498 _, err := bobMessenger.Start() 499 s.Require().NoError(err) 500 501 alice1TmpDir := filepath.Join(s.tmpdir, "alice1") 502 alice1Backend := s.prepareBackendWithAccount("", alice1TmpDir) 503 alice1Messenger := alice1Backend.Messenger() 504 _, err = alice1Messenger.Start() 505 s.Require().NoError(err) 506 507 alice2TmpDir := filepath.Join(s.tmpdir, "alice2") 508 alice2Backend := s.prepareBackendWithoutAccount(alice2TmpDir) 509 510 alice3TmpDir := filepath.Join(s.tmpdir, "alice3") 511 alice3Backend := s.prepareBackendWithoutAccount(alice3TmpDir) 512 513 defer func() { 514 require.NoError(s.T(), bobBackend.Logout()) 515 require.NoError(s.T(), alice1Backend.Logout()) 516 require.NoError(s.T(), alice2Backend.Logout()) 517 require.NoError(s.T(), alice3Backend.Logout()) 518 }() 519 520 // Make Alice and Bob mutual contacts 521 bobPublicKey := bobMessenger.GetSelfContact().ID 522 request := &requests.SendContactRequest{ 523 ID: bobPublicKey, 524 Message: protocol.RandomLettersString(5), 525 } 526 s.sendContactRequest(request, alice1Messenger) 527 contactRequest := s.receiveContactRequest(request.Message, bobMessenger) 528 s.acceptContactRequest(contactRequest, alice1Messenger, bobMessenger) 529 s.checkMutualContact(alice1Backend, bobPublicKey) 530 531 // We shouldn't sync ourselves as a contact, so we check there's only Bob 532 // https://github.com/status-im/status-go/issues/3667 533 s.Require().Equal(1, len(alice1Backend.Messenger().Contacts())) 534 535 // Pair alice-1 <-> alice-2 536 s.logger.Info("pairing Alice-1 and Alice-2") 537 s.pairAccounts(alice1Backend, alice1TmpDir, alice2Backend, alice2TmpDir) 538 539 s.checkMutualContact(alice2Backend, bobPublicKey) 540 s.Require().Equal(1, len(alice2Backend.Messenger().Contacts())) 541 542 // Pair Alice-2 <-> ALice-3 543 s.logger.Info("pairing Alice-2 and Alice-3") 544 s.pairAccounts(alice2Backend, alice2TmpDir, alice3Backend, alice3TmpDir) 545 546 s.checkMutualContact(alice3Backend, bobPublicKey) 547 s.Require().Equal(1, len(alice3Backend.Messenger().Contacts())) 548 } 549 550 func (s *SyncDeviceSuite) createUser(name string) (*api.GethStatusBackend, string) { 551 tmpDir := filepath.Join(s.tmpdir, name) 552 backend := s.prepareBackendWithAccount("", tmpDir) 553 _, err := backend.Messenger().Start() 554 s.Require().NoError(err) 555 return backend, tmpDir 556 } 557 558 func (s *SyncDeviceSuite) TestPairPendingContactRequest() { 559 bobBackend, _ := s.createUser("bob") 560 defer func() { 561 s.Require().NoError(bobBackend.Logout()) 562 }() 563 564 alice1Backend, alice1TmpDir := s.createUser("alice1") 565 defer func() { 566 s.Require().NoError(alice1Backend.Logout()) 567 }() 568 569 // Create a pending CR from alice to bob 570 bobPublicKey := bobBackend.Messenger().IdentityPublicKeyString() 571 alicePublicKey := alice1Backend.Messenger().IdentityPublicKeyString() 572 request := &requests.SendContactRequest{ 573 ID: alicePublicKey, 574 Message: protocol.RandomLettersString(5), 575 } 576 s.sendContactRequest(request, bobBackend.Messenger()) 577 contactRequest := s.receiveContactRequest(request.Message, alice1Backend.Messenger()) 578 s.Require().Equal(request.Message, contactRequest.Text) 579 580 alice2TmpDir := filepath.Join(s.tmpdir, "alice2") 581 alice2Backend := s.prepareBackendWithoutAccount(alice2TmpDir) 582 defer func() { 583 s.Require().NoError(alice2Backend.Logout()) 584 }() 585 586 // Pair alice-1 <-> alice-2 587 s.logger.Info("pairing Alice-1 and Alice-2") 588 s.pairAccounts(alice1Backend, alice1TmpDir, alice2Backend, alice2TmpDir) 589 590 s.logger.Debug("public keys", 591 zap.String("alice", alice1Backend.Messenger().IdentityPublicKeyString()), 592 zap.String("bob", bobBackend.Messenger().IdentityPublicKeyString()), 593 ) 594 595 ensurePendingContact := func(m *protocol.Messenger) { 596 contacts := m.Contacts() 597 s.Require().Len(contacts, 1) 598 599 c := contacts[0] 600 s.Require().Equal(bobPublicKey, c.ID) 601 s.Require().Equal(protocol.ContactRequestStateReceived, c.ContactRequestRemoteState) 602 s.Require().Equal(protocol.ContactRequestStateNone, c.ContactRequestLocalState) 603 604 acRequest := protocol.ActivityCenterNotificationsRequest{ 605 ActivityTypes: []protocol.ActivityCenterType{ 606 protocol.ActivityCenterNotificationTypeContactRequest, 607 }, 608 ReadType: protocol.ActivityCenterQueryParamsReadAll, 609 Limit: 10, 610 } 611 r, err := m.ActivityCenterNotifications(acRequest) 612 s.Require().NoError(err) 613 s.Require().Len(r.Notifications, 1) 614 } 615 616 // Ensure both devices have the pending Bob contact 617 ensurePendingContact(alice1Backend.Messenger()) 618 ensurePendingContact(alice2Backend.Messenger()) 619 } 620 621 type contactRequestAction func(messenger *protocol.Messenger, contactRequestID string) (*protocol.MessengerResponse, error) 622 type notificationValidateFunc func(r *protocol.ActivityCenterPaginationResponse) 623 624 func (s *SyncDeviceSuite) testPairContactRequest(requestAction contactRequestAction, validateFunc notificationValidateFunc) { 625 bobBackend, _ := s.createUser("bob") 626 defer func() { 627 s.Require().NoError(bobBackend.Logout()) 628 }() 629 630 alice1Backend, alice1TmpDir := s.createUser("alice1") 631 defer func() { 632 s.Require().NoError(alice1Backend.Logout()) 633 }() 634 635 alicePublicKey := alice1Backend.Messenger().IdentityPublicKeyString() 636 request := &requests.SendContactRequest{ 637 ID: alicePublicKey, 638 Message: protocol.RandomLettersString(5), 639 } 640 s.sendContactRequest(request, bobBackend.Messenger()) 641 contactRequest := s.receiveContactRequest(request.Message, alice1Backend.Messenger()) 642 s.Require().Equal(request.Message, contactRequest.Text) 643 _, err := requestAction(alice1Backend.Messenger(), contactRequest.ID) 644 s.Require().NoError(err) 645 646 alice2TmpDir := filepath.Join(s.tmpdir, "alice2") 647 alice2Backend := s.prepareBackendWithoutAccount(alice2TmpDir) 648 defer func() { 649 s.Require().NoError(alice2Backend.Logout()) 650 }() 651 s.pairAccounts(alice1Backend, alice1TmpDir, alice2Backend, alice2TmpDir) 652 653 internalNotificationValidateFunc := func(m *protocol.Messenger) { 654 acRequest := protocol.ActivityCenterNotificationsRequest{ 655 ActivityTypes: []protocol.ActivityCenterType{ 656 protocol.ActivityCenterNotificationTypeContactRequest, 657 }, 658 ReadType: protocol.ActivityCenterQueryParamsReadAll, 659 Limit: 10, 660 } 661 r, err := m.ActivityCenterNotifications(acRequest) 662 s.Require().NoError(err) 663 validateFunc(r) 664 } 665 666 internalNotificationValidateFunc(alice1Backend.Messenger()) 667 internalNotificationValidateFunc(alice2Backend.Messenger()) 668 } 669 670 func (s *SyncDeviceSuite) TestPairDeclineContactRequest() { 671 declineContactRequest := func(messenger *protocol.Messenger, contactRequestID string) (*protocol.MessengerResponse, error) { 672 return messenger.DeclineContactRequest(context.Background(), &requests.DeclineContactRequest{ID: types.Hex2Bytes(contactRequestID)}) 673 } 674 s.testPairContactRequest(declineContactRequest, func(r *protocol.ActivityCenterPaginationResponse) { 675 s.Require().Len(r.Notifications, 1) 676 s.Require().False(r.Notifications[0].Accepted) 677 s.Require().True(r.Notifications[0].Dismissed) 678 s.Require().True(r.Notifications[0].Read) 679 }) 680 } 681 682 func (s *SyncDeviceSuite) TestPairAcceptContactRequest() { 683 acceptContactRequest := func(messenger *protocol.Messenger, contactRequestID string) (*protocol.MessengerResponse, error) { 684 return messenger.AcceptContactRequest(context.Background(), &requests.AcceptContactRequest{ID: types.Hex2Bytes(contactRequestID)}) 685 } 686 s.testPairContactRequest(acceptContactRequest, func(r *protocol.ActivityCenterPaginationResponse) { 687 s.Require().Len(r.Notifications, 1) 688 s.Require().True(r.Notifications[0].Accepted) 689 s.Require().False(r.Notifications[0].Dismissed) 690 s.Require().True(r.Notifications[0].Read) 691 }) 692 } 693 694 type testTimeSource struct{} 695 696 func (t *testTimeSource) GetCurrentTime() uint64 { 697 return uint64(time.Now().Unix()) 698 } 699 700 func buildTestMessage(chat *protocol.Chat) *common.Message { 701 clock, timestamp := chat.NextClockAndTimestamp(&testTimeSource{}) 702 message := common.NewMessage() 703 message.Text = "text-input-message" 704 message.ChatId = chat.ID 705 message.Clock = clock 706 message.Timestamp = timestamp 707 message.WhisperTimestamp = clock 708 message.LocalChatID = chat.ID 709 message.ContentType = protobuf.ChatMessage_TEXT_PLAIN 710 switch chat.ChatType { 711 case protocol.ChatTypePublic, protocol.ChatTypeProfile: 712 message.MessageType = protobuf.MessageType_PUBLIC_GROUP 713 case protocol.ChatTypeOneToOne: 714 message.MessageType = protobuf.MessageType_ONE_TO_ONE 715 case protocol.ChatTypePrivateGroupChat: 716 message.MessageType = protobuf.MessageType_PRIVATE_GROUP 717 } 718 719 return message 720 } 721 722 func (s *SyncDeviceSuite) getSeedPhraseKeypairForTest(backend *api.GethStatusBackend, mnemonic string, server bool) *accounts.Keypair { 723 generatedAccount, err := backend.AccountManager().AccountsGenerator().ImportMnemonic(mnemonic, "") 724 require.NoError(s.T(), err) 725 generatedDerivedAccs, err := backend.AccountManager().AccountsGenerator().DeriveAddresses(generatedAccount.ID, []string{path0, path1}) 726 require.NoError(s.T(), err) 727 728 seedPhraseKp := &accounts.Keypair{ 729 KeyUID: generatedAccount.KeyUID, 730 Name: "SeedPhraseImported", 731 Type: accounts.KeypairTypeSeed, 732 DerivedFrom: generatedAccount.Address, 733 } 734 i := 0 735 for path, ga := range generatedDerivedAccs { 736 acc := &accounts.Account{ 737 Address: types.HexToAddress(ga.Address), 738 KeyUID: generatedAccount.KeyUID, 739 Wallet: false, 740 Chat: false, 741 Type: accounts.AccountTypeSeed, 742 Path: path, 743 PublicKey: types.HexBytes(ga.PublicKey), 744 Name: fmt.Sprintf("Acc_%d", i), 745 Operable: accounts.AccountFullyOperable, 746 Emoji: fmt.Sprintf("Emoji_%d", i), 747 ColorID: "blue", 748 } 749 if !server { 750 acc.Operable = accounts.AccountNonOperable 751 } 752 seedPhraseKp.Accounts = append(seedPhraseKp.Accounts, acc) 753 i++ 754 } 755 756 return seedPhraseKp 757 } 758 759 func containsKeystoreFile(directory, key string) bool { 760 files, err := os.ReadDir(directory) 761 if err != nil { 762 return false 763 } 764 765 for _, file := range files { 766 if strings.Contains(file.Name(), strings.ToLower(key)) { 767 return true 768 } 769 } 770 return false 771 } 772 773 func (s *SyncDeviceSuite) TestTransferringKeystoreFiles() { 774 ctx := context.TODO() 775 776 serverTmpDir := filepath.Join(s.tmpdir, "server") 777 serverBackend := s.prepareBackendWithAccount(profileKeypairMnemonic, serverTmpDir) 778 779 clientTmpDir := filepath.Join(s.tmpdir, "client") 780 clientBackend := s.prepareBackendWithAccount(profileKeypairMnemonic, clientTmpDir) 781 defer func() { 782 require.NoError(s.T(), clientBackend.Logout()) 783 require.NoError(s.T(), serverBackend.Logout()) 784 }() 785 786 serverBackend.Messenger().SetLocalPairing(true) 787 clientBackend.Messenger().SetLocalPairing(true) 788 789 serverActiveAccount, err := serverBackend.GetActiveAccount() 790 require.NoError(s.T(), err) 791 792 clientActiveAccount, err := clientBackend.GetActiveAccount() 793 require.NoError(s.T(), err) 794 795 require.True(s.T(), serverActiveAccount.KeyUID == clientActiveAccount.KeyUID) 796 797 serverSeedPhraseKp := s.getSeedPhraseKeypairForTest(serverBackend, seedKeypairMnemonic, true) 798 serverAccountsAPI := serverBackend.StatusNode().AccountService().APIs()[1].Service.(*accservice.API) 799 err = serverAccountsAPI.ImportMnemonic(ctx, seedKeypairMnemonic, s.password) 800 require.NoError(s.T(), err, "importing mnemonic for new keypair on server") 801 err = serverAccountsAPI.AddKeypair(ctx, s.password, serverSeedPhraseKp) 802 require.NoError(s.T(), err, "saving seed phrase keypair on server with keystore files created") 803 804 clientSeedPhraseKp := s.getSeedPhraseKeypairForTest(serverBackend, seedKeypairMnemonic, true) 805 clientAccountsAPI := clientBackend.StatusNode().AccountService().APIs()[1].Service.(*accservice.API) 806 err = clientAccountsAPI.SaveKeypair(ctx, clientSeedPhraseKp) 807 require.NoError(s.T(), err, "saving seed phrase keypair on client without keystore files") 808 809 // check server - server should contain keystore files for imported seed phrase 810 serverKeystorePath := filepath.Join(serverTmpDir, api.DefaultKeystoreRelativePath, serverActiveAccount.KeyUID) 811 require.True(s.T(), containsKeystoreFile(serverKeystorePath, serverSeedPhraseKp.DerivedFrom[2:])) 812 for _, acc := range serverSeedPhraseKp.Accounts { 813 require.True(s.T(), containsKeystoreFile(serverKeystorePath, acc.Address.String()[2:])) 814 } 815 816 // check client - client should not contain keystore files for imported seed phrase 817 clientKeystorePath := filepath.Join(clientTmpDir, api.DefaultKeystoreRelativePath, clientActiveAccount.KeyUID) 818 require.False(s.T(), containsKeystoreFile(clientKeystorePath, clientSeedPhraseKp.DerivedFrom[2:])) 819 for _, acc := range clientSeedPhraseKp.Accounts { 820 require.False(s.T(), containsKeystoreFile(clientKeystorePath, acc.Address.String()[2:])) 821 } 822 823 // prepare sender 824 var config = KeystoreFilesSenderServerConfig{ 825 SenderConfig: &KeystoreFilesSenderConfig{ 826 KeystoreFilesConfig: KeystoreFilesConfig{ 827 KeystorePath: serverKeystorePath, 828 LoggedInKeyUID: serverActiveAccount.KeyUID, 829 Password: s.password, 830 }, 831 KeypairsToExport: []string{serverSeedPhraseKp.KeyUID}, 832 }, 833 ServerConfig: new(ServerConfig), 834 } 835 configBytes, err := json.Marshal(config) 836 require.NoError(s.T(), err) 837 cs, err := StartUpKeystoreFilesSenderServer(serverBackend, string(configBytes)) 838 require.NoError(s.T(), err) 839 840 // prepare receiver 841 clientPayloadSourceConfig := KeystoreFilesReceiverClientConfig{ 842 ReceiverConfig: &KeystoreFilesReceiverConfig{ 843 KeystoreFilesConfig: KeystoreFilesConfig{ 844 KeystorePath: clientKeystorePath, 845 LoggedInKeyUID: clientActiveAccount.KeyUID, 846 Password: s.password, 847 }, 848 KeypairsToImport: []string{serverSeedPhraseKp.KeyUID}, 849 }, 850 ClientConfig: new(ClientConfig), 851 } 852 clientConfigBytes, err := json.Marshal(clientPayloadSourceConfig) 853 require.NoError(s.T(), err) 854 err = StartUpKeystoreFilesReceivingClient(clientBackend, cs, string(clientConfigBytes)) 855 require.NoError(s.T(), err) 856 857 // check client - client should contain keystore files for imported seed phrase 858 accountManager := clientBackend.AccountManager() 859 accGenerator := accountManager.AccountsGenerator() 860 require.True(s.T(), containsKeystoreFile(clientKeystorePath, clientSeedPhraseKp.DerivedFrom[2:])) 861 for _, acc := range clientSeedPhraseKp.Accounts { 862 require.True(s.T(), containsKeystoreFile(clientKeystorePath, acc.Address.String()[2:])) 863 } 864 865 // reinit keystore on client 866 require.NoError(s.T(), accountManager.InitKeystore(clientKeystorePath)) 867 868 // check keystore on client 869 genAccInfo, err := accGenerator.LoadAccount(clientSeedPhraseKp.DerivedFrom, s.password) 870 require.NoError(s.T(), err) 871 require.Equal(s.T(), clientSeedPhraseKp.KeyUID, genAccInfo.KeyUID) 872 for _, acc := range clientSeedPhraseKp.Accounts { 873 genAccInfo, err := accGenerator.LoadAccount(acc.Address.String(), s.password) 874 require.NoError(s.T(), err) 875 require.Equal(s.T(), acc.Address.String(), genAccInfo.Address) 876 } 877 } 878 879 func (s *SyncDeviceSuite) TestTransferringKeystoreFilesAfterStopUisngKeycard() { 880 s.T().Skip("flaky test") 881 882 ctx := context.TODO() 883 884 // Prepare server 885 serverTmpDir := filepath.Join(s.tmpdir, "server") 886 serverBackend := s.prepareBackendWithAccount(profileKeypairMnemonic1, serverTmpDir) 887 serverMessenger := serverBackend.Messenger() 888 serverAccountsAPI := serverBackend.StatusNode().AccountService().APIs()[1].Service.(*accservice.API) 889 890 // Prepare client 891 clientTmpDir := filepath.Join(s.tmpdir, "client") 892 clientBackend := s.prepareBackendWithAccount(profileKeypairMnemonic1, clientTmpDir) 893 clientMessenger := clientBackend.Messenger() 894 clientAccountsAPI := clientBackend.StatusNode().AccountService().APIs()[1].Service.(*accservice.API) 895 896 defer func() { 897 require.NoError(s.T(), clientBackend.Logout()) 898 require.NoError(s.T(), serverBackend.Logout()) 899 }() 900 901 // Pair server and client 902 im1 := &multidevice.InstallationMetadata{ 903 Name: "client-device", 904 DeviceType: "client-device-type", 905 } 906 settings, err := clientBackend.GetSettings() 907 s.Require().NoError(err) 908 err = clientMessenger.SetInstallationMetadata(settings.InstallationID, im1) 909 s.Require().NoError(err) 910 response, err := clientMessenger.SendPairInstallation(context.Background(), nil) 911 s.Require().NoError(err) 912 s.Require().NotNil(response) 913 s.Require().Len(response.Chats(), 1) 914 s.Require().False(response.Chats()[0].Active) 915 916 response, err = protocol.WaitOnMessengerResponse( 917 serverMessenger, 918 func(r *protocol.MessengerResponse) bool { 919 for _, i := range r.Installations() { 920 if i.ID == settings.InstallationID { 921 return true 922 } 923 } 924 return false 925 }, 926 "installation not received", 927 ) 928 929 s.Require().NoError(err) 930 931 found := false 932 for _, i := range response.Installations() { 933 found = i.ID == settings.InstallationID && 934 i.InstallationMetadata != nil && 935 i.InstallationMetadata.Name == im1.Name && 936 i.InstallationMetadata.DeviceType == im1.DeviceType 937 if found { 938 break 939 } 940 } 941 s.Require().True(found) 942 943 err = serverMessenger.EnableInstallation(settings.InstallationID) 944 s.Require().NoError(err) 945 946 // Check if the logged in account is the same on server and client 947 serverActiveAccount, err := serverBackend.GetActiveAccount() 948 require.NoError(s.T(), err) 949 clientActiveAccount, err := clientBackend.GetActiveAccount() 950 require.NoError(s.T(), err) 951 require.True(s.T(), serverActiveAccount.KeyUID == clientActiveAccount.KeyUID) 952 953 ////////////////////////////////////////////////////////////////////////////// 954 // From this point this test is trying to simulate the following scenario: 955 // - add a new seed phrase keypair on server 956 // - sync it to client 957 // - convert it to a keycard keypair on server 958 // - sync it to client 959 // - stop using keycard on server 960 // - sync it to client 961 // - try to transfer keystore files from server to client 962 ////////////////////////////////////////////////////////////////////////////// 963 964 ////////////////////////////////////////////////////////////////////////////// 965 // Add new seed phrase keypair to server and sync it to client 966 ////////////////////////////////////////////////////////////////////////////// 967 serverSeedPhraseKp := s.getSeedPhraseKeypairForTest(serverBackend, seedKeypairMnemonic1, true) 968 err = serverAccountsAPI.ImportMnemonic(ctx, seedKeypairMnemonic1, s.password) 969 require.NoError(s.T(), err, "importing mnemonic for new keypair on server") 970 err = serverAccountsAPI.AddKeypair(ctx, s.password, serverSeedPhraseKp) 971 require.NoError(s.T(), err, "saving seed phrase keypair on server with keystore files created") 972 973 // Wait for sync messages to be received on client 974 err = tt.RetryWithBackOff(func() error { 975 response, err := clientMessenger.RetrieveAll() 976 if err != nil { 977 return err 978 } 979 980 for _, kp := range response.Keypairs { 981 if kp.KeyUID == serverSeedPhraseKp.KeyUID { 982 return nil 983 } 984 } 985 986 return errors.New("no sync keypair received") 987 }) 988 s.Require().NoError(err) 989 990 // Check if the keypair saved on client is the same as the one on server 991 serverKp, err := serverAccountsAPI.GetKeypairByKeyUID(ctx, serverSeedPhraseKp.KeyUID) 992 s.Require().NoError(err) 993 clientKp, err := clientAccountsAPI.GetKeypairByKeyUID(ctx, serverSeedPhraseKp.KeyUID) 994 s.Require().NoError(err) 995 996 s.Require().True(serverKp.KeyUID == clientKp.KeyUID && 997 serverKp.Name == clientKp.Name && 998 serverKp.Type == clientKp.Type && 999 serverKp.DerivedFrom == clientKp.DerivedFrom && 1000 serverKp.LastUsedDerivationIndex == clientKp.LastUsedDerivationIndex && 1001 serverKp.Clock == clientKp.Clock && 1002 len(serverKp.Accounts) == len(clientKp.Accounts) && 1003 len(serverKp.Keycards) == len(clientKp.Keycards)) 1004 1005 // Check server - server should contain keystore files for imported seed phrase 1006 serverKeystorePath := filepath.Join(serverTmpDir, api.DefaultKeystoreRelativePath, serverActiveAccount.KeyUID) 1007 require.True(s.T(), containsKeystoreFile(serverKeystorePath, serverKp.DerivedFrom[2:])) 1008 for _, acc := range serverKp.Accounts { 1009 require.True(s.T(), containsKeystoreFile(serverKeystorePath, acc.Address.String()[2:])) 1010 } 1011 1012 // Check client - client should not contain keystore files for imported seed phrase 1013 clientKeystorePath := filepath.Join(clientTmpDir, api.DefaultKeystoreRelativePath, clientActiveAccount.KeyUID) 1014 require.False(s.T(), containsKeystoreFile(clientKeystorePath, clientKp.DerivedFrom[2:])) 1015 for _, acc := range clientKp.Accounts { 1016 require.False(s.T(), containsKeystoreFile(clientKeystorePath, acc.Address.String()[2:])) 1017 } 1018 1019 ////////////////////////////////////////////////////////////////////////////// 1020 // Convert it to a keycard keypair on server and sync it to client 1021 ////////////////////////////////////////////////////////////////////////////// 1022 err = serverAccountsAPI.SaveOrUpdateKeycard(ctx, &accounts.Keycard{ 1023 KeycardUID: "1234", 1024 KeycardName: "new-keycard", 1025 KeyUID: serverKp.KeyUID, 1026 AccountsAddresses: []types.Address{serverKp.Accounts[0].Address, serverKp.Accounts[1].Address}, 1027 }, false) 1028 s.Require().NoError(err) 1029 1030 // Wait for sync messages to be received on client 1031 err = tt.RetryWithBackOff(func() error { 1032 response, err := clientMessenger.RetrieveAll() 1033 if err != nil { 1034 return err 1035 } 1036 1037 for _, kp := range response.Keypairs { 1038 if kp.KeyUID == serverKp.KeyUID { 1039 return nil 1040 } 1041 } 1042 return errors.New("no sync keypair received") 1043 }) 1044 s.Require().NoError(err) 1045 1046 // Check if the keypair saved on client is the same as the one on server 1047 serverKp, err = serverAccountsAPI.GetKeypairByKeyUID(ctx, serverSeedPhraseKp.KeyUID) 1048 s.Require().NoError(err) 1049 clientKp, err = clientAccountsAPI.GetKeypairByKeyUID(ctx, serverSeedPhraseKp.KeyUID) 1050 s.Require().NoError(err) 1051 1052 s.Require().True(serverKp.KeyUID == clientKp.KeyUID && 1053 serverKp.Name == clientKp.Name && 1054 serverKp.Type == clientKp.Type && 1055 serverKp.DerivedFrom == clientKp.DerivedFrom && 1056 serverKp.LastUsedDerivationIndex == clientKp.LastUsedDerivationIndex && 1057 serverKp.Clock == clientKp.Clock && 1058 len(serverKp.Accounts) == len(clientKp.Accounts) && 1059 len(serverKp.Keycards) == len(clientKp.Keycards) && 1060 len(serverKp.Keycards) == 1) 1061 1062 // Check server - server should not contain keystore files for imported seed phrase 1063 require.False(s.T(), containsKeystoreFile(serverKeystorePath, serverKp.DerivedFrom[2:])) 1064 for _, acc := range serverKp.Accounts { 1065 require.False(s.T(), containsKeystoreFile(serverKeystorePath, acc.Address.String()[2:])) 1066 } 1067 1068 // Check client - client should not contain keystore files for imported seed phrase 1069 require.False(s.T(), containsKeystoreFile(clientKeystorePath, clientKp.DerivedFrom[2:])) 1070 for _, acc := range clientKp.Accounts { 1071 require.False(s.T(), containsKeystoreFile(clientKeystorePath, acc.Address.String()[2:])) 1072 } 1073 1074 ////////////////////////////////////////////////////////////////////////////// 1075 // Stop using keycard on server and sync it to client 1076 ////////////////////////////////////////////////////////////////////////////// 1077 err = serverAccountsAPI.MigrateNonProfileKeycardKeypairToApp(ctx, seedKeypairMnemonic1, s.password) 1078 s.Require().NoError(err) 1079 1080 // Wait for sync messages to be received on client 1081 err = tt.RetryWithBackOff(func() error { 1082 response, err := clientMessenger.RetrieveAll() 1083 if err != nil { 1084 return err 1085 } 1086 1087 for _, kp := range response.Keypairs { 1088 if kp.KeyUID == serverKp.KeyUID { 1089 return nil 1090 } 1091 } 1092 return errors.New("no sync keypair received") 1093 }) 1094 s.Require().NoError(err) 1095 1096 // Check if the keypair saved on client is the same as the one on server 1097 serverKp, err = serverAccountsAPI.GetKeypairByKeyUID(ctx, serverSeedPhraseKp.KeyUID) 1098 s.Require().NoError(err) 1099 clientKp, err = clientAccountsAPI.GetKeypairByKeyUID(ctx, serverSeedPhraseKp.KeyUID) 1100 s.Require().NoError(err) 1101 1102 s.Require().True(serverKp.KeyUID == clientKp.KeyUID && 1103 serverKp.Name == clientKp.Name && 1104 serverKp.Type == clientKp.Type && 1105 serverKp.DerivedFrom == clientKp.DerivedFrom && 1106 serverKp.LastUsedDerivationIndex == clientKp.LastUsedDerivationIndex && 1107 serverKp.Clock == clientKp.Clock && 1108 len(serverKp.Accounts) == len(clientKp.Accounts) && 1109 len(serverKp.Keycards) == len(clientKp.Keycards) && 1110 len(serverKp.Keycards) == 0) 1111 1112 // Check server - server should contain keystore files for imported seed phrase 1113 require.True(s.T(), containsKeystoreFile(serverKeystorePath, serverKp.DerivedFrom[2:])) 1114 for _, acc := range serverKp.Accounts { 1115 require.True(s.T(), containsKeystoreFile(serverKeystorePath, acc.Address.String()[2:])) 1116 } 1117 1118 // Check client - client should not contain keystore files for imported seed phrase 1119 require.False(s.T(), containsKeystoreFile(clientKeystorePath, clientKp.DerivedFrom[2:])) 1120 for _, acc := range clientKp.Accounts { 1121 require.False(s.T(), containsKeystoreFile(clientKeystorePath, acc.Address.String()[2:])) 1122 } 1123 1124 ////////////////////////////////////////////////////////////////////////////// 1125 // Try to transfer keystore files from server to client 1126 ////////////////////////////////////////////////////////////////////////////// 1127 1128 serverMessenger.SetLocalPairing(true) 1129 clientMessenger.SetLocalPairing(true) 1130 1131 // prepare sender 1132 var config = KeystoreFilesSenderServerConfig{ 1133 SenderConfig: &KeystoreFilesSenderConfig{ 1134 KeystoreFilesConfig: KeystoreFilesConfig{ 1135 KeystorePath: serverKeystorePath, 1136 LoggedInKeyUID: serverActiveAccount.KeyUID, 1137 Password: s.password, 1138 }, 1139 KeypairsToExport: []string{serverKp.KeyUID}, 1140 }, 1141 ServerConfig: new(ServerConfig), 1142 } 1143 configBytes, err := json.Marshal(config) 1144 require.NoError(s.T(), err) 1145 cs, err := StartUpKeystoreFilesSenderServer(serverBackend, string(configBytes)) 1146 require.NoError(s.T(), err) 1147 1148 // prepare receiver 1149 clientPayloadSourceConfig := KeystoreFilesReceiverClientConfig{ 1150 ReceiverConfig: &KeystoreFilesReceiverConfig{ 1151 KeystoreFilesConfig: KeystoreFilesConfig{ 1152 KeystorePath: clientKeystorePath, 1153 LoggedInKeyUID: clientActiveAccount.KeyUID, 1154 Password: s.password, 1155 }, 1156 KeypairsToImport: []string{clientKp.KeyUID}, 1157 }, 1158 ClientConfig: new(ClientConfig), 1159 } 1160 clientConfigBytes, err := json.Marshal(clientPayloadSourceConfig) 1161 require.NoError(s.T(), err) 1162 err = StartUpKeystoreFilesReceivingClient(clientBackend, cs, string(clientConfigBytes)) 1163 require.NoError(s.T(), err) 1164 1165 // Check server - server should contain keystore files for imported seed phrase 1166 require.True(s.T(), containsKeystoreFile(serverKeystorePath, serverKp.DerivedFrom[2:])) 1167 for _, acc := range serverKp.Accounts { 1168 require.True(s.T(), containsKeystoreFile(serverKeystorePath, acc.Address.String()[2:])) 1169 } 1170 1171 // Check client - client should contain keystore files for imported seed phrase 1172 require.True(s.T(), containsKeystoreFile(clientKeystorePath, clientKp.DerivedFrom[2:])) 1173 for _, acc := range clientKp.Accounts { 1174 require.True(s.T(), containsKeystoreFile(clientKeystorePath, acc.Address.String()[2:])) 1175 } 1176 } 1177 1178 func (s *SyncDeviceSuite) TestPreventLoggedInAccountLocalPairingClientAsReceiver() { 1179 clientTmpDir := filepath.Join(s.tmpdir, "client") 1180 clientBackend := s.prepareBackendWithAccount("", clientTmpDir) 1181 serverTmpDir := filepath.Join(s.tmpdir, "server") 1182 serverBackend := s.prepareBackendWithAccount("", serverTmpDir) 1183 defer func() { 1184 s.NoError(serverBackend.Logout()) 1185 s.NoError(clientBackend.Logout()) 1186 }() 1187 1188 serverActiveAccount, err := serverBackend.GetActiveAccount() 1189 s.NoError(err) 1190 serverKeystorePath := filepath.Join(serverTmpDir, api.DefaultKeystoreRelativePath, serverActiveAccount.KeyUID) 1191 var config = &SenderServerConfig{ 1192 SenderConfig: &SenderConfig{ 1193 KeystorePath: serverKeystorePath, 1194 DeviceType: "desktop", 1195 KeyUID: serverActiveAccount.KeyUID, 1196 Password: s.password, 1197 }, 1198 ServerConfig: new(ServerConfig), 1199 } 1200 configBytes, err := json.Marshal(config) 1201 s.NoError(err) 1202 cs, err := StartUpSenderServer(serverBackend, string(configBytes)) 1203 s.NoError(err) 1204 1205 clientPayloadSourceConfig := ReceiverClientConfig{ 1206 ReceiverConfig: &ReceiverConfig{ 1207 CreateAccount: &requests.CreateAccount{ 1208 RootDataDir: clientTmpDir, 1209 KdfIterations: expectedKDFIterations, 1210 DeviceName: "client-device", 1211 }, 1212 }, 1213 ClientConfig: new(ClientConfig), 1214 } 1215 clientConfigBytes, err := json.Marshal(clientPayloadSourceConfig) 1216 s.NoError(err) 1217 err = StartUpReceivingClient(clientBackend, cs, string(clientConfigBytes)) 1218 s.ErrorIs(err, ErrLoggedInKeyUIDConflict) 1219 } 1220 1221 func (s *SyncDeviceSuite) TestPreventLoggedInAccountLocalPairingClientAsSender() { 1222 clientTmpDir := filepath.Join(s.tmpdir, "client") 1223 clientBackend := s.prepareBackendWithAccount("", clientTmpDir) 1224 serverTmpDir := filepath.Join(s.tmpdir, "server") 1225 serverBackend := s.prepareBackendWithAccount("", serverTmpDir) 1226 defer func() { 1227 s.NoError(serverBackend.Logout()) 1228 s.NoError(clientBackend.Logout()) 1229 }() 1230 1231 serverPayloadSourceConfig := &ReceiverServerConfig{ 1232 ReceiverConfig: &ReceiverConfig{ 1233 CreateAccount: &requests.CreateAccount{ 1234 RootDataDir: serverTmpDir, 1235 KdfIterations: expectedKDFIterations, 1236 DeviceName: "server-device", 1237 }, 1238 }, 1239 ServerConfig: new(ServerConfig), 1240 } 1241 1242 serverConfigBytes, err := json.Marshal(serverPayloadSourceConfig) 1243 s.NoError(err) 1244 cs, err := StartUpReceiverServer(serverBackend, string(serverConfigBytes)) 1245 s.NoError(err) 1246 1247 clientActiveAccount, err := clientBackend.GetActiveAccount() 1248 s.NoError(err) 1249 clientKeystorePath := filepath.Join(clientTmpDir, api.DefaultKeystoreRelativePath, clientActiveAccount.KeyUID) 1250 clientPayloadSourceConfig := SenderClientConfig{ 1251 SenderConfig: &SenderConfig{ 1252 KeystorePath: clientKeystorePath, 1253 DeviceType: "android", 1254 KeyUID: clientActiveAccount.KeyUID, 1255 Password: s.password, 1256 }, 1257 ClientConfig: new(ClientConfig), 1258 } 1259 clientConfigBytes, err := json.Marshal(clientPayloadSourceConfig) 1260 s.NoError(err) 1261 err = StartUpSendingClient(clientBackend, cs, string(clientConfigBytes)) 1262 s.ErrorContains(err, "[client] status not ok when sending account data, received '500 Internal Server Error'") 1263 }