github.com/status-im/status-go@v1.1.0/account/accounts.go (about)

     1  package account
     2  
     3  import (
     4  	"context"
     5  	"crypto/ecdsa"
     6  	"encoding/json"
     7  	"errors"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"os"
    11  	"path/filepath"
    12  	"strings"
    13  	"sync"
    14  	"time"
    15  
    16  	"github.com/google/uuid"
    17  
    18  	gethkeystore "github.com/ethereum/go-ethereum/accounts/keystore"
    19  	gethcommon "github.com/ethereum/go-ethereum/common"
    20  	"github.com/ethereum/go-ethereum/common/hexutil"
    21  	"github.com/ethereum/go-ethereum/log"
    22  	"github.com/status-im/status-go/account/generator"
    23  	"github.com/status-im/status-go/eth-node/crypto"
    24  	"github.com/status-im/status-go/eth-node/keystore"
    25  	"github.com/status-im/status-go/eth-node/types"
    26  	"github.com/status-im/status-go/extkeys"
    27  	"github.com/status-im/status-go/multiaccounts/accounts"
    28  	"github.com/status-im/status-go/params"
    29  	"github.com/status-im/status-go/rpc"
    30  )
    31  
    32  // errors
    33  var (
    34  	ErrAddressToAccountMappingFailure = errors.New("cannot retrieve a valid account for a given address")
    35  	ErrAccountToKeyMappingFailure     = errors.New("cannot retrieve a valid key for a given account")
    36  	ErrNoAccountSelected              = errors.New("no account has been selected, please login")
    37  	ErrInvalidMasterKeyCreated        = errors.New("can not create master extended key")
    38  	ErrOnboardingNotStarted           = errors.New("onboarding must be started before choosing an account")
    39  	ErrOnboardingAccountNotFound      = errors.New("cannot find onboarding account with the given id")
    40  	ErrAccountKeyStoreMissing         = errors.New("account key store is not set")
    41  	ErrInvalidPersonalSignAccount     = errors.New("invalid account as only the selected one can generate a signature")
    42  )
    43  
    44  type ErrCannotLocateKeyFile struct {
    45  	Msg string
    46  }
    47  
    48  func (e ErrCannotLocateKeyFile) Error() string {
    49  	return e.Msg
    50  }
    51  
    52  var zeroAddress = types.Address{}
    53  
    54  type SignParams struct {
    55  	Data     interface{} `json:"data"`
    56  	Address  string      `json:"account"`
    57  	Password string      `json:"password,omitempty"`
    58  }
    59  
    60  func (sp *SignParams) Validate(checkPassword bool) error {
    61  	if len(sp.Address) != 2*types.AddressLength+2 {
    62  		return errors.New("address has to be provided")
    63  	}
    64  
    65  	if sp.Data == "" {
    66  		return errors.New("data has to be provided")
    67  	}
    68  
    69  	if checkPassword && sp.Password == "" {
    70  		return errors.New("password has to be provided")
    71  	}
    72  
    73  	return nil
    74  }
    75  
    76  type RecoverParams struct {
    77  	Message   string `json:"message"`
    78  	Signature string `json:"signature"`
    79  }
    80  
    81  // Manager represents account manager interface
    82  type Manager interface {
    83  	GetVerifiedWalletAccount(db *accounts.Database, address, password string) (*SelectedExtKey, error)
    84  	Sign(rpcParams SignParams, verifiedAccount *SelectedExtKey) (result types.HexBytes, err error)
    85  	CanRecover(rpcParams RecoverParams, revealedAddress types.Address) (bool, error)
    86  	DeleteAccount(address types.Address) error
    87  }
    88  
    89  // DefaultManager represents default account manager implementation
    90  type DefaultManager struct {
    91  	mu         sync.RWMutex
    92  	rpcClient  *rpc.Client
    93  	rpcTimeout time.Duration
    94  	Keydir     string
    95  	keystore   types.KeyStore
    96  
    97  	accountsGenerator *generator.Generator
    98  	onboarding        *Onboarding
    99  
   100  	selectedChatAccount *SelectedExtKey // account that was processed during the last call to SelectAccount()
   101  	mainAccountAddress  types.Address
   102  	watchAddresses      []types.Address
   103  }
   104  
   105  // GetKeystore is only used in tests
   106  func (m *DefaultManager) GetKeystore() types.KeyStore {
   107  	m.mu.RLock()
   108  	defer m.mu.RUnlock()
   109  	return m.keystore
   110  }
   111  
   112  // AccountsGenerator returns accountsGenerator.
   113  func (m *DefaultManager) AccountsGenerator() *generator.Generator {
   114  	return m.accountsGenerator
   115  }
   116  
   117  // CreateAccount creates an internal geth account
   118  // BIP44-compatible keys are generated: CKD#1 is stored as account key, CKD#2 stored as sub-account root
   119  // Public key of CKD#1 is returned, with CKD#2 securely encoded into account key file (to be used for
   120  // sub-account derivations)
   121  func (m *DefaultManager) CreateAccount(password string) (generator.GeneratedAccountInfo, Info, string, error) {
   122  	var mkInfo generator.GeneratedAccountInfo
   123  	info := Info{}
   124  
   125  	// generate mnemonic phrase
   126  	mn := extkeys.NewMnemonic()
   127  	mnemonic, err := mn.MnemonicPhrase(extkeys.EntropyStrength128, extkeys.EnglishLanguage)
   128  	if err != nil {
   129  		return mkInfo, info, "", fmt.Errorf("can not create mnemonic seed: %v", err)
   130  	}
   131  
   132  	// Generate extended master key (see BIP32)
   133  	// We call extkeys.NewMaster with a seed generated with the 12 mnemonic words
   134  	// but without using the optional password as an extra entropy as described in BIP39.
   135  	// Future ideas/iterations in Status can add an an advanced options
   136  	// for expert users, to be able to add a passphrase to the generation of the seed.
   137  	extKey, err := extkeys.NewMaster(mn.MnemonicSeed(mnemonic, ""))
   138  	if err != nil {
   139  		return mkInfo, info, "", fmt.Errorf("can not create master extended key: %v", err)
   140  	}
   141  
   142  	acc := generator.NewAccount(nil, extKey)
   143  	mkInfo = acc.ToGeneratedAccountInfo("", mnemonic)
   144  
   145  	// import created key into account keystore
   146  	info.WalletAddress, info.WalletPubKey, err = m.importExtendedKey(extkeys.KeyPurposeWallet, extKey, password)
   147  	if err != nil {
   148  		return mkInfo, info, "", err
   149  	}
   150  
   151  	info.ChatAddress = info.WalletAddress
   152  	info.ChatPubKey = info.WalletPubKey
   153  
   154  	return mkInfo, info, mnemonic, nil
   155  }
   156  
   157  // RecoverAccount re-creates master key using given details.
   158  // Once master key is re-generated, it is inserted into keystore (if not already there).
   159  func (m *DefaultManager) RecoverAccount(password, mnemonic string) (Info, error) {
   160  	info := Info{}
   161  	// re-create extended key (see BIP32)
   162  	mn := extkeys.NewMnemonic()
   163  	extKey, err := extkeys.NewMaster(mn.MnemonicSeed(mnemonic, ""))
   164  	if err != nil {
   165  		return info, ErrInvalidMasterKeyCreated
   166  	}
   167  
   168  	// import re-created key into account keystore
   169  	info.WalletAddress, info.WalletPubKey, err = m.importExtendedKey(extkeys.KeyPurposeWallet, extKey, password)
   170  	if err != nil {
   171  		return info, err
   172  	}
   173  
   174  	info.ChatAddress = info.WalletAddress
   175  	info.ChatPubKey = info.WalletPubKey
   176  
   177  	return info, nil
   178  }
   179  
   180  // VerifyAccountPassword tries to decrypt a given account key file, with a provided password.
   181  // If no error is returned, then account is considered verified.
   182  func (m *DefaultManager) VerifyAccountPassword(keyStoreDir, address, password string) (*types.Key, error) {
   183  	var err error
   184  	var foundKeyFile []byte
   185  
   186  	addressObj := types.BytesToAddress(types.FromHex(address))
   187  	checkAccountKey := func(path string, fileInfo os.FileInfo) error {
   188  		if len(foundKeyFile) > 0 || fileInfo.IsDir() {
   189  			return nil
   190  		}
   191  
   192  		rawKeyFile, e := ioutil.ReadFile(path)
   193  		if e != nil {
   194  			return fmt.Errorf("invalid account key file: %v", e)
   195  		}
   196  
   197  		var accountKey struct {
   198  			Address string `json:"address"`
   199  		}
   200  		if e := json.Unmarshal(rawKeyFile, &accountKey); e != nil {
   201  			return fmt.Errorf("failed to read key file: %s", e)
   202  		}
   203  		if types.HexToAddress("0x"+accountKey.Address).Hex() == addressObj.Hex() {
   204  			foundKeyFile = rawKeyFile
   205  		}
   206  
   207  		return nil
   208  	}
   209  	// locate key within key store directory (address should be within the file)
   210  	err = filepath.Walk(keyStoreDir, func(path string, fileInfo os.FileInfo, err error) error {
   211  		if err != nil {
   212  			return err
   213  		}
   214  		return checkAccountKey(path, fileInfo)
   215  	})
   216  	if err != nil {
   217  		return nil, fmt.Errorf("cannot traverse key store folder: %v", err)
   218  	}
   219  
   220  	if len(foundKeyFile) == 0 {
   221  		return nil, &ErrCannotLocateKeyFile{fmt.Sprintf("cannot locate account for address: %s", addressObj.Hex())}
   222  	}
   223  
   224  	key, err := keystore.DecryptKey(foundKeyFile, password)
   225  	if err != nil {
   226  		return nil, err
   227  	}
   228  
   229  	// avoid swap attack
   230  	if key.Address != addressObj {
   231  		return nil, fmt.Errorf("account mismatch: have %s, want %s", key.Address.Hex(), addressObj.Hex())
   232  	}
   233  
   234  	return key, nil
   235  }
   236  
   237  // SelectAccount selects current account, by verifying that address has corresponding account which can be decrypted
   238  // using provided password. Once verification is done, all previous identities are removed).
   239  func (m *DefaultManager) SelectAccount(loginParams LoginParams) error {
   240  	m.mu.Lock()
   241  	defer m.mu.Unlock()
   242  
   243  	m.accountsGenerator.Reset()
   244  
   245  	selectedChatAccount, err := m.unlockExtendedKey(loginParams.ChatAddress.String(), loginParams.Password)
   246  	if err != nil {
   247  		return err
   248  	}
   249  	m.watchAddresses = loginParams.WatchAddresses
   250  	m.mainAccountAddress = loginParams.MainAccount
   251  	m.selectedChatAccount = selectedChatAccount
   252  	return nil
   253  }
   254  
   255  func (m *DefaultManager) SetAccountAddresses(main types.Address, secondary ...types.Address) {
   256  	m.watchAddresses = []types.Address{main}
   257  	m.watchAddresses = append(m.watchAddresses, secondary...)
   258  	m.mainAccountAddress = main
   259  }
   260  
   261  // SetChatAccount initializes selectedChatAccount with privKey
   262  func (m *DefaultManager) SetChatAccount(privKey *ecdsa.PrivateKey) error {
   263  	m.mu.Lock()
   264  	defer m.mu.Unlock()
   265  
   266  	address := crypto.PubkeyToAddress(privKey.PublicKey)
   267  	id, err := uuid.NewRandom()
   268  	if err != nil {
   269  		return err
   270  	}
   271  
   272  	key := &types.Key{
   273  		ID:         id,
   274  		Address:    address,
   275  		PrivateKey: privKey,
   276  	}
   277  
   278  	m.selectedChatAccount = &SelectedExtKey{
   279  		Address:    address,
   280  		AccountKey: key,
   281  	}
   282  	return nil
   283  }
   284  
   285  // MainAccountAddress returns main account address set during login
   286  func (m *DefaultManager) MainAccountAddress() (types.Address, error) {
   287  	m.mu.RLock()
   288  	defer m.mu.RUnlock()
   289  
   290  	if m.mainAccountAddress == zeroAddress {
   291  		return zeroAddress, ErrNoAccountSelected
   292  	}
   293  
   294  	return m.mainAccountAddress, nil
   295  }
   296  
   297  // WatchAddresses returns currently selected watch addresses.
   298  func (m *DefaultManager) WatchAddresses() []types.Address {
   299  	m.mu.RLock()
   300  	defer m.mu.RUnlock()
   301  
   302  	return m.watchAddresses
   303  }
   304  
   305  // SelectedChatAccount returns currently selected chat account
   306  func (m *DefaultManager) SelectedChatAccount() (*SelectedExtKey, error) {
   307  	m.mu.RLock()
   308  	defer m.mu.RUnlock()
   309  
   310  	if m.selectedChatAccount == nil {
   311  		return nil, ErrNoAccountSelected
   312  	}
   313  	return m.selectedChatAccount, nil
   314  }
   315  
   316  // Logout clears selected accounts.
   317  func (m *DefaultManager) Logout() {
   318  	m.mu.Lock()
   319  	defer m.mu.Unlock()
   320  
   321  	m.accountsGenerator.Reset()
   322  	m.mainAccountAddress = zeroAddress
   323  	m.watchAddresses = nil
   324  	m.selectedChatAccount = nil
   325  }
   326  
   327  // ImportAccount imports the account specified with privateKey.
   328  func (m *DefaultManager) ImportAccount(privateKey *ecdsa.PrivateKey, password string) (types.Address, error) {
   329  	if m.keystore == nil {
   330  		return types.Address{}, ErrAccountKeyStoreMissing
   331  	}
   332  
   333  	account, err := m.keystore.ImportECDSA(privateKey, password)
   334  
   335  	return account.Address, err
   336  }
   337  
   338  // ImportSingleExtendedKey imports an extended key setting it in both the PrivateKey and ExtendedKey fields
   339  // of the Key struct.
   340  // ImportExtendedKey is used in older version of Status where PrivateKey is set to be the BIP44 key at index 0,
   341  // and ExtendedKey is the extended key of the BIP44 key at index 1.
   342  func (m *DefaultManager) ImportSingleExtendedKey(extKey *extkeys.ExtendedKey, password string) (address, pubKey string, err error) {
   343  	if m.keystore == nil {
   344  		return "", "", ErrAccountKeyStoreMissing
   345  	}
   346  
   347  	// imports extended key, create key file (if necessary)
   348  	account, err := m.keystore.ImportSingleExtendedKey(extKey, password)
   349  	if err != nil {
   350  		return "", "", err
   351  	}
   352  
   353  	address = account.Address.Hex()
   354  
   355  	// obtain public key to return
   356  	account, key, err := m.keystore.AccountDecryptedKey(account, password)
   357  	if err != nil {
   358  		return address, "", err
   359  	}
   360  
   361  	pubKey = types.EncodeHex(crypto.FromECDSAPub(&key.PrivateKey.PublicKey))
   362  
   363  	return
   364  }
   365  
   366  // importExtendedKey processes incoming extended key, extracts required info and creates corresponding account key.
   367  // Once account key is formed, that key is put (if not already) into keystore i.e. key is *encoded* into key file.
   368  func (m *DefaultManager) importExtendedKey(keyPurpose extkeys.KeyPurpose, extKey *extkeys.ExtendedKey, password string) (address, pubKey string, err error) {
   369  	if m.keystore == nil {
   370  		return "", "", ErrAccountKeyStoreMissing
   371  	}
   372  
   373  	// imports extended key, create key file (if necessary)
   374  	account, err := m.keystore.ImportExtendedKeyForPurpose(keyPurpose, extKey, password)
   375  	if err != nil {
   376  		return "", "", err
   377  	}
   378  	address = account.Address.Hex()
   379  
   380  	// obtain public key to return
   381  	account, key, err := m.keystore.AccountDecryptedKey(account, password)
   382  	if err != nil {
   383  		return address, "", err
   384  	}
   385  	pubKey = types.EncodeHex(crypto.FromECDSAPub(&key.PrivateKey.PublicKey))
   386  
   387  	return
   388  }
   389  
   390  // Accounts returns list of addresses for selected account, including
   391  // subaccounts.
   392  func (m *DefaultManager) Accounts() ([]types.Address, error) {
   393  	m.mu.RLock()
   394  	defer m.mu.RUnlock()
   395  	addresses := make([]types.Address, 0)
   396  	if m.mainAccountAddress != zeroAddress {
   397  		addresses = append(addresses, m.mainAccountAddress)
   398  	}
   399  
   400  	return addresses, nil
   401  }
   402  
   403  // StartOnboarding starts the onboarding process generating accountsCount accounts and returns a slice of OnboardingAccount.
   404  func (m *DefaultManager) StartOnboarding(accountsCount, mnemonicPhraseLength int) ([]*OnboardingAccount, error) {
   405  	m.mu.Lock()
   406  	defer m.mu.Unlock()
   407  
   408  	onboarding, err := NewOnboarding(accountsCount, mnemonicPhraseLength)
   409  	if err != nil {
   410  		return nil, err
   411  	}
   412  
   413  	m.onboarding = onboarding
   414  
   415  	return m.onboarding.Accounts(), nil
   416  }
   417  
   418  // RemoveOnboarding reset the current onboarding struct setting it to nil and deleting the accounts from memory.
   419  func (m *DefaultManager) RemoveOnboarding() {
   420  	m.mu.Lock()
   421  	defer m.mu.Unlock()
   422  
   423  	m.onboarding = nil
   424  }
   425  
   426  // ImportOnboardingAccount imports the account specified by id and encrypts it with password.
   427  func (m *DefaultManager) ImportOnboardingAccount(id string, password string) (Info, string, error) {
   428  	var info Info
   429  
   430  	m.mu.Lock()
   431  	defer m.mu.Unlock()
   432  
   433  	if m.onboarding == nil {
   434  		return info, "", ErrOnboardingNotStarted
   435  	}
   436  
   437  	acc, err := m.onboarding.Account(id)
   438  	if err != nil {
   439  		return info, "", err
   440  	}
   441  
   442  	info, err = m.RecoverAccount(password, acc.mnemonic)
   443  	if err != nil {
   444  		return info, "", err
   445  	}
   446  
   447  	m.onboarding = nil
   448  
   449  	return info, acc.mnemonic, nil
   450  }
   451  
   452  // AddressToDecryptedAccount tries to load decrypted key for a given account.
   453  // The running node, has a keystore directory which is loaded on start. Key file
   454  // for a given address is expected to be in that directory prior to node start.
   455  func (m *DefaultManager) AddressToDecryptedAccount(address, password string) (types.Account, *types.Key, error) {
   456  	if m.keystore == nil {
   457  		return types.Account{}, nil, ErrAccountKeyStoreMissing
   458  	}
   459  
   460  	account, err := ParseAccountString(address)
   461  	if err != nil {
   462  		return types.Account{}, nil, ErrAddressToAccountMappingFailure
   463  	}
   464  
   465  	account, key, err := m.keystore.AccountDecryptedKey(account, password)
   466  	if err != nil {
   467  		err = fmt.Errorf("%s: %s", ErrAccountToKeyMappingFailure, err)
   468  	}
   469  
   470  	return account, key, err
   471  }
   472  
   473  func (m *DefaultManager) unlockExtendedKey(address, password string) (*SelectedExtKey, error) {
   474  	account, accountKey, err := m.AddressToDecryptedAccount(address, password)
   475  	if err != nil {
   476  		return nil, err
   477  	}
   478  
   479  	selectedExtendedKey := &SelectedExtKey{
   480  		Address:    account.Address,
   481  		AccountKey: accountKey,
   482  	}
   483  
   484  	return selectedExtendedKey, nil
   485  }
   486  
   487  func (m *DefaultManager) MigrateKeyStoreDir(oldDir, newDir string, addresses []string) error {
   488  	paths := []string{}
   489  
   490  	addressesMap := map[string]struct{}{}
   491  	for _, address := range addresses {
   492  		addressesMap[address] = struct{}{}
   493  	}
   494  
   495  	checkFile := func(path string, fileInfo os.FileInfo) error {
   496  		if fileInfo.IsDir() || filepath.Dir(path) != oldDir {
   497  			return nil
   498  		}
   499  
   500  		rawKeyFile, err := ioutil.ReadFile(path)
   501  		if err != nil {
   502  			return fmt.Errorf("invalid account key file: %v", err)
   503  		}
   504  
   505  		var accountKey struct {
   506  			Address string `json:"address"`
   507  		}
   508  		if err := json.Unmarshal(rawKeyFile, &accountKey); err != nil {
   509  			return fmt.Errorf("failed to read key file: %s", err)
   510  		}
   511  
   512  		address := types.HexToAddress("0x" + accountKey.Address).Hex()
   513  		if _, ok := addressesMap[address]; ok {
   514  			paths = append(paths, path)
   515  		}
   516  
   517  		return nil
   518  	}
   519  
   520  	err := filepath.Walk(oldDir, func(path string, fileInfo os.FileInfo, err error) error {
   521  		if err != nil {
   522  			return err
   523  		}
   524  		return checkFile(path, fileInfo)
   525  	})
   526  	if err != nil {
   527  		return fmt.Errorf("cannot traverse key store folder: %v", err)
   528  	}
   529  
   530  	for _, path := range paths {
   531  		_, fileName := filepath.Split(path)
   532  		newPath := filepath.Join(newDir, fileName)
   533  		err := os.Rename(path, newPath)
   534  		if err != nil {
   535  			return err
   536  		}
   537  	}
   538  
   539  	return nil
   540  }
   541  
   542  func (m *DefaultManager) ReEncryptKey(rawKey []byte, pass string, newPass string) (reEncryptedKey []byte, e error) {
   543  	cryptoJSON, e := keystore.RawKeyToCryptoJSON(rawKey)
   544  	if e != nil {
   545  		return reEncryptedKey, fmt.Errorf("convert to crypto json error: %v", e)
   546  	}
   547  
   548  	decryptedKey, e := keystore.DecryptKey(rawKey, pass)
   549  	if e != nil {
   550  		return reEncryptedKey, fmt.Errorf("decryption error: %v", e)
   551  	}
   552  
   553  	if cryptoJSON.KDFParams["n"] == nil || cryptoJSON.KDFParams["p"] == nil {
   554  		return reEncryptedKey, fmt.Errorf("Unable to determine `n` or `p`: %v", e)
   555  	}
   556  	n := int(cryptoJSON.KDFParams["n"].(float64))
   557  	p := int(cryptoJSON.KDFParams["p"].(float64))
   558  
   559  	gethKey := gethkeystore.Key{
   560  		Id:              decryptedKey.ID,
   561  		Address:         gethcommon.Address(decryptedKey.Address),
   562  		PrivateKey:      decryptedKey.PrivateKey,
   563  		ExtendedKey:     decryptedKey.ExtendedKey,
   564  		SubAccountIndex: decryptedKey.SubAccountIndex,
   565  	}
   566  
   567  	return gethkeystore.EncryptKey(&gethKey, newPass, n, p)
   568  }
   569  
   570  func (m *DefaultManager) ReEncryptKeyStoreDir(keyDirPath, oldPass, newPass string) error {
   571  	rencryptFileAtPath := func(tempKeyDirPath, path string, fileInfo os.FileInfo) error {
   572  		if fileInfo.IsDir() {
   573  			return nil
   574  		}
   575  
   576  		rawKeyFile, e := ioutil.ReadFile(path)
   577  		if e != nil {
   578  			return fmt.Errorf("invalid account key file: %v", e)
   579  		}
   580  
   581  		reEncryptedKey, e := m.ReEncryptKey(rawKeyFile, oldPass, newPass)
   582  		if e != nil {
   583  			return fmt.Errorf("unable to re-encrypt key file: %v, path: %s, name: %s", e, path, fileInfo.Name())
   584  		}
   585  
   586  		tempWritePath := filepath.Join(tempKeyDirPath, fileInfo.Name())
   587  		e = ioutil.WriteFile(tempWritePath, reEncryptedKey, fileInfo.Mode().Perm())
   588  		if e != nil {
   589  			return fmt.Errorf("unable write key file: %v", e)
   590  		}
   591  
   592  		return nil
   593  	}
   594  
   595  	keyDirPath = strings.TrimSuffix(keyDirPath, "/")
   596  	keyDirPath = strings.TrimSuffix(keyDirPath, "\\")
   597  	keyParent, keyDirName := filepath.Split(keyDirPath)
   598  
   599  	// backupKeyDirName used to store existing keys before final write
   600  	backupKeyDirName := keyDirName + "-backup"
   601  	// tempKeyDirName used to put re-encrypted keys
   602  	tempKeyDirName := keyDirName + "-re-encrypted"
   603  	backupKeyDirPath := filepath.Join(keyParent, backupKeyDirName)
   604  	tempKeyDirPath := filepath.Join(keyParent, tempKeyDirName)
   605  
   606  	// create temp key dir
   607  	err := os.MkdirAll(tempKeyDirPath, os.ModePerm)
   608  	if err != nil {
   609  		return fmt.Errorf("mkdirall error: %v, tempKeyDirPath: %s", err, tempKeyDirPath)
   610  	}
   611  
   612  	err = filepath.Walk(keyDirPath, func(path string, fileInfo os.FileInfo, err error) error {
   613  		if err != nil {
   614  			os.RemoveAll(tempKeyDirPath)
   615  			return fmt.Errorf("walk callback error: %v", err)
   616  		}
   617  
   618  		return rencryptFileAtPath(tempKeyDirPath, path, fileInfo)
   619  	})
   620  	if err != nil {
   621  		os.RemoveAll(tempKeyDirPath)
   622  		return fmt.Errorf("walk error: %v", err)
   623  	}
   624  
   625  	// move existing keys
   626  	err = os.Rename(keyDirPath, backupKeyDirPath)
   627  	if err != nil {
   628  		os.RemoveAll(tempKeyDirPath)
   629  		return fmt.Errorf("unable to rename keyDirPath to backupKeyDirPath: %v", err)
   630  	}
   631  
   632  	// move tempKeyDirPath to keyDirPath
   633  	err = os.Rename(tempKeyDirPath, keyDirPath)
   634  	if err != nil {
   635  		// if this happens, then the app is probably bricked, because the keystore won't exist anymore
   636  		// try to restore from backup
   637  		_ = os.Rename(backupKeyDirPath, keyDirPath)
   638  		return fmt.Errorf("unable to rename tempKeyDirPath to keyDirPath: %v", err)
   639  	}
   640  
   641  	// remove temp and backup folders and their contents
   642  	err = os.RemoveAll(tempKeyDirPath)
   643  	if err != nil {
   644  		// the re-encryption is complete so we don't throw
   645  		log.Error("unable to delete tempKeyDirPath, manual cleanup required")
   646  	}
   647  
   648  	err = os.RemoveAll(backupKeyDirPath)
   649  	if err != nil {
   650  		// the re-encryption is complete so we don't throw
   651  		log.Error("unable to delete backupKeyDirPath, manual cleanup required")
   652  	}
   653  
   654  	return nil
   655  }
   656  
   657  func (m *DefaultManager) DeleteAccount(address types.Address) error {
   658  	return m.keystore.Delete(types.Account{Address: address})
   659  }
   660  
   661  func (m *DefaultManager) GetVerifiedWalletAccount(db *accounts.Database, address, password string) (*SelectedExtKey, error) {
   662  	exists, err := db.AddressExists(types.HexToAddress(address))
   663  	if err != nil {
   664  		return nil, err
   665  	}
   666  
   667  	if !exists {
   668  		return nil, errors.New("account doesn't exist")
   669  	}
   670  
   671  	key, err := m.VerifyAccountPassword(m.Keydir, address, password)
   672  	if _, ok := err.(*ErrCannotLocateKeyFile); ok {
   673  		key, err = m.generatePartialAccountKey(db, address, password)
   674  		if err != nil {
   675  			return nil, err
   676  		}
   677  	}
   678  
   679  	if err != nil {
   680  		return nil, err
   681  	}
   682  
   683  	return &SelectedExtKey{
   684  		Address:    key.Address,
   685  		AccountKey: key,
   686  	}, nil
   687  }
   688  
   689  func (m *DefaultManager) generatePartialAccountKey(db *accounts.Database, address string, password string) (*types.Key, error) {
   690  	dbPath, err := db.GetPath(types.HexToAddress(address))
   691  	path := "m/" + dbPath[strings.LastIndex(dbPath, "/")+1:]
   692  	if err != nil {
   693  		return nil, err
   694  	}
   695  
   696  	rootAddress, err := db.GetWalletRootAddress()
   697  	if err != nil {
   698  		return nil, err
   699  	}
   700  	info, err := m.AccountsGenerator().LoadAccount(rootAddress.Hex(), password)
   701  	if err != nil {
   702  		return nil, err
   703  	}
   704  	masterID := info.ID
   705  
   706  	accInfosMap, err := m.AccountsGenerator().StoreDerivedAccounts(masterID, password, []string{path})
   707  	if err != nil {
   708  		return nil, err
   709  	}
   710  
   711  	_, key, err := m.AddressToDecryptedAccount(accInfosMap[path].Address, password)
   712  	if err != nil {
   713  		return nil, err
   714  	}
   715  
   716  	return key, nil
   717  }
   718  
   719  func (m *DefaultManager) Recover(rpcParams RecoverParams) (addr types.Address, err error) {
   720  	ctx, cancel := context.WithTimeout(context.Background(), m.rpcTimeout)
   721  	defer cancel()
   722  	var gethAddr gethcommon.Address
   723  	err = m.rpcClient.CallContextIgnoringLocalHandlers(
   724  		ctx,
   725  		&gethAddr,
   726  		m.rpcClient.UpstreamChainID,
   727  		params.PersonalRecoverMethodName,
   728  		rpcParams.Message, rpcParams.Signature)
   729  	addr = types.Address(gethAddr)
   730  
   731  	return
   732  }
   733  
   734  func (m *DefaultManager) CanRecover(rpcParams RecoverParams, revealedAddress types.Address) (bool, error) {
   735  	recovered, err := m.Recover(rpcParams)
   736  	if err != nil {
   737  		return false, err
   738  	}
   739  	return recovered == revealedAddress, nil
   740  }
   741  
   742  func (m *DefaultManager) Sign(rpcParams SignParams, verifiedAccount *SelectedExtKey) (result types.HexBytes, err error) {
   743  	if !strings.EqualFold(rpcParams.Address, verifiedAccount.Address.Hex()) {
   744  		err = ErrInvalidPersonalSignAccount
   745  		return
   746  	}
   747  
   748  	ctx, cancel := context.WithTimeout(context.Background(), m.rpcTimeout)
   749  	defer cancel()
   750  	var gethResult hexutil.Bytes
   751  	err = m.rpcClient.CallContextIgnoringLocalHandlers(
   752  		ctx,
   753  		&gethResult,
   754  		m.rpcClient.UpstreamChainID,
   755  		params.PersonalSignMethodName,
   756  		rpcParams.Data, rpcParams.Address, rpcParams.Password)
   757  	result = types.HexBytes(gethResult)
   758  
   759  	return
   760  }