diff --git a/beacon-chain/forkchoice/protoarray/nodes_test.go b/beacon-chain/forkchoice/protoarray/nodes_test.go index ab9aeb7e072..19ec552962a 100644 --- a/beacon-chain/forkchoice/protoarray/nodes_test.go +++ b/beacon-chain/forkchoice/protoarray/nodes_test.go @@ -374,10 +374,10 @@ func TestStore_HasParent(t *testing.T) { want bool }{ {r: [32]byte{'a'}, want: false}, - {m: map[[32]byte]uint64{[32]byte{'a'}: 0}, r: [32]byte{'a'}, want: false}, - {m: map[[32]byte]uint64{[32]byte{'a'}: 0}, r: [32]byte{'a'}, + {m: map[[32]byte]uint64{{'a'}: 0}, r: [32]byte{'a'}, want: false}, + {m: map[[32]byte]uint64{{'a'}: 0}, r: [32]byte{'a'}, n: []*Node{{Parent: NonExistentNode}}, want: false}, - {m: map[[32]byte]uint64{[32]byte{'a'}: 0}, + {m: map[[32]byte]uint64{{'a'}: 0}, n: []*Node{{Parent: 0}}, r: [32]byte{'a'}, want: true}, } diff --git a/validator/accounts/v2/BUILD.bazel b/validator/accounts/v2/BUILD.bazel index 72b0d449cd5..6ea1436644b 100644 --- a/validator/accounts/v2/BUILD.bazel +++ b/validator/accounts/v2/BUILD.bazel @@ -23,6 +23,7 @@ go_library( "//validator:__subpackages__", ], deps = [ + "//shared/bytesutil:go_default_library", "//shared/featureconfig:go_default_library", "//shared/params:go_default_library", "//shared/petnames:go_default_library", @@ -73,7 +74,9 @@ go_test( "//validator/keymanager/v2/remote:go_default_library", "@com_github_dustin_go_humanize//:go_default_library", "@com_github_google_uuid//:go_default_library", + "@com_github_pkg_errors//:go_default_library", "@com_github_sirupsen_logrus//:go_default_library", + "@com_github_sirupsen_logrus//hooks/test:go_default_library", "@com_github_urfave_cli_v2//:go_default_library", "@com_github_wealdtech_go_eth2_wallet_encryptor_keystorev4//:go_default_library", ], diff --git a/validator/accounts/v2/accounts_create.go b/validator/accounts/v2/accounts_create.go index 04431d7792f..1f2d7a5f0bc 100644 --- a/validator/accounts/v2/accounts_create.go +++ b/validator/accounts/v2/accounts_create.go @@ -20,25 +20,10 @@ var log = logrus.WithField("prefix", "accounts-v2") // a wallet from the user's specified path. func CreateAccount(cliCtx *cli.Context) error { ctx := context.Background() - walletDir, err := inputDirectory(cliCtx, walletDirPromptText, flags.WalletDirFlag) - if err != nil { - return errors.Wrapf(err, "Could not retrieve input directory") - } - ok, err := hasDir(walletDir) + wallet, err := createOrOpenWallet(cliCtx, CreateWallet) if err != nil { return err } - // Create a new wallet if no directory exists. - if !ok { - err = CreateWallet(cliCtx) - if err != nil { - return errors.Wrapf(err, "Could not create wallet") - } - } - wallet, err := OpenWallet(cliCtx) - if err != nil { - return errors.Wrap(err, "could not open wallet") - } skipMnemonicConfirm := cliCtx.Bool(flags.SkipMnemonicConfirmFlag.Name) keymanager, err := wallet.InitializeKeymanager(ctx, skipMnemonicConfirm) if err != nil { @@ -52,7 +37,7 @@ func CreateAccount(cliCtx *cli.Context) error { if !ok { return errors.New("not a direct keymanager") } - password, err := inputPassword(cliCtx, newAccountPasswordPromptText, confirmPass) + password, err := inputPassword(cliCtx, flags.AccountPasswordFileFlag, newAccountPasswordPromptText, confirmPass) if err != nil { return errors.Wrap(err, "could not input new account password") } diff --git a/validator/accounts/v2/accounts_create_test.go b/validator/accounts/v2/accounts_create_test.go index b1a74e63d71..9cffb2af090 100644 --- a/validator/accounts/v2/accounts_create_test.go +++ b/validator/accounts/v2/accounts_create_test.go @@ -14,15 +14,17 @@ func TestCreateAccount_Derived(t *testing.T) { walletDir, passwordsDir, passwordFile := setupWalletAndPasswordsDir(t) numAccounts := int64(5) cliCtx := setupWalletCtx(t, &testWalletConfig{ - walletDir: walletDir, - passwordsDir: passwordsDir, - passwordFile: passwordFile, - keymanagerKind: v2keymanager.Derived, - numAccounts: numAccounts, + walletDir: walletDir, + passwordsDir: passwordsDir, + walletPasswordFile: passwordFile, + accountPasswordFile: passwordFile, + keymanagerKind: v2keymanager.Derived, + numAccounts: numAccounts, }) // We attempt to create the wallet. - require.NoError(t, CreateWallet(cliCtx)) + _, err := CreateWallet(cliCtx) + require.NoError(t, err) // We attempt to open the newly created wallet. ctx := context.Background() diff --git a/validator/accounts/v2/accounts_export.go b/validator/accounts/v2/accounts_export.go index 21bc3005066..f89269d9aaa 100644 --- a/validator/accounts/v2/accounts_export.go +++ b/validator/accounts/v2/accounts_export.go @@ -28,9 +28,10 @@ func ExportAccount(cliCtx *cli.Context) error { if err != nil { return errors.Wrap(err, "could not parse output directory") } - wallet, err := OpenWallet(cliCtx) - if err != nil { + if errors.Is(err, ErrNoWalletFound) { + return errors.Wrap(err, "nothing to export, no wallet found") + } else if err != nil { return errors.Wrap(err, "could not open wallet") } keymanager, err := wallet.InitializeKeymanager(context.Background(), true /* skip mnemonic confirm */) diff --git a/validator/accounts/v2/accounts_export_test.go b/validator/accounts/v2/accounts_export_test.go index 80c1355cc36..29058a52738 100644 --- a/validator/accounts/v2/accounts_export_test.go +++ b/validator/accounts/v2/accounts_export_test.go @@ -36,10 +36,12 @@ func TestZipAndUnzip(t *testing.T) { require.NoError(t, err) require.NoError(t, wallet.SaveWallet()) ctx := context.Background() + keymanagerCfg := direct.DefaultConfig() + keymanagerCfg.AccountPasswordsDirectory = passwordsDir keymanager, err := direct.NewKeymanager( ctx, wallet, - direct.DefaultConfig(), + keymanagerCfg, ) require.NoError(t, err) _, err = keymanager.CreateAccount(ctx, password) @@ -80,6 +82,7 @@ func TestExport_Noninteractive(t *testing.T) { require.NoError(t, wallet.SaveWallet()) ctx := context.Background() keymanagerCfg := direct.DefaultConfig() + keymanagerCfg.AccountPasswordsDirectory = passwordsDir encodedCfg, err := direct.MarshalConfigFile(ctx, keymanagerCfg) require.NoError(t, err) require.NoError(t, wallet.WriteKeymanagerConfigToDisk(ctx, encodedCfg)) diff --git a/validator/accounts/v2/accounts_import.go b/validator/accounts/v2/accounts_import.go index 0ce9afdfbaf..aff0e2a2686 100644 --- a/validator/accounts/v2/accounts_import.go +++ b/validator/accounts/v2/accounts_import.go @@ -23,53 +23,41 @@ import ( // ImportAccount uses the archived account made from ExportAccount to import an account and // asks the users for account passwords. func ImportAccount(cliCtx *cli.Context) error { - walletDir, err := inputDirectory(cliCtx, walletDirPromptText, flags.WalletDirFlag) - if err != nil && !errors.Is(err, ErrNoWalletFound) { - return errors.Wrap(err, "could not parse wallet directory") - } - // Check if the user has a wallet at the specified path. If so, only let them continue if it is a non-HD wallet. - walletExists, err := hasDir(walletDir) - if err != nil { - return errors.Wrap(err, "could not check if wallet exists") - } - if walletExists { - keymanagerKind, err := readKeymanagerKindFromWalletPath(walletDir) - if err != nil { - return errors.Wrap(err, "could not read keymanager kind for existing wallet") + ctx := context.Background() + wallet, err := createOrOpenWallet(cliCtx, func(cliCtx *cli.Context) (*Wallet, error) { + w, err := NewWallet(cliCtx, v2keymanager.Direct) + if err != nil && !errors.Is(err, ErrWalletExists) { + return nil, errors.Wrap(err, "could not create new wallet") } - if keymanagerKind != v2keymanager.Direct { - return fmt.Errorf( - "importing non-HD accounts into a non-direct wallet is not allowed, given wallet path contains a %s wallet", - keymanagerKind.String(), - ) + if err = createDirectKeymanagerWallet(cliCtx, w); err != nil { + return nil, errors.Wrap(err, "could not initialize wallet") } - } - passwordsDir, err := inputDirectory(cliCtx, passwordsDirPromptText, flags.WalletPasswordsDirFlag) + log.WithField("wallet-path", w.walletDir).Info( + "Successfully created new wallet", + ) + return w, err + }) if err != nil { - return err + return errors.Wrap(err, "could not initialize wallet") + } + if wallet.KeymanagerKind() != v2keymanager.Direct { + return errors.New( + "only non-HD wallets can import accounts, try creating a new wallet with wallet-v2 create", + ) } keysDir, err := inputDirectory(cliCtx, importKeysDirPromptText, flags.KeysDirFlag) if err != nil { return errors.Wrap(err, "could not parse keys directory") } - - accountsPath := filepath.Join(walletDir, v2keymanager.Direct.String()) - if err := os.MkdirAll(accountsPath, DirectoryPermissions); err != nil { - return errors.Wrap(err, "could not create wallet directory") - } - if err := os.MkdirAll(passwordsDir, DirectoryPermissions); err != nil { - return errors.Wrap(err, "could not create passwords directory") - } - - wallet := &Wallet{ - accountsPath: accountsPath, - passwordsDir: passwordsDir, - keymanagerKind: v2keymanager.Direct, + if err := wallet.SaveWallet(); err != nil { + return errors.Wrap(err, "could not save wallet") } - var accountsImported []string - ctx := context.Background() + var pubKeysImported [][]byte if err := filepath.Walk(keysDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } if info.IsDir() { return nil } @@ -90,11 +78,12 @@ func ImportAccount(cliCtx *cli.Context) error { return nil } - accountName, err := wallet.importKeystore(ctx, path) + accountName, pubKey, err := wallet.importKeystore(ctx, path) if err != nil { return errors.Wrap(err, "could not import keystore") } accountsImported = append(accountsImported, accountName) + pubKeysImported = append(pubKeysImported, pubKey) return nil }); err != nil { return errors.Wrap(err, "could not walk files") @@ -102,8 +91,8 @@ func ImportAccount(cliCtx *cli.Context) error { au := aurora.NewAurora(true) fmt.Printf("Importing accounts: %s\n", au.BrightGreen(strings.Join(accountsImported, ", ")).Bold()) - for _, accountName := range accountsImported { - if err := wallet.enterPasswordForAccount(cliCtx, accountName); err != nil { + for i, accountName := range accountsImported { + if err := wallet.enterPasswordForAccount(cliCtx, accountName, pubKeysImported[i]); err != nil { return errors.Wrap(err, "could not verify password for keystore") } } @@ -123,25 +112,25 @@ func ImportAccount(cliCtx *cli.Context) error { return nil } -func (w *Wallet) importKeystore(ctx context.Context, keystoreFilePath string) (string, error) { +func (w *Wallet) importKeystore(ctx context.Context, keystoreFilePath string) (string, []byte, error) { keystoreBytes, err := ioutil.ReadFile(keystoreFilePath) if err != nil { - return "", errors.Wrap(err, "could not read keystore file") + return "", nil, errors.Wrap(err, "could not read keystore file") } keystoreFile := &v2keymanager.Keystore{} if err := json.Unmarshal(keystoreBytes, keystoreFile); err != nil { - return "", errors.Wrap(err, "could not decode keystore json") + return "", nil, errors.Wrap(err, "could not decode keystore json") } pubKeyBytes, err := hex.DecodeString(keystoreFile.Pubkey) if err != nil { - return "", errors.Wrap(err, "could not decode public key string in keystore") + return "", nil, errors.Wrap(err, "could not decode public key string in keystore") } accountName := petnames.DeterministicName(pubKeyBytes, "-") keystoreFileName := filepath.Base(keystoreFilePath) if err := w.WriteFileAtPath(ctx, accountName, keystoreFileName, keystoreBytes); err != nil { - return "", errors.Wrap(err, "could not write keystore to account dir") + return "", nil, errors.Wrap(err, "could not write keystore to account dir") } - return accountName, nil + return accountName, pubKeyBytes, nil } func logAccountsImported(ctx context.Context, wallet *Wallet, keymanager *direct.Keymanager, accountNames []string) error { diff --git a/validator/accounts/v2/accounts_import_test.go b/validator/accounts/v2/accounts_import_test.go index 89469f5b077..448ffa84f25 100644 --- a/validator/accounts/v2/accounts_import_test.go +++ b/validator/accounts/v2/accounts_import_test.go @@ -34,17 +34,19 @@ func TestImport_Noninteractive(t *testing.T) { }) cliCtx := setupWalletCtx(t, &testWalletConfig{ - walletDir: walletDir, - passwordsDir: passwordsDir, - keysDir: keysDir, - keymanagerKind: v2keymanager.Direct, - passwordFile: passwordFilePath, + walletDir: walletDir, + passwordsDir: passwordsDir, + keysDir: keysDir, + keymanagerKind: v2keymanager.Direct, + walletPasswordFile: passwordFilePath, + accountPasswordFile: passwordFilePath, }) wallet, err := NewWallet(cliCtx, v2keymanager.Direct) require.NoError(t, err) require.NoError(t, wallet.SaveWallet()) ctx := context.Background() keymanagerCfg := direct.DefaultConfig() + keymanagerCfg.AccountPasswordsDirectory = passwordsDir encodedCfg, err := direct.MarshalConfigFile(ctx, keymanagerCfg) require.NoError(t, err) require.NoError(t, wallet.WriteKeymanagerConfigToDisk(ctx, encodedCfg)) diff --git a/validator/accounts/v2/accounts_list.go b/validator/accounts/v2/accounts_list.go index 1e862e6a238..ea074121a02 100644 --- a/validator/accounts/v2/accounts_list.go +++ b/validator/accounts/v2/accounts_list.go @@ -24,8 +24,10 @@ func ListAccounts(cliCtx *cli.Context) error { // Read the wallet from the specified path. ctx := context.Background() wallet, err := OpenWallet(cliCtx) - if err != nil { - return errors.Wrapf(err, "could not read wallet at specified path %s", wallet.AccountsDir()) + if errors.Is(err, ErrNoWalletFound) { + return errors.Wrap(err, "no wallet found at path, create a new wallet with wallet-v2 create") + } else if err != nil { + return errors.Wrap(err, "could not open wallet") } keymanager, err := wallet.InitializeKeymanager(ctx, true /* skip mnemonic confirm */) if err != nil { @@ -103,7 +105,7 @@ func listDirectKeymanagerAccounts( if err != nil { return errors.Wrap(err, "could not get timestamp from keystore file name") } - fmt.Printf("%s | Created %s\n", au.BrightGreen(accountNames[i]).Bold(), humanize.Time(unixTimestamp)) + fmt.Printf("%s | %s | Created %s\n", au.BrightBlue(fmt.Sprintf("Account %d", i)).Bold(), au.BrightGreen(accountNames[i]).Bold(), humanize.Time(unixTimestamp)) fmt.Printf("%s %#x\n", au.BrightMagenta("[validating public key]").Bold(), pubKeys[i]) if !showDepositData { continue diff --git a/validator/accounts/v2/accounts_list_test.go b/validator/accounts/v2/accounts_list_test.go index 557e2fc77e4..0c0c135a2ac 100644 --- a/validator/accounts/v2/accounts_list_test.go +++ b/validator/accounts/v2/accounts_list_test.go @@ -127,10 +127,10 @@ func TestListAccounts_DirectKeymanager(t *testing.T) { func TestListAccounts_DerivedKeymanager(t *testing.T) { walletDir, passwordsDir, passwordFilePath := setupWalletAndPasswordsDir(t) cliCtx := setupWalletCtx(t, &testWalletConfig{ - walletDir: walletDir, - passwordsDir: passwordsDir, - keymanagerKind: v2keymanager.Derived, - passwordFile: passwordFilePath, + walletDir: walletDir, + passwordsDir: passwordsDir, + keymanagerKind: v2keymanager.Derived, + walletPasswordFile: passwordFilePath, }) wallet, err := NewWallet(cliCtx, v2keymanager.Derived) require.NoError(t, err) diff --git a/validator/accounts/v2/cmd_accounts.go b/validator/accounts/v2/cmd_accounts.go index 217598c89d1..05b1d1ee2e8 100644 --- a/validator/accounts/v2/cmd_accounts.go +++ b/validator/accounts/v2/cmd_accounts.go @@ -20,8 +20,8 @@ specified input, capable of creating a direct, derived, or remote wallet. this command outputs a deposit data string which is required to become a validator in eth2.`, Flags: []cli.Flag{ flags.WalletDirFlag, - flags.WalletPasswordsDirFlag, - flags.PasswordFileFlag, + flags.WalletPasswordFileFlag, + flags.AccountPasswordFileFlag, flags.NumAccountsFlag, featureconfig.AltonaTestnet, featureconfig.MedallaTestnet, @@ -38,8 +38,7 @@ this command outputs a deposit data string which is required to become a validat Description: "Lists all validator accounts in a user's wallet directory", Flags: []cli.Flag{ flags.WalletDirFlag, - flags.WalletPasswordsDirFlag, - flags.PasswordFileFlag, + flags.WalletPasswordFileFlag, flags.ShowDepositDataFlag, featureconfig.AltonaTestnet, featureconfig.MedallaTestnet, @@ -56,7 +55,6 @@ this command outputs a deposit data string which is required to become a validat Description: `exports the account of a given directory into a zip of the provided output path. This zip can be used to later import the account to another directory`, Flags: []cli.Flag{ flags.WalletDirFlag, - flags.WalletPasswordsDirFlag, flags.BackupDirFlag, flags.AccountsFlag, featureconfig.AltonaTestnet, @@ -76,7 +74,7 @@ this command outputs a deposit data string which is required to become a validat flags.WalletDirFlag, flags.WalletPasswordsDirFlag, flags.KeysDirFlag, - flags.PasswordFileFlag, + flags.WalletPasswordFileFlag, featureconfig.AltonaTestnet, featureconfig.MedallaTestnet, }, diff --git a/validator/accounts/v2/cmd_wallet.go b/validator/accounts/v2/cmd_wallet.go index 27c22e1af4a..4a7bd855a85 100644 --- a/validator/accounts/v2/cmd_wallet.go +++ b/validator/accounts/v2/cmd_wallet.go @@ -24,12 +24,12 @@ var WalletCommands = &cli.Command{ flags.RemoteSignerCertPathFlag, flags.RemoteSignerKeyPathFlag, flags.RemoteSignerCACertPathFlag, - flags.PasswordFileFlag, + flags.WalletPasswordFileFlag, featureconfig.AltonaTestnet, featureconfig.MedallaTestnet, }, Action: func(cliCtx *cli.Context) error { - if err := CreateWallet(cliCtx); err != nil { + if _, err := CreateWallet(cliCtx); err != nil { log.Fatalf("Could not create a wallet: %v", err) } return nil @@ -44,6 +44,7 @@ var WalletCommands = &cli.Command{ flags.RemoteSignerCertPathFlag, flags.RemoteSignerKeyPathFlag, flags.RemoteSignerCACertPathFlag, + flags.WalletPasswordsDirFlag, featureconfig.AltonaTestnet, featureconfig.MedallaTestnet, }, @@ -61,7 +62,7 @@ var WalletCommands = &cli.Command{ flags.WalletDirFlag, flags.WalletPasswordsDirFlag, flags.MnemonicFileFlag, - flags.PasswordFileFlag, + flags.WalletPasswordFileFlag, flags.NumAccountsFlag, featureconfig.AltonaTestnet, featureconfig.MedallaTestnet, diff --git a/validator/accounts/v2/prompt.go b/validator/accounts/v2/prompt.go index a7100da88ce..895cf12d70d 100644 --- a/validator/accounts/v2/prompt.go +++ b/validator/accounts/v2/prompt.go @@ -3,7 +3,6 @@ package v2 import ( "fmt" "io/ioutil" - "path/filepath" "strings" "unicode" @@ -17,16 +16,15 @@ import ( ) const ( - importDirPromptText = "Enter the file location of the exported wallet zip to import" importKeysDirPromptText = "Enter the directory where your keystores to import are located" - exportDirPromptText = "Enter a file location to write the exported wallet to" + exportDirPromptText = "Enter a file location to write the exported account(s) to" walletDirPromptText = "Enter a wallet directory" passwordsDirPromptText = "Directory where passwords will be stored" newWalletPasswordPromptText = "New wallet password" confirmPasswordPromptText = "Confirm password" walletPasswordPromptText = "Wallet password" newAccountPasswordPromptText = "New account password" - passwordForAccountPromptText = "Enter password for account %s" + passwordForAccountPromptText = "Enter password for account with public key %#x" ) type passwordConfirm int @@ -44,7 +42,7 @@ const ( ) func inputDirectory(cliCtx *cli.Context, promptText string, flag *cli.StringFlag) (string, error) { - directory := appendDirName(cliCtx.String(flag.Name), flag.Name) + directory := cliCtx.String(flag.Name) if cliCtx.IsSet(flag.Name) { return directory, nil } @@ -84,17 +82,7 @@ func inputDirectory(cliCtx *cli.Context, promptText string, flag *cli.StringFlag if inputtedDir == prompt.Default { return directory, nil } - return appendDirName(inputtedDir, flag.Name), nil -} - -func appendDirName(inputtedDir string, flagName string) string { - switch flagName { - case flags.WalletDirFlag.Name: - inputtedDir = filepath.Join(inputtedDir, WalletDefaultDirName) - case flags.WalletPasswordsDirFlag.Name: - inputtedDir = filepath.Join(inputtedDir, PasswordsDefaultDirName) - } - return inputtedDir + return inputtedDir, nil } func validateDirectoryPath(input string) error { @@ -104,9 +92,14 @@ func validateDirectoryPath(input string) error { return nil } -func inputPassword(cliCtx *cli.Context, promptText string, confirmPassword passwordConfirm) (string, error) { - if cliCtx.IsSet(flags.PasswordFileFlag.Name) { - passwordFilePath := cliCtx.String(flags.PasswordFileFlag.Name) +func inputPassword( + cliCtx *cli.Context, + passwordFileFlag *cli.StringFlag, + promptText string, + confirmPassword passwordConfirm, +) (string, error) { + if cliCtx.IsSet(passwordFileFlag.Name) { + passwordFilePath := cliCtx.String(passwordFileFlag.Name) data, err := ioutil.ReadFile(passwordFilePath) if err != nil { return "", errors.Wrap(err, "could not read password file") @@ -152,9 +145,9 @@ func inputPassword(cliCtx *cli.Context, promptText string, confirmPassword passw return strings.TrimRight(walletPassword, "\r\n"), nil } -func inputWeakPassword(cliCtx *cli.Context, promptText string) (string, error) { - if cliCtx.IsSet(flags.PasswordFileFlag.Name) { - passwordFilePath := cliCtx.String(flags.PasswordFileFlag.Name) +func inputWeakPassword(cliCtx *cli.Context, passwordFileFlag *cli.StringFlag, promptText string) (string, error) { + if cliCtx.IsSet(passwordFileFlag.Name) { + passwordFilePath := cliCtx.String(passwordFileFlag.Name) data, err := ioutil.ReadFile(passwordFilePath) if err != nil { return "", errors.Wrap(err, "could not read password file") @@ -227,71 +220,63 @@ func inputRemoteKeymanagerConfig(cliCtx *cli.Context) (*remote.Config, error) { crt := cliCtx.String(flags.RemoteSignerCertPathFlag.Name) key := cliCtx.String(flags.RemoteSignerKeyPathFlag.Name) ca := cliCtx.String(flags.RemoteSignerCACertPathFlag.Name) - - if addr != "" && crt != "" && key != "" && ca != "" { - if !(isValidUnicode(addr) && isValidUnicode(crt) && isValidUnicode(key) && isValidUnicode(ca)) { - return nil, errors.New("flag inputs contain non-unicode characters") - } - newCfg := &remote.Config{ - RemoteCertificate: &remote.CertificateConfig{ - ClientCertPath: strings.TrimRight(crt, "\r\n"), - ClientKeyPath: strings.TrimRight(key, "\r\n"), - CACertPath: strings.TrimRight(ca, "\r\n"), + 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 }, - RemoteAddr: strings.TrimRight(addr, "\r\n"), } - log.Infof("New configuration") - fmt.Printf("%s\n", newCfg) - return newCfg, nil - } - log.Infof("Input desired configuration") - 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 - }, - } - remoteAddr, err := prompt.Run() - if err != nil { - return nil, err - } - prompt = promptui.Prompt{ - Label: "Path to TLS crt (such as /path/to/client.crt)", - Validate: validateCertPath, - } - clientCrtPath, err := prompt.Run() - if err != nil { - return nil, err - } - prompt = promptui.Prompt{ - Label: "Path to TLS key (such as /path/to/client.key)", - Validate: validateCertPath, + addr, err = prompt.Run() + if err != nil { + return nil, err + } } - clientKeyPath, err := prompt.Run() - 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() + if err != nil { + return nil, err + } } - prompt = promptui.Prompt{ - Label: "(Optional) Path to certificate authority (CA) crt (such as /path/to/ca.crt)", - Validate: validateCACertPath, + if key == "" { + prompt := promptui.Prompt{ + Label: "Path to TLS key (such as /path/to/client.key)", + Validate: validateCertPath, + } + key, err = prompt.Run() + if err != nil { + return nil, err + } } - caCrtPath, err := prompt.Run() - 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() + if err != nil { + return nil, err + } } newCfg := &remote.Config{ RemoteCertificate: &remote.CertificateConfig{ - ClientCertPath: strings.TrimRight(clientCrtPath, "\r\n"), - ClientKeyPath: strings.TrimRight(clientKeyPath, "\r\n"), - CACertPath: strings.TrimRight(caCrtPath, "\r\n"), + ClientCertPath: strings.TrimRight(crt, "\r\n"), + ClientKeyPath: strings.TrimRight(key, "\r\n"), + CACertPath: strings.TrimRight(ca, "\r\n"), }, - RemoteAddr: strings.TrimRight(remoteAddr, "\r\n"), + RemoteAddr: strings.TrimRight(addr, "\r\n"), } fmt.Printf("%s\n", newCfg) return newCfg, nil @@ -310,16 +295,6 @@ func validateCertPath(input string) error { return nil } -func validateCACertPath(input string) error { - if input != "" && !fileExists(input) { - return fmt.Errorf("no crt found at path: %s", input) - } - if !isValidUnicode(input) { - return errors.New("not valid unicode") - } - return nil -} - func formatPromptError(err error) error { switch err { case promptui.ErrAbort: diff --git a/validator/accounts/v2/wallet.go b/validator/accounts/v2/wallet.go index 1dd65b84882..150b936f233 100644 --- a/validator/accounts/v2/wallet.go +++ b/validator/accounts/v2/wallet.go @@ -15,6 +15,7 @@ import ( petname "github.com/dustinkirkland/golang-petname" "github.com/logrusorgru/aurora" "github.com/pkg/errors" + "github.com/prysmaticlabs/prysm/shared/bytesutil" "github.com/prysmaticlabs/prysm/validator/flags" v2keymanager "github.com/prysmaticlabs/prysm/validator/keymanager/v2" "github.com/prysmaticlabs/prysm/validator/keymanager/v2/derived" @@ -25,23 +26,7 @@ import ( keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4" ) -var ( - // ErrNoWalletFound signifies there was no wallet directory found on-disk. - ErrNoWalletFound = errors.New( - "no wallet found at path, please create a new wallet using `./prysm.sh validator wallet-v2 create`", - ) - keymanagerKindSelections = map[v2keymanager.Kind]string{ - v2keymanager.Derived: "HD Wallet (Recommended)", - v2keymanager.Direct: "Non-HD Wallet (Most Basic)", - v2keymanager.Remote: "Remote Signing Wallet (Advanced)", - } -) - const ( - // WalletDefaultDirName for accounts-v2. - WalletDefaultDirName = ".prysm-wallet-v2" - // PasswordsDefaultDirName where account passwords are stored. - PasswordsDefaultDirName = ".prysm-wallet-v2-passwords" // KeymanagerConfigFileName for the keymanager used by the wallet: direct, derived, or remote. KeymanagerConfigFileName = "keymanageropts.json" // DirectoryPermissions for directories created under the wallet path. @@ -49,10 +34,19 @@ const ( ) var ( + // ErrNoWalletFound signifies there was no wallet directory found on-disk. + ErrNoWalletFound = errors.New( + "no wallet found at path, please create a new wallet using `./prysm.sh validator wallet-v2 create`", + ) // ErrWalletExists is an error returned when a wallet already exists in the path provided. ErrWalletExists = errors.New("you already have a wallet at the specified path. You can " + "edit your wallet configuration by running ./prysm.sh validator wallet-v2 edit-config", ) + keymanagerKindSelections = map[v2keymanager.Kind]string{ + v2keymanager.Derived: "HD Wallet (Recommended)", + v2keymanager.Direct: "Non-HD Wallet (Most Basic)", + v2keymanager.Remote: "Remote Signing Wallet (Advanced)", + } ) // Wallet is a primitive in Prysm's v2 account management which @@ -60,6 +54,7 @@ var ( // and providing secure access to eth2 secrets depending on an // associated keymanager (either direct, derived, or remote signing enabled). type Wallet struct { + walletDir string accountsPath string passwordsDir string keymanagerKind v2keymanager.Kind @@ -77,9 +72,6 @@ func NewWallet( keymanagerKind v2keymanager.Kind, ) (*Wallet, error) { walletDir, err := inputDirectory(cliCtx, walletDirPromptText, flags.WalletDirFlag) - if err != nil && !errors.Is(err, ErrNoWalletFound) { - return nil, errors.Wrap(err, "could not parse wallet directory") - } // Check if the user has a wallet at the specified path. // If a user does not have a wallet, we instantiate one // based on specified options. @@ -94,9 +86,15 @@ func NewWallet( w := &Wallet{ accountsPath: accountsPath, keymanagerKind: keymanagerKind, + walletDir: walletDir, } if keymanagerKind == v2keymanager.Derived { - walletPassword, err := inputPassword(cliCtx, newWalletPasswordPromptText, confirmPass) + walletPassword, err := inputPassword( + cliCtx, + flags.WalletPasswordFileFlag, + newWalletPasswordPromptText, + confirmPass, + ) if err != nil { return nil, errors.Wrap(err, "could not get password") } @@ -121,11 +119,16 @@ func NewWallet( func OpenWallet(cliCtx *cli.Context) (*Wallet, error) { // Read a wallet's directory from user input. walletDir, err := inputDirectory(cliCtx, walletDirPromptText, flags.WalletDirFlag) - if errors.Is(err, ErrNoWalletFound) { - return nil, errors.New("no wallet found, create a new one with ./prysm.sh validator wallet-v2 create") - } else if err != nil { + if err != nil { return nil, err } + ok, err := hasDir(walletDir) + if err != nil { + return nil, errors.Wrap(err, "could not parse wallet directory") + } + if !ok { + return nil, ErrNoWalletFound + } keymanagerKind, err := readKeymanagerKindFromWalletPath(walletDir) if err != nil { return nil, errors.Wrap(err, "could not read keymanager kind for wallet") @@ -136,19 +139,31 @@ func OpenWallet(cliCtx *cli.Context) (*Wallet, error) { keymanagerKind: keymanagerKind, } if keymanagerKind == v2keymanager.Derived { - walletPassword, err := inputPassword(cliCtx, walletPasswordPromptText, noConfirmPass) + walletPassword, err := inputPassword( + cliCtx, + flags.WalletPasswordFileFlag, + walletPasswordPromptText, + noConfirmPass, + ) if err != nil { return nil, err } w.walletPassword = walletPassword } if keymanagerKind == v2keymanager.Direct { - passwordsDir, err := inputDirectory(cliCtx, passwordsDirPromptText, flags.WalletPasswordsDirFlag) + keymanagerCfg, err := w.ReadKeymanagerConfigFromDisk(context.Background()) if err != nil { return nil, err } - w.passwordsDir = passwordsDir + directCfg, err := direct.UnmarshalConfigFile(keymanagerCfg) + if err != nil { + return nil, err + } + w.passwordsDir = directCfg.AccountPasswordsDirectory + au := aurora.NewAurora(true) + log.Infof("%s %s", au.BrightMagenta("(account passwords path)"), w.passwordsDir) } + log.Info("Successfully opened wallet") return w, nil } @@ -380,12 +395,12 @@ func (w *Wallet) ReadPasswordFromDisk(ctx context.Context, passwordFileName stri // enterPasswordForAccount checks if a user has a password specified for the new account // either from a file or from stdin. Then, it saves the password to the wallet. -func (w *Wallet) enterPasswordForAccount(cliCtx *cli.Context, accountName string) error { +func (w *Wallet) enterPasswordForAccount(cliCtx *cli.Context, accountName string, pubKey []byte) error { au := aurora.NewAurora(true) var password string var err error - if cliCtx.IsSet(flags.PasswordFileFlag.Name) { - passwordFilePath := cliCtx.String(flags.PasswordFileFlag.Name) + if cliCtx.IsSet(flags.AccountPasswordFileFlag.Name) { + passwordFilePath := cliCtx.String(flags.AccountPasswordFileFlag.Name) data, err := ioutil.ReadFile(passwordFilePath) if err != nil { return err @@ -393,7 +408,7 @@ func (w *Wallet) enterPasswordForAccount(cliCtx *cli.Context, accountName string password = string(data) err = w.checkPasswordForAccount(accountName, password) if err != nil && strings.Contains(err.Error(), "invalid checksum") { - return fmt.Errorf("invalid password entered for account %s", accountName) + return fmt.Errorf("invalid password entered for account with public key %#x", pubKey) } if err != nil { return err @@ -403,7 +418,11 @@ func (w *Wallet) enterPasswordForAccount(cliCtx *cli.Context, accountName string // Loop asking for the password until the user enters it correctly. for attemptingPassword { // Ask the user for the password to their account. - password, err = inputWeakPassword(cliCtx, fmt.Sprintf(passwordForAccountPromptText, au.BrightGreen(accountName))) + password, err = inputWeakPassword( + cliCtx, + flags.AccountPasswordFileFlag, + fmt.Sprintf(passwordForAccountPromptText, bytesutil.Trunc(pubKey)), + ) if err != nil { return errors.Wrap(err, "could not input password") } @@ -474,6 +493,27 @@ func readKeymanagerKindFromWalletPath(walletPath string) (v2keymanager.Kind, err return v2keymanager.ParseKind(list[0]) } +func createOrOpenWallet(cliCtx *cli.Context, creationFunc func(cliCtx *cli.Context) (*Wallet, error)) (*Wallet, error) { + directory := cliCtx.String(flags.WalletDirFlag.Name) + ok, err := hasDir(directory) + if err != nil { + return nil, errors.Wrapf(err, "could not check if wallet dir %s exists", directory) + } + var wallet *Wallet + if !ok { + wallet, err = creationFunc(cliCtx) + if err != nil { + return nil, errors.Wrapf(err, "Could not create wallet") + } + } else { + wallet, err = OpenWallet(cliCtx) + if err != nil { + return nil, errors.Wrap(err, "could not open wallet") + } + } + return wallet, nil +} + // Returns true if a file is not a directory and exists // at the specified path. func fileExists(filename string) bool { diff --git a/validator/accounts/v2/wallet_create.go b/validator/accounts/v2/wallet_create.go index 4058b67e241..47279a335d3 100644 --- a/validator/accounts/v2/wallet_create.go +++ b/validator/accounts/v2/wallet_create.go @@ -15,53 +15,55 @@ import ( // CreateWallet from user input with a desired keymanager. If a // wallet already exists in the path, it suggests the user alternatives // such as how to edit their existing wallet configuration. -func CreateWallet(cliCtx *cli.Context) error { +func CreateWallet(cliCtx *cli.Context) (*Wallet, error) { keymanagerKind, err := inputKeymanagerKind(cliCtx) if err != nil { - return err + return nil, err } w, err := NewWallet(cliCtx, keymanagerKind) if err != nil && !errors.Is(err, ErrWalletExists) { - return errors.Wrap(err, "could not check if wallet directory exists") + return nil, errors.Wrap(err, "could not check if wallet directory exists") } if errors.Is(err, ErrWalletExists) { - return ErrWalletExists + return nil, ErrWalletExists } switch w.KeymanagerKind() { case v2keymanager.Direct: if err = createDirectKeymanagerWallet(cliCtx, w); err != nil { - return errors.Wrap(err, "could not initialize wallet with direct keymanager") + return nil, errors.Wrap(err, "could not initialize wallet with direct keymanager") } - log.WithField("wallet-path", w.accountsPath).Infof( + log.WithField("wallet-path", w.walletDir).Info( "Successfully created wallet with on-disk keymanager configuration. " + "Make a new validator account with ./prysm.sh validator accounts-v2 create", ) case v2keymanager.Derived: if err = createDerivedKeymanagerWallet(cliCtx, w); err != nil { - return errors.Wrap(err, "could not initialize wallet with derived keymanager") + return nil, errors.Wrap(err, "could not initialize wallet with derived keymanager") } - log.WithField("wallet-path", w.accountsPath).Infof( + log.WithField("wallet-path", w.walletDir).Info( "Successfully created HD wallet and saved configuration to disk. " + "Make a new validator account with ./prysm.sh validator accounts-2 create", ) case v2keymanager.Remote: if err = createRemoteKeymanagerWallet(cliCtx, w); err != nil { - return errors.Wrap(err, "could not initialize wallet with remote keymanager") + return nil, errors.Wrap(err, "could not initialize wallet with remote keymanager") } - log.WithField("wallet-path", w.accountsPath).Infof( + log.WithField("wallet-path", w.walletDir).Info( "Successfully created wallet with remote keymanager configuration", ) default: - return errors.Wrapf(err, "keymanager type %s is not supported", w.KeymanagerKind()) + return nil, errors.Wrapf(err, "keymanager type %s is not supported", w.KeymanagerKind()) } - return nil + return w, nil } func createDirectKeymanagerWallet(cliCtx *cli.Context, wallet *Wallet) error { if err := wallet.SaveWallet(); err != nil { return errors.Wrap(err, "could not save wallet to disk") } - keymanagerConfig, err := direct.MarshalConfigFile(context.Background(), direct.DefaultConfig()) + defaultConfig := direct.DefaultConfig() + defaultConfig.AccountPasswordsDirectory = wallet.passwordsDir + keymanagerConfig, err := direct.MarshalConfigFile(context.Background(), defaultConfig) if err != nil { return errors.Wrap(err, "could not marshal keymanager config file") } diff --git a/validator/accounts/v2/wallet_create_test.go b/validator/accounts/v2/wallet_create_test.go index 576b3f88a5e..1ee60b4adb7 100644 --- a/validator/accounts/v2/wallet_create_test.go +++ b/validator/accounts/v2/wallet_create_test.go @@ -6,6 +6,7 @@ import ( "os" "testing" + "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/shared/testutil" "github.com/prysmaticlabs/prysm/shared/testutil/assert" "github.com/prysmaticlabs/prysm/shared/testutil/require" @@ -14,9 +15,43 @@ import ( "github.com/prysmaticlabs/prysm/validator/keymanager/v2/derived" "github.com/prysmaticlabs/prysm/validator/keymanager/v2/direct" "github.com/prysmaticlabs/prysm/validator/keymanager/v2/remote" + logTest "github.com/sirupsen/logrus/hooks/test" "github.com/urfave/cli/v2" ) +func TestCreateOrOpenWallet(t *testing.T) { + hook := logTest.NewGlobal() + walletDir, passwordsDir, _ := setupWalletAndPasswordsDir(t) + cliCtx := setupWalletCtx(t, &testWalletConfig{ + walletDir: walletDir, + passwordsDir: passwordsDir, + keymanagerKind: v2keymanager.Direct, + }) + createDirectWallet := func(cliCtx *cli.Context) (*Wallet, error) { + w, err := NewWallet(cliCtx, v2keymanager.Direct) + if err != nil && !errors.Is(err, ErrWalletExists) { + return nil, errors.Wrap(err, "could not create new wallet") + } + if err = createDirectKeymanagerWallet(cliCtx, w); err != nil { + return nil, errors.Wrap(err, "could not initialize wallet") + } + log.WithField("wallet-path", w.walletDir).Info( + "Successfully created new wallet", + ) + return w, err + } + createdWallet, err := createOrOpenWallet(cliCtx, createDirectWallet) + require.NoError(t, err) + testutil.AssertLogsContain(t, hook, "Successfully created new wallet") + testutil.AssertLogsDoNotContain(t, hook, "Successfully opened wallet") + + openedWallet, err := createOrOpenWallet(cliCtx, createDirectWallet) + require.NoError(t, err) + testutil.AssertLogsContain(t, hook, "Successfully opened wallet") + assert.Equal(t, createdWallet.KeymanagerKind(), openedWallet.KeymanagerKind()) + assert.Equal(t, createdWallet.AccountsDir(), openedWallet.AccountsDir()) +} + func TestCreateWallet_Direct(t *testing.T) { walletDir, passwordsDir, _ := setupWalletAndPasswordsDir(t) cliCtx := setupWalletCtx(t, &testWalletConfig{ @@ -26,7 +61,8 @@ func TestCreateWallet_Direct(t *testing.T) { }) // We attempt to create the wallet. - require.NoError(t, CreateWallet(cliCtx)) + _, err := CreateWallet(cliCtx) + require.NoError(t, err) // We attempt to open the newly created wallet. ctx := context.Background() @@ -40,20 +76,23 @@ func TestCreateWallet_Direct(t *testing.T) { assert.NoError(t, err) // We assert the created configuration was as desired. - assert.DeepEqual(t, direct.DefaultConfig(), cfg) + wantedCfg := direct.DefaultConfig() + wantedCfg.AccountPasswordsDirectory = passwordsDir + assert.DeepEqual(t, wantedCfg, cfg) } func TestCreateWallet_Derived(t *testing.T) { walletDir, passwordsDir, passwordFile := setupWalletAndPasswordsDir(t) cliCtx := setupWalletCtx(t, &testWalletConfig{ - walletDir: walletDir, - passwordsDir: passwordsDir, - passwordFile: passwordFile, - keymanagerKind: v2keymanager.Derived, + walletDir: walletDir, + passwordsDir: passwordsDir, + walletPasswordFile: passwordFile, + keymanagerKind: v2keymanager.Derived, }) // We attempt to create the wallet. - require.NoError(t, CreateWallet(cliCtx)) + _, err := CreateWallet(cliCtx) + require.NoError(t, err) // We attempt to open the newly created wallet. ctx := context.Background() @@ -101,7 +140,8 @@ func TestCreateWallet_Remote(t *testing.T) { cliCtx := cli.NewContext(&app, set, nil) // We attempt to create the wallet. - require.NoError(t, CreateWallet(cliCtx)) + _, err := CreateWallet(cliCtx) + require.NoError(t, err) // We attempt to open the newly created wallet. ctx := context.Background() diff --git a/validator/accounts/v2/wallet_edit.go b/validator/accounts/v2/wallet_edit.go index e43e50d4480..41e3a613d6f 100644 --- a/validator/accounts/v2/wallet_edit.go +++ b/validator/accounts/v2/wallet_edit.go @@ -5,7 +5,9 @@ import ( "fmt" "github.com/pkg/errors" + "github.com/prysmaticlabs/prysm/validator/flags" v2keymanager "github.com/prysmaticlabs/prysm/validator/keymanager/v2" + "github.com/prysmaticlabs/prysm/validator/keymanager/v2/direct" "github.com/prysmaticlabs/prysm/validator/keymanager/v2/remote" "github.com/urfave/cli/v2" ) @@ -21,7 +23,30 @@ func EditWalletConfiguration(cliCtx *cli.Context) error { } switch wallet.KeymanagerKind() { case v2keymanager.Direct: - return errors.New("no configuration options available to edit for direct keymanager") + enc, err := wallet.ReadKeymanagerConfigFromDisk(ctx) + if err != nil { + return errors.Wrap(err, "could not read config") + } + cfg, err := direct.UnmarshalConfigFile(enc) + if err != nil { + return errors.Wrap(err, "could not unmarshal config") + } + log.Info("Current configuration") + // Prints the current configuration to stdout. + fmt.Println(cfg) + passwordsDir, err := inputDirectory(cliCtx, passwordsDirPromptText, flags.WalletPasswordsDirFlag) + if err != nil { + return errors.Wrap(err, "could not get password directory") + } + defaultCfg := direct.DefaultConfig() + defaultCfg.AccountPasswordsDirectory = passwordsDir + encodedCfg, err := direct.MarshalConfigFile(ctx, defaultCfg) + if err != nil { + return errors.Wrap(err, "could not marshal config file") + } + if err := wallet.WriteKeymanagerConfigToDisk(ctx, encodedCfg); err != nil { + return errors.Wrap(err, "could not write config to disk") + } case v2keymanager.Derived: return errors.New("derived keymanager is not yet supported") case v2keymanager.Remote: @@ -33,8 +58,9 @@ func EditWalletConfiguration(cliCtx *cli.Context) error { if err != nil { return errors.Wrap(err, "could not unmarshal config") } - log.Infof("Current configuration") - fmt.Printf("%s\n", cfg) + log.Info("Current configuration") + // Prints the current configuration to stdout. + fmt.Println(cfg) newCfg, err := inputRemoteKeymanagerConfig(cliCtx) if err != nil { return errors.Wrap(err, "could not get keymanager config") diff --git a/validator/accounts/v2/wallet_edit_test.go b/validator/accounts/v2/wallet_edit_test.go index 60a9686be6d..5cdde2a4d50 100644 --- a/validator/accounts/v2/wallet_edit_test.go +++ b/validator/accounts/v2/wallet_edit_test.go @@ -58,9 +58,10 @@ func TestEditWalletConfiguration(t *testing.T) { assert.NoError(t, set.Set(flags.RemoteSignerCACertPathFlag.Name, wantCfg.RemoteCertificate.CACertPath)) cliCtx = cli.NewContext(&app, set, nil) - assert.NoError(t, EditWalletConfiguration(cliCtx)) + err = EditWalletConfiguration(cliCtx) + require.NoError(t, err) encoded, err := wallet.ReadKeymanagerConfigFromDisk(ctx) - assert.NoError(t, err) + require.NoError(t, err) cfg, err := remote.UnmarshalConfigFile(encoded) assert.NoError(t, err) diff --git a/validator/accounts/v2/wallet_recover_test.go b/validator/accounts/v2/wallet_recover_test.go index 15557e17c6e..28c5a83cdcc 100644 --- a/validator/accounts/v2/wallet_recover_test.go +++ b/validator/accounts/v2/wallet_recover_test.go @@ -39,13 +39,13 @@ func TestRecoverDerivedWallet(t *testing.T) { set := flag.NewFlagSet("test", 0) set.String(flags.WalletDirFlag.Name, walletDir, "") set.String(flags.WalletPasswordsDirFlag.Name, passwordsDir, "") - set.String(flags.PasswordFileFlag.Name, passwordFilePath, "") + set.String(flags.WalletPasswordFileFlag.Name, passwordFilePath, "") set.String(flags.KeymanagerKindFlag.Name, v2keymanager.Derived.String(), "") set.String(flags.MnemonicFileFlag.Name, mnemonicFilePath, "") set.Int64(flags.NumAccountsFlag.Name, numAccounts, "") assert.NoError(t, set.Set(flags.WalletDirFlag.Name, walletDir)) assert.NoError(t, set.Set(flags.WalletPasswordsDirFlag.Name, passwordsDir)) - assert.NoError(t, set.Set(flags.PasswordFileFlag.Name, passwordFilePath)) + assert.NoError(t, set.Set(flags.WalletPasswordFileFlag.Name, passwordFilePath)) assert.NoError(t, set.Set(flags.KeymanagerKindFlag.Name, v2keymanager.Derived.String())) assert.NoError(t, set.Set(flags.MnemonicFileFlag.Name, mnemonicFilePath)) assert.NoError(t, set.Set(flags.NumAccountsFlag.Name, strconv.Itoa(int(numAccounts)))) diff --git a/validator/accounts/v2/wallet_test.go b/validator/accounts/v2/wallet_test.go index 6e7cd382e1f..00d769c13dd 100644 --- a/validator/accounts/v2/wallet_test.go +++ b/validator/accounts/v2/wallet_test.go @@ -28,14 +28,15 @@ func init() { } type testWalletConfig struct { - walletDir string - passwordsDir string - exportDir string - keysDir string - accountsToExport string - passwordFile string - numAccounts int64 - keymanagerKind v2keymanager.Kind + walletDir string + passwordsDir string + exportDir string + keysDir string + accountsToExport string + walletPasswordFile string + accountPasswordFile string + numAccounts int64 + keymanagerKind v2keymanager.Kind } func setupWalletCtx( @@ -50,7 +51,8 @@ func setupWalletCtx( set.String(flags.KeymanagerKindFlag.Name, cfg.keymanagerKind.String(), "") set.String(flags.BackupDirFlag.Name, cfg.exportDir, "") set.String(flags.AccountsFlag.Name, cfg.accountsToExport, "") - set.String(flags.PasswordFileFlag.Name, cfg.passwordFile, "") + set.String(flags.WalletPasswordFileFlag.Name, cfg.walletPasswordFile, "") + set.String(flags.AccountPasswordFileFlag.Name, cfg.accountPasswordFile, "") set.Bool(flags.SkipMnemonicConfirmFlag.Name, true, "") set.Int64(flags.NumAccountsFlag.Name, cfg.numAccounts, "") assert.NoError(tb, set.Set(flags.WalletDirFlag.Name, cfg.walletDir)) @@ -59,7 +61,8 @@ func setupWalletCtx( assert.NoError(tb, set.Set(flags.KeymanagerKindFlag.Name, cfg.keymanagerKind.String())) assert.NoError(tb, set.Set(flags.BackupDirFlag.Name, cfg.exportDir)) assert.NoError(tb, set.Set(flags.AccountsFlag.Name, cfg.accountsToExport)) - assert.NoError(tb, set.Set(flags.PasswordFileFlag.Name, cfg.passwordFile)) + assert.NoError(tb, set.Set(flags.WalletPasswordFileFlag.Name, cfg.walletPasswordFile)) + assert.NoError(tb, set.Set(flags.AccountPasswordFileFlag.Name, cfg.accountPasswordFile)) assert.NoError(tb, set.Set(flags.SkipMnemonicConfirmFlag.Name, "true")) assert.NoError(tb, set.Set(flags.NumAccountsFlag.Name, strconv.Itoa(int(cfg.numAccounts)))) return cli.NewContext(&app, set, nil) @@ -92,8 +95,8 @@ func TestCreateAndReadWallet(t *testing.T) { keymanagerKind: v2keymanager.Direct, }) wallet, err := NewWallet(cliCtx, v2keymanager.Direct) - require.NoError(t, wallet.SaveWallet()) require.NoError(t, err) + require.NoError(t, createDirectKeymanagerWallet(cliCtx, wallet)) // We should be able to now read the wallet as well. _, err = OpenWallet(cliCtx) require.NoError(t, err) diff --git a/validator/flags/flags.go b/validator/flags/flags.go index db44f996eaa..34676369936 100644 --- a/validator/flags/flags.go +++ b/validator/flags/flags.go @@ -12,6 +12,13 @@ import ( "github.com/urfave/cli/v2" ) +const ( + // WalletDefaultDirName for accounts-v2. + WalletDefaultDirName = "prysm-wallet-v2" + // PasswordsDefaultDirName where account-v2 passwords are stored. + PasswordsDefaultDirName = "prysm-wallet-v2-passwords" +) + var ( // DisableAccountMetricsFlag defines the graffiti value included in proposed blocks, default false. DisableAccountMetricsFlag = &cli.BoolFlag{ @@ -124,19 +131,24 @@ var ( WalletDirFlag = &cli.StringFlag{ Name: "wallet-dir", Usage: "Path to a wallet directory on-disk for Prysm validator accounts", - Value: DefaultValidatorDir(), + Value: filepath.Join(DefaultValidatorDir(), WalletDefaultDirName), } // WalletPasswordsDirFlag defines the path for a passwords directory for // Prysm accounts-v2. WalletPasswordsDirFlag = &cli.StringFlag{ Name: "passwords-dir", Usage: "Path to a directory on-disk where account passwords are stored", - Value: DefaultValidatorDir(), + Value: filepath.Join(DefaultValidatorDir(), PasswordsDefaultDirName), + } + // AccountPasswordFileFlag is path to a file containing a password for a new validator account. + AccountPasswordFileFlag = &cli.StringFlag{ + Name: "account-password-file", + Usage: "Path to a plain-text, .txt file containing a password for a new validator account", } - // PasswordFileFlag is used to enter a file to get a password for new account creation, non-interactively. - PasswordFileFlag = &cli.StringFlag{ - Name: "password-file", - Usage: "Path to a plaintext password.txt file", + // WalletPasswordFileFlag is the path to a file containing your wallet password. + WalletPasswordFileFlag = &cli.StringFlag{ + Name: "wallet-password-file", + Usage: "Path to a plain-text, .txt file containing your wallet password", } // MnemonicFileFlag is used to enter a file to mnemonic phrase for new wallet creation, non-interactively. MnemonicFileFlag = &cli.StringFlag{ diff --git a/validator/keymanager/v2/direct/BUILD.bazel b/validator/keymanager/v2/direct/BUILD.bazel index d979569a86f..bdafaa7ee4c 100644 --- a/validator/keymanager/v2/direct/BUILD.bazel +++ b/validator/keymanager/v2/direct/BUILD.bazel @@ -20,8 +20,10 @@ go_library( "//shared/petnames:go_default_library", "//shared/roughtime:go_default_library", "//validator/accounts/v2/iface:go_default_library", + "//validator/flags:go_default_library", "//validator/keymanager/v2:go_default_library", "@com_github_google_uuid//:go_default_library", + "@com_github_logrusorgru_aurora//: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", diff --git a/validator/keymanager/v2/direct/direct.go b/validator/keymanager/v2/direct/direct.go index 62d9f59748c..2bf0b9c8557 100644 --- a/validator/keymanager/v2/direct/direct.go +++ b/validator/keymanager/v2/direct/direct.go @@ -9,9 +9,11 @@ import ( "io/ioutil" "os" "path/filepath" + "strings" "sync" "github.com/google/uuid" + "github.com/logrusorgru/aurora" "github.com/pkg/errors" "github.com/prysmaticlabs/go-ssz" validatorpb "github.com/prysmaticlabs/prysm/proto/validator/accounts/v2" @@ -21,6 +23,7 @@ import ( "github.com/prysmaticlabs/prysm/shared/petnames" "github.com/prysmaticlabs/prysm/shared/roughtime" "github.com/prysmaticlabs/prysm/validator/accounts/v2/iface" + "github.com/prysmaticlabs/prysm/validator/flags" v2keymanager "github.com/prysmaticlabs/prysm/validator/keymanager/v2" "github.com/sirupsen/logrus" keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4" @@ -47,7 +50,8 @@ const ( // Config for a direct keymanager. type Config struct { - EIPVersion string `json:"direct_eip_version"` + EIPVersion string `json:"direct_eip_version"` + AccountPasswordsDirectory string `json:"direct_accounts_passwords_directory"` } // Keymanager implementation for direct keystores utilizing EIP-2335. @@ -61,7 +65,8 @@ type Keymanager struct { // DefaultConfig for a direct keymanager implementation. func DefaultConfig() *Config { return &Config{ - EIPVersion: eipVersion, + EIPVersion: eipVersion, + AccountPasswordsDirectory: flags.WalletPasswordsDirFlag.Value, } } @@ -106,6 +111,30 @@ func MarshalConfigFile(ctx context.Context, cfg *Config) ([]byte, error) { return json.MarshalIndent(cfg, "", "\t") } +// Config for the direct keymanager. +func (dr *Keymanager) Config() *Config { + return dr.cfg +} + +// String pretty-print of a direct keymanager configuration. +func (c *Config) String() string { + au := aurora.NewAurora(true) + var b strings.Builder + strAddr := fmt.Sprintf("%s: %s\n", au.BrightMagenta("EIP Version"), c.EIPVersion) + if _, err := b.WriteString(strAddr); err != nil { + log.Error(err) + return "" + } + strCrt := fmt.Sprintf( + "%s: %s\n", au.BrightMagenta("Accounts Passwords Directory"), c.AccountPasswordsDirectory, + ) + if _, err := b.WriteString(strCrt); err != nil { + log.Error(err) + return "" + } + return b.String() +} + // ValidatingAccountNames for a direct keymanager. func (dr *Keymanager) ValidatingAccountNames() ([]string, error) { return dr.wallet.ListDirs() diff --git a/validator/main.go b/validator/main.go index ad101700478..55e72804db2 100644 --- a/validator/main.go +++ b/validator/main.go @@ -72,7 +72,7 @@ var appFlags = []cli.Flag{ flags.SlasherRPCProviderFlag, flags.SlasherCertFlag, flags.WalletPasswordsDirFlag, - flags.PasswordFileFlag, + flags.WalletPasswordFileFlag, flags.WalletDirFlag, cmd.MinimalConfigFlag, cmd.E2EConfigFlag, diff --git a/validator/usage.go b/validator/usage.go index 025f51980dd..3fc1ff648ad 100644 --- a/validator/usage.go +++ b/validator/usage.go @@ -83,7 +83,6 @@ var appHelpFlagGroups = []flagGroup{ flags.KeyManagerOpts, flags.KeystorePathFlag, flags.PasswordFlag, - flags.PasswordFileFlag, flags.DisablePenaltyRewardLogFlag, flags.UnencryptedKeysFlag, flags.GraffitiFlag, @@ -98,6 +97,7 @@ var appHelpFlagGroups = []flagGroup{ flags.DisableAccountMetricsFlag, flags.WalletDirFlag, flags.WalletPasswordsDirFlag, + flags.WalletPasswordFileFlag, }, }, {