mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 15:37:56 -05:00
Accounts Revamp Fixes: "Overall" Wallet Improvements (#6736)
* change default wallet dir path to not be hidden * gaz + pass wallet dir * gaz + move const to flags * move to flags * move to flags * use filepath join in order to create a valid dir name * add wallet dir * return err no wallet found issues * fix up edit remote * all tests passing * fix test * create or open wallet * ivan feedback * enter password for account with pubkey * Update validator/accounts/v2/accounts_create.go Co-authored-by: Ivan Martinez <ivanthegreatdev@gmail.com> * works * preston feedback * nothing to export * fmt * test for create or open * gaz Co-authored-by: shayzluf <thezluf@gmail.com> Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com> Co-authored-by: Ivan Martinez <ivanthegreatdev@gmail.com>
This commit is contained in:
@@ -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},
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 */)
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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 := wallet.SaveWallet(); err != nil {
|
||||
return errors.Wrap(err, "could not save wallet")
|
||||
}
|
||||
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,
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
addr, err = prompt.Run()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
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
|
||||
},
|
||||
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
|
||||
}
|
||||
}
|
||||
remoteAddr, err := prompt.Run()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if key == "" {
|
||||
prompt := promptui.Prompt{
|
||||
Label: "Path to TLS key (such as /path/to/client.key)",
|
||||
Validate: validateCertPath,
|
||||
}
|
||||
key, err = prompt.Run()
|
||||
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,
|
||||
}
|
||||
clientKeyPath, 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,
|
||||
}
|
||||
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:
|
||||
|
||||
@@ -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,11 +26,22 @@ import (
|
||||
keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4"
|
||||
)
|
||||
|
||||
const (
|
||||
// KeymanagerConfigFileName for the keymanager used by the wallet: direct, derived, or remote.
|
||||
KeymanagerConfigFileName = "keymanageropts.json"
|
||||
// DirectoryPermissions for directories created under the wallet path.
|
||||
DirectoryPermissions = os.ModePerm
|
||||
)
|
||||
|
||||
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)",
|
||||
@@ -37,29 +49,12 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
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.
|
||||
DirectoryPermissions = os.ModePerm
|
||||
)
|
||||
|
||||
var (
|
||||
// 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",
|
||||
)
|
||||
)
|
||||
|
||||
// Wallet is a primitive in Prysm's v2 account management which
|
||||
// has the capability of creating new accounts, reading existing accounts,
|
||||
// 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 {
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
// 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",
|
||||
// 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",
|
||||
}
|
||||
// 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{
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -72,7 +72,7 @@ var appFlags = []cli.Flag{
|
||||
flags.SlasherRPCProviderFlag,
|
||||
flags.SlasherCertFlag,
|
||||
flags.WalletPasswordsDirFlag,
|
||||
flags.PasswordFileFlag,
|
||||
flags.WalletPasswordFileFlag,
|
||||
flags.WalletDirFlag,
|
||||
cmd.MinimalConfigFlag,
|
||||
cmd.E2EConfigFlag,
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user