2017-09-26 13:44:26 +00:00
|
|
|
package account
|
2016-07-27 11:47:41 +00:00
|
|
|
|
2016-06-15 19:50:35 +00:00
|
|
|
import (
|
2019-01-24 15:44:46 +00:00
|
|
|
"crypto/ecdsa"
|
2017-10-10 09:38:49 +00:00
|
|
|
"encoding/json"
|
2016-06-20 15:21:45 +00:00
|
|
|
"errors"
|
2016-06-15 19:50:35 +00:00
|
|
|
"fmt"
|
2017-05-06 21:53:18 +00:00
|
|
|
"io/ioutil"
|
2017-05-15 21:49:22 +00:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2018-04-20 15:39:53 +00:00
|
|
|
"sync"
|
2016-06-15 19:50:35 +00:00
|
|
|
|
2019-01-24 15:44:46 +00:00
|
|
|
"github.com/pborman/uuid"
|
|
|
|
|
2019-07-24 18:59:15 +00:00
|
|
|
"github.com/status-im/status-go/account/generator"
|
2019-12-11 13:59:37 +00:00
|
|
|
"github.com/status-im/status-go/eth-node/crypto"
|
2019-12-19 18:27:27 +00:00
|
|
|
"github.com/status-im/status-go/eth-node/keystore"
|
2019-11-23 17:57:05 +00:00
|
|
|
"github.com/status-im/status-go/eth-node/types"
|
2016-09-11 11:44:14 +00:00
|
|
|
"github.com/status-im/status-go/extkeys"
|
2016-06-15 19:50:35 +00:00
|
|
|
)
|
|
|
|
|
2017-05-03 14:24:48 +00:00
|
|
|
// errors
|
2016-06-15 19:50:35 +00:00
|
|
|
var (
|
2018-03-22 12:31:12 +00:00
|
|
|
ErrAddressToAccountMappingFailure = errors.New("cannot retrieve a valid account for a given address")
|
|
|
|
ErrAccountToKeyMappingFailure = errors.New("cannot retrieve a valid key for a given account")
|
|
|
|
ErrNoAccountSelected = errors.New("no account has been selected, please login")
|
|
|
|
ErrInvalidMasterKeyCreated = errors.New("can not create master extended key")
|
2019-06-26 22:28:16 +00:00
|
|
|
ErrOnboardingNotStarted = errors.New("onboarding must be started before choosing an account")
|
|
|
|
ErrOnboardingAccountNotFound = errors.New("cannot find onboarding account with the given id")
|
2019-08-20 15:38:40 +00:00
|
|
|
ErrAccountKeyStoreMissing = errors.New("account key store is not set")
|
2016-06-15 19:50:35 +00:00
|
|
|
)
|
|
|
|
|
2019-12-11 13:59:37 +00:00
|
|
|
var zeroAddress = types.Address{}
|
2019-07-26 14:45:10 +00:00
|
|
|
|
2018-03-22 12:31:12 +00:00
|
|
|
// Manager represents account manager interface.
|
2017-09-26 13:44:26 +00:00
|
|
|
type Manager struct {
|
2019-08-20 15:38:40 +00:00
|
|
|
mu sync.RWMutex
|
2019-12-19 18:27:27 +00:00
|
|
|
keystore types.KeyStore
|
2019-06-26 22:28:16 +00:00
|
|
|
|
2019-07-24 18:59:15 +00:00
|
|
|
accountsGenerator *generator.Generator
|
|
|
|
onboarding *Onboarding
|
2019-06-26 22:28:16 +00:00
|
|
|
|
2019-07-26 14:45:10 +00:00
|
|
|
selectedChatAccount *SelectedExtKey // account that was processed during the last call to SelectAccount()
|
2019-12-11 13:59:37 +00:00
|
|
|
mainAccountAddress types.Address
|
|
|
|
watchAddresses []types.Address
|
2017-05-16 12:09:52 +00:00
|
|
|
}
|
|
|
|
|
2019-12-19 18:27:27 +00:00
|
|
|
// GetKeystore is only used in tests
|
|
|
|
func (m *Manager) GetKeystore() types.KeyStore {
|
2019-08-20 15:38:40 +00:00
|
|
|
m.mu.RLock()
|
|
|
|
defer m.mu.RUnlock()
|
|
|
|
return m.keystore
|
|
|
|
}
|
2019-07-24 18:59:15 +00:00
|
|
|
|
|
|
|
// AccountsGenerator returns accountsGenerator.
|
|
|
|
func (m *Manager) AccountsGenerator() *generator.Generator {
|
|
|
|
return m.accountsGenerator
|
2017-05-16 12:09:52 +00:00
|
|
|
}
|
|
|
|
|
2016-09-27 13:51:08 +00:00
|
|
|
// CreateAccount creates an internal geth account
|
2016-08-23 21:32:04 +00:00
|
|
|
// BIP44-compatible keys are generated: CKD#1 is stored as account key, CKD#2 stored as sub-account root
|
|
|
|
// Public key of CKD#1 is returned, with CKD#2 securely encoded into account key file (to be used for
|
|
|
|
// sub-account derivations)
|
2019-01-18 09:01:14 +00:00
|
|
|
func (m *Manager) CreateAccount(password string) (Info, string, error) {
|
|
|
|
info := Info{}
|
2016-08-23 21:32:04 +00:00
|
|
|
// generate mnemonic phrase
|
2018-05-14 17:13:56 +00:00
|
|
|
mn := extkeys.NewMnemonic()
|
2019-01-18 09:01:14 +00:00
|
|
|
mnemonic, err := mn.MnemonicPhrase(extkeys.EntropyStrength128, extkeys.EnglishLanguage)
|
2016-08-23 21:32:04 +00:00
|
|
|
if err != nil {
|
2019-01-18 09:01:14 +00:00
|
|
|
return info, "", fmt.Errorf("can not create mnemonic seed: %v", err)
|
2016-08-23 21:32:04 +00:00
|
|
|
}
|
|
|
|
|
2018-05-14 17:13:56 +00:00
|
|
|
// Generate extended master key (see BIP32)
|
|
|
|
// We call extkeys.NewMaster with a seed generated with the 12 mnemonic words
|
|
|
|
// but without using the optional password as an extra entropy as described in BIP39.
|
|
|
|
// Future ideas/iterations in Status can add an an advanced options
|
|
|
|
// for expert users, to be able to add a passphrase to the generation of the seed.
|
|
|
|
extKey, err := extkeys.NewMaster(mn.MnemonicSeed(mnemonic, ""))
|
2016-08-23 21:32:04 +00:00
|
|
|
if err != nil {
|
2019-01-18 09:01:14 +00:00
|
|
|
return info, "", fmt.Errorf("can not create master extended key: %v", err)
|
2016-08-23 21:32:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// import created key into account keystore
|
2019-01-18 09:01:14 +00:00
|
|
|
info.WalletAddress, info.WalletPubKey, err = m.importExtendedKey(extkeys.KeyPurposeWallet, extKey, password)
|
2016-08-23 21:32:04 +00:00
|
|
|
if err != nil {
|
2019-01-18 09:01:14 +00:00
|
|
|
return info, "", err
|
2016-08-23 21:32:04 +00:00
|
|
|
}
|
2016-06-29 11:32:04 +00:00
|
|
|
|
2019-01-18 09:01:14 +00:00
|
|
|
info.ChatAddress = info.WalletAddress
|
|
|
|
info.ChatPubKey = info.WalletPubKey
|
|
|
|
|
|
|
|
return info, mnemonic, nil
|
2016-08-18 00:15:58 +00:00
|
|
|
}
|
|
|
|
|
2016-09-27 13:51:08 +00:00
|
|
|
// RecoverAccount re-creates master key using given details.
|
2016-08-23 21:32:04 +00:00
|
|
|
// Once master key is re-generated, it is inserted into keystore (if not already there).
|
2019-01-18 09:01:14 +00:00
|
|
|
func (m *Manager) RecoverAccount(password, mnemonic string) (Info, error) {
|
|
|
|
info := Info{}
|
2016-08-23 21:32:04 +00:00
|
|
|
// re-create extended key (see BIP32)
|
2018-05-14 17:13:56 +00:00
|
|
|
mn := extkeys.NewMnemonic()
|
|
|
|
extKey, err := extkeys.NewMaster(mn.MnemonicSeed(mnemonic, ""))
|
2016-08-23 21:32:04 +00:00
|
|
|
if err != nil {
|
2019-01-18 09:01:14 +00:00
|
|
|
return info, ErrInvalidMasterKeyCreated
|
2016-08-23 21:32:04 +00:00
|
|
|
}
|
2016-08-18 00:15:58 +00:00
|
|
|
|
2016-08-23 21:32:04 +00:00
|
|
|
// import re-created key into account keystore
|
2019-01-18 09:01:14 +00:00
|
|
|
info.WalletAddress, info.WalletPubKey, err = m.importExtendedKey(extkeys.KeyPurposeWallet, extKey, password)
|
2016-08-23 21:32:04 +00:00
|
|
|
if err != nil {
|
2019-01-18 09:01:14 +00:00
|
|
|
return info, err
|
2016-08-18 00:15:58 +00:00
|
|
|
}
|
2016-06-15 19:50:35 +00:00
|
|
|
|
2019-01-18 09:01:14 +00:00
|
|
|
info.ChatAddress = info.WalletAddress
|
|
|
|
info.ChatPubKey = info.WalletPubKey
|
|
|
|
|
|
|
|
return info, nil
|
2016-06-15 19:50:35 +00:00
|
|
|
}
|
2016-06-20 15:21:45 +00:00
|
|
|
|
2017-05-06 21:53:18 +00:00
|
|
|
// VerifyAccountPassword tries to decrypt a given account key file, with a provided password.
|
|
|
|
// If no error is returned, then account is considered verified.
|
2019-12-19 18:27:27 +00:00
|
|
|
func (m *Manager) VerifyAccountPassword(keyStoreDir, address, password string) (*types.Key, error) {
|
2017-05-15 21:49:22 +00:00
|
|
|
var err error
|
2017-10-10 09:38:49 +00:00
|
|
|
var foundKeyFile []byte
|
2017-05-15 21:49:22 +00:00
|
|
|
|
2019-12-11 13:59:37 +00:00
|
|
|
addressObj := types.BytesToAddress(types.FromHex(address))
|
2017-05-15 21:49:22 +00:00
|
|
|
checkAccountKey := func(path string, fileInfo os.FileInfo) error {
|
2017-10-10 09:38:49 +00:00
|
|
|
if len(foundKeyFile) > 0 || fileInfo.IsDir() {
|
2017-05-15 21:49:22 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-10-20 09:06:22 +00:00
|
|
|
rawKeyFile, e := ioutil.ReadFile(path)
|
|
|
|
if e != nil {
|
|
|
|
return fmt.Errorf("invalid account key file: %v", e)
|
2017-05-15 21:49:22 +00:00
|
|
|
}
|
2017-10-10 09:38:49 +00:00
|
|
|
|
|
|
|
var accountKey struct {
|
|
|
|
Address string `json:"address"`
|
|
|
|
}
|
2017-10-20 09:06:22 +00:00
|
|
|
if e := json.Unmarshal(rawKeyFile, &accountKey); e != nil {
|
|
|
|
return fmt.Errorf("failed to read key file: %s", e)
|
2017-10-10 09:38:49 +00:00
|
|
|
}
|
|
|
|
|
2019-12-11 13:59:37 +00:00
|
|
|
if types.HexToAddress("0x"+accountKey.Address).Hex() == addressObj.Hex() {
|
2017-10-10 09:38:49 +00:00
|
|
|
foundKeyFile = rawKeyFile
|
2017-05-15 21:49:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
// locate key within key store directory (address should be within the file)
|
|
|
|
err = filepath.Walk(keyStoreDir, func(path string, fileInfo os.FileInfo, err error) error {
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return checkAccountKey(path, fileInfo)
|
|
|
|
})
|
2017-05-06 21:53:18 +00:00
|
|
|
if err != nil {
|
2017-05-15 21:49:22 +00:00
|
|
|
return nil, fmt.Errorf("cannot traverse key store folder: %v", err)
|
|
|
|
}
|
|
|
|
|
2017-10-10 09:38:49 +00:00
|
|
|
if len(foundKeyFile) == 0 {
|
|
|
|
return nil, fmt.Errorf("cannot locate account for address: %s", addressObj.Hex())
|
2017-05-06 21:53:18 +00:00
|
|
|
}
|
|
|
|
|
2017-10-10 09:38:49 +00:00
|
|
|
key, err := keystore.DecryptKey(foundKeyFile, password)
|
2017-05-06 21:53:18 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// avoid swap attack
|
2019-12-19 18:27:27 +00:00
|
|
|
if key.Address != addressObj {
|
2017-10-10 09:38:49 +00:00
|
|
|
return nil, fmt.Errorf("account mismatch: have %s, want %s", key.Address.Hex(), addressObj.Hex())
|
2017-05-06 21:53:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return key, nil
|
|
|
|
}
|
|
|
|
|
2016-09-27 13:51:08 +00:00
|
|
|
// SelectAccount selects current account, by verifying that address has corresponding account which can be decrypted
|
2018-03-22 12:31:12 +00:00
|
|
|
// using provided password. Once verification is done, all previous identities are removed).
|
2019-07-26 14:45:10 +00:00
|
|
|
func (m *Manager) SelectAccount(loginParams LoginParams) error {
|
2018-04-20 15:39:53 +00:00
|
|
|
m.mu.Lock()
|
|
|
|
defer m.mu.Unlock()
|
|
|
|
|
2019-07-24 18:59:15 +00:00
|
|
|
m.accountsGenerator.Reset()
|
|
|
|
|
2019-07-26 14:45:10 +00:00
|
|
|
selectedChatAccount, err := m.unlockExtendedKey(loginParams.ChatAddress.String(), loginParams.Password)
|
2016-09-27 13:51:08 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-07-26 14:45:10 +00:00
|
|
|
m.watchAddresses = loginParams.WatchAddresses
|
|
|
|
m.mainAccountAddress = loginParams.MainAccount
|
2019-01-18 09:01:14 +00:00
|
|
|
m.selectedChatAccount = selectedChatAccount
|
2016-08-21 06:45:59 +00:00
|
|
|
return nil
|
|
|
|
}
|
2016-06-21 18:29:38 +00:00
|
|
|
|
2019-12-11 13:59:37 +00:00
|
|
|
func (m *Manager) SetAccountAddresses(main types.Address, secondary ...types.Address) {
|
|
|
|
m.watchAddresses = []types.Address{main}
|
2019-09-02 19:03:15 +00:00
|
|
|
m.watchAddresses = append(m.watchAddresses, secondary...)
|
|
|
|
m.mainAccountAddress = main
|
|
|
|
}
|
|
|
|
|
2019-01-24 15:44:46 +00:00
|
|
|
// SetChatAccount initializes selectedChatAccount with privKey
|
|
|
|
func (m *Manager) SetChatAccount(privKey *ecdsa.PrivateKey) {
|
|
|
|
m.mu.Lock()
|
|
|
|
defer m.mu.Unlock()
|
|
|
|
|
|
|
|
address := crypto.PubkeyToAddress(privKey.PublicKey)
|
|
|
|
id := uuid.NewRandom()
|
2019-12-19 18:27:27 +00:00
|
|
|
key := &types.Key{
|
2020-02-10 11:22:37 +00:00
|
|
|
ID: id,
|
2019-12-19 18:27:27 +00:00
|
|
|
Address: address,
|
2019-01-24 15:44:46 +00:00
|
|
|
PrivateKey: privKey,
|
|
|
|
}
|
|
|
|
|
|
|
|
m.selectedChatAccount = &SelectedExtKey{
|
|
|
|
Address: address,
|
|
|
|
AccountKey: key,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-26 14:45:10 +00:00
|
|
|
// MainAccountAddress returns currently selected watch addresses.
|
2019-12-11 13:59:37 +00:00
|
|
|
func (m *Manager) MainAccountAddress() (types.Address, error) {
|
2019-01-09 08:47:06 +00:00
|
|
|
m.mu.RLock()
|
|
|
|
defer m.mu.RUnlock()
|
|
|
|
|
2019-07-26 14:45:10 +00:00
|
|
|
if m.mainAccountAddress == zeroAddress {
|
|
|
|
return zeroAddress, ErrNoAccountSelected
|
2019-01-09 08:47:06 +00:00
|
|
|
}
|
2019-07-26 14:45:10 +00:00
|
|
|
|
|
|
|
return m.mainAccountAddress, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// WatchAddresses returns currently selected watch addresses.
|
2019-12-11 13:59:37 +00:00
|
|
|
func (m *Manager) WatchAddresses() []types.Address {
|
2019-07-26 14:45:10 +00:00
|
|
|
m.mu.RLock()
|
|
|
|
defer m.mu.RUnlock()
|
|
|
|
|
|
|
|
return m.watchAddresses
|
2019-01-09 08:47:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// SelectedChatAccount returns currently selected chat account
|
|
|
|
func (m *Manager) SelectedChatAccount() (*SelectedExtKey, error) {
|
2018-04-20 15:39:53 +00:00
|
|
|
m.mu.RLock()
|
|
|
|
defer m.mu.RUnlock()
|
|
|
|
|
2019-01-09 08:47:06 +00:00
|
|
|
if m.selectedChatAccount == nil {
|
2017-05-16 12:09:52 +00:00
|
|
|
return nil, ErrNoAccountSelected
|
|
|
|
}
|
2019-01-09 08:47:06 +00:00
|
|
|
return m.selectedChatAccount, nil
|
2017-05-16 12:09:52 +00:00
|
|
|
}
|
2017-01-24 18:42:55 +00:00
|
|
|
|
2019-07-26 14:45:10 +00:00
|
|
|
// Logout clears selected accounts.
|
2018-04-20 15:39:53 +00:00
|
|
|
func (m *Manager) Logout() {
|
|
|
|
m.mu.Lock()
|
|
|
|
defer m.mu.Unlock()
|
2016-08-23 21:32:04 +00:00
|
|
|
|
2019-07-24 18:59:15 +00:00
|
|
|
m.accountsGenerator.Reset()
|
2019-07-26 14:45:10 +00:00
|
|
|
m.mainAccountAddress = zeroAddress
|
|
|
|
m.watchAddresses = nil
|
2019-01-09 08:47:06 +00:00
|
|
|
m.selectedChatAccount = nil
|
2016-08-29 00:31:16 +00:00
|
|
|
}
|
|
|
|
|
2019-07-24 18:59:15 +00:00
|
|
|
// ImportAccount imports the account specified with privateKey.
|
2019-12-11 13:59:37 +00:00
|
|
|
func (m *Manager) ImportAccount(privateKey *ecdsa.PrivateKey, password string) (types.Address, error) {
|
2019-08-20 15:38:40 +00:00
|
|
|
if m.keystore == nil {
|
2019-12-11 13:59:37 +00:00
|
|
|
return types.Address{}, ErrAccountKeyStoreMissing
|
2019-07-24 18:59:15 +00:00
|
|
|
}
|
|
|
|
|
2019-08-20 15:38:40 +00:00
|
|
|
account, err := m.keystore.ImportECDSA(privateKey, password)
|
2019-07-24 18:59:15 +00:00
|
|
|
|
2019-12-19 18:27:27 +00:00
|
|
|
return account.Address, err
|
2019-07-24 18:59:15 +00:00
|
|
|
}
|
|
|
|
|
2019-12-19 18:27:27 +00:00
|
|
|
// ImportSingleExtendedKey imports an extended key setting it in both the PrivateKey and ExtendedKey fields
|
|
|
|
// of the Key struct.
|
|
|
|
// ImportExtendedKey is used in older version of Status where PrivateKey is set to be the BIP44 key at index 0,
|
|
|
|
// and ExtendedKey is the extended key of the BIP44 key at index 1.
|
2019-07-24 18:59:15 +00:00
|
|
|
func (m *Manager) ImportSingleExtendedKey(extKey *extkeys.ExtendedKey, password string) (address, pubKey string, err error) {
|
2019-08-20 15:38:40 +00:00
|
|
|
if m.keystore == nil {
|
|
|
|
return "", "", ErrAccountKeyStoreMissing
|
2019-07-24 18:59:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// imports extended key, create key file (if necessary)
|
2019-08-20 15:38:40 +00:00
|
|
|
account, err := m.keystore.ImportSingleExtendedKey(extKey, password)
|
2019-07-24 18:59:15 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
address = account.Address.Hex()
|
|
|
|
|
|
|
|
// obtain public key to return
|
2019-08-20 15:38:40 +00:00
|
|
|
account, key, err := m.keystore.AccountDecryptedKey(account, password)
|
2019-07-24 18:59:15 +00:00
|
|
|
if err != nil {
|
|
|
|
return address, "", err
|
|
|
|
}
|
|
|
|
|
2019-11-23 17:57:05 +00:00
|
|
|
pubKey = types.EncodeHex(crypto.FromECDSAPub(&key.PrivateKey.PublicKey))
|
2019-07-24 18:59:15 +00:00
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-08-23 21:32:04 +00:00
|
|
|
// importExtendedKey processes incoming extended key, extracts required info and creates corresponding account key.
|
|
|
|
// Once account key is formed, that key is put (if not already) into keystore i.e. key is *encoded* into key file.
|
2019-01-09 08:47:06 +00:00
|
|
|
func (m *Manager) importExtendedKey(keyPurpose extkeys.KeyPurpose, extKey *extkeys.ExtendedKey, password string) (address, pubKey string, err error) {
|
2019-08-20 15:38:40 +00:00
|
|
|
if m.keystore == nil {
|
|
|
|
return "", "", ErrAccountKeyStoreMissing
|
2016-09-11 11:44:14 +00:00
|
|
|
}
|
|
|
|
|
2016-08-23 21:32:04 +00:00
|
|
|
// imports extended key, create key file (if necessary)
|
2019-08-20 15:38:40 +00:00
|
|
|
account, err := m.keystore.ImportExtendedKeyForPurpose(keyPurpose, extKey, password)
|
2016-08-23 21:32:04 +00:00
|
|
|
if err != nil {
|
2016-09-11 11:44:14 +00:00
|
|
|
return "", "", err
|
2016-08-23 21:32:04 +00:00
|
|
|
}
|
2017-10-10 09:38:49 +00:00
|
|
|
address = account.Address.Hex()
|
2016-08-23 21:32:04 +00:00
|
|
|
|
|
|
|
// obtain public key to return
|
2019-08-20 15:38:40 +00:00
|
|
|
account, key, err := m.keystore.AccountDecryptedKey(account, password)
|
2016-08-23 21:32:04 +00:00
|
|
|
if err != nil {
|
2016-09-11 11:44:14 +00:00
|
|
|
return address, "", err
|
2016-08-23 21:32:04 +00:00
|
|
|
}
|
2019-11-23 17:57:05 +00:00
|
|
|
pubKey = types.EncodeHex(crypto.FromECDSAPub(&key.PrivateKey.PublicKey))
|
2016-08-23 21:32:04 +00:00
|
|
|
|
|
|
|
return
|
|
|
|
}
|
2016-09-27 13:51:08 +00:00
|
|
|
|
2017-09-25 16:04:40 +00:00
|
|
|
// Accounts returns list of addresses for selected account, including
|
|
|
|
// subaccounts.
|
2019-12-11 13:59:37 +00:00
|
|
|
func (m *Manager) Accounts() ([]types.Address, error) {
|
2018-04-20 15:39:53 +00:00
|
|
|
m.mu.RLock()
|
|
|
|
defer m.mu.RUnlock()
|
2019-12-11 13:59:37 +00:00
|
|
|
addresses := make([]types.Address, 0)
|
2019-07-26 14:45:10 +00:00
|
|
|
if m.mainAccountAddress != zeroAddress {
|
|
|
|
addresses = append(addresses, m.mainAccountAddress)
|
2017-09-25 16:04:40 +00:00
|
|
|
}
|
|
|
|
|
2019-07-26 14:45:10 +00:00
|
|
|
return addresses, nil
|
2017-09-25 16:04:40 +00:00
|
|
|
}
|
2016-09-27 13:51:08 +00:00
|
|
|
|
2019-06-26 22:28:16 +00:00
|
|
|
// StartOnboarding starts the onboarding process generating accountsCount accounts and returns a slice of OnboardingAccount.
|
|
|
|
func (m *Manager) StartOnboarding(accountsCount, mnemonicPhraseLength int) ([]*OnboardingAccount, error) {
|
|
|
|
m.mu.Lock()
|
|
|
|
defer m.mu.Unlock()
|
|
|
|
|
|
|
|
onboarding, err := NewOnboarding(accountsCount, mnemonicPhraseLength)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
m.onboarding = onboarding
|
|
|
|
|
|
|
|
return m.onboarding.Accounts(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// RemoveOnboarding reset the current onboarding struct setting it to nil and deleting the accounts from memory.
|
|
|
|
func (m *Manager) RemoveOnboarding() {
|
|
|
|
m.mu.Lock()
|
|
|
|
defer m.mu.Unlock()
|
|
|
|
|
|
|
|
m.onboarding = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ImportOnboardingAccount imports the account specified by id and encrypts it with password.
|
|
|
|
func (m *Manager) ImportOnboardingAccount(id string, password string) (Info, string, error) {
|
|
|
|
var info Info
|
|
|
|
|
|
|
|
m.mu.Lock()
|
|
|
|
defer m.mu.Unlock()
|
|
|
|
|
|
|
|
if m.onboarding == nil {
|
|
|
|
return info, "", ErrOnboardingNotStarted
|
|
|
|
}
|
|
|
|
|
|
|
|
acc, err := m.onboarding.Account(id)
|
|
|
|
if err != nil {
|
|
|
|
return info, "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
info, err = m.RecoverAccount(password, acc.mnemonic)
|
|
|
|
if err != nil {
|
|
|
|
return info, "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
m.onboarding = nil
|
|
|
|
|
|
|
|
return info, acc.mnemonic, nil
|
|
|
|
}
|
|
|
|
|
2017-05-16 12:09:52 +00:00
|
|
|
// AddressToDecryptedAccount tries to load decrypted key for a given account.
|
|
|
|
// The running node, has a keystore directory which is loaded on start. Key file
|
|
|
|
// for a given address is expected to be in that directory prior to node start.
|
2019-12-19 18:27:27 +00:00
|
|
|
func (m *Manager) AddressToDecryptedAccount(address, password string) (types.Account, *types.Key, error) {
|
2019-08-20 15:38:40 +00:00
|
|
|
if m.keystore == nil {
|
2019-12-19 18:27:27 +00:00
|
|
|
return types.Account{}, nil, ErrAccountKeyStoreMissing
|
2017-05-16 12:09:52 +00:00
|
|
|
}
|
|
|
|
|
2018-03-22 12:31:12 +00:00
|
|
|
account, err := ParseAccountString(address)
|
2017-05-16 12:09:52 +00:00
|
|
|
if err != nil {
|
2019-12-19 18:27:27 +00:00
|
|
|
return types.Account{}, nil, ErrAddressToAccountMappingFailure
|
2017-05-16 12:09:52 +00:00
|
|
|
}
|
|
|
|
|
2019-12-19 18:27:27 +00:00
|
|
|
account, key, err := m.keystore.AccountDecryptedKey(account, password)
|
2019-01-18 09:01:14 +00:00
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("%s: %s", ErrAccountToKeyMappingFailure, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return account, key, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Manager) unlockExtendedKey(address, password string) (*SelectedExtKey, error) {
|
|
|
|
account, accountKey, err := m.AddressToDecryptedAccount(address, password)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
selectedExtendedKey := &SelectedExtKey{
|
2019-12-19 18:27:27 +00:00
|
|
|
Address: account.Address,
|
2019-07-26 14:45:10 +00:00
|
|
|
AccountKey: accountKey,
|
2019-01-18 09:01:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return selectedExtendedKey, nil
|
2017-05-16 12:09:52 +00:00
|
|
|
}
|