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  }