chore: password removed from the DeleteAccount function

Asking for a password when removing a key is not necessary.
This commit is contained in:
Sale Djenic 2023-05-05 09:43:05 +02:00 committed by saledjenic
parent a669e7d038
commit 31144ed5a3
14 changed files with 162 additions and 122 deletions

View file

@ -1 +1 @@
0.150.1
0.150.2

View file

@ -640,8 +640,8 @@ func (m *Manager) ReEncryptKeyStoreDir(keyDirPath, oldPass, newPass string) erro
return nil
}
func (m *Manager) DeleteAccount(address types.Address, password string) error {
return m.keystore.Delete(types.Account{Address: address}, password)
func (m *Manager) DeleteAccount(address types.Address) error {
return m.keystore.Delete(types.Account{Address: address})
}
func (m *Manager) GetVerifiedWalletAccount(db *accounts.Database, address, password string) (*SelectedExtKey, error) {

View file

@ -761,15 +761,15 @@ func TestConvertAccount(t *testing.T) {
const mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
const password = "111111" // represents password for a regular user
const keycardPassword = "222222" // represents password for a keycard user
const pathMaster = "m"
const pathWalletRoot = "m/44'/60'/0'/0"
const keycardUID = "1234"
const pathEIP1581Root = "m/43'/60'/1581'"
const pathEIP1581Chat = "m/43'/60'/1581'/0/0"
const pathDefaultWalletAccount = "m/44'/60'/0'/0/0"
const customWalletPath1 = "m/44'/60'/0'/0/1"
const customWalletPath2 = "m/44'/60'/0'/0/2"
const pathEIP1581Chat = pathEIP1581Root + "/0'/0"
const pathWalletRoot = "m/44'/60'/0'/0"
const pathDefaultWalletAccount = pathWalletRoot + "/0"
const customWalletPath1 = pathWalletRoot + "/1"
const customWalletPath2 = pathWalletRoot + "/2"
var allGeneratedPaths []string
allGeneratedPaths = append(allGeneratedPaths, customWalletPath1, customWalletPath2, pathWalletRoot, pathEIP1581Root, pathDefaultWalletAccount)
allGeneratedPaths = append(allGeneratedPaths, pathEIP1581Root, pathEIP1581Chat, pathWalletRoot, pathDefaultWalletAccount, customWalletPath1, customWalletPath2)
var err error
@ -823,34 +823,32 @@ func TestConvertAccount(t *testing.T) {
genAccInfo, err := backend.AccountManager().AccountsGenerator().ImportMnemonic(mnemonic, "")
assert.NoError(t, err)
masterAddress := genAccInfo.Address
accountInfo, err := backend.AccountManager().AccountsGenerator().StoreAccount(genAccInfo.ID, password)
assert.NoError(t, err)
found := keystoreContainsFileForAccount(keyStoreDir, accountInfo.Address)
require.NoError(t, err)
require.True(t, found)
derivedAccounts, err := backend.AccountManager().AccountsGenerator().StoreDerivedAccounts(genAccInfo.ID, password, allGeneratedPaths)
assert.NoError(t, err)
accs, err := backend.AccountManager().AccountsGenerator().DeriveAddresses(genAccInfo.ID, []string{pathMaster})
require.NoError(t, err)
require.Equal(t, 1, len(accs))
masterAddress := accs[pathMaster].Address
found = keystoreContainsFileForAccount(keyStoreDir, masterAddress)
require.NoError(t, err)
chatAddress := derivedAccounts[pathEIP1581Chat].Address
found = keystoreContainsFileForAccount(keyStoreDir, chatAddress)
require.True(t, found)
var accountsToStore []*accounts.Account
accountsToStore = append(accountsToStore, &accounts.Account{
Address: types.HexToAddress(masterAddress),
KeyUID: genAccInfo.KeyUID,
Type: accounts.AccountTypeGenerated,
PublicKey: types.Hex2Bytes(accountInfo.PublicKey),
Path: pathEIP1581Chat,
Wallet: false,
Chat: true,
Name: "GeneratedAccount",
Address: types.HexToAddress(chatAddress),
DerivedFrom: masterAddress,
KeyUID: genAccInfo.KeyUID,
Type: accounts.AccountTypeGenerated,
PublicKey: types.Hex2Bytes(accountInfo.PublicKey),
Path: pathEIP1581Chat,
Wallet: false,
Chat: true,
Name: "GeneratedAccount",
})
for p, dAccInfo := range derivedAccounts {
@ -927,30 +925,46 @@ func TestConvertAccount(t *testing.T) {
KeycardPairing: "pairing",
}
// Ensure we're able to open the DB
err = backend.ensureAppDBOpened(keycardAccount, keycardPassword)
require.NoError(t, err)
// db creation
db, err := accounts.NewDB(backend.appDB)
require.NoError(t, err)
// Check that there is no registered keycards
keycardKeyPairs, err := db.GetMigratedKeyPairByKeyUID(genAccInfo.KeyUID)
require.NoError(t, err)
require.Equal(t, 0, len(keycardKeyPairs))
// Converting to a keycard account
err = backend.ConvertToKeycardAccount(keycardAccount, keycardSettings, password, keycardPassword)
err = backend.ConvertToKeycardAccount(keycardAccount, keycardSettings, keycardUID, password, keycardPassword)
require.NoError(t, err)
// Validating results of converting to a keycard account.
// All keystore files for the account which is migrated need to be removed.
found = keystoreContainsFileForAccount(keyStoreDir, accountInfo.Address)
require.NoError(t, err)
found = keystoreContainsFileForAccount(keyStoreDir, masterAddress)
require.False(t, found)
for _, dAccInfo := range derivedAccounts {
found = keystoreContainsFileForAccount(keyStoreDir, dAccInfo.Address)
require.NoError(t, err)
require.False(t, found)
}
found = keystoreContainsFileForAccount(keyStoreDir, masterAddress)
require.NoError(t, err)
require.False(t, found)
// Ensure we're able to open the DB
err = backend.ensureAppDBOpened(keycardAccount, keycardPassword)
require.NoError(t, err)
// db creation after re-encryption
db1, err := accounts.NewDB(backend.appDB)
require.NoError(t, err)
// Check that there is a registered keycard
keycardKeyPairs, err = db1.GetMigratedKeyPairByKeyUID(genAccInfo.KeyUID)
require.NoError(t, err)
require.Equal(t, 1, len(keycardKeyPairs))
b1 := NewGethStatusBackend()
require.NoError(t, b1.OpenAccounts())
@ -961,23 +975,29 @@ func TestConvertAccount(t *testing.T) {
// Validating results of converting to a regular account.
// All keystore files for need to be created.
found = keystoreContainsFileForAccount(keyStoreDir, accountInfo.Address)
require.NoError(t, err)
require.True(t, found)
for _, dAccInfo := range derivedAccounts {
found = keystoreContainsFileForAccount(keyStoreDir, dAccInfo.Address)
require.NoError(t, err)
require.True(t, found)
}
found = keystoreContainsFileForAccount(keyStoreDir, masterAddress)
require.NoError(t, err)
require.True(t, found)
// Ensure we're able to open the DB
err = backend.ensureAppDBOpened(keycardAccount, password)
require.NoError(t, err)
// db creation after re-encryption
db2, err := accounts.NewDB(backend.appDB)
require.NoError(t, err)
// Check that there is no registered keycards
keycardKeyPairs, err = db2.GetMigratedKeyPairByKeyUID(genAccInfo.KeyUID)
require.NoError(t, err)
require.Equal(t, 0, len(keycardKeyPairs))
b2 := NewGethStatusBackend()
require.NoError(t, b2.OpenAccounts())
}

View file

@ -29,6 +29,7 @@ import (
"github.com/status-im/status-go/logutils"
"github.com/status-im/status-go/multiaccounts"
"github.com/status-im/status-go/multiaccounts/accounts"
"github.com/status-im/status-go/multiaccounts/keypairs"
"github.com/status-im/status-go/multiaccounts/settings"
"github.com/status-im/status-go/node"
"github.com/status-im/status-go/nodecfg"
@ -594,20 +595,7 @@ func (b *GethStatusBackend) ChangeDatabasePassword(keyUID string, password strin
return nil
}
func (b *GethStatusBackend) tryToDeleteAccount(address types.Address, password, newPassword string) error {
err := b.accountManager.DeleteAccount(address, newPassword)
if err != nil {
err = b.accountManager.DeleteAccount(address, password)
if err != nil && err.Error() != "no key for given address or file" {
b.log.Error("error on deleting account", "err", err)
return err
}
}
return nil
}
func (b *GethStatusBackend) ConvertToKeycardAccount(account multiaccounts.Account, s settings.Settings, password string, newPassword string) error {
func (b *GethStatusBackend) ConvertToKeycardAccount(account multiaccounts.Account, s settings.Settings, keycardUID string, password string, newPassword string) error {
err := b.multiaccountsDB.UpdateAccountKeycardPairing(account.KeyUID, account.KeycardPairing)
if err != nil {
return err
@ -642,12 +630,40 @@ func (b *GethStatusBackend) ConvertToKeycardAccount(account multiaccounts.Accoun
return err
}
knownAccounts, err := accountDB.GetAccounts()
relatedAccounts, err := accountDB.GetAccountsByKeyUID(account.KeyUID)
if err != nil {
return err
}
dappsAddress, err := accountDB.GetDappsAddress()
// This check is added due to mobile app cause it doesn't support a Keycard features as desktop app.
// We should remove the following line once mobile and desktop app align.
if len(keycardUID) > 0 {
displayName, err := accountDB.DisplayName()
if err != nil {
return err
}
kp := keypairs.KeyPair{
KeycardUID: keycardUID,
KeycardName: displayName,
KeycardLocked: false,
KeyUID: account.KeyUID,
LastUpdateClock: uint64(time.Now().Unix()),
}
for _, acc := range relatedAccounts {
kp.AccountsAddresses = append(kp.AccountsAddresses, acc.Address)
}
addedKc, _, err := accountDB.AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(kp)
if err != nil {
return err
}
if !addedKc {
return errors.New("couldn't register a keypair to keycards table")
}
}
masterAddress, err := accountDB.GetMasterAddress()
if err != nil {
return err
}
@ -673,26 +689,24 @@ func (b *GethStatusBackend) ConvertToKeycardAccount(account multiaccounts.Accoun
}
// We need to delete all accounts for the keypair which is being migrated
for _, acc := range knownAccounts {
if account.KeyUID == acc.KeyUID {
err = b.tryToDeleteAccount(acc.Address, password, newPassword)
if err != nil {
return err
}
for _, acc := range relatedAccounts {
err = b.accountManager.DeleteAccount(acc.Address)
if err != nil {
return err
}
}
err = b.tryToDeleteAccount(dappsAddress, password, newPassword)
err = b.accountManager.DeleteAccount(masterAddress)
if err != nil {
return err
}
err = b.tryToDeleteAccount(eip1581Address, password, newPassword)
err = b.accountManager.DeleteAccount(eip1581Address)
if err != nil {
return err
}
err = b.tryToDeleteAccount(walletRootAddress, password, newPassword)
err = b.accountManager.DeleteAccount(walletRootAddress)
if err != nil {
return err
}

View file

@ -48,13 +48,13 @@ func (k *gethKeyStoreAdapter) AccountDecryptedKey(a types.Account, auth string)
return accountFrom(gethAccount), keyFrom(gethKey), err
}
func (k *gethKeyStoreAdapter) Delete(a types.Account, auth string) error {
func (k *gethKeyStoreAdapter) Delete(a types.Account) error {
gethAccount, err := gethAccountFrom(a)
if err != nil {
return err
}
return k.keystore.Delete(gethAccount, auth)
return k.keystore.Delete(gethAccount)
}
// parseGethURL converts a user supplied URL into the accounts specific structure.

View file

@ -22,5 +22,5 @@ type KeyStore interface {
AccountDecryptedKey(a Account, auth string) (Account, *Key, error)
// Delete deletes the key matched by account if the passphrase is correct.
// If the account contains no filename, the address must match a unique key.
Delete(a Account, auth string) error
Delete(a Account) error
}

2
go.mod
View file

@ -2,7 +2,7 @@ module github.com/status-im/status-go
go 1.19
replace github.com/ethereum/go-ethereum v1.10.26 => github.com/status-im/go-ethereum v1.10.25-status.4
replace github.com/ethereum/go-ethereum v1.10.26 => github.com/status-im/go-ethereum v1.10.25-status.6
replace github.com/docker/docker => github.com/docker/engine v1.4.2-0.20190717161051-705d9623b7c1

4
go.sum
View file

@ -1989,8 +1989,8 @@ github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/y
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/status-im/doubleratchet v3.0.0+incompatible h1:aJ1ejcSERpSzmWZBgtfYtiU2nF0Q8ZkGyuEPYETXkCY=
github.com/status-im/doubleratchet v3.0.0+incompatible/go.mod h1:1sqR0+yhiM/bd+wrdX79AOt2csZuJOni0nUDzKNuqOU=
github.com/status-im/go-ethereum v1.10.25-status.4 h1:Ff35UseaP49DK2RRpSLo3DL7NUmMKzicQ/7/yub6gfU=
github.com/status-im/go-ethereum v1.10.25-status.4/go.mod h1:Dt4K5JYMhJRdtXJwBEyGZLZn9iz/chSOZyjVmt5ZhwQ=
github.com/status-im/go-ethereum v1.10.25-status.6 h1:5YC8k1inTBqA6LpON0uX6y86niOKukA/LnKxzd5GFyI=
github.com/status-im/go-ethereum v1.10.25-status.6/go.mod h1:Dt4K5JYMhJRdtXJwBEyGZLZn9iz/chSOZyjVmt5ZhwQ=
github.com/status-im/go-multiaddr-ethv4 v1.2.4 h1:7fw0Y48TJXEqx4fOHlDOUiM/uBq9zG5w4x975Mjh4E0=
github.com/status-im/go-multiaddr-ethv4 v1.2.4/go.mod h1:PDh4D7h5CvecPIy0ji0rLNwTnzzEcyz9uTPHD42VyH4=
github.com/status-im/gomoji v1.1.3-0.20220213022530-e5ac4a8732d4 h1:CtobZoiNdHpx+xurFxnuJ1xsGm3oKMfcZkB3vmomJmA=

View file

@ -904,7 +904,7 @@ func ChangeDatabasePassword(KeyUID, password, newPassword string) string {
return makeJSONResponse(nil)
}
func ConvertToKeycardAccount(accountData, settingsJSON, password, newPassword string) string {
func ConvertToKeycardAccount(accountData, settingsJSON, keycardUID, password, newPassword string) string {
var account multiaccounts.Account
err := json.Unmarshal([]byte(accountData), &account)
if err != nil {
@ -916,7 +916,7 @@ func ConvertToKeycardAccount(accountData, settingsJSON, password, newPassword st
return makeJSONResponse(err)
}
err = statusBackend.ConvertToKeycardAccount(account, settings, password, newPassword)
err = statusBackend.ConvertToKeycardAccount(account, settings, keycardUID, password, newPassword)
if err != nil {
return makeJSONResponse(err)
}

View file

@ -188,11 +188,7 @@ func (m *Messenger) AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(ctx context.
return addedKc || addedAccs, m.dispatchKeycardActivity(ctx, activityMessage)
}
func (m *Messenger) RemoveMigratedAccountsForKeycard(ctx context.Context, kcUID string, accountAddresses []string, clock uint64) error {
var addresses []types.Address
for _, addr := range accountAddresses {
addresses = append(addresses, types.HexToAddress(addr))
}
func (m *Messenger) RemoveMigratedAccountsForKeycard(ctx context.Context, kcUID string, addresses []types.Address, clock uint64) error {
err := m.settings.RemoveMigratedAccountsForKeycard(kcUID, addresses, clock)
if err != nil {

View file

@ -178,17 +178,12 @@ func (s *MessengerSyncKeycardChangeSuite) TestRemovingAccountsFromKeycard() {
s.Require().Equal(true, addedKc)
s.Require().Equal(false, addedAccs)
var accountAddressesToRemove []string
for _, acc := range keycardToSync.AccountsAddresses[:2] {
accountAddressesToRemove = append(accountAddressesToRemove, acc.String())
}
// Remove accounts from sender
updatedKeycard := getKeycardsForTest()[:1][0]
updatedKeycard.AccountsAddresses = updatedKeycard.AccountsAddresses[2:]
err = s.main.RemoveMigratedAccountsForKeycard(context.Background(), keycardToSync.KeycardUID,
accountAddressesToRemove, keycardToSync.LastUpdateClock)
keycardToSync.AccountsAddresses[:2], keycardToSync.LastUpdateClock)
s.Require().NoError(err)
// Wait for the response
@ -224,14 +219,9 @@ func (s *MessengerSyncKeycardChangeSuite) TestRemovingAllAccountsFromKeycard() {
s.Require().Equal(true, addedKc)
s.Require().Equal(false, addedAccs)
var accountAddressesToRemove []string
for _, acc := range keycardToSync.AccountsAddresses {
accountAddressesToRemove = append(accountAddressesToRemove, acc.String())
}
// Remove all accounts from sender
err = s.main.RemoveMigratedAccountsForKeycard(context.Background(), keycardToSync.KeycardUID,
accountAddressesToRemove, keycardToSync.LastUpdateClock)
keycardToSync.AccountsAddresses, keycardToSync.LastUpdateClock)
s.Require().NoError(err)
// Wait for the response

View file

@ -80,41 +80,56 @@ func (api *API) GetAccountsByKeyUID(ctx context.Context, keyUID string) ([]*acco
return api.checkDerivedFromField(accounts)
}
func (api *API) DeleteAccount(ctx context.Context, address types.Address, password string) error {
func (api *API) DeleteAccount(ctx context.Context, address types.Address) error {
acc, err := api.db.GetAccountByAddress(address)
if err != nil {
return err
}
migratedKeyPairs, err := api.db.GetMigratedKeyPairByKeyUID(acc.KeyUID)
allAccountsOfKeypairWithKeyUID, err := api.db.GetAccountsByKeyUID(acc.KeyUID)
if err != nil {
return err
}
lastAcccountOfKeypairWithTheSameKey := len(allAccountsOfKeypairWithKeyUID) == 1
if acc.Type != accounts.AccountTypeWatch && len(migratedKeyPairs) == 0 {
if len(password) == 0 {
return errors.New("`password` must be provided for non keycard accounts")
}
err = api.manager.DeleteAccount(address, password)
var e *account.ErrCannotLocateKeyFile
if err != nil && !errors.As(err, &e) {
if acc.Type != accounts.AccountTypeWatch {
migratedKeyPairs, err := api.db.GetMigratedKeyPairByKeyUID(acc.KeyUID)
if err != nil {
return err
}
if acc.Type != accounts.AccountTypeKey {
allAccountsOfKeypairWithKeyUID, err := api.db.GetAccountsByKeyUID(acc.KeyUID)
if err != nil {
if len(migratedKeyPairs) == 0 {
err = api.manager.DeleteAccount(address)
var e *account.ErrCannotLocateKeyFile
if err != nil && !errors.As(err, &e) {
return err
}
lastAcccountOfKeypairWithTheSameKey := len(allAccountsOfKeypairWithKeyUID) == 1
if acc.Type != accounts.AccountTypeKey {
if lastAcccountOfKeypairWithTheSameKey {
err = api.manager.DeleteAccount(types.Address(common.HexToAddress(acc.DerivedFrom)))
var e *account.ErrCannotLocateKeyFile
if err != nil && !errors.As(err, &e) {
return err
}
}
}
} else {
if lastAcccountOfKeypairWithTheSameKey {
err = api.manager.DeleteAccount(types.Address(common.HexToAddress(acc.DerivedFrom)), password)
var e *account.ErrCannotLocateKeyFile
if err != nil && !errors.As(err, &e) {
knownKeycards, err := api.db.GetAllKnownKeycards()
if err != nil {
return err
}
for _, kc := range knownKeycards {
if kc.KeyUID == acc.KeyUID {
clock := uint64(time.Now().Unix())
err = (*api.messenger).RemoveMigratedAccountsForKeycard(ctx, kc.KeycardUID, kc.AccountsAddresses, clock)
if err != nil {
return err
}
}
}
}
}
}
@ -254,7 +269,7 @@ func (api *API) VerifyPassword(password string) bool {
return api.VerifyKeystoreFileForAccount(address, password)
}
func (api *API) AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(ctx context.Context, kcUID string, kpName string, keyUID string, accountAddresses []string, password string) error {
func (api *API) AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(ctx context.Context, kcUID string, kpName string, keyUID string, accountAddresses []string) error {
if len(accountAddresses) == 0 {
return errors.New("cannot migrate a keypair without any address")
}
@ -279,21 +294,27 @@ func (api *API) AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(ctx context.Cont
kp.AccountsAddresses = append(kp.AccountsAddresses, types.Address(common.HexToAddress(addr)))
}
migratedKeyPairs, err := api.db.GetMigratedKeyPairByKeyUID(keyUID)
if err != nil {
return err
}
added, err := (*api.messenger).AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(ctx, &kp)
if err != nil {
return err
}
// Once we migrate a keypair, corresponding keystore files need to be deleted.
if added && len(password) > 0 {
// Once we migrate a keypair, corresponding keystore files need to be deleted
// if the keypair being migrated is not already migrated (in case user is creating a copy of an existing Keycard)
if added && len(migratedKeyPairs) == 0 && acc.Type != accounts.AccountTypeWatch {
for _, addr := range kp.AccountsAddresses {
err = api.manager.DeleteAccount(addr, password)
err = api.manager.DeleteAccount(addr)
if err != nil {
return err
}
}
err = api.manager.DeleteAccount(types.Address(common.HexToAddress(acc.DerivedFrom)), password)
err = api.manager.DeleteAccount(types.Address(common.HexToAddress(acc.DerivedFrom)))
if err != nil {
return err
}
@ -304,7 +325,11 @@ func (api *API) AddMigratedKeyPairOrAddAccountsIfKeyPairIsAdded(ctx context.Cont
func (api *API) RemoveMigratedAccountsForKeycard(ctx context.Context, kcUID string, accountAddresses []string) error {
clock := uint64(time.Now().Unix())
return (*api.messenger).RemoveMigratedAccountsForKeycard(ctx, kcUID, accountAddresses, clock)
var addresses []types.Address
for _, addr := range accountAddresses {
addresses = append(addresses, types.HexToAddress(addr))
}
return (*api.messenger).RemoveMigratedAccountsForKeycard(ctx, kcUID, addresses, clock)
}
func (api *API) GetAllKnownKeycards(ctx context.Context) ([]*keypairs.KeyPair, error) {

View file

@ -241,22 +241,17 @@ func (ks *KeyStore) AccountDecryptedKey(a accounts.Account, auth string) (accoun
return ks.getDecryptedKey(a, auth)
}
// Delete deletes the key matched by account if the passphrase is correct.
// Delete deletes the key matched by account.
// If the account contains no filename, the address must match a unique key.
func (ks *KeyStore) Delete(a accounts.Account, passphrase string) error {
// Decrypting the key isn't really necessary, but we do
// it anyway to check the password and zero out the key
// immediately afterwards.
a, key, err := ks.getDecryptedKey(a, passphrase)
if key != nil {
zeroKey(key.PrivateKey)
}
if err != nil {
return err
}
func (ks *KeyStore) Delete(a accounts.Account) error {
// The order is crucial here. The key is dropped from the
// cache after the file is gone so that a reload happening in
// between won't insert it into the cache again.
a, err := ks.Find(a)
if err != nil {
return err
}
err = os.Remove(a.URL.Path)
if err == nil {
ks.cache.delete(a)

2
vendor/modules.txt vendored
View file

@ -197,7 +197,7 @@ github.com/edsrzf/mmap-go
## explicit; go 1.14
github.com/elastic/gosigar
github.com/elastic/gosigar/sys/windows
# github.com/ethereum/go-ethereum v1.10.26 => github.com/status-im/go-ethereum v1.10.25-status.4
# github.com/ethereum/go-ethereum v1.10.26 => github.com/status-im/go-ethereum v1.10.25-status.6
## explicit; go 1.17
github.com/ethereum/go-ethereum
github.com/ethereum/go-ethereum/accounts