From d7bcea7906b399f9c981dc503cc70514d6c2f85d Mon Sep 17 00:00:00 2001 From: Ivan Martinez Date: Tue, 14 Jul 2020 19:00:58 -0400 Subject: [PATCH] Allow creating new accounts to be non-interactive (#6602) * Allow accounts to be made non-interactively * Merge branch 'master' of github.com:prysmaticlabs/prysm into make-accounts-noninteractive * Update validator/node/node.go * Update validator/accounts/v2/list.go --- validator/accounts/v2/cmd_accounts.go | 2 ++ validator/accounts/v2/list.go | 2 +- validator/accounts/v2/new.go | 29 +++++++++++++++++++--- validator/accounts/v2/wallet.go | 7 +++--- validator/flags/flags.go | 10 ++++++++ validator/keymanager/v2/direct/direct.go | 12 +++++---- validator/keymanager/v2/direct/mnemonic.go | 7 +++++- validator/node/node.go | 2 +- 8 files changed, 57 insertions(+), 14 deletions(-) diff --git a/validator/accounts/v2/cmd_accounts.go b/validator/accounts/v2/cmd_accounts.go index 493dbfa0c0..78df716137 100644 --- a/validator/accounts/v2/cmd_accounts.go +++ b/validator/accounts/v2/cmd_accounts.go @@ -15,6 +15,8 @@ this command outputs a deposit data string which is required to become a validat Flags: []cli.Flag{ flags.WalletDirFlag, flags.WalletPasswordsDirFlag, + flags.PasswordFileFlag, + flags.SkipMnemonicConfirmFlag, }, Action: NewAccount, }, diff --git a/validator/accounts/v2/list.go b/validator/accounts/v2/list.go index 7dc39974e0..1f314785d3 100644 --- a/validator/accounts/v2/list.go +++ b/validator/accounts/v2/list.go @@ -33,7 +33,7 @@ func ListAccounts(cliCtx *cli.Context) error { if err != nil { log.Fatalf("Could not read wallet at specified path %s: %v", walletDir, err) } - keymanager, err := wallet.ExistingKeyManager(ctx) + keymanager, err := wallet.ExistingKeyManager(ctx, false /* skipMnemonicConfirm */) if err != nil { log.Fatalf("Could not initialize keymanager: %v", err) } diff --git a/validator/accounts/v2/new.go b/validator/accounts/v2/new.go index 9cdcc4a8b5..82d324e147 100644 --- a/validator/accounts/v2/new.go +++ b/validator/accounts/v2/new.go @@ -3,6 +3,7 @@ package v2 import ( "context" "fmt" + "io/ioutil" "path" "unicode" @@ -78,12 +79,13 @@ func NewAccount(cliCtx *cli.Context) error { log.Fatalf("Could not read wallet at specified path %s: %v", walletDir, err) } + skipMnemonicConfirm := cliCtx.Bool(flags.SkipMnemonicConfirmFlag.Name) // We initialize a new keymanager depending on the user's selected keymanager kind. var keymanager v2keymanager.IKeymanager if isNewWallet { - keymanager, err = wallet.CreateKeymanager(ctx) + keymanager, err = wallet.CreateKeymanager(ctx, skipMnemonicConfirm) } else { - keymanager, err = wallet.ExistingKeyManager(ctx) + keymanager, err = wallet.ExistingKeyManager(ctx, skipMnemonicConfirm) } if err != nil { log.Fatalf("Could not initialize keymanager: %v", err) @@ -104,6 +106,10 @@ func NewAccount(cliCtx *cli.Context) error { func inputWalletDir(cliCtx *cli.Context) (string, error) { walletDir := cliCtx.String(flags.WalletDirFlag.Name) + if cliCtx.IsSet(flags.WalletDirFlag.Name) { + return walletDir, nil + } + if walletDir == flags.DefaultValidatorDir() { walletDir = path.Join(walletDir, WalletDefaultDirName) } @@ -135,7 +141,20 @@ func inputKeymanagerKind(_ *cli.Context) (v2keymanager.Kind, error) { return v2keymanager.Kind(selection), nil } -func inputNewAccountPassword(_ *cli.Context) (string, error) { +func inputNewAccountPassword(cliCtx *cli.Context) (string, error) { + if cliCtx.IsSet(flags.PasswordFileFlag.Name) { + passwordFilePath := cliCtx.String(flags.PasswordFileFlag.Name) + data, err := ioutil.ReadFile(passwordFilePath) + if err != nil { + return "", err + } + enteredPassword := string(data) + if err := validatePasswordInput(enteredPassword); err != nil { + log.WithError(err).Fatal("Password did not pass validation") + } + return enteredPassword, nil + } + var hasValidPassword bool var walletPassword string var err error @@ -183,6 +202,10 @@ func inputPasswordForAccount(_ *cli.Context, accountName string) (string, error) func inputPasswordsDirectory(cliCtx *cli.Context) string { passwordsDir := cliCtx.String(flags.WalletPasswordsDirFlag.Name) + if cliCtx.IsSet(flags.WalletPasswordsDirFlag.Name) { + return passwordsDir + } + if passwordsDir == flags.DefaultValidatorDir() { passwordsDir = path.Join(passwordsDir, PasswordsDefaultDirName) } diff --git a/validator/accounts/v2/wallet.go b/validator/accounts/v2/wallet.go index f8738eed77..3d0aa92708 100644 --- a/validator/accounts/v2/wallet.go +++ b/validator/accounts/v2/wallet.go @@ -192,6 +192,7 @@ func (w *Wallet) AccountNames() ([]string, error) { // unmarshals it based on the wallet's keymanager kind, and returns its value. func (w *Wallet) ExistingKeyManager( ctx context.Context, + skipMnemonicConfirm bool, ) (v2keymanager.IKeymanager, error) { configFile, err := w.ReadKeymanagerConfigFromDisk(ctx) if err != nil { @@ -204,7 +205,7 @@ func (w *Wallet) ExistingKeyManager( if err != nil { return nil, errors.Wrap(err, "could not unmarshal keymanager config file") } - keymanager, err = direct.NewKeymanager(ctx, w, cfg) + keymanager, err = direct.NewKeymanager(ctx, w, cfg, skipMnemonicConfirm) if err != nil { return nil, errors.Wrap(err, "could not initialize keymanager") } @@ -222,12 +223,12 @@ func (w *Wallet) ExistingKeyManager( // reads the config file and initializes the keymanager that way. Otherwise, // writes a new configuration file to the wallet and returns the initialized // keymanager for use. -func (w *Wallet) CreateKeymanager(ctx context.Context) (v2keymanager.IKeymanager, error) { +func (w *Wallet) CreateKeymanager(ctx context.Context, skipMnemonicConfirm bool) (v2keymanager.IKeymanager, error) { var keymanager v2keymanager.IKeymanager var err error switch w.KeymanagerKind() { case v2keymanager.Direct: - keymanager, err = direct.NewKeymanager(ctx, w, direct.DefaultConfig()) + keymanager, err = direct.NewKeymanager(ctx, w, direct.DefaultConfig(), skipMnemonicConfirm) if err != nil { return nil, errors.Wrap(err, "could not read keymanager") } diff --git a/validator/flags/flags.go b/validator/flags/flags.go index 8bc492dd66..b12faa4ca5 100644 --- a/validator/flags/flags.go +++ b/validator/flags/flags.go @@ -126,6 +126,16 @@ var ( Usage: "Path to a directory on-disk where wallet passwords are stored", Value: DefaultValidatorDir(), } + // PasswordFileFlag is used to enter a file to get a password for new account creation, non-interactively. + PasswordFileFlag = &cli.StringFlag{ + Name: "password-file", + Usage: "File to retrieve password for writing to the password dir when making a new account", + } + // SkipMnemonicConfirmFlag is used to skip the withdrawal key mnemonic phrase prompt confirmation. + SkipMnemonicConfirmFlag = &cli.BoolFlag{ + Name: "skip-mnemonic-confirm", + Usage: "Skip the withdrawal key mnemonic phrase prompt confirmation", + } // ShowDepositDataFlag for accounts-v2. ShowDepositDataFlag = &cli.BoolFlag{ Name: "show-deposit-data", diff --git a/validator/keymanager/v2/direct/direct.go b/validator/keymanager/v2/direct/direct.go index 41f7f4ed28..800097cebc 100644 --- a/validator/keymanager/v2/direct/direct.go +++ b/validator/keymanager/v2/direct/direct.go @@ -85,12 +85,14 @@ func DefaultConfig() *Config { } // NewKeymanager instantiates a new direct keymanager from configuration options. -func NewKeymanager(ctx context.Context, wallet Wallet, cfg *Config) (*Keymanager, error) { +func NewKeymanager(ctx context.Context, wallet Wallet, cfg *Config, skipMnemonicConfirm bool) (*Keymanager, error) { k := &Keymanager{ - wallet: wallet, - cfg: cfg, - mnemonicGenerator: &EnglishMnemonicGenerator{}, - keysCache: make(map[[48]byte]bls.SecretKey), + wallet: wallet, + cfg: cfg, + mnemonicGenerator: &EnglishMnemonicGenerator{ + skipMnemonicConfirm: skipMnemonicConfirm, + }, + keysCache: make(map[[48]byte]bls.SecretKey), } // If the wallet has the capability of unlocking accounts using // passphrases, then we initialize a cache of public key -> secret keys diff --git a/validator/keymanager/v2/direct/mnemonic.go b/validator/keymanager/v2/direct/mnemonic.go index 87484095b7..3a17578693 100644 --- a/validator/keymanager/v2/direct/mnemonic.go +++ b/validator/keymanager/v2/direct/mnemonic.go @@ -20,7 +20,9 @@ type SeedPhraseFactory interface { // EnglishMnemonicGenerator implements methods for creating // mnemonic seed phrases in english using a given // source of entropy such as a private key. -type EnglishMnemonicGenerator struct{} +type EnglishMnemonicGenerator struct { + skipMnemonicConfirm bool +} // Generate a mnemonic seed phrase in english using a source of // entropy given as raw bytes. @@ -42,6 +44,9 @@ func (m *EnglishMnemonicGenerator) ConfirmAcknowledgement(phrase string) error { =================================================================== `, phrase) + 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)", diff --git a/validator/node/node.go b/validator/node/node.go index 8e254a0b82..8473b7182c 100644 --- a/validator/node/node.go +++ b/validator/node/node.go @@ -106,7 +106,7 @@ func NewValidatorClient(cliCtx *cli.Context) (*ValidatorClient, error) { if err != nil { log.Fatalf("Could not open wallet: %v", err) } - keyManagerV2, err = wallet.ExistingKeyManager(context.Background()) + keyManagerV2, err = wallet.ExistingKeyManager(context.Background(), false /* skipMnemonicConfirm */) if err != nil { log.Fatalf("Could not read existing keymanager for wallet: %v", err) }