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

     1  package account
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/pborman/uuid"
     7  
     8  	"github.com/status-im/status-go/account/generator"
     9  	"github.com/status-im/status-go/eth-node/crypto"
    10  	"github.com/status-im/status-go/eth-node/types"
    11  	"github.com/status-im/status-go/extkeys"
    12  )
    13  
    14  // OnboardingAccount is returned during onboarding and contains its ID and the mnemonic to re-generate the same account Info keys.
    15  type OnboardingAccount struct {
    16  	ID       string `json:"id"`
    17  	mnemonic string
    18  	Info     Info `json:"info"`
    19  }
    20  
    21  // Onboarding is a struct contains a slice of OnboardingAccount.
    22  type Onboarding struct {
    23  	accounts map[string]*OnboardingAccount
    24  }
    25  
    26  // NewOnboarding returns a new onboarding struct generating n accounts.
    27  func NewOnboarding(n, mnemonicPhraseLength int) (*Onboarding, error) {
    28  	onboarding := &Onboarding{
    29  		accounts: make(map[string]*OnboardingAccount),
    30  	}
    31  
    32  	for i := 0; i < n; i++ {
    33  		account, err := onboarding.generateAccount(mnemonicPhraseLength)
    34  		if err != nil {
    35  			return nil, err
    36  		}
    37  		onboarding.accounts[account.ID] = account
    38  	}
    39  
    40  	return onboarding, nil
    41  }
    42  
    43  // Accounts return the list of OnboardingAccount generated.
    44  func (o *Onboarding) Accounts() []*OnboardingAccount {
    45  	accounts := make([]*OnboardingAccount, 0)
    46  	for _, a := range o.accounts {
    47  		accounts = append(accounts, a)
    48  	}
    49  
    50  	return accounts
    51  }
    52  
    53  // Account returns an OnboardingAccount by id.
    54  func (o *Onboarding) Account(id string) (*OnboardingAccount, error) {
    55  	account, ok := o.accounts[id]
    56  	if !ok {
    57  		return nil, ErrOnboardingAccountNotFound
    58  	}
    59  
    60  	return account, nil
    61  }
    62  
    63  func (o *Onboarding) generateAccount(mnemonicPhraseLength int) (*OnboardingAccount, error) {
    64  	entropyStrength, err := generator.MnemonicPhraseLengthToEntropyStrength(mnemonicPhraseLength)
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  
    69  	mnemonic := extkeys.NewMnemonic()
    70  	mnemonicPhrase, err := mnemonic.MnemonicPhrase(entropyStrength, extkeys.EnglishLanguage)
    71  	if err != nil {
    72  		return nil, fmt.Errorf("can not create mnemonic seed: %v", err)
    73  	}
    74  
    75  	masterExtendedKey, err := extkeys.NewMaster(mnemonic.MnemonicSeed(mnemonicPhrase, ""))
    76  	if err != nil {
    77  		return nil, fmt.Errorf("can not create master extended key: %v", err)
    78  	}
    79  
    80  	walletAddress, walletPubKey, err := o.deriveAccount(masterExtendedKey, extkeys.KeyPurposeWallet, 0)
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  
    85  	info := Info{
    86  		WalletAddress: walletAddress,
    87  		WalletPubKey:  walletPubKey,
    88  		ChatAddress:   walletAddress,
    89  		ChatPubKey:    walletPubKey,
    90  	}
    91  
    92  	account := &OnboardingAccount{
    93  		ID:       uuid.NewRandom().String(),
    94  		mnemonic: mnemonicPhrase,
    95  		Info:     info,
    96  	}
    97  
    98  	return account, nil
    99  }
   100  
   101  func (o *Onboarding) deriveAccount(masterExtendedKey *extkeys.ExtendedKey, purpose extkeys.KeyPurpose, index uint32) (string, string, error) {
   102  	extendedKey, err := masterExtendedKey.ChildForPurpose(purpose, index)
   103  	if err != nil {
   104  		return "", "", err
   105  	}
   106  
   107  	privateKeyECDSA := extendedKey.ToECDSA()
   108  	address := crypto.PubkeyToAddress(privateKeyECDSA.PublicKey)
   109  	publicKeyHex := types.EncodeHex(crypto.FromECDSAPub(&privateKeyECDSA.PublicKey))
   110  
   111  	return address.Hex(), publicKeyHex, nil
   112  }