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 }