Accounts-V2: Cleanup password entry and disable export (#6778)

* Cleanup prompts
* Merge branch 'master' of github.com:prysmaticlabs/prysm into cleanup-accounts-v2
* Fix
* add todo
* Merge branch 'master' of github.com:prysmaticlabs/prysm into cleanup-accounts-v2
* Clean
* Merge refs/heads/master into cleanup-accounts-v2
* Merge refs/heads/master into cleanup-accounts-v2
* Change with error
* Merge branch 'cleanup-accounts-v2' of github.com:prysmaticlabs/prysm into cleanup-accounts-v2
* Merge refs/heads/master into cleanup-accounts-v2
* cleanup and remove deposit tx file
* display deposit data
This commit is contained in:
Ivan Martinez
2020-07-30 11:30:19 -04:00
committed by GitHub
parent bcea2c1b22
commit 48bfffbb3e
13 changed files with 51 additions and 191 deletions

View File

@@ -84,7 +84,7 @@ func PasswordPrompt(promptText string, validateFunc func(string) error) (string,
var responseValid bool
var response string
for !responseValid {
fmt.Printf("%s:\n", au.Bold(promptText))
fmt.Printf("\n%s: ", au.Bold(promptText))
bytePassword, err := terminal.ReadPassword(int(os.Stdin.Fd()))
if err != nil {
return "", err

View File

@@ -50,7 +50,6 @@ go_test(
name = "go_default_test",
srcs = [
"accounts_create_test.go",
"accounts_export_test.go",
"accounts_import_test.go",
"accounts_list_test.go",
"consts_test.go",

View File

@@ -29,6 +29,7 @@ func CreateAccount(cliCtx *cli.Context) error {
if err != nil {
return errors.Wrap(err, "could not initialize keymanager")
}
log.Info("Creating a new account...")
switch wallet.KeymanagerKind() {
case v2keymanager.Remote:
return errors.New("cannot create a new account for a remote keymanager")

View File

@@ -2,7 +2,6 @@ package v2
import (
"archive/zip"
"context"
"fmt"
"io"
"os"
@@ -24,45 +23,8 @@ const archiveFilename = "backup.zip"
// ExportAccount creates a zip archive of the selected accounts to be used in the future for importing accounts.
func ExportAccount(cliCtx *cli.Context) error {
outputDir, err := inputDirectory(cliCtx, exportDirPromptText, flags.BackupDirFlag)
if err != nil {
return errors.Wrap(err, "could not parse output directory")
}
wallet, err := OpenWallet(cliCtx)
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 */)
if err != nil {
return errors.Wrap(err, "could not initialize keymanager")
}
km, ok := keymanager.(*direct.Keymanager)
if !ok {
return errors.New("can only export accounts for a non-HD wallet")
}
allAccounts, err := km.ValidatingAccountNames()
if err != nil {
return errors.Wrap(err, "could not get account names")
}
accounts, err := selectAccounts(cliCtx, allAccounts)
if err != nil {
return errors.Wrap(err, "could not select accounts")
}
if len(accounts) == 0 {
return errors.New("no accounts to export")
}
if err := wallet.zipAccounts(accounts, outputDir); err != nil {
return errors.Wrap(err, "could not export accounts")
}
if err := logAccountsExported(wallet, km, accounts); err != nil {
return errors.Wrap(err, "could not log out exported accounts")
}
return nil
// TODO(#6777): Re-enable export command.
return errors.New("this feature is unimplemented")
}
func selectAccounts(cliCtx *cli.Context, accounts []string) ([]string, error) {

View File

@@ -1,101 +0,0 @@
package v2
import (
"context"
"crypto/rand"
"fmt"
"math/big"
"os"
"path/filepath"
"testing"
"github.com/prysmaticlabs/prysm/shared/testutil"
"github.com/prysmaticlabs/prysm/shared/testutil/require"
v2keymanager "github.com/prysmaticlabs/prysm/validator/keymanager/v2"
"github.com/prysmaticlabs/prysm/validator/keymanager/v2/direct"
)
func TestZipAndUnzip(t *testing.T) {
t.Skip("skipping until exporting is implemented")
walletDir, passwordsDir, _ := setupWalletAndPasswordsDir(t)
randPath, err := rand.Int(rand.Reader, big.NewInt(1000000))
require.NoError(t, err, "Could not generate random file path")
exportDir := filepath.Join(testutil.TempDir(), fmt.Sprintf("/%d", randPath), "export")
importDir := filepath.Join(testutil.TempDir(), fmt.Sprintf("/%d", randPath), "import")
t.Cleanup(func() {
require.NoError(t, os.RemoveAll(exportDir), "Failed to remove directory")
require.NoError(t, os.RemoveAll(importDir), "Failed to remove directory")
})
cliCtx := setupWalletCtx(t, &testWalletConfig{
walletDir: walletDir,
passwordsDir: passwordsDir,
exportDir: exportDir,
keymanagerKind: v2keymanager.Direct,
})
wallet, err := NewWallet(cliCtx, v2keymanager.Direct)
require.NoError(t, err)
require.NoError(t, wallet.SaveWallet())
ctx := context.Background()
keymanagerCfg := direct.DefaultConfig()
keymanagerCfg.AccountPasswordsDirectory = passwordsDir
keymanager, err := direct.NewKeymanager(
ctx,
wallet,
keymanagerCfg,
)
require.NoError(t, err)
_, err = keymanager.CreateAccount(ctx, password)
require.NoError(t, err)
accounts, err := keymanager.ValidatingAccountNames()
require.NoError(t, err)
if len(accounts) == 0 {
t.Fatal("Expected more accounts, received 0")
}
err = wallet.zipAccounts(accounts, exportDir)
require.NoError(t, err)
if _, err := os.Stat(filepath.Join(exportDir, archiveFilename)); os.IsNotExist(err) {
t.Fatal("Expected file to exist")
}
}
func TestExport_Noninteractive(t *testing.T) {
walletDir, passwordsDir, _ := setupWalletAndPasswordsDir(t)
randPath, err := rand.Int(rand.Reader, big.NewInt(1000000))
require.NoError(t, err, "Could not generate random file path")
exportDir := filepath.Join(testutil.TempDir(), fmt.Sprintf("/%d", randPath), "export")
t.Cleanup(func() {
require.NoError(t, os.RemoveAll(exportDir), "Failed to remove directory")
})
accounts := "all"
cliCtx := setupWalletCtx(t, &testWalletConfig{
walletDir: walletDir,
passwordsDir: passwordsDir,
exportDir: exportDir,
accountsToExport: accounts,
keymanagerKind: v2keymanager.Direct,
})
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))
keymanager, err := direct.NewKeymanager(
ctx,
wallet,
keymanagerCfg,
)
require.NoError(t, err)
_, err = keymanager.CreateAccount(ctx, password)
require.NoError(t, err)
require.NoError(t, ExportAccount(cliCtx))
if _, err := os.Stat(filepath.Join(exportDir, archiveFilename)); os.IsNotExist(err) {
t.Fatal("Expected file to exist")
}
}

View File

@@ -110,17 +110,17 @@ func listDirectKeymanagerAccounts(
if !showDepositData {
continue
}
enc, err := wallet.ReadFileAtPath(ctx, accountNames[i], direct.DepositTransactionFileName)
enc, err := wallet.ReadFileAtPath(ctx, accountNames[i], direct.DepositDataFileName)
if err != nil {
return errors.Wrapf(err, "could not read file for account: %s", direct.DepositTransactionFileName)
return errors.Wrapf(err, "could not read file for account: %s", direct.DepositDataFileName)
}
fmt.Printf(
"%s %s\n",
"(deposit tx file)",
filepath.Join(wallet.AccountsDir(), accountNames[i], direct.DepositTransactionFileName),
"(deposit_data.ssz file)",
filepath.Join(wallet.AccountsDir(), accountNames[i], direct.DepositDataFileName),
)
fmt.Printf(`
======================Deposit Transaction Data=====================
======================SSZ Deposit Data=====================
%#x
@@ -195,17 +195,17 @@ func listDerivedKeymanagerAccounts(
if !showDepositData {
continue
}
enc, err := wallet.ReadFileAtPath(ctx, withdrawalKeyPath, derived.DepositTransactionFileName)
enc, err := wallet.ReadFileAtPath(ctx, withdrawalKeyPath, derived.DepositDataFileName)
if err != nil {
return errors.Wrapf(err, "could not read file for account: %s", direct.DepositTransactionFileName)
return errors.Wrapf(err, "could not read file for account: %s", direct.DepositDataFileName)
}
fmt.Printf(
"%s %s\n",
"(deposit tx file)",
filepath.Join(wallet.AccountsDir(), withdrawalKeyPath, derived.DepositTransactionFileName),
filepath.Join(wallet.AccountsDir(), withdrawalKeyPath, derived.DepositDataFileName),
)
fmt.Printf(`
======================Deposit Transaction Data=====================
======================SSZ Deposit Data=====================
%#x

View File

@@ -59,7 +59,7 @@ func TestListAccounts_DirectKeymanager(t *testing.T) {
for i := 0; i < numAccounts; i++ {
accountName, err := keymanager.CreateAccount(ctx, "hello world")
require.NoError(t, err)
depositData, err := wallet.ReadFileAtPath(ctx, accountName, direct.DepositTransactionFileName)
depositData, err := wallet.ReadFileAtPath(ctx, accountName, direct.DepositDataFileName)
require.NoError(t, err)
depositDataForAccounts[i] = depositData
keystoreFileName, err := wallet.FileNameAtPath(ctx, accountName, direct.KeystoreFileName)
@@ -161,7 +161,7 @@ func TestListAccounts_DerivedKeymanager(t *testing.T) {
_, err := keymanager.CreateAccount(ctx, false /*logAccountInfo*/)
require.NoError(t, err)
withdrawalKeyPath := fmt.Sprintf(derived.WithdrawalKeyDerivationPathTemplate, i)
depositData, err := wallet.ReadFileAtPath(ctx, withdrawalKeyPath, direct.DepositTransactionFileName)
depositData, err := wallet.ReadFileAtPath(ctx, withdrawalKeyPath, direct.DepositDataFileName)
require.NoError(t, err)
depositDataForAccounts[i] = depositData
unixTimestamp, err := wallet.ReadFileAtPath(ctx, withdrawalKeyPath, direct.TimestampFileName)

View File

@@ -27,7 +27,7 @@ const (
confirmPasswordPromptText = "Confirm password"
walletPasswordPromptText = "Wallet password"
newAccountPasswordPromptText = "New account password"
passwordForAccountPromptText = "Enter password for account with public key %#x"
passwordForAccountPromptText = "Enter password for account with public key %s"
)
type passwordConfirm int

View File

@@ -148,9 +148,11 @@ func OpenWallet(cliCtx *cli.Context) (*Wallet, error) {
}
walletPath := filepath.Join(walletDir, keymanagerKind.String())
w := &Wallet{
walletDir: walletDir,
accountsPath: walletPath,
keymanagerKind: keymanagerKind,
}
log.Infof("%s %s", au.BrightMagenta("(wallet directory)"), w.walletDir)
if keymanagerKind == v2keymanager.Derived {
walletPassword, err := inputPassword(
cliCtx,
@@ -176,7 +178,7 @@ func OpenWallet(cliCtx *cli.Context) (*Wallet, error) {
au := aurora.NewAurora(true)
log.Infof("%s %s", au.BrightMagenta("(account passwords path)"), w.passwordsDir)
}
log.Info("Successfully opened wallet")
log.Debug("Successfully opened wallet")
return w, nil
}
@@ -427,6 +429,7 @@ func (w *Wallet) enterPasswordForAccount(cliCtx *cli.Context, accountName string
return err
}
} else {
pubKeyStr := fmt.Sprintf("%#x", bytesutil.Trunc(pubKey))
attemptingPassword := true
// Loop asking for the password until the user enters it correctly.
for attemptingPassword {
@@ -434,21 +437,22 @@ func (w *Wallet) enterPasswordForAccount(cliCtx *cli.Context, accountName string
password, err = inputWeakPassword(
cliCtx,
flags.AccountPasswordFileFlag,
fmt.Sprintf(passwordForAccountPromptText, bytesutil.Trunc(pubKey)),
fmt.Sprintf(passwordForAccountPromptText, au.BrightGreen(pubKeyStr)),
)
if err != nil {
return errors.Wrap(err, "could not input password")
}
err = w.checkPasswordForAccount(accountName, password)
if err != nil && strings.Contains(err.Error(), "invalid checksum") {
fmt.Println(au.Red("Incorrect password entered, please try again"))
fmt.Print(au.Red("X").Bold())
fmt.Print(au.Red("\nIncorrect password entered, please try again"))
continue
}
if err != nil {
return err
}
attemptingPassword = false
fmt.Print(au.Green("✔️\n").Bold())
}
}
ctx := context.Background()

View File

@@ -73,7 +73,7 @@ func RecoverWallet(cliCtx *cli.Context) error {
}
}
log.WithField("wallet-path", wallet.AccountsDir()).Infof(
"Successfully recovered HD wallet with %d accounts. Please use accounts-v2 list to view details for your accounts.",
"Successfully recovered HD wallet with %d accounts. Please use accounts-v2 list to view details for your accounts",
numAccounts,
)
return nil

View File

@@ -47,9 +47,6 @@ const (
// keys for Prysm eth2 validators. According to EIP-2334, the format is as follows:
// m / purpose / coin_type / account_index / withdrawal_key / validating_key
ValidatingKeyDerivationPathTemplate = "m/12381/3600/%d/0/0"
// DepositTransactionFileName for the encoded, eth1 raw deposit tx data
// for a validator account.
DepositTransactionFileName = "deposit_transaction.rlp"
// DepositDataFileName for the raw, ssz-encoded deposit data object.
DepositDataFileName = "deposit_data.ssz"
// EncryptedSeedFileName for persisting a wallet's seed when using a derived keymanager.
@@ -308,21 +305,11 @@ func (dr *Keymanager) CreateAccount(ctx context.Context, logAccountInfo bool) (s
if err != nil {
return "", err
}
tx, depositData, err := depositutil.GenerateDepositTransaction(blsValidatingKey, blsWithdrawalKey)
_, depositData, err := depositutil.GenerateDepositTransaction(blsValidatingKey, blsWithdrawalKey)
if err != nil {
return "", errors.Wrap(err, "could not generate deposit transaction data")
}
if logAccountInfo {
// Log the deposit transaction data to the user.
depositutil.LogDepositTransaction(log, tx)
}
// We write the raw deposit transaction as an .rlp encoded file.
if err := dr.wallet.WriteFileAtPath(ctx, withdrawalKeyPath, DepositTransactionFileName, tx.Data()); err != nil {
return "", errors.Wrapf(err, "could not write for account %s: %s", withdrawalKeyPath, DepositTransactionFileName)
}
// We write the ssz-encoded deposit data to disk as a .ssz file.
encodedDepositData, err := ssz.Marshal(depositData)
if err != nil {
@@ -332,6 +319,16 @@ func (dr *Keymanager) CreateAccount(ctx context.Context, logAccountInfo bool) (s
return "", errors.Wrapf(err, "could not write for account %s: %s", withdrawalKeyPath, encodedDepositData)
}
if logAccountInfo {
// Log the deposit transaction data to the user.
fmt.Printf(`
========================SSZ Deposit Data===============================
%#x
===================================================================`, encodedDepositData)
}
// Finally, write the account creation timestamps as a files.
createdAt := roughtime.Now().Unix()
createdAtStr := strconv.FormatInt(createdAt, 10)

View File

@@ -32,9 +32,6 @@ import (
var log = logrus.WithField("prefix", "direct-keymanager-v2")
const (
// DepositTransactionFileName for the encoded, eth1 raw deposit tx data
// for a validator account.
DepositTransactionFileName = "deposit_transaction.rlp"
// TimestampFileName stores a timestamp for account creation as a
// file for a direct keymanager account.
TimestampFileName = "created_at.txt"
@@ -43,8 +40,9 @@ const (
// KeystoreFileNameFormat exposes the filename the keystore should be formatted in.
KeystoreFileNameFormat = "keystore-%d.json"
// PasswordFileSuffix for passwords persisted as text to disk.
PasswordFileSuffix = ".pass"
depositDataFileName = "deposit_data.ssz"
PasswordFileSuffix = ".pass"
// DepositDataFileName for the ssz-encoded deposit.
DepositDataFileName = "deposit_data.ssz"
eipVersion = "EIP-2335"
)
@@ -180,28 +178,28 @@ func (dr *Keymanager) CreateAccount(ctx context.Context, password string) (strin
// Upon confirmation of the withdrawal key, proceed to display
// and write associated deposit data to disk.
tx, depositData, err := depositutil.GenerateDepositTransaction(validatingKey, withdrawalKey)
_, depositData, err := depositutil.GenerateDepositTransaction(validatingKey, withdrawalKey)
if err != nil {
return "", errors.Wrap(err, "could not generate deposit transaction data")
}
// Log the deposit transaction data to the user.
depositutil.LogDepositTransaction(log, tx)
// We write the raw deposit transaction as an .rlp encoded file.
if err := dr.wallet.WriteFileAtPath(ctx, accountName, DepositTransactionFileName, tx.Data()); err != nil {
return "", errors.Wrapf(err, "could not write for account %s: %s", accountName, DepositTransactionFileName)
}
// We write the ssz-encoded deposit data to disk as a .ssz file.
encodedDepositData, err := ssz.Marshal(depositData)
if err != nil {
return "", errors.Wrap(err, "could not marshal deposit data")
}
if err := dr.wallet.WriteFileAtPath(ctx, accountName, depositDataFileName, encodedDepositData); err != nil {
if err := dr.wallet.WriteFileAtPath(ctx, accountName, DepositDataFileName, encodedDepositData); err != nil {
return "", errors.Wrapf(err, "could not write for account %s: %s", accountName, encodedDepositData)
}
// Log the deposit transaction data to the user.
fmt.Printf(`
========================SSZ Deposit Data===============================
%#x
===================================================================`, encodedDepositData)
// Write the encoded keystore to disk with the timestamp appended
createdAt := roughtime.Now().Unix()
if err := dr.wallet.WriteFileAtPath(ctx, accountName, fmt.Sprintf(KeystoreFileNameFormat, createdAt), encoded); err != nil {

View File

@@ -60,8 +60,8 @@ func TestDirectKeymanager_CreateAccount(t *testing.T) {
// Decode the deposit_data.ssz file and confirm
// the public key matches the public key from the
// account's decrypted keystore.
encodedDepositData, ok := wallet.Files[accountName][depositDataFileName]
require.Equal(t, true, ok, "Expected to have stored %s in wallet", depositDataFileName)
encodedDepositData, ok := wallet.Files[accountName][DepositDataFileName]
require.Equal(t, true, ok, "Expected to have stored %s in wallet", DepositDataFileName)
depositData := &ethpb.Deposit_Data{}
require.NoError(t, ssz.Unmarshal(encodedDepositData, depositData))