mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-10 07:58:22 -05:00
Accounts-V2: Replace promptui with shared/promptutil (#6759)
* Add promptutil to shared/promptutil * fixes * comment * Implement promptutil into accounts-v2 * gaz * Merge branch 'master' of github.com:prysmaticlabs/prysm into implement-promptutil * Apply suggestions from code review * gofmt and fix all scanned input * Merge refs/heads/master into implement-promptutil
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_test")
|
||||
load("@prysm//tools/go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
@@ -14,3 +15,9 @@ go_library(
|
||||
"@org_golang_x_crypto//ssh/terminal:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["validate_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package promptutil
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
@@ -17,15 +19,17 @@ func ValidatePrompt(promptText string, validateFunc func(string) error) (string,
|
||||
var response string
|
||||
for !responseValid {
|
||||
fmt.Printf("%s:\n", au.Bold(promptText))
|
||||
_, err := fmt.Scanln(&response)
|
||||
if err != nil && !strings.Contains(err.Error(), "unexpected newline") {
|
||||
return "", err
|
||||
}
|
||||
response = strings.TrimRight(response, "\r\n")
|
||||
if err := validateFunc(response); err != nil {
|
||||
fmt.Printf("Entry not valid: %s\n", au.BrightRed(err))
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
if ok := scanner.Scan(); ok {
|
||||
item := scanner.Text()
|
||||
response = strings.TrimRight(item, "\r\n")
|
||||
if err := validateFunc(response); err != nil {
|
||||
fmt.Printf("Entry not valid: %s\n", au.BrightRed(err))
|
||||
} else {
|
||||
responseValid = true
|
||||
}
|
||||
} else {
|
||||
responseValid = true
|
||||
return "", errors.New("could not scan text input")
|
||||
}
|
||||
}
|
||||
return response, nil
|
||||
@@ -35,15 +39,16 @@ func ValidatePrompt(promptText string, validateFunc func(string) error) (string,
|
||||
func DefaultPrompt(promptText string, defaultValue string) (string, error) {
|
||||
var response string
|
||||
fmt.Printf("%s %s:\n", promptText, fmt.Sprintf("(%s: %s)", au.BrightGreen("default"), defaultValue))
|
||||
_, err := fmt.Scanln(&response)
|
||||
if err != nil && !strings.Contains(err.Error(), "unexpected newline") {
|
||||
return "", err
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
if ok := scanner.Scan(); ok {
|
||||
item := scanner.Text()
|
||||
response = strings.TrimRight(item, "\r\n")
|
||||
if response == "" {
|
||||
return defaultValue, nil
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
response = strings.TrimRight(response, "\r\n")
|
||||
if response == "" {
|
||||
return defaultValue, nil
|
||||
}
|
||||
return response, nil
|
||||
return "", errors.New("could not scan text input")
|
||||
}
|
||||
|
||||
// DefaultAndValidatePrompt prompts the user for any text and expects it to fulfill a validation function. If nothing is entered
|
||||
@@ -53,16 +58,17 @@ func DefaultAndValidatePrompt(promptText string, defaultValue string, validateFu
|
||||
var response string
|
||||
for !responseValid {
|
||||
fmt.Printf("%s %s:\n", promptText, fmt.Sprintf("(%s: %s)", au.BrightGreen("default"), defaultValue))
|
||||
_, err := fmt.Scanln(&response)
|
||||
if err != nil && !strings.Contains(err.Error(), "unexpected newline") {
|
||||
return "", err
|
||||
}
|
||||
response = strings.TrimRight(response, "\r\n")
|
||||
if response == "" {
|
||||
return defaultValue, nil
|
||||
}
|
||||
if err := validateFunc(response); err != nil {
|
||||
fmt.Printf("Entry not valid: %s\n", au.BrightRed(err))
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
if ok := scanner.Scan(); ok {
|
||||
item := scanner.Text()
|
||||
response = strings.TrimRight(item, "\r\n")
|
||||
if err := validateFunc(response); err != nil {
|
||||
fmt.Printf("Entry not valid: %s\n", au.BrightRed(err))
|
||||
} else {
|
||||
responseValid = true
|
||||
}
|
||||
} else {
|
||||
return "", errors.New("could not scan text input")
|
||||
}
|
||||
}
|
||||
return response, nil
|
||||
|
||||
85
shared/promptutil/validate_test.go
Normal file
85
shared/promptutil/validate_test.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package promptutil
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestValidatePasswordInput(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "no numbers nor special characters",
|
||||
input: "abcdefghijklmnopqrs",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "number and letters but no special characters",
|
||||
input: "abcdefghijklmnopqrs2020",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "numbers, letters, special characters, but too short",
|
||||
input: "abc2$",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "proper length and strong password",
|
||||
input: "%Str0ngpassword32kjAjsd22020$%",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "password format correct but weak entropy score",
|
||||
input: "aaaaaaa1$",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Unicode strings separated by a space character",
|
||||
input: "x*329293@aAJSD i22903saj",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := ValidatePasswordInput(tt.input); (err != nil) != tt.wantErr {
|
||||
t.Errorf("validatePasswordInput() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsValidUnicode(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "Regular alphanumeric",
|
||||
input: "Someone23xx",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "Unicode strings separated by a space character",
|
||||
input: "x*329293@aAJSD i22903saj",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "Japanese",
|
||||
input: "僕は絵お見るのが好きです",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "Other foreign",
|
||||
input: "Etérium",
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := IsValidUnicode(tt.input); got != tt.want {
|
||||
t.Errorf("isValidUnicode() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ go_library(
|
||||
"//shared/featureconfig:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/petnames:go_default_library",
|
||||
"//shared/promptutil:go_default_library",
|
||||
"//validator/flags:go_default_library",
|
||||
"//validator/keymanager/v2:go_default_library",
|
||||
"//validator/keymanager/v2/derived:go_default_library",
|
||||
@@ -36,7 +37,6 @@ go_library(
|
||||
"@com_github_dustinkirkland_golang_petname//:go_default_library",
|
||||
"@com_github_logrusorgru_aurora//:go_default_library",
|
||||
"@com_github_manifoldco_promptui//:go_default_library",
|
||||
"@com_github_nbutton23_zxcvbn_go//:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_urfave_cli_v2//:go_default_library",
|
||||
|
||||
@@ -52,85 +52,3 @@ func TestCreateAccount_Derived(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
require.Equal(t, len(names), int(numAccounts))
|
||||
}
|
||||
|
||||
func Test_validatePasswordInput(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "no numbers nor special characters",
|
||||
input: "abcdefghijklmnopqrs",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "number and letters but no special characters",
|
||||
input: "abcdefghijklmnopqrs2020",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "numbers, letters, special characters, but too short",
|
||||
input: "abc2$",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "proper length and strong password",
|
||||
input: "%Str0ngpassword32kjAjsd22020$%",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "password format correct but weak entropy score",
|
||||
input: "aaaaaaa1$",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Unicode strings separated by a space character",
|
||||
input: "x*329293@aAJSD i22903saj",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := validatePasswordInput(tt.input); (err != nil) != tt.wantErr {
|
||||
t.Errorf("validatePasswordInput() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_isValidUnicode(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "Regular alphanumeric",
|
||||
input: "Someone23xx",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "Unicode strings separated by a space character",
|
||||
input: "x*329293@aAJSD i22903saj",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "Japanese",
|
||||
input: "僕は絵お見るのが好きです",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "Other foreign",
|
||||
input: "Etérium",
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := isValidUnicode(tt.input); got != tt.want {
|
||||
t.Errorf("isValidUnicode() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,11 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/logrusorgru/aurora"
|
||||
"github.com/manifoldco/promptui"
|
||||
strongPasswords "github.com/nbutton23/zxcvbn-go"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/shared/promptutil"
|
||||
"github.com/prysmaticlabs/prysm/validator/flags"
|
||||
"github.com/prysmaticlabs/prysm/validator/keymanager/v2/remote"
|
||||
"github.com/urfave/cli/v2"
|
||||
@@ -30,17 +29,14 @@ const (
|
||||
type passwordConfirm int
|
||||
|
||||
const (
|
||||
// Constants for passwords.
|
||||
minPasswordLength = 8
|
||||
// Min password score of 3 out of 5 based on the https://github.com/nbutton23/zxcvbn-go
|
||||
// library for strong-entropy password computation.
|
||||
minPasswordScore = 3
|
||||
// An enum to indicate the prompt that confirming the password is not needed.
|
||||
// An enum to indicate to the prompt that confirming the password is not needed.
|
||||
noConfirmPass passwordConfirm = iota
|
||||
// An enum to indicate the prompt to confirm the password entered.
|
||||
// An enum to indicate to the prompt to confirm the password entered.
|
||||
confirmPass
|
||||
)
|
||||
|
||||
var au = aurora.NewAurora(true)
|
||||
|
||||
func inputDirectory(cliCtx *cli.Context, promptText string, flag *cli.StringFlag) (string, error) {
|
||||
directory := cliCtx.String(flag.Name)
|
||||
if cliCtx.IsSet(flag.Name) {
|
||||
@@ -54,7 +50,6 @@ func inputDirectory(cliCtx *cli.Context, promptText string, flag *cli.StringFlag
|
||||
return "", errors.Wrapf(err, "could not check if wallet dir %s exists", directory)
|
||||
}
|
||||
if ok {
|
||||
au := aurora.NewAurora(true)
|
||||
log.Infof("%s %s", au.BrightMagenta("(wallet path)"), directory)
|
||||
return directory, nil
|
||||
}
|
||||
@@ -64,34 +59,21 @@ func inputDirectory(cliCtx *cli.Context, promptText string, flag *cli.StringFlag
|
||||
return "", errors.Wrapf(err, "could not check if passwords dir %s exists", directory)
|
||||
}
|
||||
if ok {
|
||||
au := aurora.NewAurora(true)
|
||||
log.Infof("%s %s", au.BrightMagenta("(account passwords path)"), directory)
|
||||
return directory, nil
|
||||
}
|
||||
}
|
||||
|
||||
prompt := promptui.Prompt{
|
||||
Label: promptText,
|
||||
Validate: validateDirectoryPath,
|
||||
Default: directory,
|
||||
}
|
||||
inputtedDir, err := prompt.Run()
|
||||
inputtedDir, err := promptutil.DefaultPrompt(au.Bold(promptText).String(), directory)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not determine directory: %v", formatPromptError(err))
|
||||
return "", err
|
||||
}
|
||||
if inputtedDir == prompt.Default {
|
||||
if inputtedDir == directory {
|
||||
return directory, nil
|
||||
}
|
||||
return inputtedDir, nil
|
||||
}
|
||||
|
||||
func validateDirectoryPath(input string) error {
|
||||
if len(input) == 0 {
|
||||
return errors.New("directory path must not be empty")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func inputPassword(
|
||||
cliCtx *cli.Context,
|
||||
passwordFileFlag *cli.StringFlag,
|
||||
@@ -105,7 +87,7 @@ func inputPassword(
|
||||
return "", errors.Wrap(err, "could not read password file")
|
||||
}
|
||||
enteredPassword := strings.TrimRight(string(data), "\r\n")
|
||||
if err := validatePasswordInput(enteredPassword); err != nil {
|
||||
if err := promptutil.ValidatePasswordInput(enteredPassword); err != nil {
|
||||
return "", errors.Wrap(err, "password did not pass validation")
|
||||
}
|
||||
return enteredPassword, nil
|
||||
@@ -114,35 +96,26 @@ func inputPassword(
|
||||
var walletPassword string
|
||||
var err error
|
||||
for !hasValidPassword {
|
||||
prompt := promptui.Prompt{
|
||||
Label: promptText,
|
||||
Validate: validatePasswordInput,
|
||||
Mask: '*',
|
||||
walletPassword, err = promptutil.PasswordPrompt(promptText, promptutil.ValidatePasswordInput)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not read account password: %v", err)
|
||||
}
|
||||
|
||||
walletPassword, err = prompt.Run()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not read account password: %v", formatPromptError(err))
|
||||
}
|
||||
if confirmPassword == confirmPass {
|
||||
prompt = promptui.Prompt{
|
||||
Label: confirmPasswordPromptText,
|
||||
Mask: '*',
|
||||
}
|
||||
confirmPassword, err := prompt.Run()
|
||||
passwordConfirmation, err := promptutil.PasswordPrompt(confirmPasswordPromptText, promptutil.ValidatePasswordInput)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not read password confirmation: %v", formatPromptError(err))
|
||||
return "", fmt.Errorf("could not read password confirmation: %v", err)
|
||||
}
|
||||
if walletPassword != confirmPassword {
|
||||
if walletPassword != passwordConfirmation {
|
||||
log.Error("Passwords do not match")
|
||||
continue
|
||||
}
|
||||
hasValidPassword = true
|
||||
} else {
|
||||
return strings.TrimRight(walletPassword, "\r\n"), nil
|
||||
return walletPassword, nil
|
||||
}
|
||||
}
|
||||
return strings.TrimRight(walletPassword, "\r\n"), nil
|
||||
return walletPassword, nil
|
||||
}
|
||||
|
||||
func inputWeakPassword(cliCtx *cli.Context, passwordFileFlag *cli.StringFlag, promptText string) (string, error) {
|
||||
@@ -155,64 +128,11 @@ func inputWeakPassword(cliCtx *cli.Context, passwordFileFlag *cli.StringFlag, pr
|
||||
return strings.TrimRight(string(data), "\r\n"), nil
|
||||
}
|
||||
|
||||
prompt := promptui.Prompt{
|
||||
Label: promptText,
|
||||
Validate: func(input string) error {
|
||||
if input == "" {
|
||||
return errors.New("password cannot be empty")
|
||||
}
|
||||
if !isValidUnicode(input) {
|
||||
return errors.New("not valid unicode")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Mask: '*',
|
||||
}
|
||||
|
||||
walletPassword, err := prompt.Run()
|
||||
walletPassword, err := promptutil.PasswordPrompt(promptText, promptutil.NotEmpty)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not read account password: %v", formatPromptError(err))
|
||||
return "", fmt.Errorf("could not read account password: %v", err)
|
||||
}
|
||||
return strings.TrimRight(walletPassword, "\r\n"), nil
|
||||
}
|
||||
|
||||
// Validate a strong password input for new accounts,
|
||||
// including a min length, at least 1 number and at least
|
||||
// 1 special character.
|
||||
func validatePasswordInput(input string) error {
|
||||
var (
|
||||
hasMinLen = false
|
||||
hasLetter = false
|
||||
hasNumber = false
|
||||
hasSpecial = false
|
||||
)
|
||||
if len(input) >= minPasswordLength {
|
||||
hasMinLen = true
|
||||
}
|
||||
for _, char := range input {
|
||||
switch {
|
||||
case !(unicode.IsLetter(char) || unicode.IsNumber(char) || unicode.IsPunct(char) || unicode.IsSymbol(char)):
|
||||
return errors.New("password must only contain alphanumeric characters, punctuation, or symbols")
|
||||
case unicode.IsLetter(char):
|
||||
hasLetter = true
|
||||
case unicode.IsNumber(char):
|
||||
hasNumber = true
|
||||
case unicode.IsPunct(char) || unicode.IsSymbol(char):
|
||||
hasSpecial = true
|
||||
}
|
||||
}
|
||||
if !(hasMinLen && hasLetter && hasNumber && hasSpecial) {
|
||||
return errors.New(
|
||||
"password must have more than 8 characters, at least 1 special character, and 1 number",
|
||||
)
|
||||
}
|
||||
strength := strongPasswords.PasswordStrength(input, nil)
|
||||
if strength.Score < minPasswordScore {
|
||||
return errors.New(
|
||||
"password is too easy to guess, try a stronger password",
|
||||
)
|
||||
}
|
||||
return nil
|
||||
return walletPassword, nil
|
||||
}
|
||||
|
||||
func inputRemoteKeymanagerConfig(cliCtx *cli.Context) (*remote.Config, error) {
|
||||
@@ -223,60 +143,36 @@ func inputRemoteKeymanagerConfig(cliCtx *cli.Context) (*remote.Config, error) {
|
||||
log.Info("Input desired configuration")
|
||||
var err error
|
||||
if addr == "" {
|
||||
prompt := promptui.Prompt{
|
||||
Label: "Remote gRPC address (such as host.example.com:4000)",
|
||||
Validate: func(input string) error {
|
||||
if input == "" {
|
||||
return errors.New("remote host address cannot be empty")
|
||||
}
|
||||
if !isValidUnicode(input) {
|
||||
return errors.New("not valid unicode")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
addr, err = prompt.Run()
|
||||
addr, err = promptutil.ValidatePrompt("Remote gRPC address (such as host.example.com:4000)", promptutil.NotEmpty)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if crt == "" {
|
||||
prompt := promptui.Prompt{
|
||||
Label: "Path to TLS crt (such as /path/to/client.crt)",
|
||||
Validate: validateCertPath,
|
||||
}
|
||||
crt, err = prompt.Run()
|
||||
crt, err = promptutil.ValidatePrompt("Path to TLS crt (such as /path/to/client.crt)", validateCertPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if key == "" {
|
||||
prompt := promptui.Prompt{
|
||||
Label: "Path to TLS key (such as /path/to/client.key)",
|
||||
Validate: validateCertPath,
|
||||
}
|
||||
key, err = prompt.Run()
|
||||
key, err = promptutil.ValidatePrompt("Path to TLS key (such as /path/to/client.key)", validateCertPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if ca == "" {
|
||||
prompt := promptui.Prompt{
|
||||
Label: "Path to certificate authority (CA) crt (such as /path/to/ca.crt)",
|
||||
Validate: validateCertPath,
|
||||
}
|
||||
ca, err = prompt.Run()
|
||||
ca, err = promptutil.ValidatePrompt("Path to certificate authority (CA) crt (such as /path/to/ca.crt)", validateCertPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
newCfg := &remote.Config{
|
||||
RemoteCertificate: &remote.CertificateConfig{
|
||||
ClientCertPath: strings.TrimRight(crt, "\r\n"),
|
||||
ClientKeyPath: strings.TrimRight(key, "\r\n"),
|
||||
CACertPath: strings.TrimRight(ca, "\r\n"),
|
||||
ClientCertPath: crt,
|
||||
ClientKeyPath: key,
|
||||
CACertPath: ca,
|
||||
},
|
||||
RemoteAddr: strings.TrimRight(addr, "\r\n"),
|
||||
RemoteAddr: addr,
|
||||
}
|
||||
fmt.Printf("%s\n", newCfg)
|
||||
return newCfg, nil
|
||||
@@ -286,7 +182,7 @@ func validateCertPath(input string) error {
|
||||
if input == "" {
|
||||
return errors.New("crt path cannot be empty")
|
||||
}
|
||||
if !isValidUnicode(input) {
|
||||
if !promptutil.IsValidUnicode(input) {
|
||||
return errors.New("not valid unicode")
|
||||
}
|
||||
if !fileExists(input) {
|
||||
@@ -307,18 +203,3 @@ func formatPromptError(err error) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if an input string is a valid unicode string comprised of only
|
||||
// letters, numbers, punctuation, or symbols.
|
||||
func isValidUnicode(input string) bool {
|
||||
for _, char := range input {
|
||||
if !(unicode.IsLetter(char) ||
|
||||
unicode.IsNumber(char) ||
|
||||
unicode.IsPunct(char) ||
|
||||
unicode.IsSymbol(char)) {
|
||||
log.Info(char)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/manifoldco/promptui"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/shared/promptutil"
|
||||
"github.com/prysmaticlabs/prysm/validator/flags"
|
||||
v2keymanager "github.com/prysmaticlabs/prysm/validator/keymanager/v2"
|
||||
"github.com/prysmaticlabs/prysm/validator/keymanager/v2/derived"
|
||||
@@ -99,15 +99,11 @@ func inputMnemonic(cliCtx *cli.Context) (string, error) {
|
||||
}
|
||||
return enteredMnemonic, nil
|
||||
}
|
||||
prompt := promptui.Prompt{
|
||||
Label: "Enter the seed phrase for the wallet you would like to recover",
|
||||
Validate: validateMnemonic,
|
||||
}
|
||||
menmonicPhrase, err := prompt.Run()
|
||||
mnemonicPhrase, err := promptutil.ValidatePrompt("Enter the seed phrase for the wallet you would like to recover", validateMnemonic)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not determine wallet directory: %v", formatPromptError(err))
|
||||
return "", fmt.Errorf("could not get mnemonic phrase: %v", err)
|
||||
}
|
||||
return menmonicPhrase, nil
|
||||
return mnemonicPhrase, nil
|
||||
}
|
||||
|
||||
func inputNumAccounts(cliCtx *cli.Context) (int64, error) {
|
||||
@@ -115,20 +111,9 @@ func inputNumAccounts(cliCtx *cli.Context) (int64, error) {
|
||||
numAccounts := cliCtx.Int64(flags.NumAccountsFlag.Name)
|
||||
return numAccounts, nil
|
||||
}
|
||||
prompt := promptui.Prompt{
|
||||
Label: "Enter how many accounts you would like to recover",
|
||||
Validate: func(input string) error {
|
||||
_, err := strconv.Atoi(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Default: "0",
|
||||
}
|
||||
numAccounts, err := prompt.Run()
|
||||
numAccounts, err := promptutil.DefaultAndValidatePrompt("Enter how many accounts you would like to recover", "0", promptutil.ValidateNumber)
|
||||
if err != nil {
|
||||
return 0, formatPromptError(err)
|
||||
return 0, err
|
||||
}
|
||||
numAccountsInt, err := strconv.Atoi(numAccounts)
|
||||
if err != nil {
|
||||
|
||||
@@ -18,12 +18,12 @@ go_library(
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/depositutil:go_default_library",
|
||||
"//shared/petnames:go_default_library",
|
||||
"//shared/promptutil:go_default_library",
|
||||
"//shared/rand:go_default_library",
|
||||
"//shared/roughtime:go_default_library",
|
||||
"//validator/accounts/v2/iface:go_default_library",
|
||||
"//validator/keymanager/v2:go_default_library",
|
||||
"@com_github_google_uuid//:go_default_library",
|
||||
"@com_github_manifoldco_promptui//:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_ssz//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
|
||||
@@ -2,12 +2,13 @@ package derived
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/manifoldco/promptui"
|
||||
"github.com/prysmaticlabs/prysm/shared/promptutil"
|
||||
"github.com/tyler-smith/go-bip39"
|
||||
)
|
||||
|
||||
const confirmationText = "Confirm you have written down the recovery words somewhere safe (offline) [y|Y]"
|
||||
|
||||
// SeedPhraseFactory defines a struct which
|
||||
// can generate new seed phrases in human-readable
|
||||
// format from a source of entropy in raw bytes. It
|
||||
@@ -38,29 +39,21 @@ func (m *EnglishMnemonicGenerator) ConfirmAcknowledgement(phrase string) error {
|
||||
"Write down the sentence below, as it is your only " +
|
||||
"means of recovering your wallet",
|
||||
)
|
||||
fmt.Printf(`
|
||||
=================Wallet Seed Recovery Phrase====================
|
||||
fmt.Printf(
|
||||
`=================Wallet Seed Recovery Phrase====================
|
||||
|
||||
%s
|
||||
|
||||
===================================================================
|
||||
`, phrase)
|
||||
===================================================================`,
|
||||
phrase)
|
||||
fmt.Println("")
|
||||
if m.skipMnemonicConfirm {
|
||||
return nil
|
||||
}
|
||||
// Confirm the user has written down the mnemonic phrase offline.
|
||||
prompt := promptui.Prompt{
|
||||
Label: "Confirm you have written down the recovery words somewhere safe (offline)",
|
||||
IsConfirm: true,
|
||||
}
|
||||
expected := "y"
|
||||
var result string
|
||||
var err error
|
||||
for strings.ToLower(result) != expected {
|
||||
result, err = prompt.Run()
|
||||
if err != nil {
|
||||
log.Errorf("Could not confirm acknowledgement of prompt, please enter y")
|
||||
}
|
||||
_, err := promptutil.ValidatePrompt(confirmationText, promptutil.ValidateConfirmation)
|
||||
if err != nil {
|
||||
log.Errorf("Could not confirm acknowledgement of prompt, please enter y")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user