diff --git a/validator/accounts/v2/BUILD.bazel b/validator/accounts/v2/BUILD.bazel index 1df604890d..f3b0cc4ee3 100644 --- a/validator/accounts/v2/BUILD.bazel +++ b/validator/accounts/v2/BUILD.bazel @@ -7,7 +7,6 @@ go_library( "accounts_backup.go", "accounts_create.go", "accounts_delete.go", - "accounts_deposit.go", "accounts_exit.go", "accounts_helper.go", "accounts_import.go", @@ -30,7 +29,6 @@ go_library( "//shared/cmd:go_default_library", "//shared/featureconfig:go_default_library", "//shared/fileutil:go_default_library", - "//shared/params:go_default_library", "//shared/petnames:go_default_library", "//shared/promptutil:go_default_library", "//validator/accounts/v2/prompt:go_default_library", @@ -61,7 +59,6 @@ go_test( "accounts_backup_test.go", "accounts_create_test.go", "accounts_delete_test.go", - "accounts_deposit_test.go", "accounts_exit_test.go", "accounts_import_test.go", "accounts_list_test.go", diff --git a/validator/accounts/v2/accounts_deposit.go b/validator/accounts/v2/accounts_deposit.go deleted file mode 100644 index 92d738ccc1..0000000000 --- a/validator/accounts/v2/accounts_deposit.go +++ /dev/null @@ -1,207 +0,0 @@ -package v2 - -import ( - "fmt" - "os" - "strings" - "time" - - "github.com/manifoldco/promptui" - "github.com/pkg/errors" - "github.com/prysmaticlabs/prysm/shared/bls" - "github.com/prysmaticlabs/prysm/shared/fileutil" - "github.com/prysmaticlabs/prysm/shared/params" - "github.com/prysmaticlabs/prysm/shared/promptutil" - "github.com/prysmaticlabs/prysm/validator/accounts/v2/prompt" - "github.com/prysmaticlabs/prysm/validator/accounts/v2/wallet" - "github.com/prysmaticlabs/prysm/validator/flags" - v2keymanager "github.com/prysmaticlabs/prysm/validator/keymanager/v2" - "github.com/prysmaticlabs/prysm/validator/keymanager/v2/derived" - "github.com/urfave/cli/v2" -) - -// SendDepositCli transaction for user specified accounts via an interactive -// CLI process or via command-line flags. -func SendDepositCli(cliCtx *cli.Context) error { - w, err := wallet.OpenWalletOrElseCli(cliCtx, func(cliCtx *cli.Context) (*wallet.Wallet, error) { - return nil, errors.New( - "no wallet found, nothing to deposit", - ) - }) - if err != nil { - return errors.Wrap(err, "could not open wallet") - } - keymanager, err := w.InitializeKeymanager( - cliCtx.Context, - true, /* skip mnemonic confirm */ - ) - if err != nil && strings.Contains(err.Error(), "invalid checksum") { - return errors.New("wrong wallet password entered") - } - if err != nil { - return errors.Wrap(err, "could not initialize keymanager") - } - switch w.KeymanagerKind() { - case v2keymanager.Derived: - km, ok := keymanager.(*derived.Keymanager) - if !ok { - return errors.New("could not assert keymanager interface to concrete type") - } - depositConfig, err := createDepositConfig(cliCtx, km) - if err != nil { - return errors.Wrap(err, "could not initialize deposit config") - } - if err := km.SendDepositTx(depositConfig); err != nil { - return err - } - default: - return errors.New("only Prysm HD wallets support sending deposits at the moment") - } - return nil -} - -func createDepositConfig(cliCtx *cli.Context, km *derived.Keymanager) (*derived.SendDepositConfig, error) { - pubKeysBytes, err := km.FetchValidatingPublicKeys(cliCtx.Context) - if err != nil { - return nil, errors.Wrap(err, "could not fetch validating public keys") - } - pubKeys := make([]bls.PublicKey, len(pubKeysBytes)) - for i, pk := range pubKeysBytes { - pubKeys[i], err = bls.PublicKeyFromBytes(pk[:]) - if err != nil { - return nil, errors.Wrap(err, "could not parse BLS public key") - } - } - // Allow the user to interactively select the accounts to deposit or optionally - // provide them via cli flags as a string of comma-separated, hex strings. If the user has - // selected to deposit all accounts, we skip this part. - if !cliCtx.IsSet(flags.DepositAllAccountsFlag.Name) { - pubKeys, err = filterPublicKeysFromUserInput( - cliCtx, - flags.DepositPublicKeysFlag, - pubKeysBytes, - prompt.SelectAccountsDepositPromptText, - ) - if err != nil { - return nil, errors.Wrap(err, "could not filter validating public keys for deposit") - } - } - - web3Provider := cliCtx.String(flags.HTTPWeb3ProviderFlag.Name) - // Enter the web3provider information. - if web3Provider == "" { - web3Provider, err = promptutil.DefaultAndValidatePrompt( - "Enter the HTTP address of your eth1 endpoint for the Goerli testnet", - cliCtx.String(flags.HTTPWeb3ProviderFlag.Name), - func(input string) error { - return nil - }, - ) - if err != nil { - return nil, errors.Wrap(err, "could not validate web3 provider endpoint") - } - } - depositDelaySeconds := cliCtx.Int(flags.DepositDelaySecondsFlag.Name) - config := &derived.SendDepositConfig{ - DepositContractAddress: cliCtx.String(flags.DepositContractAddressFlag.Name), - DepositDelaySeconds: time.Duration(depositDelaySeconds) * time.Second, - DepositPublicKeys: pubKeys, - Web3Provider: web3Provider, - } - - if !cliCtx.Bool(flags.SkipDepositConfirmationFlag.Name) { - confirmDepositPrompt := "You are about to send %d ETH into contract address %s for %d eth2 validator accounts. " + - "Are you sure you want to do this? Enter the words 'yes I do' to continue" - gweiPerEth := params.BeaconConfig().GweiPerEth - ethDepositTotal := uint64(len(pubKeys)) * params.BeaconConfig().MaxEffectiveBalance / gweiPerEth - if _, err := promptutil.ValidatePrompt( - os.Stdin, - fmt.Sprintf(confirmDepositPrompt, ethDepositTotal, config.DepositContractAddress, len(pubKeys)), - func(input string) error { - if input != "yes I do" { - return errors.New("please enter 'yes I do' or exit") - } - return nil - }, - ); err != nil { - return nil, errors.Wrap(err, "could not confirm deposit acknowledgement") - } - } - - // If the user passes any of the specified flags, we read them and return the - // config struct directly, bypassing any CLI input. - hasPrivateKey := cliCtx.IsSet(flags.Eth1PrivateKeyFileFlag.Name) - hasEth1Keystore := cliCtx.IsSet(flags.Eth1KeystoreUTCPathFlag.Name) - if hasPrivateKey || hasEth1Keystore { - if hasPrivateKey { - fileBytes, err := fileutil.ReadFileAsBytes(cliCtx.String(flags.Eth1PrivateKeyFileFlag.Name)) - if err != nil { - return nil, err - } - config.Eth1PrivateKey = strings.TrimRight(string(fileBytes), "\r\n") - } else { - config.Eth1KeystoreUTCFile = cliCtx.String(flags.Eth1KeystoreUTCPathFlag.Name) - if cliCtx.IsSet(flags.Eth1KeystorePasswordFileFlag.Name) { - config.Eth1KeystorePasswordFile = cliCtx.String(flags.Eth1KeystorePasswordFileFlag.Name) - } else { - config.Eth1KeystorePasswordFile, err = prompt.InputWeakPassword( - cliCtx, - flags.Eth1KeystorePasswordFileFlag, - "Enter the file path of a text file containing your eth1 keystore password", - ) - if err != nil { - return nil, errors.Wrap(err, "could not read eth1 keystore password file path") - } - } - } - return config, nil - } - - usePrivateKeyPrompt := "Inputting an eth1 private key hex string directly" - useEth1KeystorePrompt := "Using an encrypted eth1 keystore UTC file" - eth1Prompt := promptui.Select{ - Label: "Select how you wish to sign your eth1 transaction", - Items: []string{ - usePrivateKeyPrompt, - useEth1KeystorePrompt, - }, - } - _, selection, err := eth1Prompt.Run() - if err != nil { - return nil, err - } - // If the user wants to proceed by inputting their private key directly, ask for it securely. - if selection == usePrivateKeyPrompt { - eth1PrivateKeyString, err := promptutil.PasswordPrompt( - "Enter the hex string value of your eth1 private key", - promptutil.NotEmpty, - ) - if err != nil { - return nil, errors.Wrap(err, "could not read eth1 private key string") - } - config.Eth1PrivateKey = strings.TrimRight(eth1PrivateKeyString, "\r\n") - } else if selection == useEth1KeystorePrompt { - // Otherwise, ask the user for paths to their keystore UTC file and its password. - eth1KeystoreUTCFile, err := promptutil.DefaultAndValidatePrompt( - "Enter the file path for your encrypted, eth1 keystore-utc file", - cliCtx.String(flags.Eth1KeystoreUTCPathFlag.Name), - func(input string) error { - return nil - }, - ) - if err != nil { - return nil, errors.Wrap(err, "could not read eth1 keystore UTC path") - } - eth1KeystorePasswordFile, err := prompt.InputWeakPassword( - cliCtx, - flags.Eth1KeystorePasswordFileFlag, - "Enter the file path to a text file containing your eth1 keystore password", - ) - if err != nil { - return nil, errors.Wrap(err, "could not read eth1 keystore password file path") - } - config.Eth1KeystoreUTCFile = eth1KeystoreUTCFile - config.Eth1KeystorePasswordFile = eth1KeystorePasswordFile - } - return config, nil -} diff --git a/validator/accounts/v2/accounts_deposit_test.go b/validator/accounts/v2/accounts_deposit_test.go deleted file mode 100644 index 3dc05baca5..0000000000 --- a/validator/accounts/v2/accounts_deposit_test.go +++ /dev/null @@ -1,202 +0,0 @@ -package v2 - -import ( - "encoding/hex" - "errors" - "flag" - "io/ioutil" - "os" - "strconv" - "strings" - "testing" - - "github.com/prysmaticlabs/prysm/shared/bytesutil" - "github.com/prysmaticlabs/prysm/shared/testutil/assert" - "github.com/prysmaticlabs/prysm/shared/testutil/require" - "github.com/prysmaticlabs/prysm/validator/accounts/v2/wallet" - "github.com/prysmaticlabs/prysm/validator/flags" - v2keymanager "github.com/prysmaticlabs/prysm/validator/keymanager/v2" - "github.com/prysmaticlabs/prysm/validator/keymanager/v2/derived" - "github.com/urfave/cli/v2" -) - -type depositTestWalletConfig struct { - walletDir string - walletPasswordFile string - eth1KeystoreFile string - eth1KeystorePasswordFile string - eth1PrivateKeyFile string - httpWeb3ProviderFlag string - publicKeysFlag string - depositAllAccountsFlag bool - skipDepositConfirmationFlag bool - keymanagerKind v2keymanager.Kind -} - -func setupWalletCtxforDeposits( - t *testing.T, - cfg *depositTestWalletConfig, -) *cli.Context { - app := cli.App{} - set := flag.NewFlagSet("test", 0) - set.String(flags.WalletDirFlag.Name, cfg.walletDir, "") - set.String(flags.KeymanagerKindFlag.Name, cfg.keymanagerKind.String(), "") - set.String(flags.WalletPasswordFileFlag.Name, cfg.walletPasswordFile, "") - set.String(flags.HTTPWeb3ProviderFlag.Name, cfg.httpWeb3ProviderFlag, "") - set.String(flags.Eth1KeystoreUTCPathFlag.Name, cfg.eth1KeystoreFile, "") - set.String(flags.Eth1KeystorePasswordFileFlag.Name, cfg.eth1KeystorePasswordFile, "") - set.String(flags.Eth1PrivateKeyFileFlag.Name, cfg.eth1PrivateKeyFile, "") - set.String(flags.DepositPublicKeysFlag.Name, cfg.publicKeysFlag, "") - set.Bool(flags.DepositAllAccountsFlag.Name, cfg.depositAllAccountsFlag, "") - set.Bool(flags.SkipDepositConfirmationFlag.Name, cfg.skipDepositConfirmationFlag, "") - assert.NoError(t, set.Set(flags.WalletDirFlag.Name, cfg.walletDir)) - assert.NoError(t, set.Set(flags.KeymanagerKindFlag.Name, cfg.keymanagerKind.String())) - assert.NoError(t, set.Set(flags.WalletPasswordFileFlag.Name, cfg.walletPasswordFile)) - assert.NoError(t, set.Set(flags.HTTPWeb3ProviderFlag.Name, cfg.httpWeb3ProviderFlag)) - if cfg.eth1KeystoreFile != "" { - assert.NoError(t, set.Set(flags.Eth1KeystoreUTCPathFlag.Name, cfg.eth1KeystoreFile)) - assert.NoError(t, set.Set(flags.Eth1KeystorePasswordFileFlag.Name, cfg.eth1KeystorePasswordFile)) - } - if cfg.eth1PrivateKeyFile != "" { - assert.NoError(t, set.Set(flags.Eth1PrivateKeyFileFlag.Name, cfg.eth1PrivateKeyFile)) - } - if cfg.publicKeysFlag != "" { - assert.NoError(t, set.Set(flags.DepositPublicKeysFlag.Name, cfg.publicKeysFlag)) - } - if cfg.depositAllAccountsFlag == true { - assert.NoError(t, set.Set(flags.DepositAllAccountsFlag.Name, strconv.FormatBool(cfg.depositAllAccountsFlag))) - } - assert.NoError(t, set.Set(flags.SkipDepositConfirmationFlag.Name, strconv.FormatBool(cfg.skipDepositConfirmationFlag))) - return cli.NewContext(&app, set, nil) -} - -func TestCreateDepositConfig(t *testing.T) { - walletDir, _, passwordFilePath := setupWalletAndPasswordsDir(t) - - // First, create the wallet and several accounts - cliCtx := setupWalletCtx(t, &testWalletConfig{ - keymanagerKind: v2keymanager.Derived, - walletDir: walletDir, - walletPasswordFile: passwordFilePath, - skipDepositConfirm: true, - }) - w, err := CreateAndSaveWalletCli(cliCtx) - require.NoError(t, err) - - err = CreateAccount(cliCtx.Context, &CreateAccountConfig{ - Wallet: w, - NumAccounts: 3, - }) - require.NoError(t, err) - keymanager, err := w.InitializeKeymanager( - cliCtx.Context, - true, /* skip mnemonic confirm */ - ) - require.NoError(t, err) - - // Save public keys for comparison and selection purposes later - pubkeys, err := keymanager.FetchValidatingPublicKeys(cliCtx.Context) - require.NoError(t, err) - var hexPubkeys []string - for _, pubkey := range pubkeys { - encoded := make([]byte, hex.EncodedLen(len(pubkey))) - hex.Encode(encoded, pubkey[:]) - hexPubkeys = append(hexPubkeys, string(encoded)) - } - // Remove the last key so that we can select a portion and not all of the accounts later - hexPubkeys = hexPubkeys[:len(hexPubkeys)-1] - hexPubkeysString := strings.Join(hexPubkeys, ",") - - // Create a file holding the test ETH1 private key - eth1PrivateKeyFile, err := ioutil.TempFile("", "testing") - require.NoError(t, err) - defer func() { - err = eth1PrivateKeyFile.Close() - require.NoError(t, err) - err = os.Remove(eth1PrivateKeyFile.Name()) - require.NoError(t, err) - }() - _, err = eth1PrivateKeyFile.WriteString("This should be an ETH1 private key") - require.NoError(t, err) - - // First we test the behavior when depositAllAccountsFlag is set to true - depositConfig := createDepositConfigHelper(t, &depositTestWalletConfig{ - keymanagerKind: v2keymanager.Derived, - walletDir: walletDir, - walletPasswordFile: passwordFilePath, - skipDepositConfirmationFlag: true, - depositAllAccountsFlag: true, - httpWeb3ProviderFlag: "http://localhost:8545", - eth1PrivateKeyFile: eth1PrivateKeyFile.Name(), - }) - - require.Equal(t, 3, len(depositConfig.DepositPublicKeys), "wrong number of public keys") - require.Equal(t, "This should be an ETH1 private key", depositConfig.Eth1PrivateKey, "eth1 private key does not match") - require.Equal(t, "http://localhost:8545", depositConfig.Web3Provider, "web3 provider does not match") - require.Equal(t, "", depositConfig.Eth1KeystoreUTCFile, "keystore file path should be empty") - require.Equal(t, "", depositConfig.Eth1KeystorePasswordFile, "keystore password file path should be empty") - - // Test the case of providing the public keys via command-line. We also pass in the test eth1 private key file. - // hexPubkeysString holds 1 less than all the accounts. - depositConfig = createDepositConfigHelper(t, &depositTestWalletConfig{ - keymanagerKind: v2keymanager.Derived, - walletDir: walletDir, - walletPasswordFile: passwordFilePath, - skipDepositConfirmationFlag: true, - publicKeysFlag: hexPubkeysString, - httpWeb3ProviderFlag: "http://localhost:8545", - eth1PrivateKeyFile: eth1PrivateKeyFile.Name(), - }) - require.Equal(t, 2, len(depositConfig.DepositPublicKeys), "wrong number of public keys") - - // Compare the keys in the config object with the keys we obtained earlier from the keymanager - for keyNum, configPubKey := range depositConfig.DepositPublicKeys { - for index, eachByte := range bytesutil.ToBytes48(configPubKey.Marshal()) { - if eachByte != pubkeys[keyNum][index] { - require.NoError(t, errors.New("public keys do not match")) - } - } - } - - // Now we test when private key file is not provided but rather the keystore and keystore password files - depositConfig = createDepositConfigHelper(t, &depositTestWalletConfig{ - keymanagerKind: v2keymanager.Derived, - walletDir: walletDir, - walletPasswordFile: passwordFilePath, - skipDepositConfirmationFlag: true, - depositAllAccountsFlag: true, - httpWeb3ProviderFlag: "http://localhost:8545", - eth1KeystoreFile: "This would be eth1 keystore file path", - eth1KeystorePasswordFile: "This would be eth1 keystore password file path", - }) - require.Equal(t, 3, len(depositConfig.DepositPublicKeys), "wrong number of public keys") - require.Equal(t, "This would be eth1 keystore file path", depositConfig.Eth1KeystoreUTCFile, - "eth1 keystore file path incorrect") - require.Equal(t, "This would be eth1 keystore password file path", depositConfig.Eth1KeystorePasswordFile, - "eth1 keystore password file path incorrect") - require.Equal(t, "", depositConfig.Eth1PrivateKey, "eth1 private key should be empty string") -} - -// createDepositConfigHelper returns a SendDepositConfig when given a particular wallet configuration. -func createDepositConfigHelper(t *testing.T, config *depositTestWalletConfig) *derived.SendDepositConfig { - cliCtx := setupWalletCtxforDeposits(t, config) - w, err := wallet.OpenWalletOrElseCli(cliCtx, func(cliCtx *cli.Context) (*wallet.Wallet, error) { - err := errors.New("could not open wallet") - require.NoError(t, err) - return nil, err - }) - require.NoError(t, err) - - keymanager, err := w.InitializeKeymanager( - cliCtx.Context, - true, /* skip mnemonic confirm */ - ) - require.NoError(t, err) - km, ok := keymanager.(*derived.Keymanager) - require.Equal(t, true, ok, "keymanager must be derived type") - - // Now we finally call the function we are testing. - depositConfig, err := createDepositConfig(cliCtx, km) - require.NoError(t, err) - return depositConfig -} diff --git a/validator/accounts/v2/cmd_accounts.go b/validator/accounts/v2/cmd_accounts.go index 2e9067c8ea..f9886cd283 100644 --- a/validator/accounts/v2/cmd_accounts.go +++ b/validator/accounts/v2/cmd_accounts.go @@ -151,33 +151,5 @@ this command outputs a deposit data string which is required to become a validat return nil }, }, - { - Name: "deposit", - Description: "Submits a deposit to the eth2 deposit contract for a validator key by connecting " + - "to an eth1 endpoint to submit a transaction. Requires signing the transaction with an eth1 private key", - Flags: []cli.Flag{ - flags.WalletDirFlag, - flags.WalletPasswordFileFlag, - flags.HTTPWeb3ProviderFlag, - flags.Eth1KeystoreUTCPathFlag, - flags.Eth1KeystorePasswordFileFlag, - flags.Eth1PrivateKeyFileFlag, - flags.DepositDelaySecondsFlag, - flags.DepositContractAddressFlag, - flags.DepositPublicKeysFlag, - flags.SkipDepositConfirmationFlag, - flags.DepositAllAccountsFlag, - featureconfig.AltonaTestnet, - featureconfig.OnyxTestnet, - featureconfig.SpadinaTestnet, - }, - Action: func(cliCtx *cli.Context) error { - featureconfig.ConfigureValidator(cliCtx) - if err := SendDepositCli(cliCtx); err != nil { - log.Fatalf("Could not send validator deposit(s): %v", err) - } - return nil - }, - }, }, }