mirror of https://github.com/status-im/op-geth.git
account update: migrate or change password
* account.Update * KeyStore.Cleanup * fix dir rm for old format deleteKey
This commit is contained in:
parent
fc17a527bc
commit
1959346793
|
@ -36,6 +36,7 @@ import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
crand "crypto/rand"
|
crand "crypto/rand"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
@ -158,6 +159,20 @@ func (am *Manager) NewAccount(auth string) (Account, error) {
|
||||||
return Account{Address: key.Address}, nil
|
return Account{Address: key.Address}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (am *Manager) AddressByIndex(index int) (addr string, err error) {
|
||||||
|
var addrs []common.Address
|
||||||
|
addrs, err = am.keyStore.GetKeyAddresses()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if index < 0 || index >= len(addrs) {
|
||||||
|
err = fmt.Errorf("index out of range: %d (should be 0-%d)", index, len(addrs)-1)
|
||||||
|
} else {
|
||||||
|
addr = addrs[index].Hex()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (am *Manager) Accounts() ([]Account, error) {
|
func (am *Manager) Accounts() ([]Account, error) {
|
||||||
addresses, err := am.keyStore.GetKeyAddresses()
|
addresses, err := am.keyStore.GetKeyAddresses()
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
|
@ -204,6 +219,19 @@ func (am *Manager) Import(path string, keyAuth string) (Account, error) {
|
||||||
return Account{Address: key.Address}, nil
|
return Account{Address: key.Address}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (am *Manager) Update(addr common.Address, authFrom, authTo string) (err error) {
|
||||||
|
var key *crypto.Key
|
||||||
|
key, err = am.keyStore.GetKey(addr, authFrom)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
err = am.keyStore.StoreKey(key, authTo)
|
||||||
|
if err == nil {
|
||||||
|
am.keyStore.Cleanup(addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (am *Manager) ImportPreSaleKey(keyJSON []byte, password string) (acc Account, err error) {
|
func (am *Manager) ImportPreSaleKey(keyJSON []byte, password string) (acc Account, err error) {
|
||||||
var key *crypto.Key
|
var key *crypto.Key
|
||||||
key, err = crypto.ImportPreSaleKey(am.keyStore, keyJSON, password)
|
key, err = crypto.ImportPreSaleKey(am.keyStore, keyJSON, password)
|
||||||
|
|
|
@ -189,6 +189,33 @@ Note, this is meant to be used for testing only, it is a bad idea to save your
|
||||||
password to file or expose in any other way.
|
password to file or expose in any other way.
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Action: accountUpdate,
|
||||||
|
Name: "update",
|
||||||
|
Usage: "update an existing account",
|
||||||
|
Description: `
|
||||||
|
|
||||||
|
ethereum account update <address>
|
||||||
|
|
||||||
|
Update an existing account.
|
||||||
|
|
||||||
|
The account is saved in the newest version in encrypted format, you are prompted
|
||||||
|
for a passphrase to unlock the account and another to save the updated file.
|
||||||
|
|
||||||
|
This same command can therefore be used to migrate an account of a deprecated
|
||||||
|
format to the newest format or change the password for an account.
|
||||||
|
|
||||||
|
For non-interactive use the passphrase can be specified with the --password flag:
|
||||||
|
|
||||||
|
ethereum --password <passwordfile> account new
|
||||||
|
|
||||||
|
Since only one password can be given, only format update can be performed,
|
||||||
|
changing your password is only possible interactively.
|
||||||
|
|
||||||
|
Note that account update has the a side effect that the order of your accounts
|
||||||
|
changes.
|
||||||
|
`,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Action: accountImport,
|
Action: accountImport,
|
||||||
Name: "import",
|
Name: "import",
|
||||||
|
@ -433,19 +460,30 @@ func execJSFiles(ctx *cli.Context) {
|
||||||
ethereum.WaitForShutdown()
|
ethereum.WaitForShutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
func unlockAccount(ctx *cli.Context, am *accounts.Manager, account string, i int) {
|
func unlockAccount(ctx *cli.Context, am *accounts.Manager, addr string, i int) (addrHex, auth string) {
|
||||||
var err error
|
var err error
|
||||||
// Load startup keys. XXX we are going to need a different format
|
// Load startup keys. XXX we are going to need a different format
|
||||||
|
|
||||||
if !((len(account) == 40) || (len(account) == 42)) { // with or without 0x
|
if !((len(addr) == 40) || (len(addr) == 42)) { // with or without 0x
|
||||||
utils.Fatalf("Invalid account address '%s'", account)
|
var index int
|
||||||
|
index, err = strconv.Atoi(addr)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Invalid account address '%s'", addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
addrHex, err = am.AddressByIndex(index)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addrHex = addr
|
||||||
}
|
}
|
||||||
// Attempt to unlock the account 3 times
|
// Attempt to unlock the account 3 times
|
||||||
attempts := 3
|
attempts := 3
|
||||||
for tries := 0; tries < attempts; tries++ {
|
for tries := 0; tries < attempts; tries++ {
|
||||||
msg := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", account, tries+1, attempts)
|
msg := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", addr, tries+1, attempts)
|
||||||
passphrase := getPassPhrase(ctx, msg, false, i)
|
auth = getPassPhrase(ctx, msg, false, i)
|
||||||
err = am.Unlock(common.HexToAddress(account), passphrase)
|
err = am.Unlock(common.HexToAddress(addrHex), auth)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -453,7 +491,8 @@ func unlockAccount(ctx *cli.Context, am *accounts.Manager, account string, i int
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Unlock account failed '%v'", err)
|
utils.Fatalf("Unlock account failed '%v'", err)
|
||||||
}
|
}
|
||||||
fmt.Printf("Account '%s' unlocked.\n", account)
|
fmt.Printf("Account '%s' unlocked.\n", addr)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func blockRecovery(ctx *cli.Context) {
|
func blockRecovery(ctx *cli.Context) {
|
||||||
|
@ -578,6 +617,21 @@ func accountCreate(ctx *cli.Context) {
|
||||||
fmt.Printf("Address: %x\n", acct)
|
fmt.Printf("Address: %x\n", acct)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func accountUpdate(ctx *cli.Context) {
|
||||||
|
am := utils.MakeAccountManager(ctx)
|
||||||
|
arg := ctx.Args().First()
|
||||||
|
if len(arg) == 0 {
|
||||||
|
utils.Fatalf("account address or index must be given as argument")
|
||||||
|
}
|
||||||
|
|
||||||
|
addr, authFrom := unlockAccount(ctx, am, arg, 0)
|
||||||
|
authTo := getPassPhrase(ctx, "Please give a new password. Do not forget this password.", true, 0)
|
||||||
|
err := am.Update(common.HexToAddress(addr), authFrom, authTo)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Could not update the account: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func importWallet(ctx *cli.Context) {
|
func importWallet(ctx *cli.Context) {
|
||||||
keyfile := ctx.Args().First()
|
keyfile := ctx.Args().First()
|
||||||
if len(keyfile) == 0 {
|
if len(keyfile) == 0 {
|
||||||
|
|
|
@ -72,16 +72,19 @@ func (ks keyStorePassphrase) GenerateNewKey(rand io.Reader, auth string) (key *K
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ks keyStorePassphrase) GetKey(keyAddr common.Address, auth string) (key *Key, err error) {
|
func (ks keyStorePassphrase) GetKey(keyAddr common.Address, auth string) (key *Key, err error) {
|
||||||
keyBytes, keyId, err := decryptKeyFromFile(ks, keyAddr, auth)
|
keyBytes, keyId, err := decryptKeyFromFile(ks.keysDirPath, keyAddr, auth)
|
||||||
if err != nil {
|
if err == nil {
|
||||||
return nil, err
|
key = &Key{
|
||||||
|
Id: uuid.UUID(keyId),
|
||||||
|
Address: keyAddr,
|
||||||
|
PrivateKey: ToECDSA(keyBytes),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
key = &Key{
|
return
|
||||||
Id: uuid.UUID(keyId),
|
}
|
||||||
Address: keyAddr,
|
|
||||||
PrivateKey: ToECDSA(keyBytes),
|
func (ks keyStorePassphrase) Cleanup(keyAddr common.Address) (err error) {
|
||||||
}
|
return cleanup(ks.keysDirPath, keyAddr)
|
||||||
return key, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ks keyStorePassphrase) GetKeyAddresses() (addresses []common.Address, err error) {
|
func (ks keyStorePassphrase) GetKeyAddresses() (addresses []common.Address, err error) {
|
||||||
|
@ -142,7 +145,7 @@ func (ks keyStorePassphrase) StoreKey(key *Key, auth string) (err error) {
|
||||||
|
|
||||||
func (ks keyStorePassphrase) DeleteKey(keyAddr common.Address, auth string) (err error) {
|
func (ks keyStorePassphrase) DeleteKey(keyAddr common.Address, auth string) (err error) {
|
||||||
// only delete if correct passphrase is given
|
// only delete if correct passphrase is given
|
||||||
_, _, err = decryptKeyFromFile(ks, keyAddr, auth)
|
_, _, err = decryptKeyFromFile(ks.keysDirPath, keyAddr, auth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -150,25 +153,25 @@ func (ks keyStorePassphrase) DeleteKey(keyAddr common.Address, auth string) (err
|
||||||
return deleteKey(ks.keysDirPath, keyAddr)
|
return deleteKey(ks.keysDirPath, keyAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func decryptKeyFromFile(ks keyStorePassphrase, keyAddr common.Address, auth string) (keyBytes []byte, keyId []byte, err error) {
|
func decryptKeyFromFile(keysDirPath string, keyAddr common.Address, auth string) (keyBytes []byte, keyId []byte, err error) {
|
||||||
|
fmt.Printf("%v\n", keyAddr.Hex())
|
||||||
m := make(map[string]interface{})
|
m := make(map[string]interface{})
|
||||||
err = getKey(ks.keysDirPath, keyAddr, &m)
|
err = getKey(keysDirPath, keyAddr, &m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("get key error: %v\n", err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
v := reflect.ValueOf(m["version"])
|
v := reflect.ValueOf(m["version"])
|
||||||
if v.Kind() == reflect.String && v.String() == "1" {
|
if v.Kind() == reflect.String && v.String() == "1" {
|
||||||
k := new(encryptedKeyJSONV1)
|
k := new(encryptedKeyJSONV1)
|
||||||
getKey(ks.keysDirPath, keyAddr, &k)
|
err = getKey(keysDirPath, keyAddr, &k)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return decryptKeyV1(k, auth)
|
return decryptKeyV1(k, auth)
|
||||||
} else {
|
} else {
|
||||||
k := new(encryptedKeyJSONV3)
|
k := new(encryptedKeyJSONV3)
|
||||||
getKey(ks.keysDirPath, keyAddr, &k)
|
err = getKey(keysDirPath, keyAddr, &k)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@ type KeyStore interface {
|
||||||
GetKeyAddresses() ([]common.Address, error) // get all addresses
|
GetKeyAddresses() ([]common.Address, error) // get all addresses
|
||||||
StoreKey(*Key, string) error // store key optionally using auth string
|
StoreKey(*Key, string) error // store key optionally using auth string
|
||||||
DeleteKey(common.Address, string) error // delete key by addr and auth string
|
DeleteKey(common.Address, string) error // delete key by addr and auth string
|
||||||
|
Cleanup(keyAddr common.Address) (err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type keyStorePlain struct {
|
type keyStorePlain struct {
|
||||||
|
@ -86,6 +87,10 @@ func (ks keyStorePlain) GetKeyAddresses() (addresses []common.Address, err error
|
||||||
return getKeyAddresses(ks.keysDirPath)
|
return getKeyAddresses(ks.keysDirPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ks keyStorePlain) Cleanup(keyAddr common.Address) (err error) {
|
||||||
|
return cleanup(ks.keysDirPath, keyAddr)
|
||||||
|
}
|
||||||
|
|
||||||
func (ks keyStorePlain) StoreKey(key *Key, auth string) (err error) {
|
func (ks keyStorePlain) StoreKey(key *Key, auth string) (err error) {
|
||||||
keyJSON, err := json.Marshal(key)
|
keyJSON, err := json.Marshal(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -100,10 +105,14 @@ func (ks keyStorePlain) DeleteKey(keyAddr common.Address, auth string) (err erro
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteKey(keysDirPath string, keyAddr common.Address) (err error) {
|
func deleteKey(keysDirPath string, keyAddr common.Address) (err error) {
|
||||||
var keyFilePath string
|
var path string
|
||||||
keyFilePath, err = getKeyFilePath(keysDirPath, keyAddr)
|
path, err = getKeyFilePath(keysDirPath, keyAddr)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = os.Remove(keyFilePath)
|
addrHex := hex.EncodeToString(keyAddr[:])
|
||||||
|
if path == filepath.Join(keysDirPath, addrHex, addrHex) {
|
||||||
|
path = filepath.Join(keysDirPath, addrHex)
|
||||||
|
}
|
||||||
|
err = os.RemoveAll(path)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -122,6 +131,36 @@ func getKeyFilePath(keysDirPath string, keyAddr common.Address) (keyFilePath str
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cleanup(keysDirPath string, keyAddr common.Address) (err error) {
|
||||||
|
fileInfos, err := ioutil.ReadDir(keysDirPath)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var paths []string
|
||||||
|
account := hex.EncodeToString(keyAddr[:])
|
||||||
|
for _, fileInfo := range fileInfos {
|
||||||
|
path := filepath.Join(keysDirPath, fileInfo.Name())
|
||||||
|
if len(path) >= 40 {
|
||||||
|
addr := path[len(path)-40 : len(path)]
|
||||||
|
if addr == account {
|
||||||
|
if path == filepath.Join(keysDirPath, addr, addr) {
|
||||||
|
path = filepath.Join(keysDirPath, addr)
|
||||||
|
}
|
||||||
|
paths = append(paths, path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(paths) > 1 {
|
||||||
|
for i := 0; err == nil && i < len(paths)-1; i++ {
|
||||||
|
err = os.RemoveAll(paths[i])
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func getKeyFile(keysDirPath string, keyAddr common.Address) (fileContent []byte, err error) {
|
func getKeyFile(keysDirPath string, keyAddr common.Address) (fileContent []byte, err error) {
|
||||||
var keyFilePath string
|
var keyFilePath string
|
||||||
keyFilePath, err = getKeyFilePath(keysDirPath, keyAddr)
|
keyFilePath, err = getKeyFilePath(keysDirPath, keyAddr)
|
||||||
|
|
Loading…
Reference in New Issue