github.com/status-im/status-go@v1.1.0/services/accounts/accounts.go (about) 1 package accounts 2 3 import ( 4 "context" 5 "errors" 6 "strings" 7 8 "github.com/ethereum/go-ethereum/common" 9 "github.com/ethereum/go-ethereum/event" 10 11 "github.com/ethereum/go-ethereum/log" 12 "github.com/status-im/status-go/account" 13 "github.com/status-im/status-go/eth-node/types" 14 "github.com/status-im/status-go/multiaccounts/accounts" 15 walletsettings "github.com/status-im/status-go/multiaccounts/settings_wallet" 16 "github.com/status-im/status-go/params" 17 "github.com/status-im/status-go/protocol" 18 "github.com/status-im/status-go/services/accounts/accountsevent" 19 ) 20 21 func NewAccountsAPI(manager *account.GethManager, config *params.NodeConfig, db *accounts.Database, feed *event.Feed, messenger **protocol.Messenger) *API { 22 return &API{manager, config, db, feed, messenger} 23 } 24 25 // API is class with methods available over RPC. 26 type API struct { 27 manager *account.GethManager 28 config *params.NodeConfig 29 db *accounts.Database 30 feed *event.Feed 31 messenger **protocol.Messenger 32 } 33 34 type DerivedAddress struct { 35 Address common.Address `json:"address"` 36 Path string `json:"path"` 37 HasActivity bool `json:"hasActivity"` 38 AlreadyCreated bool `json:"alreadyCreated"` 39 } 40 41 func (api *API) SaveAccount(ctx context.Context, account *accounts.Account) error { 42 log.Info("[AccountsAPI::SaveAccount]") 43 err := (*api.messenger).SaveOrUpdateAccount(account) 44 if err != nil { 45 return err 46 } 47 48 api.feed.Send(accountsevent.Event{ 49 Type: accountsevent.EventTypeAdded, 50 Accounts: []common.Address{common.Address(account.Address)}, 51 }) 52 return nil 53 } 54 55 // Setting `Keypair` without `Accounts` will update keypair only, `Keycards` won't be saved/updated this way. 56 func (api *API) SaveKeypair(ctx context.Context, keypair *accounts.Keypair) error { 57 log.Info("[AccountsAPI::SaveKeypair]") 58 err := (*api.messenger).SaveOrUpdateKeypair(keypair) 59 if err != nil { 60 return err 61 } 62 63 commonAddresses := []common.Address{} 64 for _, acc := range keypair.Accounts { 65 commonAddresses = append(commonAddresses, common.Address(acc.Address)) 66 } 67 68 api.feed.Send(accountsevent.Event{ 69 Type: accountsevent.EventTypeAdded, 70 Accounts: commonAddresses, 71 }) 72 return nil 73 } 74 75 func (api *API) HasPairedDevices(ctx context.Context) bool { 76 return (*api.messenger).HasPairedDevices() 77 } 78 79 // Setting `Keypair` without `Accounts` will update keypair only. 80 func (api *API) UpdateKeypairName(ctx context.Context, keyUID string, name string) error { 81 return (*api.messenger).UpdateKeypairName(keyUID, name) 82 } 83 84 func (api *API) MoveWalletAccount(ctx context.Context, fromPosition int64, toPosition int64) error { 85 return (*api.messenger).MoveWalletAccount(fromPosition, toPosition) 86 } 87 88 func (api *API) UpdateTokenPreferences(ctx context.Context, preferences []walletsettings.TokenPreferences) error { 89 return (*api.messenger).UpdateTokenPreferences(preferences) 90 } 91 92 func (api *API) GetTokenPreferences(ctx context.Context) ([]walletsettings.TokenPreferences, error) { 93 return (*api.messenger).GetTokenPreferences() 94 } 95 96 func (api *API) UpdateCollectiblePreferences(ctx context.Context, preferences []walletsettings.CollectiblePreferences) error { 97 return (*api.messenger).UpdateCollectiblePreferences(preferences) 98 } 99 100 func (api *API) GetCollectiblePreferences(ctx context.Context) ([]walletsettings.CollectiblePreferences, error) { 101 return (*api.messenger).GetCollectiblePreferences() 102 } 103 104 func (api *API) GetAccounts(ctx context.Context) ([]*accounts.Account, error) { 105 return api.db.GetActiveAccounts() 106 } 107 108 func (api *API) GetWatchOnlyAccounts(ctx context.Context) ([]*accounts.Account, error) { 109 return api.db.GetActiveWatchOnlyAccounts() 110 } 111 112 func (api *API) GetKeypairs(ctx context.Context) ([]*accounts.Keypair, error) { 113 return api.db.GetActiveKeypairs() 114 } 115 116 func (api *API) GetAccountByAddress(ctx context.Context, address types.Address) (*accounts.Account, error) { 117 return api.db.GetAccountByAddress(address) 118 } 119 120 func (api *API) GetKeypairByKeyUID(ctx context.Context, keyUID string) (*accounts.Keypair, error) { 121 return api.db.GetKeypairByKeyUID(keyUID) 122 } 123 124 func (api *API) DeleteAccount(ctx context.Context, address types.Address) error { 125 err := (*api.messenger).DeleteAccount(address) 126 if err != nil { 127 return err 128 } 129 130 api.feed.Send(accountsevent.Event{ 131 Type: accountsevent.EventTypeRemoved, 132 Accounts: []common.Address{common.Address(address)}, 133 }) 134 135 return nil 136 } 137 138 func (api *API) DeleteKeypair(ctx context.Context, keyUID string) error { 139 keypair, err := api.db.GetKeypairByKeyUID(keyUID) 140 if err != nil { 141 return err 142 } 143 144 err = (*api.messenger).DeleteKeypair(keyUID) 145 if err != nil { 146 return err 147 } 148 149 var addresses []common.Address 150 for _, acc := range keypair.Accounts { 151 if acc.Chat { 152 continue 153 } 154 addresses = append(addresses, common.Address(acc.Address)) 155 } 156 157 api.feed.Send(accountsevent.Event{ 158 Type: accountsevent.EventTypeRemoved, 159 Accounts: addresses, 160 }) 161 162 return nil 163 } 164 165 func (api *API) AddKeypair(ctx context.Context, password string, keypair *accounts.Keypair) error { 166 if len(keypair.KeyUID) == 0 { 167 return errors.New("`KeyUID` field of a keypair must be set") 168 } 169 170 if len(keypair.Name) == 0 { 171 return errors.New("`Name` field of a keypair must be set") 172 } 173 174 if len(keypair.Type) == 0 { 175 return errors.New("`Type` field of a keypair must be set") 176 } 177 178 if keypair.Type != accounts.KeypairTypeKey { 179 if len(keypair.DerivedFrom) == 0 { 180 return errors.New("`DerivedFrom` field of a keypair must be set") 181 } 182 } 183 184 for _, acc := range keypair.Accounts { 185 if acc.KeyUID != keypair.KeyUID { 186 return errors.New("all accounts of a keypair must have the same `KeyUID` as keypair key uid") 187 } 188 189 err := api.checkAccountValidity(acc) 190 if err != nil { 191 return err 192 } 193 } 194 195 err := api.SaveKeypair(ctx, keypair) 196 if err != nil { 197 return err 198 } 199 200 if len(password) > 0 { 201 for _, acc := range keypair.Accounts { 202 if acc.Type == accounts.AccountTypeGenerated || acc.Type == accounts.AccountTypeSeed { 203 err = api.createKeystoreFileForAccount(keypair.DerivedFrom, password, acc) 204 if err != nil { 205 return err 206 } 207 } 208 } 209 } 210 211 return nil 212 } 213 214 // RemainingAccountCapacity returns the number of accounts that can be added. 215 func (api *API) RemainingAccountCapacity(ctx context.Context) (int, error) { 216 return (*api.messenger).RemainingAccountCapacity() 217 } 218 219 // RemainingKeypairCapacity returns the number of keypairs that can be added. 220 func (api *API) RemainingKeypairCapacity(ctx context.Context) (int, error) { 221 return (*api.messenger).RemainingKeypairCapacity() 222 } 223 224 // RemainingWatchOnlyAccountCapacity returns the number of watch-only accounts that can be added. 225 func (api *API) RemainingWatchOnlyAccountCapacity(ctx context.Context) (int, error) { 226 return (*api.messenger).RemainingWatchOnlyAccountCapacity() 227 } 228 229 func (api *API) checkAccountValidity(account *accounts.Account) error { 230 if len(account.Address) == 0 { 231 return errors.New("`Address` field of an account must be set") 232 } 233 234 if len(account.Type) == 0 { 235 return errors.New("`Type` field of an account must be set") 236 } 237 238 if account.Wallet || account.Chat { 239 return errors.New("default wallet and chat account cannot be added this way") 240 } 241 242 if len(account.Name) == 0 { 243 return errors.New("`Name` field of an account must be set") 244 } 245 246 if len(account.Emoji) == 0 { 247 return errors.New("`Emoji` field of an account must be set") 248 } 249 250 if len(account.ColorID) == 0 { 251 return errors.New("`ColorID` field of an account must be set") 252 } 253 254 if account.Type != accounts.AccountTypeWatch { 255 256 if len(account.KeyUID) == 0 { 257 return errors.New("`KeyUID` field of an account must be set") 258 } 259 260 if len(account.PublicKey) == 0 { 261 return errors.New("`PublicKey` field of an account must be set") 262 } 263 264 if account.Type != accounts.AccountTypeKey { 265 if len(account.Path) == 0 { 266 return errors.New("`Path` field of an account must be set") 267 } 268 } 269 } 270 271 addressExists, err := api.db.AddressExists(account.Address) 272 if err != nil { 273 return err 274 } 275 276 if addressExists { 277 return errors.New("account already exists") 278 } 279 280 return nil 281 } 282 283 func (api *API) createKeystoreFileForAccount(masterAddress string, password string, account *accounts.Account) error { 284 if account.Type != accounts.AccountTypeGenerated && account.Type != accounts.AccountTypeSeed { 285 return errors.New("cannot create keystore file if account is not of `generated` or `seed` type") 286 } 287 if masterAddress == "" { 288 return errors.New("cannot create keystore file if master address is empty") 289 } 290 if password == "" { 291 return errors.New("cannot create keystore file if password is empty") 292 } 293 294 info, err := api.manager.AccountsGenerator().LoadAccount(masterAddress, password) 295 if err != nil { 296 return err 297 } 298 299 _, err = api.manager.AccountsGenerator().StoreDerivedAccounts(info.ID, password, []string{account.Path}) 300 return err 301 } 302 303 func (api *API) AddAccount(ctx context.Context, password string, account *accounts.Account) error { 304 err := api.checkAccountValidity(account) 305 if err != nil { 306 return err 307 } 308 309 if account.Type != accounts.AccountTypeWatch { 310 kp, err := api.db.GetKeypairByKeyUID(account.KeyUID) 311 if err != nil { 312 if err == accounts.ErrDbKeypairNotFound { 313 return errors.New("cannot add an account for an unknown keypair") 314 } 315 return err 316 } 317 318 // we need to create local keystore file only if password is provided and the account is being added is of 319 // "generated" or "seed" type. 320 if (account.Type == accounts.AccountTypeGenerated || account.Type == accounts.AccountTypeSeed) && len(password) > 0 { 321 err = api.createKeystoreFileForAccount(kp.DerivedFrom, password, account) 322 if err != nil { 323 return err 324 } 325 } 326 } 327 328 if account.Type == accounts.AccountTypeGenerated { 329 account.AddressWasNotShown = true 330 } 331 332 return api.SaveAccount(ctx, account) 333 } 334 335 // Imports a new private key and creates local keystore file. 336 func (api *API) ImportPrivateKey(ctx context.Context, privateKey string, password string) error { 337 info, err := api.manager.AccountsGenerator().ImportPrivateKey(privateKey) 338 if err != nil { 339 return err 340 } 341 342 kp, err := api.db.GetKeypairByKeyUID(info.KeyUID) 343 if err != nil && err != accounts.ErrDbKeypairNotFound { 344 return err 345 } 346 347 if kp != nil { 348 return errors.New("provided private key was already imported") 349 } 350 351 _, err = api.manager.AccountsGenerator().StoreAccount(info.ID, password) 352 return err 353 } 354 355 // Creates all keystore files for a keypair and mark it in db as fully operable. 356 func (api *API) MakePrivateKeyKeypairFullyOperable(ctx context.Context, privateKey string, password string) error { 357 info, err := api.manager.AccountsGenerator().ImportPrivateKey(privateKey) 358 if err != nil { 359 return err 360 } 361 362 kp, err := api.db.GetKeypairByKeyUID(info.KeyUID) 363 if err != nil { 364 return err 365 } 366 367 if kp == nil { 368 return errors.New("keypair for the provided private key is not known") 369 } 370 371 _, err = api.manager.AccountsGenerator().StoreAccount(info.ID, password) 372 if err != nil { 373 return err 374 } 375 376 return (*api.messenger).MarkKeypairFullyOperable(info.KeyUID) 377 } 378 379 func (api *API) MakePartiallyOperableAccoutsFullyOperable(ctx context.Context, password string) (addresses []types.Address, err error) { 380 profileKeypair, err := api.db.GetProfileKeypair() 381 if err != nil { 382 return 383 } 384 385 if !profileKeypair.MigratedToKeycard() && !api.VerifyPassword(password) { 386 err = errors.New("wrong password provided") 387 return 388 } 389 390 keypairs, err := api.db.GetActiveKeypairs() 391 if err != nil { 392 return 393 } 394 395 for _, kp := range keypairs { 396 for _, acc := range kp.Accounts { 397 if acc.Operable != accounts.AccountPartiallyOperable { 398 continue 399 } 400 err = api.createKeystoreFileForAccount(kp.DerivedFrom, password, acc) 401 if err != nil { 402 return 403 } 404 err = api.db.MarkAccountFullyOperable(acc.Address) 405 if err != nil { 406 return 407 } 408 addresses = append(addresses, acc.Address) 409 } 410 } 411 return 412 } 413 414 // Imports a new mnemonic and creates local keystore file. 415 func (api *API) ImportMnemonic(ctx context.Context, mnemonic string, password string) error { 416 mnemonicNoExtraSpaces := strings.Join(strings.Fields(mnemonic), " ") 417 418 generatedAccountInfo, err := api.manager.AccountsGenerator().ImportMnemonic(mnemonicNoExtraSpaces, "") 419 if err != nil { 420 return err 421 } 422 423 kp, err := api.db.GetKeypairByKeyUID(generatedAccountInfo.KeyUID) 424 if err != nil && err != accounts.ErrDbKeypairNotFound { 425 return err 426 } 427 428 if kp != nil { 429 return errors.New("provided mnemonic was already imported, to add new account use `AddAccount` endpoint") 430 } 431 432 _, err = api.manager.AccountsGenerator().StoreAccount(generatedAccountInfo.ID, password) 433 return err 434 } 435 436 // Creates all keystore files for a keypair and mark it in db as fully operable. 437 func (api *API) MakeSeedPhraseKeypairFullyOperable(ctx context.Context, mnemonic string, password string) error { 438 mnemonicNoExtraSpaces := strings.Join(strings.Fields(mnemonic), " ") 439 440 generatedAccountInfo, err := api.manager.AccountsGenerator().ImportMnemonic(mnemonicNoExtraSpaces, "") 441 if err != nil { 442 return err 443 } 444 445 kp, err := api.db.GetKeypairByKeyUID(generatedAccountInfo.KeyUID) 446 if err != nil { 447 return err 448 } 449 450 if kp == nil { 451 return errors.New("keypair for the provided seed phrase is not known") 452 } 453 454 _, err = api.manager.AccountsGenerator().StoreAccount(generatedAccountInfo.ID, password) 455 if err != nil { 456 return err 457 } 458 459 var paths []string 460 for _, acc := range kp.Accounts { 461 paths = append(paths, acc.Path) 462 } 463 464 _, err = api.manager.AccountsGenerator().StoreDerivedAccounts(generatedAccountInfo.ID, password, paths) 465 if err != nil { 466 return err 467 } 468 469 return (*api.messenger).MarkKeypairFullyOperable(generatedAccountInfo.KeyUID) 470 } 471 472 // Creates a random new mnemonic. 473 func (api *API) GetRandomMnemonic(ctx context.Context) (string, error) { 474 return account.GetRandomMnemonic() 475 } 476 477 func (api *API) VerifyKeystoreFileForAccount(address types.Address, password string) bool { 478 _, err := api.manager.VerifyAccountPassword(api.config.KeyStoreDir, address.Hex(), password) 479 return err == nil 480 } 481 482 func (api *API) VerifyPassword(password string) bool { 483 address, err := api.db.GetChatAddress() 484 if err != nil { 485 return false 486 } 487 return api.VerifyKeystoreFileForAccount(address, password) 488 } 489 490 func (api *API) MigrateNonProfileKeycardKeypairToApp(ctx context.Context, mnemonic string, password string) error { 491 mnemonicNoExtraSpaces := strings.Join(strings.Fields(mnemonic), " ") 492 493 generatedAccountInfo, err := api.manager.AccountsGenerator().ImportMnemonic(mnemonicNoExtraSpaces, "") 494 if err != nil { 495 return err 496 } 497 498 kp, err := api.db.GetKeypairByKeyUID(generatedAccountInfo.KeyUID) 499 if err != nil { 500 return err 501 } 502 503 if kp.Type == accounts.KeypairTypeProfile { 504 return errors.New("cannot migrate profile keypair") 505 } 506 507 if !kp.MigratedToKeycard() { 508 return errors.New("keypair being migrated is not a keycard keypair") 509 } 510 511 profileKeypair, err := api.db.GetProfileKeypair() 512 if err != nil { 513 return err 514 } 515 516 if !profileKeypair.MigratedToKeycard() && !api.VerifyPassword(password) { 517 return errors.New("wrong password provided") 518 } 519 520 _, err = api.manager.AccountsGenerator().StoreAccount(generatedAccountInfo.ID, password) 521 if err != nil { 522 return err 523 } 524 525 for _, acc := range kp.Accounts { 526 err = api.createKeystoreFileForAccount(kp.DerivedFrom, password, acc) 527 if err != nil { 528 return err 529 } 530 } 531 532 // this will emit SyncKeypair message 533 return (*api.messenger).DeleteAllKeycardsWithKeyUID(ctx, generatedAccountInfo.KeyUID) 534 } 535 536 // If keypair is migrated from keycard to app, then `accountsComingFromKeycard` should be set to true, otherwise false. 537 // If keycard is new `Position` will be determined and set by the backend and `KeycardLocked` will be set to false. 538 // If keycard is already added, `Position` and `KeycardLocked` will be unchanged. 539 func (api *API) SaveOrUpdateKeycard(ctx context.Context, keycard *accounts.Keycard, accountsComingFromKeycard bool) error { 540 if len(keycard.AccountsAddresses) == 0 { 541 return errors.New("cannot migrate a keypair without accounts") 542 } 543 544 kpDb, err := api.db.GetKeypairByKeyUID(keycard.KeyUID) 545 if err != nil { 546 if err == accounts.ErrDbKeypairNotFound { 547 return errors.New("cannot migrate an unknown keypair") 548 } 549 return err 550 } 551 552 err = (*api.messenger).SaveOrUpdateKeycard(ctx, keycard) 553 if err != nil { 554 return err 555 } 556 557 if !accountsComingFromKeycard { 558 // Once we migrate a keypair, corresponding keystore files need to be deleted 559 // if the keypair being migrated is not already migrated (in case user is creating a copy of an existing Keycard) 560 // and if keypair operability is different from non operable (otherwise there are not keystore files to be deleted). 561 if !kpDb.MigratedToKeycard() && kpDb.Operability() != accounts.AccountNonOperable { 562 for _, acc := range kpDb.Accounts { 563 if acc.Operable != accounts.AccountFullyOperable { 564 continue 565 } 566 err = api.manager.DeleteAccount(acc.Address) 567 if err != nil { 568 return err 569 } 570 } 571 572 err = api.manager.DeleteAccount(types.Address(common.HexToAddress(kpDb.DerivedFrom))) 573 if err != nil { 574 return err 575 } 576 } 577 578 err = (*api.messenger).MarkKeypairFullyOperable(keycard.KeyUID) 579 if err != nil { 580 return err 581 } 582 } 583 584 return nil 585 } 586 587 func (api *API) GetAllKnownKeycards(ctx context.Context) ([]*accounts.Keycard, error) { 588 return api.db.GetAllKnownKeycards() 589 } 590 591 func (api *API) GetKeycardsWithSameKeyUID(ctx context.Context, keyUID string) ([]*accounts.Keycard, error) { 592 return api.db.GetKeycardsWithSameKeyUID(keyUID) 593 } 594 595 func (api *API) GetKeycardByKeycardUID(ctx context.Context, keycardUID string) (*accounts.Keycard, error) { 596 return api.db.GetKeycardByKeycardUID(keycardUID) 597 } 598 599 func (api *API) SetKeycardName(ctx context.Context, keycardUID string, kpName string) error { 600 return (*api.messenger).SetKeycardName(ctx, keycardUID, kpName) 601 } 602 603 func (api *API) KeycardLocked(ctx context.Context, keycardUID string) error { 604 return (*api.messenger).KeycardLocked(ctx, keycardUID) 605 } 606 607 func (api *API) KeycardUnlocked(ctx context.Context, keycardUID string) error { 608 return (*api.messenger).KeycardUnlocked(ctx, keycardUID) 609 } 610 611 func (api *API) DeleteKeycardAccounts(ctx context.Context, keycardUID string, accountAddresses []types.Address) error { 612 return (*api.messenger).DeleteKeycardAccounts(ctx, keycardUID, accountAddresses) 613 } 614 615 func (api *API) DeleteKeycard(ctx context.Context, keycardUID string) error { 616 return (*api.messenger).DeleteKeycard(ctx, keycardUID) 617 } 618 619 func (api *API) DeleteAllKeycardsWithKeyUID(ctx context.Context, keyUID string) error { 620 return (*api.messenger).DeleteAllKeycardsWithKeyUID(ctx, keyUID) 621 } 622 623 func (api *API) UpdateKeycardUID(ctx context.Context, oldKeycardUID string, newKeycardUID string) error { 624 return (*api.messenger).UpdateKeycardUID(ctx, oldKeycardUID, newKeycardUID) 625 } 626 627 func (api *API) AddressWasShown(address types.Address) error { 628 return api.db.AddressWasShown(address) 629 } 630 631 func (api *API) GetNumOfAddressesToGenerateForKeypair(keyUID string) (uint64, error) { 632 return api.db.GetNumOfAddressesToGenerateForKeypair(keyUID) 633 } 634 635 func (api *API) ResolveSuggestedPathForKeypair(keyUID string) (string, error) { 636 return api.db.ResolveSuggestedPathForKeypair(keyUID) 637 }