Merge pull request #2327 from nspcc-dev/change-wallet-acc-password

cli/wallet: add change-password command, fix #2233
This commit is contained in:
Roman Khimov 2022-01-17 18:03:51 +03:00 committed by GitHub
commit a23943c161
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 143 additions and 7 deletions

View file

@ -105,6 +105,18 @@ func NewCommands() []cli.Command {
},
},
},
{
Name: "change-password",
Usage: "change password for accounts",
Action: changePassword,
Flags: []cli.Flag{
walletPathFlag,
flags.AddressFlag{
Name: "address, a",
Usage: "address to change password for",
},
},
},
{
Name: "convert",
Usage: "convert addresses from existing NEO2 NEP6-wallet to NEO3 format",
@ -289,6 +301,55 @@ func claimGas(ctx *cli.Context) error {
return nil
}
func changePassword(ctx *cli.Context) error {
wall, err := openWallet(ctx.String("wallet"))
if err != nil {
return cli.NewExitError(err, 1)
}
addrFlag := ctx.Generic("address").(*flags.Address)
if addrFlag.IsSet {
// Check for account presence first before asking for password.
acc := wall.GetAccount(addrFlag.Uint160())
if acc == nil {
return cli.NewExitError("account is missing", 1)
}
}
oldPass, err := input.ReadPassword("Enter password > ")
if err != nil {
return cli.NewExitError(fmt.Errorf("Error reading old password: %w", err), 1)
}
for i := range wall.Accounts {
if addrFlag.IsSet && wall.Accounts[i].Address != addrFlag.String() {
continue
}
err := wall.Accounts[i].Decrypt(oldPass, wall.Scrypt)
if err != nil {
return cli.NewExitError(fmt.Errorf("unable to decrypt account %s: %w", wall.Accounts[i].Address, err), 1)
}
}
pass, err := readNewPassword()
if err != nil {
return cli.NewExitError(fmt.Errorf("Error reading new password: %w", err), 1)
}
for i := range wall.Accounts {
if addrFlag.IsSet && wall.Accounts[i].Address != addrFlag.String() {
continue
}
err := wall.Accounts[i].Encrypt(pass, wall.Scrypt)
if err != nil {
return cli.NewExitError(err, 1)
}
}
err = wall.Save()
if err != nil {
return cli.NewExitError(fmt.Errorf("Error saving the wallet: %w", err), 1)
}
return nil
}
func convertWallet(ctx *cli.Context) error {
wall, err := newWalletV2FromFile(ctx.String("wallet"))
if err != nil {
@ -656,22 +717,31 @@ func createWallet(ctx *cli.Context) error {
}
func readAccountInfo() (string, string, error) {
rawName, _ := input.ReadLine("Enter the name of the account > ")
name, err := input.ReadLine("Enter the name of the account > ")
if err != nil {
return "", "", err
}
phrase, err := readNewPassword()
if err != nil {
return "", "", err
}
return name, phrase, nil
}
func readNewPassword() (string, error) {
phrase, err := input.ReadPassword("Enter passphrase > ")
if err != nil {
return "", "", fmt.Errorf("Error reading password: %w", err)
return "", fmt.Errorf("Error reading password: %w", err)
}
phraseCheck, err := input.ReadPassword("Confirm passphrase > ")
if err != nil {
return "", "", fmt.Errorf("Error reading password: %w", err)
return "", fmt.Errorf("Error reading password: %w", err)
}
if phrase != phraseCheck {
return "", "", errPhraseMismatch
return "", errPhraseMismatch
}
name := strings.TrimRight(rawName, "\n")
return name, phrase, nil
return phrase, nil
}
func createAccount(wall *wallet.Wallet) error {

View file

@ -45,6 +45,72 @@ func TestWalletAccountRemove(t *testing.T) {
require.NoError(t, json.Unmarshal(rawWallet, new(wallet.Wallet)))
}
func TestWalletChangePassword(t *testing.T) {
tmpDir := t.TempDir()
e := newExecutor(t, false)
walletPath := filepath.Join(tmpDir, "wallet.json")
e.In.WriteString("acc1\r")
e.In.WriteString("pass\r")
e.In.WriteString("pass\r")
e.Run(t, "neo-go", "wallet", "init", "--wallet", walletPath, "--account")
e.In.WriteString("acc2\r")
e.In.WriteString("pass\r")
e.In.WriteString("pass\r")
e.Run(t, "neo-go", "wallet", "create", "--wallet", walletPath)
w, err := wallet.NewWalletFromFile(walletPath)
require.NoError(t, err)
addr1 := w.Accounts[0].Address
addr2 := w.Accounts[1].Address
t.Run("bad old password", func(t *testing.T) {
e.In.WriteString("ssap\r")
e.In.WriteString("aaa\r") // Pretend for the password to be fine.
e.In.WriteString("aaa\r")
e.RunWithError(t, "neo-go", "wallet", "change-password", "--wallet", walletPath)
})
t.Run("no account", func(t *testing.T) {
e.RunWithError(t, "neo-go", "wallet", "change-password", "--wallet", walletPath, "--address", "NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq")
})
t.Run("bad new password, multiaccount", func(t *testing.T) {
e.In.WriteString("pass\r")
e.In.WriteString("pass1\r")
e.In.WriteString("pass2\r")
e.RunWithError(t, "neo-go", "wallet", "change-password", "--wallet", walletPath)
})
t.Run("good, multiaccount", func(t *testing.T) {
e.In.WriteString("pass\r")
e.In.WriteString("asdf\r")
e.In.WriteString("asdf\r")
e.Run(t, "neo-go", "wallet", "change-password", "--wallet", walletPath)
})
t.Run("good, single account", func(t *testing.T) {
e.In.WriteString("asdf\r")
e.In.WriteString("jkl\r")
e.In.WriteString("jkl\r")
e.Run(t, "neo-go", "wallet", "change-password", "--wallet", walletPath, "--address", addr1)
})
t.Run("bad, different passwords", func(t *testing.T) {
e.In.WriteString("jkl\r")
e.RunWithError(t, "neo-go", "wallet", "change-password", "--wallet", walletPath)
})
t.Run("good, second account", func(t *testing.T) {
e.In.WriteString("asdf\r")
e.In.WriteString("jkl\r")
e.In.WriteString("jkl\r")
e.Run(t, "neo-go", "wallet", "change-password", "--wallet", walletPath, "--address", addr2)
})
t.Run("good, second multiaccount", func(t *testing.T) {
e.In.WriteString("jkl\r")
e.In.WriteString("pass\r")
e.In.WriteString("pass\r")
e.Run(t, "neo-go", "wallet", "change-password", "--wallet", walletPath)
})
}
func TestWalletInit(t *testing.T) {
tmpDir := t.TempDir()
e := newExecutor(t, false)