From 0dda24771953696acda224e5d3481fcc2bf6c1b2 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Mon, 31 Aug 2020 12:22:09 +0300 Subject: [PATCH] cli: move input handling to a separate package --- cli/input/input.go | 44 +++++++++++++++++++++++++++++ cli/smartcontract/smart_contract.go | 7 ++--- cli/testdata/testwallet.json | 1 + cli/wallet/validator.go | 4 ++- cli/wallet/wallet.go | 35 ++++++----------------- 5 files changed, 60 insertions(+), 31 deletions(-) create mode 100644 cli/input/input.go create mode 100644 cli/testdata/testwallet.json diff --git a/cli/input/input.go b/cli/input/input.go new file mode 100644 index 000000000..1a15f541e --- /dev/null +++ b/cli/input/input.go @@ -0,0 +1,44 @@ +package input + +import ( + "bufio" + "fmt" + "io" + "os" + "strings" + "syscall" + + "golang.org/x/crypto/ssh/terminal" +) + +// Terminal is a terminal used for input. If `nil`, stdin is used. +var Terminal *terminal.Terminal + +// ReadLine reads line from the input without trailing '\n' +func ReadLine(w io.Writer, prompt string) (string, error) { + if Terminal != nil { + _, err := Terminal.Write([]byte(prompt)) + if err != nil { + return "", err + } + raw, err := Terminal.ReadLine() + return strings.TrimRight(raw, "\n"), err + } + fmt.Fprint(w, prompt) + buf := bufio.NewReader(os.Stdin) + return buf.ReadString('\n') +} + +// ReadPassword reads user password with prompt. +func ReadPassword(w io.Writer, prompt string) (string, error) { + if Terminal != nil { + return Terminal.ReadPassword(prompt) + } + fmt.Fprint(w, prompt) + rawPass, err := terminal.ReadPassword(syscall.Stdin) + if err != nil { + return "", err + } + fmt.Fprintln(w) + return strings.TrimRight(string(rawPass), "\n"), nil +} diff --git a/cli/smartcontract/smart_contract.go b/cli/smartcontract/smart_contract.go index 0410e87d7..5284630f5 100644 --- a/cli/smartcontract/smart_contract.go +++ b/cli/smartcontract/smart_contract.go @@ -9,9 +9,9 @@ import ( "os" "path/filepath" "strings" - "syscall" "github.com/nspcc-dev/neo-go/cli/flags" + "github.com/nspcc-dev/neo-go/cli/input" "github.com/nspcc-dev/neo-go/cli/options" "github.com/nspcc-dev/neo-go/pkg/compiler" "github.com/nspcc-dev/neo-go/pkg/core/transaction" @@ -25,7 +25,6 @@ import ( "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/urfave/cli" - "golang.org/x/crypto/ssh/terminal" "gopkg.in/yaml.v2" ) @@ -680,8 +679,8 @@ func getAccFromContext(ctx *cli.Context) (*wallet.Account, error) { return nil, cli.NewExitError(fmt.Errorf("wallet contains no account for '%s'", address.Uint160ToString(addr)), 1) } - fmt.Fprintf(ctx.App.Writer, "Enter account %s password > ", address.Uint160ToString(addr)) - rawPass, err := terminal.ReadPassword(syscall.Stdin) + rawPass, err := input.ReadPassword(ctx.App.Writer, + fmt.Sprintf("Enter account %s password > ", address.Uint160ToString(addr))) fmt.Fprintln(ctx.App.Writer) if err != nil { return nil, cli.NewExitError(err, 1) diff --git a/cli/testdata/testwallet.json b/cli/testdata/testwallet.json new file mode 100644 index 000000000..97b2e913a --- /dev/null +++ b/cli/testdata/testwallet.json @@ -0,0 +1 @@ +{"version":"3.0","accounts":[{"address":"NNuJqXDnRqvwgzhSzhH4jnVFWB1DyZ34EM","key":"6PYT6enT6eh4gu4ew3Mx58pFFDQNhuR1qQPuU594Eo5u4sA2ZvE4MqJV12","label":"kek","contract":{"script":"DCECl3UyEIq6T5RRIXS6z4tNdZPTzQ7NvXyx7FwK05d9UyYLQZVEDXg=","parameters":[{"name":"parameter0","type":"Signature"}],"deployed":false},"lock":false,"isdefault":false}],"scrypt":{"n":16384,"r":8,"p":8},"extra":{"Tokens":null}} diff --git a/cli/wallet/validator.go b/cli/wallet/validator.go index 64c12d51c..0d9d7ad97 100644 --- a/cli/wallet/validator.go +++ b/cli/wallet/validator.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/nspcc-dev/neo-go/cli/flags" + "github.com/nspcc-dev/neo-go/cli/input" "github.com/nspcc-dev/neo-go/cli/options" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/encoding/address" @@ -178,7 +179,8 @@ func getDecryptedAccount(ctx *cli.Context, wall *wallet.Wallet, addr util.Uint16 return nil, fmt.Errorf("can't find account for the address: %s", address.Uint160ToString(addr)) } - if pass, err := readPassword(ctx.App.Writer, "Password > "); err != nil { + if pass, err := input.ReadPassword(ctx.App.Writer, "Password > "); err != nil { + fmt.Println("ERROR", pass, err) return nil, err } else if err := acc.Decrypt(pass); err != nil { return nil, err diff --git a/cli/wallet/wallet.go b/cli/wallet/wallet.go index 30b742512..c74e7a17b 100644 --- a/cli/wallet/wallet.go +++ b/cli/wallet/wallet.go @@ -1,16 +1,14 @@ package wallet import ( - "bufio" "encoding/hex" "errors" "fmt" "io" - "os" "strings" - "syscall" "github.com/nspcc-dev/neo-go/cli/flags" + "github.com/nspcc-dev/neo-go/cli/input" "github.com/nspcc-dev/neo-go/cli/options" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/encoding/address" @@ -18,7 +16,6 @@ import ( "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/urfave/cli" - "golang.org/x/crypto/ssh/terminal" ) var ( @@ -266,7 +263,7 @@ func convertWallet(ctx *cli.Context) error { for _, acc := range wall.Accounts { address.Prefix = address.NEO2Prefix - pass, err := readPassword(ctx.App.Writer, fmt.Sprintf("Enter passphrase for account %s (label '%s') > ", acc.Address, acc.Label)) + pass, err := input.ReadPassword(ctx.App.Writer, fmt.Sprintf("Enter passphrase for account %s (label '%s') > ", acc.Address, acc.Label)) if err != nil { return cli.NewExitError(err, -1) } else if err := acc.Decrypt(pass); err != nil { @@ -348,7 +345,7 @@ loop: for _, wif := range wifs { if decrypt { - pass, err := readPassword(ctx.App.Writer, "Enter password > ") + pass, err := input.ReadPassword(ctx.App.Writer, "Enter password > ") if err != nil { return cli.NewExitError(err, 1) } @@ -518,9 +515,7 @@ func removeAccount(ctx *cli.Context) error { } func askForConsent(w io.Writer) bool { - fmt.Fprintln(w, "Are you sure? [y/N]: ") - reader := bufio.NewReader(os.Stdin) - response, err := reader.ReadString('\n') + response, err := input.ReadLine(w, "Are you sure? [y/N]: ") if err == nil { response = strings.ToLower(strings.TrimSpace(response)) if response == "y" || response == "yes" { @@ -537,7 +532,7 @@ func dumpWallet(ctx *cli.Context) error { return cli.NewExitError(err, 1) } if ctx.Bool("decrypt") { - pass, err := readPassword(ctx.App.Writer, "Enter wallet password > ") + pass, err := input.ReadPassword(ctx.App.Writer, "Enter wallet password > ") if err != nil { return cli.NewExitError(err, 1) } @@ -578,14 +573,12 @@ func createWallet(ctx *cli.Context) error { } func readAccountInfo(w io.Writer) (string, string, error) { - buf := bufio.NewReader(os.Stdin) - fmt.Fprint(w, "Enter the name of the account > ") - rawName, _ := buf.ReadBytes('\n') - phrase, err := readPassword(w, "Enter passphrase > ") + rawName, _ := input.ReadLine(w, "Enter the name of the account > ") + phrase, err := input.ReadPassword(w, "Enter passphrase > ") if err != nil { return "", "", err } - phraseCheck, err := readPassword(w, "Confirm passphrase > ") + phraseCheck, err := input.ReadPassword(w, "Confirm passphrase > ") if err != nil { return "", "", err } @@ -617,7 +610,7 @@ func newAccountFromWIF(w io.Writer, wif string) (*wallet.Account, error) { // note: NEP2 strings always have length of 58 even though // base58 strings can have different lengths even if slice lengths are equal if len(wif) == 58 { - pass, err := readPassword(w, "Enter password > ") + pass, err := input.ReadPassword(w, "Enter password > ") if err != nil { return nil, err } @@ -655,16 +648,6 @@ func addAccountAndSave(w *wallet.Wallet, acc *wallet.Account) error { return w.Save() } -func readPassword(w io.Writer, prompt string) (string, error) { - fmt.Fprint(w, prompt) - rawPass, err := terminal.ReadPassword(syscall.Stdin) - fmt.Fprintln(w) - if err != nil { - return "", err - } - return strings.TrimRight(string(rawPass), "\n"), nil -} - func fmtPrintWallet(w io.Writer, wall *wallet.Wallet) { b, _ := wall.JSON() fmt.Fprintln(w, "")