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

     1  package generator
     2  
     3  import (
     4  	"crypto/ecdsa"
     5  	"errors"
     6  	"fmt"
     7  	"strings"
     8  	"sync"
     9  
    10  	"github.com/pborman/uuid"
    11  
    12  	"github.com/status-im/status-go/eth-node/crypto"
    13  	"github.com/status-im/status-go/eth-node/keystore"
    14  	"github.com/status-im/status-go/eth-node/types"
    15  	"github.com/status-im/status-go/extkeys"
    16  )
    17  
    18  var (
    19  	// ErrAccountNotFoundByID is returned when the selected account doesn't exist in memory.
    20  	ErrAccountNotFoundByID = errors.New("account not found")
    21  	// ErrAccountCannotDeriveChildKeys is returned when trying to derive child accounts from a normal key.
    22  	ErrAccountCannotDeriveChildKeys = errors.New("selected account cannot derive child keys")
    23  	// ErrAccountManagerNotSet is returned when the account mananger instance is not set.
    24  	ErrAccountManagerNotSet = errors.New("account manager not set")
    25  )
    26  
    27  type AccountManager interface {
    28  	AddressToDecryptedAccount(address, password string) (types.Account, *types.Key, error)
    29  	ImportSingleExtendedKey(extKey *extkeys.ExtendedKey, password string) (address, pubKey string, err error)
    30  	ImportAccount(privateKey *ecdsa.PrivateKey, password string) (types.Address, error)
    31  }
    32  
    33  type Generator struct {
    34  	am       AccountManager
    35  	accounts map[string]*Account
    36  	sync.Mutex
    37  }
    38  
    39  func New(am AccountManager) *Generator {
    40  	return &Generator{
    41  		am:       am,
    42  		accounts: make(map[string]*Account),
    43  	}
    44  }
    45  
    46  func (g *Generator) Generate(mnemonicPhraseLength int, n int, bip39Passphrase string) ([]GeneratedAccountInfo, error) {
    47  	entropyStrength, err := MnemonicPhraseLengthToEntropyStrength(mnemonicPhraseLength)
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  
    52  	infos := make([]GeneratedAccountInfo, 0)
    53  
    54  	for i := 0; i < n; i++ {
    55  		mnemonic := extkeys.NewMnemonic()
    56  		mnemonicPhrase, err := mnemonic.MnemonicPhrase(entropyStrength, extkeys.EnglishLanguage)
    57  		if err != nil {
    58  			return nil, fmt.Errorf("can not create mnemonic seed: %v", err)
    59  		}
    60  
    61  		info, err := g.ImportMnemonic(mnemonicPhrase, bip39Passphrase)
    62  		if err != nil {
    63  			return nil, err
    64  		}
    65  
    66  		infos = append(infos, info)
    67  	}
    68  
    69  	return infos, err
    70  }
    71  
    72  func (g *Generator) CreateAccountFromPrivateKey(privateKeyHex string) (IdentifiedAccountInfo, error) {
    73  	privateKeyHex = strings.TrimPrefix(privateKeyHex, "0x")
    74  	privateKey, err := crypto.HexToECDSA(privateKeyHex)
    75  	if err != nil {
    76  		return IdentifiedAccountInfo{}, err
    77  	}
    78  
    79  	acc := &Account{
    80  		privateKey: privateKey,
    81  	}
    82  
    83  	return acc.ToIdentifiedAccountInfo(""), nil
    84  }
    85  
    86  func (g *Generator) ImportPrivateKey(privateKeyHex string) (IdentifiedAccountInfo, error) {
    87  	privateKeyHex = strings.TrimPrefix(privateKeyHex, "0x")
    88  	privateKey, err := crypto.HexToECDSA(privateKeyHex)
    89  	if err != nil {
    90  		return IdentifiedAccountInfo{}, err
    91  	}
    92  
    93  	acc := &Account{
    94  		privateKey: privateKey,
    95  	}
    96  
    97  	id := g.addAccount(acc)
    98  
    99  	return acc.ToIdentifiedAccountInfo(id), nil
   100  }
   101  
   102  func (g *Generator) ImportJSONKey(json string, password string) (IdentifiedAccountInfo, error) {
   103  	key, err := keystore.DecryptKey([]byte(json), password)
   104  	if err != nil {
   105  		return IdentifiedAccountInfo{}, err
   106  	}
   107  
   108  	acc := &Account{
   109  		privateKey: key.PrivateKey,
   110  	}
   111  
   112  	id := g.addAccount(acc)
   113  
   114  	return acc.ToIdentifiedAccountInfo(id), nil
   115  }
   116  
   117  func (g *Generator) CreateAccountFromMnemonicAndDeriveAccountsForPaths(mnemonicPhrase string, bip39Passphrase string, paths []string) (GeneratedAndDerivedAccountInfo, error) {
   118  	mnemonic := extkeys.NewMnemonic()
   119  	masterExtendedKey, err := extkeys.NewMaster(mnemonic.MnemonicSeed(mnemonicPhrase, bip39Passphrase))
   120  	if err != nil {
   121  		return GeneratedAndDerivedAccountInfo{}, fmt.Errorf("can not create master extended key: %v", err)
   122  	}
   123  
   124  	acc := &Account{
   125  		privateKey:  masterExtendedKey.ToECDSA(),
   126  		extendedKey: masterExtendedKey,
   127  	}
   128  
   129  	derivedAccountsInfo := make(map[string]AccountInfo)
   130  	if len(paths) > 0 {
   131  		derivedAccounts, err := g.deriveChildAccounts(acc, paths)
   132  		if err != nil {
   133  			return GeneratedAndDerivedAccountInfo{}, err
   134  		}
   135  
   136  		for pathString, childAccount := range derivedAccounts {
   137  			derivedAccountsInfo[pathString] = childAccount.ToAccountInfo()
   138  		}
   139  	}
   140  
   141  	accInfo := acc.ToGeneratedAccountInfo("", mnemonicPhrase)
   142  
   143  	return accInfo.toGeneratedAndDerived(derivedAccountsInfo), nil
   144  }
   145  
   146  func (g *Generator) ImportMnemonic(mnemonicPhrase string, bip39Passphrase string) (GeneratedAccountInfo, error) {
   147  	mnemonic := extkeys.NewMnemonic()
   148  	masterExtendedKey, err := extkeys.NewMaster(mnemonic.MnemonicSeed(mnemonicPhrase, bip39Passphrase))
   149  	if err != nil {
   150  		return GeneratedAccountInfo{}, fmt.Errorf("can not create master extended key: %v", err)
   151  	}
   152  
   153  	acc := &Account{
   154  		privateKey:  masterExtendedKey.ToECDSA(),
   155  		extendedKey: masterExtendedKey,
   156  	}
   157  
   158  	id := g.addAccount(acc)
   159  
   160  	return acc.ToGeneratedAccountInfo(id, mnemonicPhrase), nil
   161  }
   162  
   163  func (g *Generator) GenerateAndDeriveAddresses(mnemonicPhraseLength int, n int, bip39Passphrase string, pathStrings []string) ([]GeneratedAndDerivedAccountInfo, error) {
   164  	masterAccounts, err := g.Generate(mnemonicPhraseLength, n, bip39Passphrase)
   165  	if err != nil {
   166  		return nil, err
   167  	}
   168  
   169  	accs := make([]GeneratedAndDerivedAccountInfo, n)
   170  
   171  	for i := 0; i < len(masterAccounts); i++ {
   172  		acc := masterAccounts[i]
   173  		derived, err := g.DeriveAddresses(acc.ID, pathStrings)
   174  		if err != nil {
   175  			return nil, err
   176  		}
   177  
   178  		accs[i] = acc.toGeneratedAndDerived(derived)
   179  	}
   180  
   181  	return accs, nil
   182  }
   183  
   184  func (g *Generator) DeriveAddresses(accountID string, pathStrings []string) (map[string]AccountInfo, error) {
   185  	acc, err := g.findAccount(accountID)
   186  	if err != nil {
   187  		return nil, err
   188  	}
   189  
   190  	pathAccounts, err := g.deriveChildAccounts(acc, pathStrings)
   191  	if err != nil {
   192  		return nil, err
   193  	}
   194  
   195  	pathAccountsInfo := make(map[string]AccountInfo)
   196  
   197  	for pathString, childAccount := range pathAccounts {
   198  		pathAccountsInfo[pathString] = childAccount.ToAccountInfo()
   199  	}
   200  
   201  	return pathAccountsInfo, nil
   202  }
   203  
   204  func (g *Generator) StoreAccount(accountID string, password string) (AccountInfo, error) {
   205  	if g.am == nil {
   206  		return AccountInfo{}, ErrAccountManagerNotSet
   207  	}
   208  
   209  	acc, err := g.findAccount(accountID)
   210  	if err != nil {
   211  		return AccountInfo{}, err
   212  	}
   213  
   214  	return g.store(acc, password)
   215  }
   216  
   217  func (g *Generator) StoreDerivedAccounts(accountID string, password string, pathStrings []string) (map[string]AccountInfo, error) {
   218  	if g.am == nil {
   219  		return nil, ErrAccountManagerNotSet
   220  	}
   221  
   222  	acc, err := g.findAccount(accountID)
   223  	if err != nil {
   224  		return nil, err
   225  	}
   226  
   227  	pathAccounts, err := g.deriveChildAccounts(acc, pathStrings)
   228  	if err != nil {
   229  		return nil, err
   230  	}
   231  
   232  	pathAccountsInfo := make(map[string]AccountInfo)
   233  
   234  	for pathString, childAccount := range pathAccounts {
   235  		info, err := g.store(childAccount, password)
   236  		if err != nil {
   237  			return nil, err
   238  		}
   239  
   240  		pathAccountsInfo[pathString] = info
   241  	}
   242  
   243  	return pathAccountsInfo, nil
   244  }
   245  
   246  func (g *Generator) LoadAccount(address string, password string) (IdentifiedAccountInfo, error) {
   247  	if g.am == nil {
   248  		return IdentifiedAccountInfo{}, ErrAccountManagerNotSet
   249  	}
   250  
   251  	_, key, err := g.am.AddressToDecryptedAccount(address, password)
   252  	if err != nil {
   253  		return IdentifiedAccountInfo{}, err
   254  	}
   255  
   256  	if err := ValidateKeystoreExtendedKey(key); err != nil {
   257  		return IdentifiedAccountInfo{}, err
   258  	}
   259  
   260  	acc := &Account{
   261  		privateKey:  key.PrivateKey,
   262  		extendedKey: key.ExtendedKey,
   263  	}
   264  
   265  	id := g.addAccount(acc)
   266  
   267  	return acc.ToIdentifiedAccountInfo(id), nil
   268  }
   269  
   270  func (g *Generator) deriveChildAccounts(acc *Account, pathStrings []string) (map[string]*Account, error) {
   271  	pathAccounts := make(map[string]*Account)
   272  
   273  	for _, pathString := range pathStrings {
   274  		childAccount, err := g.deriveChildAccount(acc, pathString)
   275  		if err != nil {
   276  			return pathAccounts, err
   277  		}
   278  
   279  		pathAccounts[pathString] = childAccount
   280  	}
   281  
   282  	return pathAccounts, nil
   283  }
   284  
   285  func (g *Generator) deriveChildAccount(acc *Account, pathString string) (*Account, error) {
   286  	_, path, err := decodePath(pathString)
   287  	if err != nil {
   288  		return nil, err
   289  	}
   290  
   291  	if acc.extendedKey.IsZeroed() && len(path) == 0 {
   292  		return acc, nil
   293  	}
   294  
   295  	if acc.extendedKey.IsZeroed() {
   296  		return nil, ErrAccountCannotDeriveChildKeys
   297  	}
   298  
   299  	childExtendedKey, err := acc.extendedKey.Derive(path)
   300  	if err != nil {
   301  		return nil, err
   302  	}
   303  
   304  	return &Account{
   305  		privateKey:  childExtendedKey.ToECDSA(),
   306  		extendedKey: childExtendedKey,
   307  	}, nil
   308  }
   309  
   310  func (g *Generator) store(acc *Account, password string) (AccountInfo, error) {
   311  	if acc.extendedKey != nil {
   312  		if _, _, err := g.am.ImportSingleExtendedKey(acc.extendedKey, password); err != nil {
   313  			return AccountInfo{}, err
   314  		}
   315  	} else {
   316  		if _, err := g.am.ImportAccount(acc.privateKey, password); err != nil {
   317  			return AccountInfo{}, err
   318  		}
   319  	}
   320  
   321  	return acc.ToAccountInfo(), nil
   322  }
   323  
   324  func (g *Generator) addAccount(acc *Account) string {
   325  	g.Lock()
   326  	defer g.Unlock()
   327  
   328  	id := uuid.NewRandom().String()
   329  	g.accounts[id] = acc
   330  
   331  	return id
   332  }
   333  
   334  // Reset resets the accounts map removing all the accounts from memory.
   335  func (g *Generator) Reset() {
   336  	g.Lock()
   337  	defer g.Unlock()
   338  
   339  	g.accounts = make(map[string]*Account)
   340  }
   341  
   342  func (g *Generator) findAccount(accountID string) (*Account, error) {
   343  	g.Lock()
   344  	defer g.Unlock()
   345  
   346  	acc, ok := g.accounts[accountID]
   347  	if !ok {
   348  		return nil, ErrAccountNotFoundByID
   349  	}
   350  
   351  	return acc, nil
   352  }