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 }