From 99164761f555178bdb5577ed422f8f4aca7d0dfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kapka?= Date: Thu, 24 Sep 2020 13:00:44 +0200 Subject: [PATCH] Make voluntary exits release-ready (#7305) * phrase validation utility * use passphrase from docs portal * enable voluntary exits * Merge branch 'master' into enable-exits * rename 'voluntary-exit' to 'exit' * change passphrase * gazelle * change constant to error variable * Merge branch 'master' into enable-exits * rename error variable * build fix * Merge refs/heads/master into enable-exits * do not remove whitespace in the middle * Merge refs/heads/master into enable-exits * Merge refs/heads/master into enable-exits * Merge refs/heads/master into enable-exits * Merge refs/heads/master into enable-exits * code review changes * Merge refs/heads/master into enable-exits * Merge refs/heads/master into enable-exits --- shared/promptutil/validate.go | 10 ++++++++++ shared/promptutil/validate_test.go | 22 ++++++++++++++++++++- validator/accounts/v2/BUILD.bazel | 2 -- validator/accounts/v2/accounts_exit.go | 17 +++++++--------- validator/accounts/v2/accounts_exit_test.go | 2 +- validator/accounts/v2/cmd_accounts.go | 2 +- 6 files changed, 40 insertions(+), 15 deletions(-) diff --git a/shared/promptutil/validate.go b/shared/promptutil/validate.go index ebca0de68d..9e1e5092d9 100644 --- a/shared/promptutil/validate.go +++ b/shared/promptutil/validate.go @@ -17,6 +17,8 @@ const ( minPasswordScore = 2 ) +var errIncorrectPhrase = errors.New("input does not match wanted phrase") + // NotEmpty is a validation function to make sure the input given isn't empty and is valid unicode. func NotEmpty(input string) error { if input == "" { @@ -111,3 +113,11 @@ func ValidatePasswordInput(input string) error { } return nil } + +// ValidatePhrase checks whether the user input is equal to the wanted phrase. The verification is case sensitive. +func ValidatePhrase(input string, wantedPhrase string) error { + if strings.TrimSpace(input) != wantedPhrase { + return errIncorrectPhrase + } + return nil +} diff --git a/shared/promptutil/validate_test.go b/shared/promptutil/validate_test.go index bbc28e9ccc..9e02120798 100644 --- a/shared/promptutil/validate_test.go +++ b/shared/promptutil/validate_test.go @@ -153,5 +153,25 @@ func TestDefaultAndValidatePrompt(t *testing.T) { require.NoError(t, err) }) } - +} + +func TestValidatePhrase(t *testing.T) { + wantedPhrase := "wanted phrase" + + t.Run("correct input", func(t *testing.T) { + assert.NoError(t, ValidatePhrase(wantedPhrase, wantedPhrase)) + }) + t.Run("correct input with whitespace", func(t *testing.T) { + assert.NoError(t, ValidatePhrase(" wanted phrase ", wantedPhrase)) + }) + t.Run("incorrect input", func(t *testing.T) { + err := ValidatePhrase("foo", wantedPhrase) + assert.NotNil(t, err) + assert.ErrorContains(t, errIncorrectPhrase.Error(), err) + }) + t.Run("wrong letter case", func(t *testing.T) { + err := ValidatePhrase("Wanted Phrase", wantedPhrase) + assert.NotNil(t, err) + assert.ErrorContains(t, errIncorrectPhrase.Error(), err) + }) } diff --git a/validator/accounts/v2/BUILD.bazel b/validator/accounts/v2/BUILD.bazel index 60980d2e03..1df604890d 100644 --- a/validator/accounts/v2/BUILD.bazel +++ b/validator/accounts/v2/BUILD.bazel @@ -52,8 +52,6 @@ go_library( "@com_github_urfave_cli_v2//:go_default_library", "@com_github_wealdtech_go_eth2_wallet_encryptor_keystorev4//:go_default_library", "@org_golang_google_grpc//:go_default_library", - "@org_golang_google_grpc//codes:go_default_library", - "@org_golang_google_grpc//status:go_default_library", ], ) diff --git a/validator/accounts/v2/accounts_exit.go b/validator/accounts/v2/accounts_exit.go index d78615aaa9..f5d77b2f85 100644 --- a/validator/accounts/v2/accounts_exit.go +++ b/validator/accounts/v2/accounts_exit.go @@ -18,8 +18,6 @@ import ( v2 "github.com/prysmaticlabs/prysm/validator/keymanager/v2" "github.com/urfave/cli/v2" "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" ) type performExitCfg struct { @@ -30,6 +28,8 @@ type performExitCfg struct { formattedPubKeys []string } +const exitPassphrase = "Exit my validator" + // ExitAccountsCli performs a voluntary exit on one or more accounts. func ExitAccountsCli(cliCtx *cli.Context, r io.Reader) error { validatingPublicKeys, keymanager, err := prepareWallet(cliCtx) @@ -73,11 +73,6 @@ func ExitAccountsCli(cliCtx *cli.Context, r io.Reader) error { return nil } -// ExitAccountsUnimplemented is a stub for ExitAccounts until the latter is fully implemented. -func ExitAccountsUnimplemented(cliCtx *cli.Context, r io.Reader) error { - return status.Errorf(codes.Unimplemented, "method ExitAccounts not implemented") -} - func prepareWallet(cliCtx *cli.Context) ([][48]byte, v2.IKeymanager, error) { w, err := wallet.OpenWalletOrElseCli(cliCtx, func(cliCtx *cli.Context) (*wallet.Wallet, error) { return nil, errors.New( @@ -158,10 +153,12 @@ func interact(cliCtx *cli.Context, r io.Reader, validatingPublicKeys [][48]byte) promptDescription := "Withdrawing funds is not possible in Phase 0 of the system. " + "Please navigate to the following website and make sure you understand the current implications " + "of a voluntary exit before making the final decision:" - promptURL := au.Blue("https://docs.prylabs.network/docs/faq/#can-i-get-back-my-testnet-eth-how-can-i-withdraw-my-validator-gains") - promptQuestion := "Do you still want to continue with the voluntary exit? Y/N" + promptURL := au.Blue("https://docs.prylabs.network/docs/wallet/nondeterministic/exiting-a-validator/#withdrawal-delay-warning") + promptQuestion := "If you still want to continue with the voluntary exit, please input the passphrase from the above URL" promptText := fmt.Sprintf("%s\n%s\n%s\n%s", promptHeader, promptDescription, promptURL, promptQuestion) - resp, err := promptutil.ValidatePrompt(r, promptText, promptutil.ValidateYesOrNo) + resp, err := promptutil.ValidatePrompt(r, promptText, func(input string) error { + return promptutil.ValidatePhrase(input, exitPassphrase) + }) if err != nil { return nil, nil, err } diff --git a/validator/accounts/v2/accounts_exit_test.go b/validator/accounts/v2/accounts_exit_test.go index cddb7ee6b2..af72722c34 100644 --- a/validator/accounts/v2/accounts_exit_test.go +++ b/validator/accounts/v2/accounts_exit_test.go @@ -88,7 +88,7 @@ func TestExitAccountsCli_Ok(t *testing.T) { // Prepare user input for final confirmation step var stdin bytes.Buffer - stdin.Write([]byte("Y\n")) + stdin.Write([]byte(exitPassphrase)) rawPubKeys, formattedPubKeys, err := interact(cliCtx, &stdin, validatingPublicKeys) require.NoError(t, err) require.NotNil(t, rawPubKeys) diff --git a/validator/accounts/v2/cmd_accounts.go b/validator/accounts/v2/cmd_accounts.go index b6e0697817..2e9067c8ea 100644 --- a/validator/accounts/v2/cmd_accounts.go +++ b/validator/accounts/v2/cmd_accounts.go @@ -145,7 +145,7 @@ this command outputs a deposit data string which is required to become a validat }, Action: func(cliCtx *cli.Context) error { featureconfig.ConfigureValidator(cliCtx) - if err := ExitAccountsUnimplemented(cliCtx, os.Stdin); err != nil { + if err := ExitAccountsCli(cliCtx, os.Stdin); err != nil { log.Fatalf("Could not perform voluntary exit: %v", err) } return nil