diff --git a/account/accounts.go b/account/accounts.go index 73af4753a..ecbeafb80 100644 --- a/account/accounts.go +++ b/account/accounts.go @@ -222,14 +222,18 @@ func (m *Manager) SelectAccount(loginParams LoginParams) error { if err != nil { return err } - m.watchAddresses = loginParams.WatchAddresses m.mainAccountAddress = loginParams.MainAccount m.selectedChatAccount = selectedChatAccount - return nil } +func (m *Manager) SetAccountAddresses(main common.Address, secondary ...common.Address) { + m.watchAddresses = []common.Address{main} + m.watchAddresses = append(m.watchAddresses, secondary...) + m.mainAccountAddress = main +} + // SetChatAccount initializes selectedChatAccount with privKey func (m *Manager) SetChatAccount(privKey *ecdsa.PrivateKey) { m.mu.Lock() diff --git a/api/backend.go b/api/backend.go index f613cdb43..8282ea7ee 100644 --- a/api/backend.go +++ b/api/backend.go @@ -189,6 +189,54 @@ func (b *StatusBackend) ensureAppDBOpened(account multiaccounts.Account, passwor return nil } +func (b *StatusBackend) SaveAccountAndStartNodeWithKey(acc multiaccounts.Account, conf *params.NodeConfig, password string, keyHex string) error { + err := b.SaveAccount(acc) + if err != nil { + return err + } + err = b.ensureAppDBOpened(acc, password) + if err != nil { + return err + } + err = b.saveNodeConfig(conf) + if err != nil { + return err + } + return b.StartNodeWithKey(acc, password, keyHex) +} + +// StartNodeWithKey instead of loading addresses from database this method derives address from key +// and uses it in application. +func (b *StatusBackend) StartNodeWithKey(acc multiaccounts.Account, password string, keyHex string) error { + err := b.ensureAppDBOpened(acc, password) + if err != nil { + return err + } + conf, err := b.loadNodeConfig() + if err != nil { + return err + } + if err := logutils.OverrideRootLogWithConfig(conf, false); err != nil { + return err + } + + chatKey, err := ethcrypto.HexToECDSA(keyHex) + if err != nil { + return err + } + err = b.StartNode(conf) + if err != nil { + return err + } + b.accountManager.SetChatAccount(chatKey) + chatAcc, err := b.accountManager.SelectedChatAccount() + if err != nil { + return err + } + b.accountManager.SetAccountAddresses(chatAcc.Address) + return b.injectAccountIntoServices() +} + func (b *StatusBackend) StartNodeWithAccount(acc multiaccounts.Account, password string) error { err := b.ensureAppDBOpened(acc, password) if err != nil { @@ -753,6 +801,10 @@ func (b *StatusBackend) SelectAccount(loginParams account.LoginParams) error { return err } + return b.injectAccountIntoServices() +} + +func (b *StatusBackend) injectAccountIntoServices() error { chatAccount, err := b.accountManager.SelectedChatAccount() if err != nil { return err @@ -779,10 +831,10 @@ func (b *StatusBackend) SelectAccount(loginParams account.LoginParams) error { return err } } - return b.startWallet(loginParams.Password) + return b.startWallet() } -func (b *StatusBackend) startWallet(password string) error { +func (b *StatusBackend) startWallet() error { if !b.statusNode.Config().WalletConfig.Enabled { return nil } diff --git a/api/backend_test.go b/api/backend_test.go index db4248ca2..436a71b80 100644 --- a/api/backend_test.go +++ b/api/backend_test.go @@ -537,3 +537,35 @@ func TestBackendGetVerifiedAccount(t *testing.T) { require.Equal(t, address, key.Address) }) } + +func TestLoginWithKey(t *testing.T) { + b := NewStatusBackend() + pkey, err := crypto.GenerateKey() + require.NoError(t, err) + main := multiaccounts.Account{ + Address: crypto.PubkeyToAddress(pkey.PublicKey), + } + tmpdir, err := ioutil.TempDir("", "login-with-key-test-") + require.NoError(t, err) + defer os.Remove(tmpdir) + conf, err := params.NewNodeConfig(tmpdir, 1777) + require.NoError(t, err) + keyhex := hex.EncodeToString(crypto.FromECDSA(pkey)) + + require.NoError(t, b.accountManager.InitKeystore(conf.KeyStoreDir)) + b.UpdateRootDataDir(conf.DataDir) + require.NoError(t, b.OpenAccounts()) + + require.NoError(t, b.SaveAccountAndStartNodeWithKey(main, conf, "test-pass", keyhex)) + require.NoError(t, b.Logout()) + require.NoError(t, b.StopNode()) + + require.NoError(t, b.StartNodeWithKey(main, "test-pass", keyhex)) + defer func() { + assert.NoError(t, b.Logout()) + assert.NoError(t, b.StopNode()) + }() + extkey, err := b.accountManager.SelectedChatAccount() + require.NoError(t, err) + require.Equal(t, crypto.PubkeyToAddress(pkey.PublicKey), extkey.Address) +} diff --git a/mobile/status.go b/mobile/status.go index 0c8d36cee..0ec832843 100644 --- a/mobile/status.go +++ b/mobile/status.go @@ -376,11 +376,50 @@ func InitKeystore(keydir string) string { return makeJSONResponse(err) } +// SaveAccountAndLoginWithKeycard saves account in status-go database.. +func SaveAccountAndLoginWithKeycard(accountData, password, configJSON, keyHex string) string { + var account multiaccounts.Account + err := json.Unmarshal([]byte(accountData), &account) + if err != nil { + return makeJSONResponse(err) + } + var conf params.NodeConfig + err = json.Unmarshal([]byte(configJSON), &conf) + if err != nil { + return makeJSONResponse(err) + } + api.RunAsync(func() error { + log.Debug("starting a node, and saving account with configuration", "address", account.Address) + err := statusBackend.SaveAccountAndStartNodeWithKey(account, &conf, password, keyHex) + if err != nil { + log.Error("failed to start node and save account", "address", account.Address, "error", err) + return err + } + log.Debug("started a node, and saved account", "address", account.Address) + return nil + }) + return makeJSONResponse(nil) +} + // LoginWithKeycard initializes an account with a chat key and encryption key used for PFS. // It purges all the previous identities from Whisper, and injects the key as shh identity. -func LoginWithKeycard(chatKeyData, encryptionKeyData string) string { - err := statusBackend.InjectChatAccount(chatKeyData, encryptionKeyData) - return makeJSONResponse(err) +func LoginWithKeycard(accountData, password, keyHex string) string { + var account multiaccounts.Account + err := json.Unmarshal([]byte(accountData), &account) + if err != nil { + return makeJSONResponse(err) + } + api.RunAsync(func() error { + log.Debug("start a node with account", "address", account.Address) + err := statusBackend.StartNodeWithKey(account, password, keyHex) + if err != nil { + log.Error("failed to start a node", "address", account.Address, "error", err) + return err + } + log.Debug("started a node with", "address", account.Address) + return nil + }) + return makeJSONResponse(nil) } // Logout is equivalent to clearing whisper identities.