github.com/status-im/status-go@v1.1.0/account/accounts.go (about) 1 package account 2 3 import ( 4 "context" 5 "crypto/ecdsa" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "io/ioutil" 10 "os" 11 "path/filepath" 12 "strings" 13 "sync" 14 "time" 15 16 "github.com/google/uuid" 17 18 gethkeystore "github.com/ethereum/go-ethereum/accounts/keystore" 19 gethcommon "github.com/ethereum/go-ethereum/common" 20 "github.com/ethereum/go-ethereum/common/hexutil" 21 "github.com/ethereum/go-ethereum/log" 22 "github.com/status-im/status-go/account/generator" 23 "github.com/status-im/status-go/eth-node/crypto" 24 "github.com/status-im/status-go/eth-node/keystore" 25 "github.com/status-im/status-go/eth-node/types" 26 "github.com/status-im/status-go/extkeys" 27 "github.com/status-im/status-go/multiaccounts/accounts" 28 "github.com/status-im/status-go/params" 29 "github.com/status-im/status-go/rpc" 30 ) 31 32 // errors 33 var ( 34 ErrAddressToAccountMappingFailure = errors.New("cannot retrieve a valid account for a given address") 35 ErrAccountToKeyMappingFailure = errors.New("cannot retrieve a valid key for a given account") 36 ErrNoAccountSelected = errors.New("no account has been selected, please login") 37 ErrInvalidMasterKeyCreated = errors.New("can not create master extended key") 38 ErrOnboardingNotStarted = errors.New("onboarding must be started before choosing an account") 39 ErrOnboardingAccountNotFound = errors.New("cannot find onboarding account with the given id") 40 ErrAccountKeyStoreMissing = errors.New("account key store is not set") 41 ErrInvalidPersonalSignAccount = errors.New("invalid account as only the selected one can generate a signature") 42 ) 43 44 type ErrCannotLocateKeyFile struct { 45 Msg string 46 } 47 48 func (e ErrCannotLocateKeyFile) Error() string { 49 return e.Msg 50 } 51 52 var zeroAddress = types.Address{} 53 54 type SignParams struct { 55 Data interface{} `json:"data"` 56 Address string `json:"account"` 57 Password string `json:"password,omitempty"` 58 } 59 60 func (sp *SignParams) Validate(checkPassword bool) error { 61 if len(sp.Address) != 2*types.AddressLength+2 { 62 return errors.New("address has to be provided") 63 } 64 65 if sp.Data == "" { 66 return errors.New("data has to be provided") 67 } 68 69 if checkPassword && sp.Password == "" { 70 return errors.New("password has to be provided") 71 } 72 73 return nil 74 } 75 76 type RecoverParams struct { 77 Message string `json:"message"` 78 Signature string `json:"signature"` 79 } 80 81 // Manager represents account manager interface 82 type Manager interface { 83 GetVerifiedWalletAccount(db *accounts.Database, address, password string) (*SelectedExtKey, error) 84 Sign(rpcParams SignParams, verifiedAccount *SelectedExtKey) (result types.HexBytes, err error) 85 CanRecover(rpcParams RecoverParams, revealedAddress types.Address) (bool, error) 86 DeleteAccount(address types.Address) error 87 } 88 89 // DefaultManager represents default account manager implementation 90 type DefaultManager struct { 91 mu sync.RWMutex 92 rpcClient *rpc.Client 93 rpcTimeout time.Duration 94 Keydir string 95 keystore types.KeyStore 96 97 accountsGenerator *generator.Generator 98 onboarding *Onboarding 99 100 selectedChatAccount *SelectedExtKey // account that was processed during the last call to SelectAccount() 101 mainAccountAddress types.Address 102 watchAddresses []types.Address 103 } 104 105 // GetKeystore is only used in tests 106 func (m *DefaultManager) GetKeystore() types.KeyStore { 107 m.mu.RLock() 108 defer m.mu.RUnlock() 109 return m.keystore 110 } 111 112 // AccountsGenerator returns accountsGenerator. 113 func (m *DefaultManager) AccountsGenerator() *generator.Generator { 114 return m.accountsGenerator 115 } 116 117 // CreateAccount creates an internal geth account 118 // BIP44-compatible keys are generated: CKD#1 is stored as account key, CKD#2 stored as sub-account root 119 // Public key of CKD#1 is returned, with CKD#2 securely encoded into account key file (to be used for 120 // sub-account derivations) 121 func (m *DefaultManager) CreateAccount(password string) (generator.GeneratedAccountInfo, Info, string, error) { 122 var mkInfo generator.GeneratedAccountInfo 123 info := Info{} 124 125 // generate mnemonic phrase 126 mn := extkeys.NewMnemonic() 127 mnemonic, err := mn.MnemonicPhrase(extkeys.EntropyStrength128, extkeys.EnglishLanguage) 128 if err != nil { 129 return mkInfo, info, "", fmt.Errorf("can not create mnemonic seed: %v", err) 130 } 131 132 // Generate extended master key (see BIP32) 133 // We call extkeys.NewMaster with a seed generated with the 12 mnemonic words 134 // but without using the optional password as an extra entropy as described in BIP39. 135 // Future ideas/iterations in Status can add an an advanced options 136 // for expert users, to be able to add a passphrase to the generation of the seed. 137 extKey, err := extkeys.NewMaster(mn.MnemonicSeed(mnemonic, "")) 138 if err != nil { 139 return mkInfo, info, "", fmt.Errorf("can not create master extended key: %v", err) 140 } 141 142 acc := generator.NewAccount(nil, extKey) 143 mkInfo = acc.ToGeneratedAccountInfo("", mnemonic) 144 145 // import created key into account keystore 146 info.WalletAddress, info.WalletPubKey, err = m.importExtendedKey(extkeys.KeyPurposeWallet, extKey, password) 147 if err != nil { 148 return mkInfo, info, "", err 149 } 150 151 info.ChatAddress = info.WalletAddress 152 info.ChatPubKey = info.WalletPubKey 153 154 return mkInfo, info, mnemonic, nil 155 } 156 157 // RecoverAccount re-creates master key using given details. 158 // Once master key is re-generated, it is inserted into keystore (if not already there). 159 func (m *DefaultManager) RecoverAccount(password, mnemonic string) (Info, error) { 160 info := Info{} 161 // re-create extended key (see BIP32) 162 mn := extkeys.NewMnemonic() 163 extKey, err := extkeys.NewMaster(mn.MnemonicSeed(mnemonic, "")) 164 if err != nil { 165 return info, ErrInvalidMasterKeyCreated 166 } 167 168 // import re-created key into account keystore 169 info.WalletAddress, info.WalletPubKey, err = m.importExtendedKey(extkeys.KeyPurposeWallet, extKey, password) 170 if err != nil { 171 return info, err 172 } 173 174 info.ChatAddress = info.WalletAddress 175 info.ChatPubKey = info.WalletPubKey 176 177 return info, nil 178 } 179 180 // VerifyAccountPassword tries to decrypt a given account key file, with a provided password. 181 // If no error is returned, then account is considered verified. 182 func (m *DefaultManager) VerifyAccountPassword(keyStoreDir, address, password string) (*types.Key, error) { 183 var err error 184 var foundKeyFile []byte 185 186 addressObj := types.BytesToAddress(types.FromHex(address)) 187 checkAccountKey := func(path string, fileInfo os.FileInfo) error { 188 if len(foundKeyFile) > 0 || fileInfo.IsDir() { 189 return nil 190 } 191 192 rawKeyFile, e := ioutil.ReadFile(path) 193 if e != nil { 194 return fmt.Errorf("invalid account key file: %v", e) 195 } 196 197 var accountKey struct { 198 Address string `json:"address"` 199 } 200 if e := json.Unmarshal(rawKeyFile, &accountKey); e != nil { 201 return fmt.Errorf("failed to read key file: %s", e) 202 } 203 if types.HexToAddress("0x"+accountKey.Address).Hex() == addressObj.Hex() { 204 foundKeyFile = rawKeyFile 205 } 206 207 return nil 208 } 209 // locate key within key store directory (address should be within the file) 210 err = filepath.Walk(keyStoreDir, func(path string, fileInfo os.FileInfo, err error) error { 211 if err != nil { 212 return err 213 } 214 return checkAccountKey(path, fileInfo) 215 }) 216 if err != nil { 217 return nil, fmt.Errorf("cannot traverse key store folder: %v", err) 218 } 219 220 if len(foundKeyFile) == 0 { 221 return nil, &ErrCannotLocateKeyFile{fmt.Sprintf("cannot locate account for address: %s", addressObj.Hex())} 222 } 223 224 key, err := keystore.DecryptKey(foundKeyFile, password) 225 if err != nil { 226 return nil, err 227 } 228 229 // avoid swap attack 230 if key.Address != addressObj { 231 return nil, fmt.Errorf("account mismatch: have %s, want %s", key.Address.Hex(), addressObj.Hex()) 232 } 233 234 return key, nil 235 } 236 237 // SelectAccount selects current account, by verifying that address has corresponding account which can be decrypted 238 // using provided password. Once verification is done, all previous identities are removed). 239 func (m *DefaultManager) SelectAccount(loginParams LoginParams) error { 240 m.mu.Lock() 241 defer m.mu.Unlock() 242 243 m.accountsGenerator.Reset() 244 245 selectedChatAccount, err := m.unlockExtendedKey(loginParams.ChatAddress.String(), loginParams.Password) 246 if err != nil { 247 return err 248 } 249 m.watchAddresses = loginParams.WatchAddresses 250 m.mainAccountAddress = loginParams.MainAccount 251 m.selectedChatAccount = selectedChatAccount 252 return nil 253 } 254 255 func (m *DefaultManager) SetAccountAddresses(main types.Address, secondary ...types.Address) { 256 m.watchAddresses = []types.Address{main} 257 m.watchAddresses = append(m.watchAddresses, secondary...) 258 m.mainAccountAddress = main 259 } 260 261 // SetChatAccount initializes selectedChatAccount with privKey 262 func (m *DefaultManager) SetChatAccount(privKey *ecdsa.PrivateKey) error { 263 m.mu.Lock() 264 defer m.mu.Unlock() 265 266 address := crypto.PubkeyToAddress(privKey.PublicKey) 267 id, err := uuid.NewRandom() 268 if err != nil { 269 return err 270 } 271 272 key := &types.Key{ 273 ID: id, 274 Address: address, 275 PrivateKey: privKey, 276 } 277 278 m.selectedChatAccount = &SelectedExtKey{ 279 Address: address, 280 AccountKey: key, 281 } 282 return nil 283 } 284 285 // MainAccountAddress returns main account address set during login 286 func (m *DefaultManager) MainAccountAddress() (types.Address, error) { 287 m.mu.RLock() 288 defer m.mu.RUnlock() 289 290 if m.mainAccountAddress == zeroAddress { 291 return zeroAddress, ErrNoAccountSelected 292 } 293 294 return m.mainAccountAddress, nil 295 } 296 297 // WatchAddresses returns currently selected watch addresses. 298 func (m *DefaultManager) WatchAddresses() []types.Address { 299 m.mu.RLock() 300 defer m.mu.RUnlock() 301 302 return m.watchAddresses 303 } 304 305 // SelectedChatAccount returns currently selected chat account 306 func (m *DefaultManager) SelectedChatAccount() (*SelectedExtKey, error) { 307 m.mu.RLock() 308 defer m.mu.RUnlock() 309 310 if m.selectedChatAccount == nil { 311 return nil, ErrNoAccountSelected 312 } 313 return m.selectedChatAccount, nil 314 } 315 316 // Logout clears selected accounts. 317 func (m *DefaultManager) Logout() { 318 m.mu.Lock() 319 defer m.mu.Unlock() 320 321 m.accountsGenerator.Reset() 322 m.mainAccountAddress = zeroAddress 323 m.watchAddresses = nil 324 m.selectedChatAccount = nil 325 } 326 327 // ImportAccount imports the account specified with privateKey. 328 func (m *DefaultManager) ImportAccount(privateKey *ecdsa.PrivateKey, password string) (types.Address, error) { 329 if m.keystore == nil { 330 return types.Address{}, ErrAccountKeyStoreMissing 331 } 332 333 account, err := m.keystore.ImportECDSA(privateKey, password) 334 335 return account.Address, err 336 } 337 338 // ImportSingleExtendedKey imports an extended key setting it in both the PrivateKey and ExtendedKey fields 339 // of the Key struct. 340 // ImportExtendedKey is used in older version of Status where PrivateKey is set to be the BIP44 key at index 0, 341 // and ExtendedKey is the extended key of the BIP44 key at index 1. 342 func (m *DefaultManager) ImportSingleExtendedKey(extKey *extkeys.ExtendedKey, password string) (address, pubKey string, err error) { 343 if m.keystore == nil { 344 return "", "", ErrAccountKeyStoreMissing 345 } 346 347 // imports extended key, create key file (if necessary) 348 account, err := m.keystore.ImportSingleExtendedKey(extKey, password) 349 if err != nil { 350 return "", "", err 351 } 352 353 address = account.Address.Hex() 354 355 // obtain public key to return 356 account, key, err := m.keystore.AccountDecryptedKey(account, password) 357 if err != nil { 358 return address, "", err 359 } 360 361 pubKey = types.EncodeHex(crypto.FromECDSAPub(&key.PrivateKey.PublicKey)) 362 363 return 364 } 365 366 // importExtendedKey processes incoming extended key, extracts required info and creates corresponding account key. 367 // Once account key is formed, that key is put (if not already) into keystore i.e. key is *encoded* into key file. 368 func (m *DefaultManager) importExtendedKey(keyPurpose extkeys.KeyPurpose, extKey *extkeys.ExtendedKey, password string) (address, pubKey string, err error) { 369 if m.keystore == nil { 370 return "", "", ErrAccountKeyStoreMissing 371 } 372 373 // imports extended key, create key file (if necessary) 374 account, err := m.keystore.ImportExtendedKeyForPurpose(keyPurpose, extKey, password) 375 if err != nil { 376 return "", "", err 377 } 378 address = account.Address.Hex() 379 380 // obtain public key to return 381 account, key, err := m.keystore.AccountDecryptedKey(account, password) 382 if err != nil { 383 return address, "", err 384 } 385 pubKey = types.EncodeHex(crypto.FromECDSAPub(&key.PrivateKey.PublicKey)) 386 387 return 388 } 389 390 // Accounts returns list of addresses for selected account, including 391 // subaccounts. 392 func (m *DefaultManager) Accounts() ([]types.Address, error) { 393 m.mu.RLock() 394 defer m.mu.RUnlock() 395 addresses := make([]types.Address, 0) 396 if m.mainAccountAddress != zeroAddress { 397 addresses = append(addresses, m.mainAccountAddress) 398 } 399 400 return addresses, nil 401 } 402 403 // StartOnboarding starts the onboarding process generating accountsCount accounts and returns a slice of OnboardingAccount. 404 func (m *DefaultManager) StartOnboarding(accountsCount, mnemonicPhraseLength int) ([]*OnboardingAccount, error) { 405 m.mu.Lock() 406 defer m.mu.Unlock() 407 408 onboarding, err := NewOnboarding(accountsCount, mnemonicPhraseLength) 409 if err != nil { 410 return nil, err 411 } 412 413 m.onboarding = onboarding 414 415 return m.onboarding.Accounts(), nil 416 } 417 418 // RemoveOnboarding reset the current onboarding struct setting it to nil and deleting the accounts from memory. 419 func (m *DefaultManager) RemoveOnboarding() { 420 m.mu.Lock() 421 defer m.mu.Unlock() 422 423 m.onboarding = nil 424 } 425 426 // ImportOnboardingAccount imports the account specified by id and encrypts it with password. 427 func (m *DefaultManager) ImportOnboardingAccount(id string, password string) (Info, string, error) { 428 var info Info 429 430 m.mu.Lock() 431 defer m.mu.Unlock() 432 433 if m.onboarding == nil { 434 return info, "", ErrOnboardingNotStarted 435 } 436 437 acc, err := m.onboarding.Account(id) 438 if err != nil { 439 return info, "", err 440 } 441 442 info, err = m.RecoverAccount(password, acc.mnemonic) 443 if err != nil { 444 return info, "", err 445 } 446 447 m.onboarding = nil 448 449 return info, acc.mnemonic, nil 450 } 451 452 // AddressToDecryptedAccount tries to load decrypted key for a given account. 453 // The running node, has a keystore directory which is loaded on start. Key file 454 // for a given address is expected to be in that directory prior to node start. 455 func (m *DefaultManager) AddressToDecryptedAccount(address, password string) (types.Account, *types.Key, error) { 456 if m.keystore == nil { 457 return types.Account{}, nil, ErrAccountKeyStoreMissing 458 } 459 460 account, err := ParseAccountString(address) 461 if err != nil { 462 return types.Account{}, nil, ErrAddressToAccountMappingFailure 463 } 464 465 account, key, err := m.keystore.AccountDecryptedKey(account, password) 466 if err != nil { 467 err = fmt.Errorf("%s: %s", ErrAccountToKeyMappingFailure, err) 468 } 469 470 return account, key, err 471 } 472 473 func (m *DefaultManager) unlockExtendedKey(address, password string) (*SelectedExtKey, error) { 474 account, accountKey, err := m.AddressToDecryptedAccount(address, password) 475 if err != nil { 476 return nil, err 477 } 478 479 selectedExtendedKey := &SelectedExtKey{ 480 Address: account.Address, 481 AccountKey: accountKey, 482 } 483 484 return selectedExtendedKey, nil 485 } 486 487 func (m *DefaultManager) MigrateKeyStoreDir(oldDir, newDir string, addresses []string) error { 488 paths := []string{} 489 490 addressesMap := map[string]struct{}{} 491 for _, address := range addresses { 492 addressesMap[address] = struct{}{} 493 } 494 495 checkFile := func(path string, fileInfo os.FileInfo) error { 496 if fileInfo.IsDir() || filepath.Dir(path) != oldDir { 497 return nil 498 } 499 500 rawKeyFile, err := ioutil.ReadFile(path) 501 if err != nil { 502 return fmt.Errorf("invalid account key file: %v", err) 503 } 504 505 var accountKey struct { 506 Address string `json:"address"` 507 } 508 if err := json.Unmarshal(rawKeyFile, &accountKey); err != nil { 509 return fmt.Errorf("failed to read key file: %s", err) 510 } 511 512 address := types.HexToAddress("0x" + accountKey.Address).Hex() 513 if _, ok := addressesMap[address]; ok { 514 paths = append(paths, path) 515 } 516 517 return nil 518 } 519 520 err := filepath.Walk(oldDir, func(path string, fileInfo os.FileInfo, err error) error { 521 if err != nil { 522 return err 523 } 524 return checkFile(path, fileInfo) 525 }) 526 if err != nil { 527 return fmt.Errorf("cannot traverse key store folder: %v", err) 528 } 529 530 for _, path := range paths { 531 _, fileName := filepath.Split(path) 532 newPath := filepath.Join(newDir, fileName) 533 err := os.Rename(path, newPath) 534 if err != nil { 535 return err 536 } 537 } 538 539 return nil 540 } 541 542 func (m *DefaultManager) ReEncryptKey(rawKey []byte, pass string, newPass string) (reEncryptedKey []byte, e error) { 543 cryptoJSON, e := keystore.RawKeyToCryptoJSON(rawKey) 544 if e != nil { 545 return reEncryptedKey, fmt.Errorf("convert to crypto json error: %v", e) 546 } 547 548 decryptedKey, e := keystore.DecryptKey(rawKey, pass) 549 if e != nil { 550 return reEncryptedKey, fmt.Errorf("decryption error: %v", e) 551 } 552 553 if cryptoJSON.KDFParams["n"] == nil || cryptoJSON.KDFParams["p"] == nil { 554 return reEncryptedKey, fmt.Errorf("Unable to determine `n` or `p`: %v", e) 555 } 556 n := int(cryptoJSON.KDFParams["n"].(float64)) 557 p := int(cryptoJSON.KDFParams["p"].(float64)) 558 559 gethKey := gethkeystore.Key{ 560 Id: decryptedKey.ID, 561 Address: gethcommon.Address(decryptedKey.Address), 562 PrivateKey: decryptedKey.PrivateKey, 563 ExtendedKey: decryptedKey.ExtendedKey, 564 SubAccountIndex: decryptedKey.SubAccountIndex, 565 } 566 567 return gethkeystore.EncryptKey(&gethKey, newPass, n, p) 568 } 569 570 func (m *DefaultManager) ReEncryptKeyStoreDir(keyDirPath, oldPass, newPass string) error { 571 rencryptFileAtPath := func(tempKeyDirPath, path string, fileInfo os.FileInfo) error { 572 if fileInfo.IsDir() { 573 return nil 574 } 575 576 rawKeyFile, e := ioutil.ReadFile(path) 577 if e != nil { 578 return fmt.Errorf("invalid account key file: %v", e) 579 } 580 581 reEncryptedKey, e := m.ReEncryptKey(rawKeyFile, oldPass, newPass) 582 if e != nil { 583 return fmt.Errorf("unable to re-encrypt key file: %v, path: %s, name: %s", e, path, fileInfo.Name()) 584 } 585 586 tempWritePath := filepath.Join(tempKeyDirPath, fileInfo.Name()) 587 e = ioutil.WriteFile(tempWritePath, reEncryptedKey, fileInfo.Mode().Perm()) 588 if e != nil { 589 return fmt.Errorf("unable write key file: %v", e) 590 } 591 592 return nil 593 } 594 595 keyDirPath = strings.TrimSuffix(keyDirPath, "/") 596 keyDirPath = strings.TrimSuffix(keyDirPath, "\\") 597 keyParent, keyDirName := filepath.Split(keyDirPath) 598 599 // backupKeyDirName used to store existing keys before final write 600 backupKeyDirName := keyDirName + "-backup" 601 // tempKeyDirName used to put re-encrypted keys 602 tempKeyDirName := keyDirName + "-re-encrypted" 603 backupKeyDirPath := filepath.Join(keyParent, backupKeyDirName) 604 tempKeyDirPath := filepath.Join(keyParent, tempKeyDirName) 605 606 // create temp key dir 607 err := os.MkdirAll(tempKeyDirPath, os.ModePerm) 608 if err != nil { 609 return fmt.Errorf("mkdirall error: %v, tempKeyDirPath: %s", err, tempKeyDirPath) 610 } 611 612 err = filepath.Walk(keyDirPath, func(path string, fileInfo os.FileInfo, err error) error { 613 if err != nil { 614 os.RemoveAll(tempKeyDirPath) 615 return fmt.Errorf("walk callback error: %v", err) 616 } 617 618 return rencryptFileAtPath(tempKeyDirPath, path, fileInfo) 619 }) 620 if err != nil { 621 os.RemoveAll(tempKeyDirPath) 622 return fmt.Errorf("walk error: %v", err) 623 } 624 625 // move existing keys 626 err = os.Rename(keyDirPath, backupKeyDirPath) 627 if err != nil { 628 os.RemoveAll(tempKeyDirPath) 629 return fmt.Errorf("unable to rename keyDirPath to backupKeyDirPath: %v", err) 630 } 631 632 // move tempKeyDirPath to keyDirPath 633 err = os.Rename(tempKeyDirPath, keyDirPath) 634 if err != nil { 635 // if this happens, then the app is probably bricked, because the keystore won't exist anymore 636 // try to restore from backup 637 _ = os.Rename(backupKeyDirPath, keyDirPath) 638 return fmt.Errorf("unable to rename tempKeyDirPath to keyDirPath: %v", err) 639 } 640 641 // remove temp and backup folders and their contents 642 err = os.RemoveAll(tempKeyDirPath) 643 if err != nil { 644 // the re-encryption is complete so we don't throw 645 log.Error("unable to delete tempKeyDirPath, manual cleanup required") 646 } 647 648 err = os.RemoveAll(backupKeyDirPath) 649 if err != nil { 650 // the re-encryption is complete so we don't throw 651 log.Error("unable to delete backupKeyDirPath, manual cleanup required") 652 } 653 654 return nil 655 } 656 657 func (m *DefaultManager) DeleteAccount(address types.Address) error { 658 return m.keystore.Delete(types.Account{Address: address}) 659 } 660 661 func (m *DefaultManager) GetVerifiedWalletAccount(db *accounts.Database, address, password string) (*SelectedExtKey, error) { 662 exists, err := db.AddressExists(types.HexToAddress(address)) 663 if err != nil { 664 return nil, err 665 } 666 667 if !exists { 668 return nil, errors.New("account doesn't exist") 669 } 670 671 key, err := m.VerifyAccountPassword(m.Keydir, address, password) 672 if _, ok := err.(*ErrCannotLocateKeyFile); ok { 673 key, err = m.generatePartialAccountKey(db, address, password) 674 if err != nil { 675 return nil, err 676 } 677 } 678 679 if err != nil { 680 return nil, err 681 } 682 683 return &SelectedExtKey{ 684 Address: key.Address, 685 AccountKey: key, 686 }, nil 687 } 688 689 func (m *DefaultManager) generatePartialAccountKey(db *accounts.Database, address string, password string) (*types.Key, error) { 690 dbPath, err := db.GetPath(types.HexToAddress(address)) 691 path := "m/" + dbPath[strings.LastIndex(dbPath, "/")+1:] 692 if err != nil { 693 return nil, err 694 } 695 696 rootAddress, err := db.GetWalletRootAddress() 697 if err != nil { 698 return nil, err 699 } 700 info, err := m.AccountsGenerator().LoadAccount(rootAddress.Hex(), password) 701 if err != nil { 702 return nil, err 703 } 704 masterID := info.ID 705 706 accInfosMap, err := m.AccountsGenerator().StoreDerivedAccounts(masterID, password, []string{path}) 707 if err != nil { 708 return nil, err 709 } 710 711 _, key, err := m.AddressToDecryptedAccount(accInfosMap[path].Address, password) 712 if err != nil { 713 return nil, err 714 } 715 716 return key, nil 717 } 718 719 func (m *DefaultManager) Recover(rpcParams RecoverParams) (addr types.Address, err error) { 720 ctx, cancel := context.WithTimeout(context.Background(), m.rpcTimeout) 721 defer cancel() 722 var gethAddr gethcommon.Address 723 err = m.rpcClient.CallContextIgnoringLocalHandlers( 724 ctx, 725 &gethAddr, 726 m.rpcClient.UpstreamChainID, 727 params.PersonalRecoverMethodName, 728 rpcParams.Message, rpcParams.Signature) 729 addr = types.Address(gethAddr) 730 731 return 732 } 733 734 func (m *DefaultManager) CanRecover(rpcParams RecoverParams, revealedAddress types.Address) (bool, error) { 735 recovered, err := m.Recover(rpcParams) 736 if err != nil { 737 return false, err 738 } 739 return recovered == revealedAddress, nil 740 } 741 742 func (m *DefaultManager) Sign(rpcParams SignParams, verifiedAccount *SelectedExtKey) (result types.HexBytes, err error) { 743 if !strings.EqualFold(rpcParams.Address, verifiedAccount.Address.Hex()) { 744 err = ErrInvalidPersonalSignAccount 745 return 746 } 747 748 ctx, cancel := context.WithTimeout(context.Background(), m.rpcTimeout) 749 defer cancel() 750 var gethResult hexutil.Bytes 751 err = m.rpcClient.CallContextIgnoringLocalHandlers( 752 ctx, 753 &gethResult, 754 m.rpcClient.UpstreamChainID, 755 params.PersonalSignMethodName, 756 rpcParams.Data, rpcParams.Address, rpcParams.Password) 757 result = types.HexBytes(gethResult) 758 759 return 760 }