diff --git a/validator/accounts/BUILD.bazel b/validator/accounts/BUILD.bazel index bba35cbc2..59440cbfc 100644 --- a/validator/accounts/BUILD.bazel +++ b/validator/accounts/BUILD.bazel @@ -14,8 +14,10 @@ go_library( "//shared/cmd:go_default_library", "//shared/keystore:go_default_library", "//shared/params:go_default_library", + "//validator/flags:go_default_library", "@com_github_pkg_errors//:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", + "@in_gopkg_urfave_cli_v2//:go_default_library", ], ) @@ -28,5 +30,7 @@ go_test( "//shared/keystore:go_default_library", "//shared/params:go_default_library", "//shared/testutil:go_default_library", + "//validator/flags:go_default_library", + "@in_gopkg_urfave_cli_v2//:go_default_library", ], ) diff --git a/validator/accounts/account.go b/validator/accounts/account.go index 810423eb4..b15b28eb4 100644 --- a/validator/accounts/account.go +++ b/validator/accounts/account.go @@ -17,7 +17,9 @@ import ( "github.com/prysmaticlabs/prysm/shared/cmd" "github.com/prysmaticlabs/prysm/shared/keystore" "github.com/prysmaticlabs/prysm/shared/params" + "github.com/prysmaticlabs/prysm/validator/flags" "github.com/sirupsen/logrus" + "gopkg.in/urfave/cli.v2" ) var log = logrus.WithField("prefix", "accounts") @@ -137,27 +139,6 @@ func Exists(keystorePath string) (bool, error) { // CreateValidatorAccount creates a validator account from the given cli context. func CreateValidatorAccount(path string, passphrase string) (string, string, error) { - if passphrase == "" { - log.Info("Creating a new validator account for eth2") - enteredPassphrase, err := cmd.EnterPassword() - if err != nil { - return path, enteredPassphrase, err - } - passphrase = enteredPassphrase - } - - if path == "" { - path = DefaultValidatorDir() - log.Infof("Please specify a keystore path to save your private keys (default: %q):", path) - reader := bufio.NewReader(os.Stdin) - text, err := reader.ReadString('\n') - if err != nil { - return path, passphrase, err - } - if text = strings.Replace(text, "\n", "", -1); text != "" { - path = text - } - } // Forces user to create directory if using non-default path. if path != DefaultValidatorDir() { exists, err := Exists(path) @@ -174,6 +155,18 @@ func CreateValidatorAccount(path string, passphrase string) (string, string, err return path, passphrase, nil } +// PrintPublicAndPrivateKeys uses the passed in path and prints out the public and private keys in that directory. +func PrintPublicAndPrivateKeys(path string, passphrase string) error { + keystores, err := DecryptKeysFromKeystore(path, passphrase) + if err != nil { + return errors.Wrapf(err, "failed to decrypt keystore keys at path %s", path) + } + for _, v := range keystores { + fmt.Printf("Public key: %#x private key: %#x\n", v.PublicKey.Marshal(), v.SecretKey.Marshal()) + } + return nil +} + // DefaultValidatorDir returns OS-specific default keystore directory. func DefaultValidatorDir() string { // Try to place the data folder in the user's home dir @@ -191,6 +184,36 @@ func DefaultValidatorDir() string { return "" } +// HandleEmptyFlags checks what the set flags are and allows the user to manually enter them if they're empty. +func HandleEmptyFlags(cliCtx *cli.Context) (string, string, error) { + path := cliCtx.String(flags.KeystorePathFlag.Name) + passphrase := cliCtx.String(flags.PasswordFlag.Name) + + if path == "" { + path = DefaultValidatorDir() + log.Infof("Please specify the keystore path for your private keys (default: %q):", path) + reader := bufio.NewReader(os.Stdin) + text, err := reader.ReadString('\n') + if err != nil { + return path, passphrase, errors.Wrap(err, "could not read input path") + } + if text = strings.Replace(text, "\n", "", -1); text != "" { + path = text + } + } + + if passphrase == "" { + log.Info("Please enter the password for your private keys") + enteredPassphrase, err := cmd.EnterPassword() + if err != nil { + return path, enteredPassphrase, errors.Wrap(err, "could not read entered passphrase") + } + passphrase = enteredPassphrase + } + + return path, passphrase, nil +} + // homeDir returns home directory path. func homeDir() string { if home := os.Getenv("HOME"); home != "" { diff --git a/validator/accounts/account_test.go b/validator/accounts/account_test.go index 6e9d7ab64..03c19efdb 100644 --- a/validator/accounts/account_test.go +++ b/validator/accounts/account_test.go @@ -1,6 +1,7 @@ package accounts import ( + "flag" "fmt" "io/ioutil" "os" @@ -9,6 +10,8 @@ import ( "github.com/prysmaticlabs/prysm/shared/keystore" "github.com/prysmaticlabs/prysm/shared/params" "github.com/prysmaticlabs/prysm/shared/testutil" + "github.com/prysmaticlabs/prysm/validator/flags" + "gopkg.in/urfave/cli.v2" ) func TestNewValidatorAccount_AccountExists(t *testing.T) { @@ -49,3 +52,25 @@ func TestNewValidatorAccount_CreateValidatorAccount(t *testing.T) { t.Errorf("expected error not thrown, want: %v, got: %v", wantErrString, err) } } + +func TestHandleEmptyFlags_FlagsSetF(t *testing.T) { + passedPath := "~/path/given" + passedPassword := "password" + + app := &cli.App{} + set := flag.NewFlagSet("test", 0) + set.String(flags.KeystorePathFlag.Name, passedPath, "set keystore path") + set.String(flags.PasswordFlag.Name, passedPassword, "set keystore password") + ctx := cli.NewContext(app, set, nil) + path, passphrase, err := HandleEmptyFlags(ctx) + if err != nil { + t.Fatal(err) + } + + if passedPath != path { + t.Fatalf("Expected set path to be unchanged, expected %s, received %s", passedPath, path) + } + if passedPassword != passphrase { + t.Fatalf("Expected set password to be unchanged, expected %s, received %s", passedPassword, passphrase) + } +} diff --git a/validator/main.go b/validator/main.go index 12e0d3702..fed2c2759 100644 --- a/validator/main.go +++ b/validator/main.go @@ -81,8 +81,8 @@ func init() { func main() { app := cli.App{} app.Name = "validator" - app.Usage = `launches an Ethereum Serenity validator client that interacts with a beacon chain, - starts proposer services, shardp2p connections, and more` + app.Usage = `launches an Ethereum 2.0 validator client that interacts with a beacon chain, + starts proposer and attester services, p2p connections, and more` app.Version = version.GetVersion() app.Action = startNode app.Commands = []*cli.Command{ @@ -93,22 +93,26 @@ func main() { Subcommands: []*cli.Command{ { Name: "create", - Description: `creates a new validator account keystore containing private keys for Ethereum Serenity - + Description: `creates a new validator account keystore containing private keys for Ethereum 2.0 - this command outputs a deposit data string which can be used to deposit Ether into the ETH1.0 deposit contract in order to activate the validator client`, Flags: []cli.Flag{ flags.KeystorePathFlag, flags.PasswordFlag, }, - Action: func(ctx *cli.Context) error { - featureconfig.ConfigureValidator(ctx) + Action: func(cliCtx *cli.Context) error { + featureconfig.ConfigureValidator(cliCtx) if featureconfig.Get().MinimalConfig { log.Warn("Using Minimal Config") params.UseMinimalConfig() } - if keystoreDir, _, err := accounts.CreateValidatorAccount(ctx.String(flags.KeystorePathFlag.Name), ctx.String(flags.PasswordFlag.Name)); err != nil { - log.WithError(err).Fatalf("Could not create validator at path: %s", keystoreDir) + keystorePath, passphrase, err := accounts.HandleEmptyFlags(cliCtx) + if err != nil { + log.WithError(err).Error("Could not list keys") + } + if _, _, err := accounts.CreateValidatorAccount(keystorePath, passphrase); err != nil { + log.WithError(err).Fatalf("Could not create validator at path: %s", keystorePath) } return nil }, @@ -120,19 +124,13 @@ contract in order to activate the validator client`, flags.KeystorePathFlag, flags.PasswordFlag, }, - Action: func(ctx *cli.Context) error { - if ctx.String(flags.KeystorePathFlag.Name) == "" { - log.Fatalf("%s is required", flags.KeystorePathFlag.Name) - } - if ctx.String(flags.PasswordFlag.Name) == "" { - log.Fatalf("%s is required", flags.PasswordFlag.Name) - } - keystores, err := accounts.DecryptKeysFromKeystore(ctx.String(flags.KeystorePathFlag.Name), ctx.String(flags.PasswordFlag.Name)) + Action: func(cliCtx *cli.Context) error { + keystorePath, passphrase, err := accounts.HandleEmptyFlags(cliCtx) if err != nil { - log.WithError(err).Fatalf("Failed to decrypt keystore keys at path %s", ctx.String(flags.KeystorePathFlag.Name)) + log.WithError(err).Error("Could not list keys") } - for _, v := range keystores { - fmt.Printf("Public key: %#x private key: %#x\n", v.PublicKey.Marshal(), v.SecretKey.Marshal()) + if err := accounts.PrintPublicAndPrivateKeys(keystorePath, passphrase); err != nil { + log.WithError(err).Errorf("Could not list private and public keys in path %s", keystorePath) } return nil },