package pairing import ( "context" "crypto/ecdsa" "fmt" "strings" ethcrypto "github.com/ethereum/go-ethereum/crypto" "github.com/status-im/status-go/internal/accounts-management/types" "github.com/status-im/status-go/internal/db/multiaccounts/settings" "github.com/status-im/status-go/internal/protocol/protobuf" "github.com/status-im/status-go/internal/protocol/requests" "github.com/status-im/status-go/internal/signal" "github.com/status-im/status-go/pkg/backend" "github.com/status-im/status-go/pkg/messaging" ) type SyncRawMessageHandler struct { backend *backend.StatusBackend } func NewSyncRawMessageHandler(backend *backend.StatusBackend) *SyncRawMessageHandler { return &SyncRawMessageHandler{backend: backend} } func (s *SyncRawMessageHandler) CollectInstallationData(rawMessageCollector *RawMessageCollector, deviceType string) error { // TODO Could this function be part of the installation data exchange flow? // https://github.com/status-im/status-go/issues/3304 messenger := s.backend.Messenger() if messenger == nil { return fmt.Errorf("messenger is nil when CollectInstallationData") } err := messenger.SetInstallationDeviceType(deviceType) if err != nil { return err } _, err = messenger.SendPairInstallation(context.TODO(), "", rawMessageCollector.dispatchMessage) return err } func (s *SyncRawMessageHandler) PrepareRawMessage(keyUID, deviceType string) (rm []*protobuf.RawMessage, kp *types.Keypair, syncSettings *settings.Settings, err error) { syncSettings = new(settings.Settings) messenger := s.backend.Messenger() if messenger == nil { return nil, nil, nil, fmt.Errorf("messenger is nil when PrepareRawMessage") } currentAccount, err := s.backend.GetActiveAccount() if err != nil { return } if keyUID != currentAccount.KeyUID { return nil, nil, nil, fmt.Errorf("keyUID not equal") } messageSyncEnabled := s.backend.LocalPairingStateManager.IsMessageSyncEnabled() messenger.SetLocalPairing(true) defer func() { messenger.SetLocalPairing(false) }() rawMessageCollector := new(RawMessageCollector) err = messenger.SyncDevices(context.TODO(), currentAccount.Name, currentAccount.Identicon, messageSyncEnabled, rawMessageCollector.dispatchMessage) if err != nil { return } err = s.CollectInstallationData(rawMessageCollector, deviceType) if err != nil { return } rsm := rawMessageCollector.convertToSyncRawMessage() rm = rsm.RawMessages accountService := s.backend.StatusNode().AccountService() kp, err = accountService.GetKeypairByKeyUID(keyUID) if err != nil { return } *syncSettings, err = accountService.GetSettings() if err != nil { return } return } func (s *SyncRawMessageHandler) HandleRawMessage( accountPayload *AccountPayload, createAccountRequest *requests.CreateAccount, deviceType string, rmp *RawMessagesPayload) (err error) { activeAccount, _ := s.backend.GetActiveAccount() if activeAccount == nil { // not login yet err = s.login(accountPayload, createAccountRequest, rmp) if err != nil { return err } } messenger := s.backend.Messenger() if messenger == nil { return fmt.Errorf("messenger is nil when HandleRawMessage") } err = messenger.SetInstallationDeviceType(deviceType) if err != nil { return err } installations := GetMessengerInstallationsMap(messenger) err = messenger.HandleSyncRawMessages(rmp.rawMessages) if err != nil { return err } if newInstallation := FindNewInstallations(messenger, installations); newInstallation != nil { signal.SendLocalPairingEvent(Event{ Type: EventReceivedInstallation, Action: ActionPairingInstallation, Data: newInstallation}) } return nil } func (s *SyncRawMessageHandler) login(accountPayload *AccountPayload, createAccountRequest *requests.CreateAccount, rmp *RawMessagesPayload) error { account := accountPayload.multiaccount for _, acc := range rmp.profileKeypair.Accounts { if acc.Chat { err := backend.EnrichMultiAccountByPublicKey(account, acc.PublicKey) if err != nil { return err } break } } installationID := messaging.GenerateInstallationID() nodeConfig, err := backend.DefaultNodeConfig(installationID, account.KeyUID, createAccountRequest) if err != nil { return err } s.backend.UpdateRootDataDir(nodeConfig.RootDataDir) var chatKey *ecdsa.PrivateKey if accountPayload.chatKey != "" { chatKeyHex := strings.Trim(accountPayload.chatKey, "0x") chatKey, err = ethcrypto.HexToECDSA(chatKeyHex) if err != nil { return err } } if accountPayload.exist { return s.backend.StartNodeWithAccount(*account, accountPayload.password, nodeConfig, chatKey) } // Override some of received settings rmp.setting.DeviceName = createAccountRequest.DeviceName rmp.setting.InstallationID = installationID rmp.setting.CurrentNetwork = backend.DefaultCurrentNetwork return s.backend.StartNodeWithAccountAndInitialConfig( account, accountPayload.password, *rmp.setting, nodeConfig, rmp.profileKeypair, chatKey, ) }