Wallet Create CLI manager integration (#11331)

* Wallet recover CLI Manager migration

* bazel run //:gazelle -- fix

* Wallet create CLI Manager migration

* Wallet recover CLI Manager migration (#11278)

* Wallet recover CLI Manager migration

* bazel run //:gazelle -- fix

* fix lint and build errors

* add TODO to remove duplicate code

Co-authored-by: james-prysm <90280386+james-prysm@users.noreply.github.com>
Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Wallet recover CLI Manager migration

* bazel run //:gazelle -- fix

* Wallet recover CLI Manager migration

* bazel run //:gazelle -- fix

* Wallet recover CLI Manager migration

* bazel run //:gazelle -- fix

* fix lint and build errors

* Wallet recover CLI Manager migration (#11278)

* Wallet recover CLI Manager migration

* bazel run //:gazelle -- fix

* fix lint and build errors

* add TODO to remove duplicate code

Co-authored-by: james-prysm <90280386+james-prysm@users.noreply.github.com>
Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* bazel run //:gazelle -- fix

* rename to ConstructCLIManagerOpts

Co-authored-by: james-prysm <90280386+james-prysm@users.noreply.github.com>
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
This commit is contained in:
Mike Neuder
2022-09-02 10:56:47 -04:00
committed by GitHub
parent 8627fe72e8
commit cbc2153664
24 changed files with 568 additions and 500 deletions

View File

@@ -18,7 +18,6 @@ import (
"github.com/prysmaticlabs/prysm/v3/testing/require"
"github.com/prysmaticlabs/prysm/v3/validator/accounts"
"github.com/prysmaticlabs/prysm/v3/validator/accounts/iface"
"github.com/prysmaticlabs/prysm/v3/validator/accounts/wallet"
"github.com/prysmaticlabs/prysm/v3/validator/keymanager"
"github.com/prysmaticlabs/prysm/v3/validator/keymanager/derived"
constant "github.com/prysmaticlabs/prysm/v3/validator/testing"
@@ -53,13 +52,14 @@ func TestBackupAccounts_Noninteractive_Derived(t *testing.T) {
backupPasswordFile: backupPasswordFile,
backupDir: backupDir,
})
w, err := accounts.CreateWalletWithKeymanager(cliCtx.Context, &accounts.CreateWalletConfig{
WalletCfg: &wallet.Config{
WalletDir: walletDir,
KeymanagerKind: keymanager.Derived,
WalletPassword: password,
},
})
opts := []accounts.Option{
accounts.WithWalletDir(walletDir),
accounts.WithKeymanagerType(keymanager.Derived),
accounts.WithWalletPassword(password),
}
acc, err := accounts.NewCLIManager(opts...)
require.NoError(t, err)
w, err := acc.WalletCreate(cliCtx.Context)
require.NoError(t, err)
km, err := w.InitializeKeymanager(cliCtx.Context, iface.InitKeymanagerConfig{ListenForChanges: false})
@@ -170,13 +170,14 @@ func TestBackupAccounts_Noninteractive_Imported(t *testing.T) {
backupPasswordFile: backupPasswordFile,
backupDir: backupDir,
})
_, err = accounts.CreateWalletWithKeymanager(cliCtx.Context, &accounts.CreateWalletConfig{
WalletCfg: &wallet.Config{
WalletDir: walletDir,
KeymanagerKind: keymanager.Local,
WalletPassword: password,
},
})
opts := []accounts.Option{
accounts.WithWalletDir(walletDir),
accounts.WithKeymanagerType(keymanager.Local),
accounts.WithWalletPassword(password),
}
acc, err := accounts.NewCLIManager(opts...)
require.NoError(t, err)
_, err = acc.WalletCreate(cliCtx.Context)
require.NoError(t, err)
// We attempt to import accounts we wrote to the keys directory

View File

@@ -21,7 +21,6 @@ import (
"github.com/prysmaticlabs/prysm/v3/testing/require"
prysmTime "github.com/prysmaticlabs/prysm/v3/time"
"github.com/prysmaticlabs/prysm/v3/validator/accounts"
"github.com/prysmaticlabs/prysm/v3/validator/accounts/wallet"
"github.com/prysmaticlabs/prysm/v3/validator/keymanager"
"github.com/prysmaticlabs/prysm/v3/validator/keymanager/local"
"github.com/urfave/cli/v2"
@@ -160,13 +159,14 @@ func TestDeleteAccounts_Noninteractive(t *testing.T) {
// Flags required for DeleteAccounts to work.
deletePublicKeys: deletePublicKeys,
})
w, err := accounts.CreateWalletWithKeymanager(cliCtx.Context, &accounts.CreateWalletConfig{
WalletCfg: &wallet.Config{
WalletDir: walletDir,
KeymanagerKind: keymanager.Local,
WalletPassword: password,
},
})
opts := []accounts.Option{
accounts.WithWalletDir(walletDir),
accounts.WithKeymanagerType(keymanager.Local),
accounts.WithWalletPassword(password),
}
acc, err := accounts.NewCLIManager(opts...)
require.NoError(t, err)
w, err := acc.WalletCreate(cliCtx.Context)
require.NoError(t, err)
// We attempt to import accounts.

View File

@@ -14,7 +14,6 @@ import (
mock2 "github.com/prysmaticlabs/prysm/v3/testing/mock"
"github.com/prysmaticlabs/prysm/v3/testing/require"
"github.com/prysmaticlabs/prysm/v3/validator/accounts"
"github.com/prysmaticlabs/prysm/v3/validator/accounts/wallet"
"github.com/prysmaticlabs/prysm/v3/validator/keymanager"
"google.golang.org/protobuf/types/known/timestamppb"
)
@@ -67,13 +66,14 @@ func TestExitAccountsCli_OK(t *testing.T) {
// Flag required for ExitAccounts to work.
voluntaryExitPublicKeys: keystore.Pubkey,
})
_, err := accounts.CreateWalletWithKeymanager(cliCtx.Context, &accounts.CreateWalletConfig{
WalletCfg: &wallet.Config{
WalletDir: walletDir,
KeymanagerKind: keymanager.Local,
WalletPassword: password,
},
})
opts := []accounts.Option{
accounts.WithWalletDir(walletDir),
accounts.WithKeymanagerType(keymanager.Local),
accounts.WithWalletPassword(password),
}
acc, err := accounts.NewCLIManager(opts...)
require.NoError(t, err)
_, err = acc.WalletCreate(cliCtx.Context)
require.NoError(t, err)
require.NoError(t, accountsImport(cliCtx))
@@ -167,13 +167,14 @@ func TestExitAccountsCli_OK_AllPublicKeys(t *testing.T) {
// Exit all public keys.
exitAll: true,
})
_, err := accounts.CreateWalletWithKeymanager(cliCtx.Context, &accounts.CreateWalletConfig{
WalletCfg: &wallet.Config{
WalletDir: walletDir,
KeymanagerKind: keymanager.Local,
WalletPassword: password,
},
})
opts := []accounts.Option{
accounts.WithWalletDir(walletDir),
accounts.WithKeymanagerType(keymanager.Local),
accounts.WithWalletPassword(password),
}
acc, err := accounts.NewCLIManager(opts...)
require.NoError(t, err)
_, err = acc.WalletCreate(cliCtx.Context)
require.NoError(t, err)
require.NoError(t, accountsImport(cliCtx))

View File

@@ -6,6 +6,7 @@ import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v3/cmd"
"github.com/prysmaticlabs/prysm/v3/cmd/validator/flags"
"github.com/prysmaticlabs/prysm/v3/io/prompt"
"github.com/prysmaticlabs/prysm/v3/validator/accounts"
"github.com/prysmaticlabs/prysm/v3/validator/accounts/iface"
"github.com/prysmaticlabs/prysm/v3/validator/accounts/userprompt"
@@ -93,21 +94,51 @@ func walletImport(c *cli.Context) (*wallet.Wallet, error) {
})
}
cfg, err := accounts.ExtractWalletCreationConfigFromCli(cliCtx, keymanager.Local)
wCfg, err := ExtractWalletDirPassword(cliCtx)
if err != nil {
return nil, err
}
w := wallet.New(&wallet.Config{
KeymanagerKind: cfg.WalletCfg.KeymanagerKind,
WalletDir: cfg.WalletCfg.WalletDir,
WalletPassword: cfg.WalletCfg.WalletPassword,
KeymanagerKind: keymanager.Local,
WalletDir: wCfg.Dir,
WalletPassword: wCfg.Password,
})
if err = accounts.CreateLocalKeymanagerWallet(cliCtx.Context, w); err != nil {
return nil, errors.Wrap(err, "could not create keymanager")
}
log.WithField("wallet-path", cfg.WalletCfg.WalletDir).Info(
log.WithField("wallet-path", wCfg.Dir).Info(
"Successfully created new wallet",
)
return w, nil
})
}
// WalletDirPassword holds the directory and password of a wallet.
type WalletDirPassword struct {
Dir string
Password string
}
// ExtractWalletDirPassword prompts the user for wallet directory and password.
func ExtractWalletDirPassword(cliCtx *cli.Context) (WalletDirPassword, error) {
// Get wallet dir and check that no wallet exists at the location.
walletDir, err := userprompt.InputDirectory(cliCtx, userprompt.WalletDirPromptText, flags.WalletDirFlag)
if err != nil {
return WalletDirPassword{}, err
}
walletPassword, err := prompt.InputPassword(
cliCtx,
flags.WalletPasswordFileFlag,
wallet.NewWalletPasswordPromptText,
wallet.ConfirmPasswordPromptText,
true, /* Should confirm password */
prompt.ValidatePasswordInput,
)
if err != nil {
return WalletDirPassword{}, err
}
return WalletDirPassword{
Dir: walletDir,
Password: walletPassword,
}, nil
}

View File

@@ -36,13 +36,14 @@ func TestImport_Noninteractive(t *testing.T) {
walletPasswordFile: passwordFilePath,
accountPasswordFile: passwordFilePath,
})
w, err := accounts.CreateWalletWithKeymanager(cliCtx.Context, &accounts.CreateWalletConfig{
WalletCfg: &wallet.Config{
WalletDir: walletDir,
KeymanagerKind: keymanager.Local,
WalletPassword: password,
},
})
opts := []accounts.Option{
accounts.WithWalletDir(walletDir),
accounts.WithKeymanagerType(keymanager.Local),
accounts.WithWalletPassword(password),
}
acc, err := accounts.NewCLIManager(opts...)
require.NoError(t, err)
w, err := acc.WalletCreate(cliCtx.Context)
require.NoError(t, err)
newKm, err := local.NewKeymanager(
cliCtx.Context,
@@ -93,13 +94,14 @@ func TestImport_DuplicateKeys(t *testing.T) {
walletPasswordFile: passwordFilePath,
accountPasswordFile: passwordFilePath,
})
w, err := accounts.CreateWalletWithKeymanager(cliCtx.Context, &accounts.CreateWalletConfig{
WalletCfg: &wallet.Config{
WalletDir: walletDir,
KeymanagerKind: keymanager.Local,
WalletPassword: password,
},
})
opts := []accounts.Option{
accounts.WithWalletDir(walletDir),
accounts.WithKeymanagerType(keymanager.Local),
accounts.WithWalletPassword(password),
}
acc, err := accounts.NewCLIManager(opts...)
require.NoError(t, err)
w, err := acc.WalletCreate(cliCtx.Context)
require.NoError(t, err)
// Create a key and then copy it to create a duplicate
@@ -141,13 +143,14 @@ func TestImport_Noninteractive_RandomName(t *testing.T) {
walletPasswordFile: passwordFilePath,
accountPasswordFile: passwordFilePath,
})
w, err := accounts.CreateWalletWithKeymanager(cliCtx.Context, &accounts.CreateWalletConfig{
WalletCfg: &wallet.Config{
WalletDir: walletDir,
KeymanagerKind: keymanager.Local,
WalletPassword: password,
},
})
opts := []accounts.Option{
accounts.WithWalletDir(walletDir),
accounts.WithKeymanagerType(keymanager.Local),
accounts.WithWalletPassword(password),
}
acc, err := accounts.NewCLIManager(opts...)
require.NoError(t, err)
w, err := acc.WalletCreate(cliCtx.Context)
require.NoError(t, err)
newKm, err := local.NewKeymanager(
cliCtx.Context,
@@ -224,13 +227,14 @@ func TestImport_Noninteractive_Filepath(t *testing.T) {
walletPasswordFile: passwordFilePath,
accountPasswordFile: passwordFilePath,
})
w, err := accounts.CreateWalletWithKeymanager(cliCtx.Context, &accounts.CreateWalletConfig{
WalletCfg: &wallet.Config{
WalletDir: walletDir,
KeymanagerKind: keymanager.Local,
WalletPassword: password,
},
})
opts := []accounts.Option{
accounts.WithWalletDir(walletDir),
accounts.WithKeymanagerType(keymanager.Local),
accounts.WithWalletPassword(password),
}
acc, err := accounts.NewCLIManager(opts...)
require.NoError(t, err)
w, err := acc.WalletCreate(cliCtx.Context)
require.NoError(t, err)
newKm, err := local.NewKeymanager(
cliCtx.Context,

View File

@@ -3,6 +3,7 @@ load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"create.go",
"edit.go",
"recover.go",
"wallet.go",
@@ -20,6 +21,7 @@ go_library(
"//validator/accounts/wallet:go_default_library",
"//validator/keymanager:go_default_library",
"//validator/keymanager/remote:go_default_library",
"@com_github_manifoldco_promptui//:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_tyler_smith_go_bip39//:go_default_library",
@@ -31,11 +33,13 @@ go_library(
go_test(
name = "go_default_test",
srcs = [
"create_test.go",
"edit_test.go",
"recover_test.go",
],
embed = [":go_default_library"],
deps = [
"//cmd/validator/accounts:go_default_library",
"//cmd/validator/flags:go_default_library",
"//config/params:go_default_library",
"//testing/assert:go_default_library",
@@ -45,7 +49,11 @@ go_test(
"//validator/accounts/wallet:go_default_library",
"//validator/keymanager:go_default_library",
"//validator/keymanager/derived:go_default_library",
"//validator/keymanager/local:go_default_library",
"//validator/keymanager/remote: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",
],
)

View File

@@ -0,0 +1,171 @@
package wallet
import (
"fmt"
"os"
"strings"
"github.com/manifoldco/promptui"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v3/cmd/validator/flags"
"github.com/prysmaticlabs/prysm/v3/io/prompt"
"github.com/prysmaticlabs/prysm/v3/validator/accounts"
"github.com/prysmaticlabs/prysm/v3/validator/accounts/userprompt"
"github.com/prysmaticlabs/prysm/v3/validator/accounts/wallet"
"github.com/prysmaticlabs/prysm/v3/validator/keymanager"
"github.com/urfave/cli/v2"
)
const (
// #nosec G101 -- Not sensitive data
newMnemonicPassphraseYesNoText = "(Advanced) Do you want to setup a '25th word' passphrase for your mnemonic? [y/n]"
// #nosec G101 -- Not sensitive data
newMnemonicPassphrasePromptText = "(Advanced) Setup a passphrase '25th word' for your mnemonic " +
"(WARNING: You cannot recover your keys from your mnemonic if you forget this passphrase!)"
)
func walletCreate(c *cli.Context) error {
keymanagerKind, err := inputKeymanagerKind(c)
if err != nil {
return err
}
opts, err := ConstructCLIManagerOpts(c, keymanagerKind)
if err != nil {
return err
}
acc, err := accounts.NewCLIManager(opts...)
if err != nil {
return err
}
if _, err := acc.WalletCreate(c.Context); err != nil {
return errors.Wrap(err, "could not create wallet")
}
return nil
}
// ConstructCLIManagerOpts prompts the user for wallet creation input.
func ConstructCLIManagerOpts(cliCtx *cli.Context, keymanagerKind keymanager.Kind) ([]accounts.Option, error) {
cliOpts := []accounts.Option{}
// Get wallet dir and check that no wallet exists at the location.
walletDir, err := userprompt.InputDirectory(cliCtx, userprompt.WalletDirPromptText, flags.WalletDirFlag)
if err != nil {
return []accounts.Option{}, err
}
dirExists, err := wallet.Exists(walletDir)
if err != nil {
return []accounts.Option{}, err
}
if dirExists {
return []accounts.Option{}, errors.New("a wallet already exists at this location. Please input an" +
" alternative location for the new wallet or remove the current wallet")
}
walletPassword, err := prompt.InputPassword(
cliCtx,
flags.WalletPasswordFileFlag,
wallet.NewWalletPasswordPromptText,
wallet.ConfirmPasswordPromptText,
true, /* Should confirm password */
prompt.ValidatePasswordInput,
)
if err != nil {
return []accounts.Option{}, err
}
cliOpts = append(cliOpts, accounts.WithWalletDir(walletDir))
cliOpts = append(cliOpts, accounts.WithWalletPassword(walletPassword))
cliOpts = append(cliOpts, accounts.WithKeymanagerType(keymanagerKind))
cliOpts = append(cliOpts, accounts.WithSkipMnemonicConfirm(cliCtx.Bool(flags.SkipDepositConfirmationFlag.Name)))
skipMnemonic25thWord := cliCtx.IsSet(flags.SkipMnemonic25thWordCheckFlag.Name)
has25thWordFile := cliCtx.IsSet(flags.Mnemonic25thWordFileFlag.Name)
if keymanagerKind == keymanager.Derived {
numAccounts, err := inputNumAccounts(cliCtx)
if err != nil {
return []accounts.Option{}, errors.Wrap(err, "could not get number of accounts to generate")
}
cliOpts = append(cliOpts, accounts.WithNumAccounts(int(numAccounts)))
}
if keymanagerKind == keymanager.Derived && !skipMnemonic25thWord && !has25thWordFile {
resp, err := prompt.ValidatePrompt(
os.Stdin, newMnemonicPassphraseYesNoText, prompt.ValidateYesOrNo,
)
if err != nil {
return []accounts.Option{}, errors.Wrap(err, "could not validate choice")
}
if strings.EqualFold(resp, "y") {
mnemonicPassphrase, err := prompt.InputPassword(
cliCtx,
flags.Mnemonic25thWordFileFlag,
newMnemonicPassphrasePromptText,
"Confirm mnemonic passphrase",
true, /* Should confirm password */
func(input string) error {
if strings.TrimSpace(input) == "" {
return errors.New("input cannot be empty")
}
return nil
},
)
if err != nil {
return []accounts.Option{}, err
}
cliOpts = append(cliOpts, accounts.WithMnemonic25thWord(mnemonicPassphrase))
}
}
if keymanagerKind == keymanager.Remote {
opts, err := userprompt.InputRemoteKeymanagerConfig(cliCtx)
if err != nil {
return []accounts.Option{}, errors.Wrap(err, "could not input remote keymanager config")
}
cliOpts = append(cliOpts, accounts.WithKeymanagerOpts(opts))
}
if keymanagerKind == keymanager.Web3Signer {
return []accounts.Option{}, errors.New("web3signer keymanager does not require persistent wallets.")
}
return cliOpts, nil
}
func inputKeymanagerKind(cliCtx *cli.Context) (keymanager.Kind, error) {
if cliCtx.IsSet(flags.KeymanagerKindFlag.Name) {
return keymanager.ParseKind(cliCtx.String(flags.KeymanagerKindFlag.Name))
}
promptSelect := promptui.Select{
Label: "Select a type of wallet",
Items: []string{
wallet.KeymanagerKindSelections[keymanager.Local],
wallet.KeymanagerKindSelections[keymanager.Derived],
wallet.KeymanagerKindSelections[keymanager.Remote],
wallet.KeymanagerKindSelections[keymanager.Web3Signer],
},
}
selection, _, err := promptSelect.Run()
if err != nil {
return keymanager.Local, fmt.Errorf("could not select wallet type: %w", userprompt.FormatPromptError(err))
}
return keymanager.Kind(selection), nil
}
// CreateAndSaveWalletCli 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 CreateAndSaveWalletCli(cliCtx *cli.Context) (*wallet.Wallet, error) {
keymanagerKind, err := inputKeymanagerKind(cliCtx)
if err != nil {
return nil, err
}
opts, err := ConstructCLIManagerOpts(cliCtx, keymanagerKind)
if err != nil {
return nil, err
}
acc, err := accounts.NewCLIManager(opts...)
if err != nil {
return nil, err
}
w, err := acc.WalletCreate(cliCtx.Context)
if err != nil {
return nil, errors.Wrap(err, "could not create wallet")
}
return w, nil
}

View File

@@ -1,19 +1,17 @@
package accounts
package wallet
import (
"context"
"flag"
"io"
"os"
"path/filepath"
"strconv"
"testing"
"github.com/pkg/errors"
cmdacc "github.com/prysmaticlabs/prysm/v3/cmd/validator/accounts"
"github.com/prysmaticlabs/prysm/v3/cmd/validator/flags"
"github.com/prysmaticlabs/prysm/v3/config/params"
"github.com/prysmaticlabs/prysm/v3/testing/assert"
"github.com/prysmaticlabs/prysm/v3/testing/require"
"github.com/prysmaticlabs/prysm/v3/validator/accounts"
"github.com/prysmaticlabs/prysm/v3/validator/accounts/wallet"
"github.com/prysmaticlabs/prysm/v3/validator/keymanager"
"github.com/prysmaticlabs/prysm/v3/validator/keymanager/local"
@@ -23,89 +21,11 @@ import (
"github.com/urfave/cli/v2"
)
const (
passwordFileName = "password.txt"
password = "OhWOWthisisatest42!$"
)
func init() {
logrus.SetLevel(logrus.DebugLevel)
logrus.SetOutput(io.Discard)
}
type testWalletConfig struct {
exitAll bool
skipDepositConfirm bool
keymanagerKind keymanager.Kind
numAccounts int64
grpcHeaders string
privateKeyFile string
accountPasswordFile string
walletPasswordFile string
backupPasswordFile string
backupPublicKeys string
voluntaryExitPublicKeys string
deletePublicKeys string
keysDir string
backupDir string
passwordsDir string
walletDir string
}
func setupWalletCtx(
tb testing.TB,
cfg *testWalletConfig,
) *cli.Context {
app := cli.App{}
set := flag.NewFlagSet("test", 0)
set.String(flags.WalletDirFlag.Name, cfg.walletDir, "")
set.String(flags.KeysDirFlag.Name, cfg.keysDir, "")
set.String(flags.KeymanagerKindFlag.Name, cfg.keymanagerKind.String(), "")
set.String(flags.DeletePublicKeysFlag.Name, cfg.deletePublicKeys, "")
set.String(flags.VoluntaryExitPublicKeysFlag.Name, cfg.voluntaryExitPublicKeys, "")
set.String(flags.BackupDirFlag.Name, cfg.backupDir, "")
set.String(flags.BackupPasswordFile.Name, cfg.backupPasswordFile, "")
set.String(flags.BackupPublicKeysFlag.Name, cfg.backupPublicKeys, "")
set.String(flags.WalletPasswordFileFlag.Name, cfg.walletPasswordFile, "")
set.String(flags.AccountPasswordFileFlag.Name, cfg.accountPasswordFile, "")
set.Int64(flags.NumAccountsFlag.Name, cfg.numAccounts, "")
set.Bool(flags.SkipDepositConfirmationFlag.Name, cfg.skipDepositConfirm, "")
set.Bool(flags.SkipMnemonic25thWordCheckFlag.Name, true, "")
set.Bool(flags.ExitAllFlag.Name, cfg.exitAll, "")
set.String(flags.GrpcHeadersFlag.Name, cfg.grpcHeaders, "")
if cfg.privateKeyFile != "" {
set.String(flags.ImportPrivateKeyFileFlag.Name, cfg.privateKeyFile, "")
assert.NoError(tb, set.Set(flags.ImportPrivateKeyFileFlag.Name, cfg.privateKeyFile))
}
assert.NoError(tb, set.Set(flags.WalletDirFlag.Name, cfg.walletDir))
assert.NoError(tb, set.Set(flags.SkipMnemonic25thWordCheckFlag.Name, "true"))
assert.NoError(tb, set.Set(flags.KeysDirFlag.Name, cfg.keysDir))
assert.NoError(tb, set.Set(flags.KeymanagerKindFlag.Name, cfg.keymanagerKind.String()))
assert.NoError(tb, set.Set(flags.DeletePublicKeysFlag.Name, cfg.deletePublicKeys))
assert.NoError(tb, set.Set(flags.VoluntaryExitPublicKeysFlag.Name, cfg.voluntaryExitPublicKeys))
assert.NoError(tb, set.Set(flags.BackupDirFlag.Name, cfg.backupDir))
assert.NoError(tb, set.Set(flags.BackupPublicKeysFlag.Name, cfg.backupPublicKeys))
assert.NoError(tb, set.Set(flags.BackupPasswordFile.Name, cfg.backupPasswordFile))
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.NumAccountsFlag.Name, strconv.Itoa(int(cfg.numAccounts))))
assert.NoError(tb, set.Set(flags.SkipDepositConfirmationFlag.Name, strconv.FormatBool(cfg.skipDepositConfirm)))
assert.NoError(tb, set.Set(flags.ExitAllFlag.Name, strconv.FormatBool(cfg.exitAll)))
assert.NoError(tb, set.Set(flags.GrpcHeadersFlag.Name, cfg.grpcHeaders))
return cli.NewContext(&app, set, nil)
}
func setupWalletAndPasswordsDir(t testing.TB) (string, string, string) {
walletDir := filepath.Join(t.TempDir(), "wallet")
passwordsDir := filepath.Join(t.TempDir(), "passwords")
passwordFileDir := filepath.Join(t.TempDir(), "passwordFile")
require.NoError(t, os.MkdirAll(passwordFileDir, params.BeaconIoConfig().ReadWriteExecutePermissions))
passwordFilePath := filepath.Join(passwordFileDir, passwordFileName)
require.NoError(t, os.WriteFile(passwordFilePath, []byte(password), os.ModePerm))
return walletDir, passwordsDir, passwordFilePath
}
func TestCreateOrOpenWallet(t *testing.T) {
hook := logTest.NewGlobal()
walletDir, passwordsDir, walletPasswordFile := setupWalletAndPasswordsDir(t)
@@ -116,19 +36,19 @@ func TestCreateOrOpenWallet(t *testing.T) {
walletPasswordFile: walletPasswordFile,
})
createLocalWallet := func(cliCtx *cli.Context) (*wallet.Wallet, error) {
cfg, err := ExtractWalletCreationConfigFromCli(cliCtx, keymanager.Local)
cfg, err := cmdacc.ExtractWalletDirPassword(cliCtx)
if err != nil {
return nil, err
}
w := wallet.New(&wallet.Config{
KeymanagerKind: cfg.WalletCfg.KeymanagerKind,
WalletDir: cfg.WalletCfg.WalletDir,
WalletPassword: cfg.WalletCfg.WalletPassword,
KeymanagerKind: keymanager.Local,
WalletDir: cfg.Dir,
WalletPassword: cfg.Password,
})
if err = CreateLocalKeymanagerWallet(cliCtx.Context, w); err != nil {
if err = accounts.CreateLocalKeymanagerWallet(cliCtx.Context, w); err != nil {
return nil, errors.Wrap(err, "could not create keymanager")
}
log.WithField("wallet-path", cfg.WalletCfg.WalletDir).Info(
log.WithField("wallet-path", cfg.Dir).Info(
"Successfully created new wallet",
)
return w, nil

View File

@@ -12,7 +12,6 @@ import (
"github.com/prysmaticlabs/prysm/v3/testing/assert"
"github.com/prysmaticlabs/prysm/v3/testing/require"
"github.com/prysmaticlabs/prysm/v3/validator/accounts"
"github.com/prysmaticlabs/prysm/v3/validator/accounts/wallet"
"github.com/prysmaticlabs/prysm/v3/validator/keymanager"
"github.com/prysmaticlabs/prysm/v3/validator/keymanager/remote"
"github.com/urfave/cli/v2"
@@ -52,6 +51,7 @@ type testWalletConfig struct {
keysDir string
backupDir string
walletDir string
passwordsDir string
}
func setupWalletCtx(
@@ -104,13 +104,14 @@ func TestEditWalletConfiguration(t *testing.T) {
walletDir: walletDir,
keymanagerKind: keymanager.Remote,
})
w, err := accounts.CreateWalletWithKeymanager(cliCtx.Context, &accounts.CreateWalletConfig{
WalletCfg: &wallet.Config{
WalletDir: walletDir,
KeymanagerKind: keymanager.Remote,
WalletPassword: "Passwordz0320$",
},
})
opts := []accounts.Option{
accounts.WithWalletDir(walletDir),
accounts.WithKeymanagerType(keymanager.Remote),
accounts.WithWalletPassword("Passwordz0320$"),
}
acc, err := accounts.NewCLIManager(opts...)
require.NoError(t, err)
w, err := acc.WalletCreate(cliCtx.Context)
require.NoError(t, err)
originalCfg := &remote.KeymanagerOpts{

View File

@@ -5,7 +5,6 @@ import (
"github.com/prysmaticlabs/prysm/v3/cmd/validator/flags"
"github.com/prysmaticlabs/prysm/v3/config/features"
"github.com/prysmaticlabs/prysm/v3/runtime/tos"
"github.com/prysmaticlabs/prysm/v3/validator/accounts"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
)
@@ -43,13 +42,13 @@ var Commands = &cli.Command{
if err := cmd.LoadFlagsFromConfig(cliCtx, cliCtx.Command.Flags); err != nil {
return err
}
return tos.VerifyTosAcceptedOrPrompt(cliCtx)
},
Action: func(cliCtx *cli.Context) error {
if err := features.ConfigureValidator(cliCtx); err != nil {
if err := tos.VerifyTosAcceptedOrPrompt(cliCtx); err != nil {
return err
}
if _, err := accounts.CreateAndSaveWalletCli(cliCtx); err != nil {
return features.ConfigureValidator(cliCtx)
},
Action: func(cliCtx *cli.Context) error {
if err := walletCreate(cliCtx); err != nil {
log.WithError(err).Fatal("Could not create a wallet")
}
return nil

View File

@@ -45,7 +45,6 @@ go_library(
"//validator/keymanager/derived:go_default_library",
"//validator/keymanager/local:go_default_library",
"//validator/keymanager/remote:go_default_library",
"//validator/keymanager/remote-web3signer:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_google_uuid//:go_default_library",
"@com_github_logrusorgru_aurora//:go_default_library",
@@ -65,7 +64,6 @@ go_test(
"accounts_exit_test.go",
"accounts_import_test.go",
"accounts_list_test.go",
"wallet_create_test.go",
"wallet_recover_fuzz_test.go",
"wallet_recover_test.go",
],
@@ -96,7 +94,6 @@ go_test(
"@com_github_golang_mock//gomock: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",

View File

@@ -15,7 +15,6 @@ import (
"github.com/prysmaticlabs/prysm/v3/testing/assert"
"github.com/prysmaticlabs/prysm/v3/testing/require"
"github.com/prysmaticlabs/prysm/v3/validator/accounts/iface"
"github.com/prysmaticlabs/prysm/v3/validator/accounts/wallet"
"github.com/prysmaticlabs/prysm/v3/validator/keymanager"
"github.com/prysmaticlabs/prysm/v3/validator/keymanager/local"
)
@@ -34,13 +33,14 @@ func TestImportAccounts_NoPassword(t *testing.T) {
walletPasswordFile: passwordFilePath,
accountPasswordFile: passwordFilePath,
})
w, err := CreateWalletWithKeymanager(cliCtx.Context, &CreateWalletConfig{
WalletCfg: &wallet.Config{
WalletDir: walletDir,
KeymanagerKind: keymanager.Local,
WalletPassword: password,
},
})
opts := []Option{
WithWalletDir(walletDir),
WithKeymanagerType(keymanager.Local),
WithWalletPassword(password),
}
acc, err := NewCLIManager(opts...)
require.NoError(t, err)
w, err := acc.WalletCreate(cliCtx.Context)
require.NoError(t, err)
km, err := w.InitializeKeymanager(cliCtx.Context, iface.InitKeymanagerConfig{ListenForChanges: false})
require.NoError(t, err)
@@ -141,13 +141,14 @@ func Test_importPrivateKeyAsAccount(t *testing.T) {
privateKeyFile: privKeyFileName,
})
walletPass := "Passwordz0320$"
w, err := CreateWalletWithKeymanager(cliCtx.Context, &CreateWalletConfig{
WalletCfg: &wallet.Config{
WalletDir: walletDir,
KeymanagerKind: keymanager.Local,
WalletPassword: walletPass,
},
})
opts := []Option{
WithWalletDir(walletDir),
WithKeymanagerType(keymanager.Local),
WithWalletPassword(walletPass),
}
acc, err := NewCLIManager(opts...)
require.NoError(t, err)
w, err := acc.WalletCreate(cliCtx.Context)
require.NoError(t, err)
km, err := local.NewKeymanager(
cliCtx.Context,

View File

@@ -2,10 +2,12 @@ package accounts
import (
"context"
"flag"
"fmt"
"io"
"math"
"os"
"path/filepath"
"strconv"
"strings"
"testing"
@@ -13,7 +15,9 @@ import (
"github.com/golang/mock/gomock"
"github.com/google/uuid"
"github.com/prysmaticlabs/prysm/v3/async/event"
"github.com/prysmaticlabs/prysm/v3/cmd/validator/flags"
fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams"
"github.com/prysmaticlabs/prysm/v3/config/params"
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v3/crypto/bls"
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
@@ -30,9 +34,88 @@ import (
"github.com/prysmaticlabs/prysm/v3/validator/keymanager/local"
"github.com/prysmaticlabs/prysm/v3/validator/keymanager/remote"
constant "github.com/prysmaticlabs/prysm/v3/validator/testing"
"github.com/urfave/cli/v2"
keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4"
)
const (
passwordFileName = "password.txt"
password = "OhWOWthisisatest42!$"
)
type testWalletConfig struct {
exitAll bool
skipDepositConfirm bool
keymanagerKind keymanager.Kind
numAccounts int64
grpcHeaders string
privateKeyFile string
accountPasswordFile string
walletPasswordFile string
backupPasswordFile string
backupPublicKeys string
voluntaryExitPublicKeys string
deletePublicKeys string
keysDir string
backupDir string
passwordsDir string
walletDir string
}
func setupWalletCtx(
tb testing.TB,
cfg *testWalletConfig,
) *cli.Context {
app := cli.App{}
set := flag.NewFlagSet("test", 0)
set.String(flags.WalletDirFlag.Name, cfg.walletDir, "")
set.String(flags.KeysDirFlag.Name, cfg.keysDir, "")
set.String(flags.KeymanagerKindFlag.Name, cfg.keymanagerKind.String(), "")
set.String(flags.DeletePublicKeysFlag.Name, cfg.deletePublicKeys, "")
set.String(flags.VoluntaryExitPublicKeysFlag.Name, cfg.voluntaryExitPublicKeys, "")
set.String(flags.BackupDirFlag.Name, cfg.backupDir, "")
set.String(flags.BackupPasswordFile.Name, cfg.backupPasswordFile, "")
set.String(flags.BackupPublicKeysFlag.Name, cfg.backupPublicKeys, "")
set.String(flags.WalletPasswordFileFlag.Name, cfg.walletPasswordFile, "")
set.String(flags.AccountPasswordFileFlag.Name, cfg.accountPasswordFile, "")
set.Int64(flags.NumAccountsFlag.Name, cfg.numAccounts, "")
set.Bool(flags.SkipDepositConfirmationFlag.Name, cfg.skipDepositConfirm, "")
set.Bool(flags.SkipMnemonic25thWordCheckFlag.Name, true, "")
set.Bool(flags.ExitAllFlag.Name, cfg.exitAll, "")
set.String(flags.GrpcHeadersFlag.Name, cfg.grpcHeaders, "")
if cfg.privateKeyFile != "" {
set.String(flags.ImportPrivateKeyFileFlag.Name, cfg.privateKeyFile, "")
assert.NoError(tb, set.Set(flags.ImportPrivateKeyFileFlag.Name, cfg.privateKeyFile))
}
assert.NoError(tb, set.Set(flags.WalletDirFlag.Name, cfg.walletDir))
assert.NoError(tb, set.Set(flags.SkipMnemonic25thWordCheckFlag.Name, "true"))
assert.NoError(tb, set.Set(flags.KeysDirFlag.Name, cfg.keysDir))
assert.NoError(tb, set.Set(flags.KeymanagerKindFlag.Name, cfg.keymanagerKind.String()))
assert.NoError(tb, set.Set(flags.DeletePublicKeysFlag.Name, cfg.deletePublicKeys))
assert.NoError(tb, set.Set(flags.VoluntaryExitPublicKeysFlag.Name, cfg.voluntaryExitPublicKeys))
assert.NoError(tb, set.Set(flags.BackupDirFlag.Name, cfg.backupDir))
assert.NoError(tb, set.Set(flags.BackupPublicKeysFlag.Name, cfg.backupPublicKeys))
assert.NoError(tb, set.Set(flags.BackupPasswordFile.Name, cfg.backupPasswordFile))
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.NumAccountsFlag.Name, strconv.Itoa(int(cfg.numAccounts))))
assert.NoError(tb, set.Set(flags.SkipDepositConfirmationFlag.Name, strconv.FormatBool(cfg.skipDepositConfirm)))
assert.NoError(tb, set.Set(flags.ExitAllFlag.Name, strconv.FormatBool(cfg.exitAll)))
assert.NoError(tb, set.Set(flags.GrpcHeadersFlag.Name, cfg.grpcHeaders))
return cli.NewContext(&app, set, nil)
}
func setupWalletAndPasswordsDir(t testing.TB) (string, string, string) {
walletDir := filepath.Join(t.TempDir(), "wallet")
passwordsDir := filepath.Join(t.TempDir(), "passwords")
passwordFileDir := filepath.Join(t.TempDir(), "passwordFile")
require.NoError(t, os.MkdirAll(passwordFileDir, params.BeaconIoConfig().ReadWriteExecutePermissions))
passwordFilePath := filepath.Join(passwordFileDir, passwordFileName)
require.NoError(t, os.WriteFile(passwordFilePath, []byte(password), os.ModePerm))
return walletDir, passwordsDir, passwordFilePath
}
type mockRemoteKeymanager struct {
publicKeys [][fieldparams.BLSPubkeyLength]byte
opts *remote.KeymanagerOpts
@@ -42,15 +125,15 @@ func (m *mockRemoteKeymanager) FetchValidatingPublicKeys(_ context.Context) ([][
return m.publicKeys, nil
}
func (_ *mockRemoteKeymanager) Sign(context.Context, *validatorpb.SignRequest) (bls.Signature, error) {
func (*mockRemoteKeymanager) Sign(context.Context, *validatorpb.SignRequest) (bls.Signature, error) {
return nil, nil
}
func (_ *mockRemoteKeymanager) SubscribeAccountChanges(_ chan [][fieldparams.BLSPubkeyLength]byte) event.Subscription {
func (*mockRemoteKeymanager) SubscribeAccountChanges(_ chan [][fieldparams.BLSPubkeyLength]byte) event.Subscription {
return nil
}
func (_ *mockRemoteKeymanager) ExtractKeystores(
func (*mockRemoteKeymanager) ExtractKeystores(
_ context.Context, _ []bls.PublicKey, _ string,
) ([]*keymanager.Keystore, error) {
return nil, nil
@@ -90,13 +173,14 @@ func TestListAccounts_LocalKeymanager(t *testing.T) {
keymanagerKind: keymanager.Local,
walletPasswordFile: walletPasswordFile,
})
w, err := CreateWalletWithKeymanager(cliCtx.Context, &CreateWalletConfig{
WalletCfg: &wallet.Config{
WalletDir: walletDir,
KeymanagerKind: keymanager.Local,
WalletPassword: "Passwordz0320$",
},
})
opts := []Option{
WithWalletDir(walletDir),
WithKeymanagerType(keymanager.Local),
WithWalletPassword("Passwordz0320$"),
}
acc, err := NewCLIManager(opts...)
require.NoError(t, err)
w, err := acc.WalletCreate(cliCtx.Context)
require.NoError(t, err)
km, err := local.NewKeymanager(
cliCtx.Context,
@@ -245,13 +329,14 @@ func TestListAccounts_DerivedKeymanager(t *testing.T) {
keymanagerKind: keymanager.Derived,
walletPasswordFile: passwordFilePath,
})
w, err := CreateWalletWithKeymanager(cliCtx.Context, &CreateWalletConfig{
WalletCfg: &wallet.Config{
WalletDir: walletDir,
KeymanagerKind: keymanager.Derived,
WalletPassword: "Passwordz0320$",
},
})
opts := []Option{
WithWalletDir(walletDir),
WithKeymanagerType(keymanager.Derived),
WithWalletPassword("Passwordz0320$"),
}
acc, err := NewCLIManager(opts...)
require.NoError(t, err)
w, err := acc.WalletCreate(cliCtx.Context)
require.NoError(t, err)
km, err := derived.NewKeymanager(
@@ -384,13 +469,14 @@ func TestListAccounts_RemoteKeymanager(t *testing.T) {
walletDir: walletDir,
keymanagerKind: keymanager.Remote,
})
w, err := CreateWalletWithKeymanager(cliCtx.Context, &CreateWalletConfig{
WalletCfg: &wallet.Config{
WalletDir: walletDir,
KeymanagerKind: keymanager.Remote,
WalletPassword: password,
},
})
opts := []Option{
WithWalletDir(walletDir),
WithKeymanagerType(keymanager.Remote),
WithWalletPassword(password),
}
acc, err := NewCLIManager(opts...)
require.NoError(t, err)
w, err := acc.WalletCreate(cliCtx.Context)
require.NoError(t, err)
rescueStdout := os.Stdout

View File

@@ -29,6 +29,7 @@ func NewCLIManager(opts ...Option) (*AccountsCLIManager, error) {
type AccountsCLIManager struct {
wallet *wallet.Wallet
keymanager keymanager.IKeymanager
keymanagerKind keymanager.Kind
keymanagerOpts *remote.KeymanagerOpts
showDepositData bool
showPrivateKeys bool
@@ -36,6 +37,7 @@ type AccountsCLIManager struct {
deletePublicKeys bool
importPrivateKeys bool
readPasswordFile bool
skipMnemonicConfirm bool
dialOpts []grpc.DialOption
grpcHeaders []string
beaconRPCProvider string

View File

@@ -27,6 +27,14 @@ func WithKeymanager(km keymanager.IKeymanager) Option {
}
}
// WithKeymanagerType provides a keymanager to the accounts cli manager.
func WithKeymanagerType(k keymanager.Kind) Option {
return func(acc *AccountsCLIManager) error {
acc.keymanagerKind = k
return nil
}
}
// WithKeymanagerOpts provides a keymanager configuration to the accounts cli manager.
func WithKeymanagerOpts(kmo *remote.KeymanagerOpts) Option {
return func(acc *AccountsCLIManager) error {
@@ -115,6 +123,14 @@ func WithImportPrivateKeys(importPrivateKeys bool) Option {
}
}
// WithSkipMnemonicConfirm indicates whether to skip the mnemonic confirmation.
func WithSkipMnemonicConfirm(s bool) Option {
return func(acc *AccountsCLIManager) error {
acc.skipMnemonicConfirm = s
return nil
}
}
// WithPrivateKeyFile specifies the private key path.
func WithPrivateKeyFile(privateKeyFile string) Option {
return func(acc *AccountsCLIManager) error {

View File

@@ -3,80 +3,22 @@ package accounts
import (
"context"
"encoding/json"
"fmt"
"os"
"strconv"
"strings"
"github.com/manifoldco/promptui"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v3/cmd/validator/flags"
"github.com/prysmaticlabs/prysm/v3/io/prompt"
"github.com/prysmaticlabs/prysm/v3/validator/accounts/iface"
"github.com/prysmaticlabs/prysm/v3/validator/accounts/userprompt"
"github.com/prysmaticlabs/prysm/v3/validator/accounts/wallet"
"github.com/prysmaticlabs/prysm/v3/validator/keymanager"
"github.com/prysmaticlabs/prysm/v3/validator/keymanager/derived"
"github.com/prysmaticlabs/prysm/v3/validator/keymanager/local"
"github.com/prysmaticlabs/prysm/v3/validator/keymanager/remote"
remoteweb3signer "github.com/prysmaticlabs/prysm/v3/validator/keymanager/remote-web3signer"
"github.com/urfave/cli/v2"
)
const (
// #nosec G101 -- Not sensitive data
newMnemonicPassphraseYesNoText = "(Advanced) Do you want to setup a '25th word' passphrase for your mnemonic? [y/n]"
// #nosec G101 -- Not sensitive data
newMnemonicPassphrasePromptText = "(Advanced) Setup a passphrase '25th word' for your mnemonic " +
"(WARNING: You cannot recover your keys from your mnemonic if you forget this passphrase!)"
)
// CreateWalletConfig defines the parameters needed to call the create wallet functions.
type CreateWalletConfig struct {
SkipMnemonicConfirm bool
NumAccounts int
RemoteKeymanagerOpts *remote.KeymanagerOpts
Web3SignerSetupConfig *remoteweb3signer.SetupConfig
WalletCfg *wallet.Config
Mnemonic25thWord string
}
// CreateAndSaveWalletCli 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 CreateAndSaveWalletCli(cliCtx *cli.Context) (*wallet.Wallet, error) {
keymanagerKind, err := extractKeymanagerKindFromCli(cliCtx)
if err != nil {
return nil, err
}
createWalletConfig, err := ExtractWalletCreationConfigFromCli(cliCtx, keymanagerKind)
if err != nil {
return nil, err
}
dir := createWalletConfig.WalletCfg.WalletDir
dirExists, err := wallet.Exists(dir)
if err != nil {
return nil, err
}
if dirExists {
return nil, errors.New("a wallet already exists at this location. Please input an" +
" alternative location for the new wallet or remove the current wallet")
}
w, err := CreateWalletWithKeymanager(cliCtx.Context, createWalletConfig)
if err != nil {
return nil, errors.Wrap(err, "could not create wallet")
}
return w, nil
}
// CreateWalletWithKeymanager specified by configuration options.
func CreateWalletWithKeymanager(ctx context.Context, cfg *CreateWalletConfig) (*wallet.Wallet, error) {
func (acm *AccountsCLIManager) WalletCreate(ctx context.Context) (*wallet.Wallet, error) {
w := wallet.New(&wallet.Config{
WalletDir: cfg.WalletCfg.WalletDir,
KeymanagerKind: cfg.WalletCfg.KeymanagerKind,
WalletPassword: cfg.WalletCfg.WalletPassword,
WalletDir: acm.walletDir,
KeymanagerKind: acm.keymanagerKind,
WalletPassword: acm.walletPassword,
})
var err error
switch w.KeymanagerKind() {
@@ -105,27 +47,27 @@ func CreateWalletWithKeymanager(ctx context.Context, cfg *CreateWalletConfig) (*
return nil, err
}
log.WithField("--wallet-dir", cfg.WalletCfg.WalletDir).Info(
log.WithField("--wallet-dir", acm.walletDir).Info(
"Successfully created wallet with ability to import keystores",
)
case keymanager.Derived:
if err = createDerivedKeymanagerWallet(
ctx,
w,
cfg.Mnemonic25thWord,
cfg.SkipMnemonicConfirm,
cfg.NumAccounts,
acm.mnemonic25thWord,
acm.skipMnemonicConfirm,
acm.numAccounts,
); err != nil {
return nil, errors.Wrap(err, "could not initialize wallet")
}
log.WithField("--wallet-dir", cfg.WalletCfg.WalletDir).Info(
log.WithField("--wallet-dir", acm.walletDir).Info(
"Successfully created HD wallet from mnemonic and regenerated accounts",
)
case keymanager.Remote:
if err = createRemoteKeymanagerWallet(ctx, w, cfg.RemoteKeymanagerOpts); err != nil {
if err = createRemoteKeymanagerWallet(ctx, w, acm.keymanagerOpts); err != nil {
return nil, errors.Wrap(err, "could not initialize wallet")
}
log.WithField("--wallet-dir", cfg.WalletCfg.WalletDir).Info(
log.WithField("--wallet-dir", acm.walletDir).Info(
"Successfully created wallet with remote keymanager configuration",
)
case keymanager.Web3Signer:
@@ -136,84 +78,6 @@ func CreateWalletWithKeymanager(ctx context.Context, cfg *CreateWalletConfig) (*
return w, nil
}
func extractKeymanagerKindFromCli(cliCtx *cli.Context) (keymanager.Kind, error) {
return inputKeymanagerKind(cliCtx)
}
// ExtractWalletCreationConfigFromCli prompts the user for wallet creation input.
func ExtractWalletCreationConfigFromCli(cliCtx *cli.Context, keymanagerKind keymanager.Kind) (*CreateWalletConfig, error) {
walletDir, err := userprompt.InputDirectory(cliCtx, userprompt.WalletDirPromptText, flags.WalletDirFlag)
if err != nil {
return nil, err
}
walletPassword, err := prompt.InputPassword(
cliCtx,
flags.WalletPasswordFileFlag,
wallet.NewWalletPasswordPromptText,
wallet.ConfirmPasswordPromptText,
true, /* Should confirm password */
prompt.ValidatePasswordInput,
)
if err != nil {
return nil, err
}
createWalletConfig := &CreateWalletConfig{
WalletCfg: &wallet.Config{
WalletDir: walletDir,
KeymanagerKind: keymanagerKind,
WalletPassword: walletPassword,
},
SkipMnemonicConfirm: cliCtx.Bool(flags.SkipDepositConfirmationFlag.Name),
}
skipMnemonic25thWord := cliCtx.IsSet(flags.SkipMnemonic25thWordCheckFlag.Name)
has25thWordFile := cliCtx.IsSet(flags.Mnemonic25thWordFileFlag.Name)
if keymanagerKind == keymanager.Derived {
numAccounts, err := inputNumAccounts(cliCtx)
if err != nil {
return nil, errors.Wrap(err, "could not get number of accounts to generate")
}
createWalletConfig.NumAccounts = int(numAccounts)
}
if keymanagerKind == keymanager.Derived && !skipMnemonic25thWord && !has25thWordFile {
resp, err := prompt.ValidatePrompt(
os.Stdin, newMnemonicPassphraseYesNoText, prompt.ValidateYesOrNo,
)
if err != nil {
return nil, errors.Wrap(err, "could not validate choice")
}
if strings.EqualFold(resp, "y") {
mnemonicPassphrase, err := prompt.InputPassword(
cliCtx,
flags.Mnemonic25thWordFileFlag,
newMnemonicPassphrasePromptText,
"Confirm mnemonic passphrase",
true, /* Should confirm password */
func(input string) error {
if strings.TrimSpace(input) == "" {
return errors.New("input cannot be empty")
}
return nil
},
)
if err != nil {
return nil, err
}
createWalletConfig.Mnemonic25thWord = mnemonicPassphrase
}
}
if keymanagerKind == keymanager.Remote {
opts, err := userprompt.InputRemoteKeymanagerConfig(cliCtx)
if err != nil {
return nil, errors.Wrap(err, "could not input remote keymanager config")
}
createWalletConfig.RemoteKeymanagerOpts = opts
}
if keymanagerKind == keymanager.Web3Signer {
return nil, errors.New("web3signer keymanager does not require persistent wallets.")
}
return createWalletConfig, nil
}
func CreateLocalKeymanagerWallet(_ context.Context, wallet *wallet.Wallet) error {
if wallet == nil {
return errors.New("nil wallet")
@@ -267,47 +131,3 @@ func createRemoteKeymanagerWallet(ctx context.Context, wallet *wallet.Wallet, op
}
return nil
}
func inputKeymanagerKind(cliCtx *cli.Context) (keymanager.Kind, error) {
if cliCtx.IsSet(flags.KeymanagerKindFlag.Name) {
return keymanager.ParseKind(cliCtx.String(flags.KeymanagerKindFlag.Name))
}
promptSelect := promptui.Select{
Label: "Select a type of wallet",
Items: []string{
wallet.KeymanagerKindSelections[keymanager.Local],
wallet.KeymanagerKindSelections[keymanager.Derived],
wallet.KeymanagerKindSelections[keymanager.Remote],
wallet.KeymanagerKindSelections[keymanager.Web3Signer],
},
}
selection, _, err := promptSelect.Run()
if err != nil {
return keymanager.Local, fmt.Errorf("could not select wallet type: %w", userprompt.FormatPromptError(err))
}
return keymanager.Kind(selection), nil
}
// TODO(mikeneuder): Remove duplicate function when migration wallet create
// to cmd/validator/wallet.
func inputNumAccounts(cliCtx *cli.Context) (int64, error) {
if cliCtx.IsSet(flags.NumAccountsFlag.Name) {
numAccounts := cliCtx.Int64(flags.NumAccountsFlag.Name)
if numAccounts <= 0 {
return 0, errors.New("must recover at least 1 account")
}
return numAccounts, nil
}
numAccounts, err := prompt.ValidatePrompt(os.Stdin, "Enter how many accounts you would like to generate from the mnemonic", prompt.ValidateNumber)
if err != nil {
return 0, err
}
numAccountsInt, err := strconv.Atoi(numAccounts)
if err != nil {
return 0, err
}
if numAccountsInt <= 0 {
return 0, errors.New("must recover at least 1 account")
}
return int64(numAccountsInt), nil
}

View File

@@ -15,7 +15,6 @@ go_test(
"//testing/assert:go_default_library",
"//testing/require:go_default_library",
"//validator/accounts:go_default_library",
"//validator/accounts/wallet:go_default_library",
"//validator/keymanager:go_default_library",
"//validator/keymanager/remote-web3signer:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",

View File

@@ -20,7 +20,6 @@ import (
"github.com/prysmaticlabs/prysm/v3/testing/assert"
"github.com/prysmaticlabs/prysm/v3/testing/require"
"github.com/prysmaticlabs/prysm/v3/validator/accounts"
"github.com/prysmaticlabs/prysm/v3/validator/accounts/wallet"
"github.com/prysmaticlabs/prysm/v3/validator/keymanager"
remoteweb3signer "github.com/prysmaticlabs/prysm/v3/validator/keymanager/remote-web3signer"
logtest "github.com/sirupsen/logrus/hooks/test"
@@ -48,13 +47,15 @@ func TestNode_Builds(t *testing.T) {
set.String("verbosity", "debug", "log verbosity")
require.NoError(t, set.Set(flags.WalletPasswordFileFlag.Name, passwordFile))
ctx := cli.NewContext(&app, set, nil)
_, err := accounts.CreateWalletWithKeymanager(ctx.Context, &accounts.CreateWalletConfig{
WalletCfg: &wallet.Config{
WalletDir: dir,
KeymanagerKind: keymanager.Local,
WalletPassword: walletPassword,
},
})
opts := []accounts.Option{
accounts.WithWalletDir(dir),
accounts.WithKeymanagerType(keymanager.Local),
accounts.WithWalletPassword(walletPassword),
accounts.WithSkipMnemonicConfirm(true),
}
acc, err := accounts.NewCLIManager(opts...)
require.NoError(t, err)
_, err = acc.WalletCreate(ctx.Context)
require.NoError(t, err)
valClient, err := NewValidatorClient(ctx)

View File

@@ -21,7 +21,6 @@ import (
"github.com/prysmaticlabs/prysm/v3/validator/accounts"
"github.com/prysmaticlabs/prysm/v3/validator/accounts/iface"
mock "github.com/prysmaticlabs/prysm/v3/validator/accounts/testing"
"github.com/prysmaticlabs/prysm/v3/validator/accounts/wallet"
"github.com/prysmaticlabs/prysm/v3/validator/client"
"github.com/prysmaticlabs/prysm/v3/validator/keymanager"
"github.com/prysmaticlabs/prysm/v3/validator/keymanager/derived"
@@ -38,14 +37,15 @@ func TestServer_ListAccounts(t *testing.T) {
localWalletDir := setupWalletDir(t)
defaultWalletPath = localWalletDir
// We attempt to create the wallet.
w, err := accounts.CreateWalletWithKeymanager(ctx, &accounts.CreateWalletConfig{
WalletCfg: &wallet.Config{
WalletDir: defaultWalletPath,
KeymanagerKind: keymanager.Derived,
WalletPassword: strongPass,
},
SkipMnemonicConfirm: true,
})
opts := []accounts.Option{
accounts.WithWalletDir(defaultWalletPath),
accounts.WithKeymanagerType(keymanager.Derived),
accounts.WithWalletPassword(strongPass),
accounts.WithSkipMnemonicConfirm(true),
}
acc, err := accounts.NewCLIManager(opts...)
require.NoError(t, err)
w, err := acc.WalletCreate(ctx)
require.NoError(t, err)
km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false})
require.NoError(t, err)
@@ -110,14 +110,15 @@ func TestServer_BackupAccounts(t *testing.T) {
localWalletDir := setupWalletDir(t)
defaultWalletPath = localWalletDir
// We attempt to create the wallet.
w, err := accounts.CreateWalletWithKeymanager(ctx, &accounts.CreateWalletConfig{
WalletCfg: &wallet.Config{
WalletDir: defaultWalletPath,
KeymanagerKind: keymanager.Derived,
WalletPassword: strongPass,
},
SkipMnemonicConfirm: true,
})
opts := []accounts.Option{
accounts.WithWalletDir(defaultWalletPath),
accounts.WithKeymanagerType(keymanager.Derived),
accounts.WithWalletPassword(strongPass),
accounts.WithSkipMnemonicConfirm(true),
}
acc, err := accounts.NewCLIManager(opts...)
require.NoError(t, err)
w, err := acc.WalletCreate(ctx)
require.NoError(t, err)
km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false})
require.NoError(t, err)
@@ -220,14 +221,15 @@ func TestServer_VoluntaryExit(t *testing.T) {
localWalletDir := setupWalletDir(t)
defaultWalletPath = localWalletDir
// We attempt to create the wallet.
w, err := accounts.CreateWalletWithKeymanager(ctx, &accounts.CreateWalletConfig{
WalletCfg: &wallet.Config{
WalletDir: defaultWalletPath,
KeymanagerKind: keymanager.Derived,
WalletPassword: strongPass,
},
SkipMnemonicConfirm: true,
})
opts := []accounts.Option{
accounts.WithWalletDir(defaultWalletPath),
accounts.WithKeymanagerType(keymanager.Derived),
accounts.WithWalletPassword(strongPass),
accounts.WithSkipMnemonicConfirm(true),
}
acc, err := accounts.NewCLIManager(opts...)
require.NoError(t, err)
w, err := acc.WalletCreate(ctx)
require.NoError(t, err)
km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false})
require.NoError(t, err)

View File

@@ -39,7 +39,7 @@ func (s *Server) GetBeaconNodeConnection(ctx context.Context, _ *emptypb.Empty)
}
// GetLogsEndpoints for the beacon and validator client.
func (_ *Server) GetLogsEndpoints(_ context.Context, _ *emptypb.Empty) (*validatorpb.LogsEndpointResponse, error) {
func (*Server) GetLogsEndpoints(_ context.Context, _ *emptypb.Empty) (*validatorpb.LogsEndpointResponse, error) {
return nil, status.Error(codes.Unimplemented, "unimplemented")
}

View File

@@ -9,7 +9,6 @@ import (
pb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1/validator-client"
"github.com/prysmaticlabs/prysm/v3/testing/require"
"github.com/prysmaticlabs/prysm/v3/validator/accounts"
"github.com/prysmaticlabs/prysm/v3/validator/accounts/wallet"
"github.com/prysmaticlabs/prysm/v3/validator/db/kv"
"github.com/prysmaticlabs/prysm/v3/validator/keymanager"
"github.com/prysmaticlabs/prysm/v3/validator/slashing-protection-history/format"
@@ -34,14 +33,15 @@ func TestImportSlashingProtection_Preconditions(t *testing.T) {
require.ErrorContains(t, "err finding validator database at path", err)
// Create Wallet and add to server for more realistic testing.
w, err := accounts.CreateWalletWithKeymanager(ctx, &accounts.CreateWalletConfig{
WalletCfg: &wallet.Config{
WalletDir: defaultWalletPath,
KeymanagerKind: keymanager.Local,
WalletPassword: strongPass,
},
SkipMnemonicConfirm: true,
})
opts := []accounts.Option{
accounts.WithWalletDir(defaultWalletPath),
accounts.WithKeymanagerType(keymanager.Local),
accounts.WithWalletPassword(strongPass),
accounts.WithSkipMnemonicConfirm(true),
}
acc, err := accounts.NewCLIManager(opts...)
require.NoError(t, err)
w, err := acc.WalletCreate(ctx)
require.NoError(t, err)
s.wallet = w

View File

@@ -46,14 +46,15 @@ func TestServer_ListKeystores(t *testing.T) {
ctx := context.Background()
localWalletDir := setupWalletDir(t)
defaultWalletPath = localWalletDir
w, err := accounts.CreateWalletWithKeymanager(ctx, &accounts.CreateWalletConfig{
WalletCfg: &wallet.Config{
WalletDir: defaultWalletPath,
KeymanagerKind: keymanager.Derived,
WalletPassword: strongPass,
},
SkipMnemonicConfirm: true,
})
opts := []accounts.Option{
accounts.WithWalletDir(defaultWalletPath),
accounts.WithKeymanagerType(keymanager.Derived),
accounts.WithWalletPassword(strongPass),
accounts.WithSkipMnemonicConfirm(true),
}
acc, err := accounts.NewCLIManager(opts...)
require.NoError(t, err)
w, err := acc.WalletCreate(ctx)
require.NoError(t, err)
km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false})
require.NoError(t, err)
@@ -102,14 +103,15 @@ func TestServer_ImportKeystores(t *testing.T) {
ctx := context.Background()
localWalletDir := setupWalletDir(t)
defaultWalletPath = localWalletDir
w, err := accounts.CreateWalletWithKeymanager(ctx, &accounts.CreateWalletConfig{
WalletCfg: &wallet.Config{
WalletDir: defaultWalletPath,
KeymanagerKind: keymanager.Derived,
WalletPassword: strongPass,
},
SkipMnemonicConfirm: true,
})
opts := []accounts.Option{
accounts.WithWalletDir(defaultWalletPath),
accounts.WithKeymanagerType(keymanager.Derived),
accounts.WithWalletPassword(strongPass),
accounts.WithSkipMnemonicConfirm(true),
}
acc, err := accounts.NewCLIManager(opts...)
require.NoError(t, err)
w, err := acc.WalletCreate(ctx)
require.NoError(t, err)
km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false})
require.NoError(t, err)
@@ -489,14 +491,15 @@ func setupServerWithWallet(t testing.TB) *Server {
ctx := context.Background()
localWalletDir := setupWalletDir(t)
defaultWalletPath = localWalletDir
w, err := accounts.CreateWalletWithKeymanager(ctx, &accounts.CreateWalletConfig{
WalletCfg: &wallet.Config{
WalletDir: defaultWalletPath,
KeymanagerKind: keymanager.Derived,
WalletPassword: strongPass,
},
SkipMnemonicConfirm: true,
})
opts := []accounts.Option{
accounts.WithWalletDir(defaultWalletPath),
accounts.WithKeymanagerType(keymanager.Derived),
accounts.WithWalletPassword(strongPass),
accounts.WithSkipMnemonicConfirm(true),
}
acc, err := accounts.NewCLIManager(opts...)
require.NoError(t, err)
w, err := acc.WalletCreate(ctx)
require.NoError(t, err)
km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false})
require.NoError(t, err)

View File

@@ -65,14 +65,17 @@ func (s *Server) CreateWallet(ctx context.Context, req *pb.CreateWalletRequest)
return nil, status.Errorf(codes.InvalidArgument, "Password too weak: %v", err)
}
if req.Keymanager == pb.KeymanagerKind_IMPORTED {
_, err := accounts.CreateWalletWithKeymanager(ctx, &accounts.CreateWalletConfig{
WalletCfg: &wallet.Config{
WalletDir: walletDir,
KeymanagerKind: keymanager.Local,
WalletPassword: req.WalletPassword,
},
SkipMnemonicConfirm: true,
})
opts := []accounts.Option{
accounts.WithWalletDir(walletDir),
accounts.WithKeymanagerType(keymanager.Local),
accounts.WithWalletPassword(req.WalletPassword),
accounts.WithSkipMnemonicConfirm(true),
}
acc, err := accounts.NewCLIManager(opts...)
if err != nil {
return nil, err
}
_, err = acc.WalletCreate(ctx)
if err != nil {
return nil, err
}
@@ -223,7 +226,7 @@ func (s *Server) RecoverWallet(ctx context.Context, req *pb.RecoverWalletRequest
// can indeed be decrypted using a password in the request. If there is no issue,
// we return an empty response with no error. If the password is incorrect for a single keystore,
// we return an appropriate error.
func (_ *Server) ValidateKeystores(
func (*Server) ValidateKeystores(
_ context.Context, req *pb.ValidateKeystoresRequest,
) (*emptypb.Empty, error) {
if req.KeystoresPassword == "" {

View File

@@ -34,14 +34,15 @@ func TestServer_CreateWallet_Local(t *testing.T) {
ctx := context.Background()
localWalletDir := setupWalletDir(t)
defaultWalletPath = localWalletDir
w, err := accounts.CreateWalletWithKeymanager(ctx, &accounts.CreateWalletConfig{
WalletCfg: &wallet.Config{
WalletDir: defaultWalletPath,
KeymanagerKind: keymanager.Derived,
WalletPassword: strongPass,
},
SkipMnemonicConfirm: true,
})
opts := []accounts.Option{
accounts.WithWalletDir(defaultWalletPath),
accounts.WithKeymanagerType(keymanager.Derived),
accounts.WithWalletPassword(strongPass),
accounts.WithSkipMnemonicConfirm(true),
}
acc, err := accounts.NewCLIManager(opts...)
require.NoError(t, err)
w, err := acc.WalletCreate(ctx)
require.NoError(t, err)
km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false})
require.NoError(t, err)
@@ -309,14 +310,15 @@ func TestServer_WalletConfig(t *testing.T) {
walletDir: defaultWalletPath,
}
// We attempt to create the wallet.
w, err := accounts.CreateWalletWithKeymanager(ctx, &accounts.CreateWalletConfig{
WalletCfg: &wallet.Config{
WalletDir: defaultWalletPath,
KeymanagerKind: keymanager.Local,
WalletPassword: strongPass,
},
SkipMnemonicConfirm: true,
})
opts := []accounts.Option{
accounts.WithWalletDir(defaultWalletPath),
accounts.WithKeymanagerType(keymanager.Local),
accounts.WithWalletPassword(strongPass),
accounts.WithSkipMnemonicConfirm(true),
}
acc, err := accounts.NewCLIManager(opts...)
require.NoError(t, err)
w, err := acc.WalletCreate(ctx)
require.NoError(t, err)
km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false})
require.NoError(t, err)