github.com/prysmaticlabs/prysm@v1.4.4/validator/accounts/wallet_create_test.go (about)

     1  package accounts
     2  
     3  import (
     4  	"context"
     5  	"flag"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"strconv"
    10  	"testing"
    11  
    12  	"github.com/pkg/errors"
    13  	"github.com/prysmaticlabs/prysm/cmd/validator/flags"
    14  	"github.com/prysmaticlabs/prysm/shared/params"
    15  	"github.com/prysmaticlabs/prysm/shared/testutil/assert"
    16  	"github.com/prysmaticlabs/prysm/shared/testutil/require"
    17  	"github.com/prysmaticlabs/prysm/validator/accounts/wallet"
    18  	"github.com/prysmaticlabs/prysm/validator/keymanager"
    19  	"github.com/prysmaticlabs/prysm/validator/keymanager/imported"
    20  	"github.com/prysmaticlabs/prysm/validator/keymanager/remote"
    21  	"github.com/sirupsen/logrus"
    22  	logTest "github.com/sirupsen/logrus/hooks/test"
    23  	"github.com/urfave/cli/v2"
    24  )
    25  
    26  const (
    27  	walletDirName    = "wallet"
    28  	passwordFileName = "password.txt"
    29  	password         = "OhWOWthisisatest42!$"
    30  	mnemonicFileName = "mnemonic.txt"
    31  	mnemonic         = "garage car helmet trade salmon embrace market giant movie wet same champion dawn chair shield drill amazing panther accident puzzle garden mosquito kind arena"
    32  )
    33  
    34  func init() {
    35  	logrus.SetLevel(logrus.DebugLevel)
    36  	logrus.SetOutput(ioutil.Discard)
    37  }
    38  
    39  type testWalletConfig struct {
    40  	exitAll                 bool
    41  	skipDepositConfirm      bool
    42  	keymanagerKind          keymanager.Kind
    43  	numAccounts             int64
    44  	grpcHeaders             string
    45  	privateKeyFile          string
    46  	accountPasswordFile     string
    47  	walletPasswordFile      string
    48  	backupPasswordFile      string
    49  	backupPublicKeys        string
    50  	voluntaryExitPublicKeys string
    51  	deletePublicKeys        string
    52  	keysDir                 string
    53  	backupDir               string
    54  	passwordsDir            string
    55  	walletDir               string
    56  }
    57  
    58  func setupWalletCtx(
    59  	tb testing.TB,
    60  	cfg *testWalletConfig,
    61  ) *cli.Context {
    62  	app := cli.App{}
    63  	set := flag.NewFlagSet("test", 0)
    64  	set.String(flags.WalletDirFlag.Name, cfg.walletDir, "")
    65  	set.String(flags.KeysDirFlag.Name, cfg.keysDir, "")
    66  	set.String(flags.KeymanagerKindFlag.Name, cfg.keymanagerKind.String(), "")
    67  	set.String(flags.DeletePublicKeysFlag.Name, cfg.deletePublicKeys, "")
    68  	set.String(flags.VoluntaryExitPublicKeysFlag.Name, cfg.voluntaryExitPublicKeys, "")
    69  	set.String(flags.BackupDirFlag.Name, cfg.backupDir, "")
    70  	set.String(flags.BackupPasswordFile.Name, cfg.backupPasswordFile, "")
    71  	set.String(flags.BackupPublicKeysFlag.Name, cfg.backupPublicKeys, "")
    72  	set.String(flags.WalletPasswordFileFlag.Name, cfg.walletPasswordFile, "")
    73  	set.String(flags.AccountPasswordFileFlag.Name, cfg.accountPasswordFile, "")
    74  	set.Int64(flags.NumAccountsFlag.Name, cfg.numAccounts, "")
    75  	set.Bool(flags.SkipDepositConfirmationFlag.Name, cfg.skipDepositConfirm, "")
    76  	set.Bool(flags.SkipMnemonic25thWordCheckFlag.Name, true, "")
    77  	set.Bool(flags.ExitAllFlag.Name, cfg.exitAll, "")
    78  	set.String(flags.GrpcHeadersFlag.Name, cfg.grpcHeaders, "")
    79  
    80  	if cfg.privateKeyFile != "" {
    81  		set.String(flags.ImportPrivateKeyFileFlag.Name, cfg.privateKeyFile, "")
    82  		assert.NoError(tb, set.Set(flags.ImportPrivateKeyFileFlag.Name, cfg.privateKeyFile))
    83  	}
    84  	assert.NoError(tb, set.Set(flags.WalletDirFlag.Name, cfg.walletDir))
    85  	assert.NoError(tb, set.Set(flags.SkipMnemonic25thWordCheckFlag.Name, "true"))
    86  	assert.NoError(tb, set.Set(flags.KeysDirFlag.Name, cfg.keysDir))
    87  	assert.NoError(tb, set.Set(flags.KeymanagerKindFlag.Name, cfg.keymanagerKind.String()))
    88  	assert.NoError(tb, set.Set(flags.DeletePublicKeysFlag.Name, cfg.deletePublicKeys))
    89  	assert.NoError(tb, set.Set(flags.VoluntaryExitPublicKeysFlag.Name, cfg.voluntaryExitPublicKeys))
    90  	assert.NoError(tb, set.Set(flags.BackupDirFlag.Name, cfg.backupDir))
    91  	assert.NoError(tb, set.Set(flags.BackupPublicKeysFlag.Name, cfg.backupPublicKeys))
    92  	assert.NoError(tb, set.Set(flags.BackupPasswordFile.Name, cfg.backupPasswordFile))
    93  	assert.NoError(tb, set.Set(flags.WalletPasswordFileFlag.Name, cfg.walletPasswordFile))
    94  	assert.NoError(tb, set.Set(flags.AccountPasswordFileFlag.Name, cfg.accountPasswordFile))
    95  	assert.NoError(tb, set.Set(flags.NumAccountsFlag.Name, strconv.Itoa(int(cfg.numAccounts))))
    96  	assert.NoError(tb, set.Set(flags.SkipDepositConfirmationFlag.Name, strconv.FormatBool(cfg.skipDepositConfirm)))
    97  	assert.NoError(tb, set.Set(flags.ExitAllFlag.Name, strconv.FormatBool(cfg.exitAll)))
    98  	assert.NoError(tb, set.Set(flags.GrpcHeadersFlag.Name, cfg.grpcHeaders))
    99  	return cli.NewContext(&app, set, nil)
   100  }
   101  
   102  func setupWalletAndPasswordsDir(t testing.TB) (string, string, string) {
   103  	walletDir := filepath.Join(t.TempDir(), "wallet")
   104  	passwordsDir := filepath.Join(t.TempDir(), "passwords")
   105  	passwordFileDir := filepath.Join(t.TempDir(), "passwordFile")
   106  	require.NoError(t, os.MkdirAll(passwordFileDir, params.BeaconIoConfig().ReadWriteExecutePermissions))
   107  	passwordFilePath := filepath.Join(passwordFileDir, passwordFileName)
   108  	require.NoError(t, ioutil.WriteFile(passwordFilePath, []byte(password), os.ModePerm))
   109  	return walletDir, passwordsDir, passwordFilePath
   110  }
   111  
   112  func TestCreateOrOpenWallet(t *testing.T) {
   113  	hook := logTest.NewGlobal()
   114  	walletDir, passwordsDir, walletPasswordFile := setupWalletAndPasswordsDir(t)
   115  	cliCtx := setupWalletCtx(t, &testWalletConfig{
   116  		walletDir:          walletDir,
   117  		passwordsDir:       passwordsDir,
   118  		keymanagerKind:     keymanager.Imported,
   119  		walletPasswordFile: walletPasswordFile,
   120  	})
   121  	createImportedWallet := func(cliCtx *cli.Context) (*wallet.Wallet, error) {
   122  		cfg, err := extractWalletCreationConfigFromCli(cliCtx, keymanager.Imported)
   123  		if err != nil {
   124  			return nil, err
   125  		}
   126  		w := wallet.New(&wallet.Config{
   127  			KeymanagerKind: cfg.WalletCfg.KeymanagerKind,
   128  			WalletDir:      cfg.WalletCfg.WalletDir,
   129  			WalletPassword: cfg.WalletCfg.WalletPassword,
   130  		})
   131  		if err = createImportedKeymanagerWallet(cliCtx.Context, w); err != nil {
   132  			return nil, errors.Wrap(err, "could not create keymanager")
   133  		}
   134  		log.WithField("wallet-path", cfg.WalletCfg.WalletDir).Info(
   135  			"Successfully created new wallet",
   136  		)
   137  		return w, nil
   138  	}
   139  	createdWallet, err := wallet.OpenWalletOrElseCli(cliCtx, createImportedWallet)
   140  	require.NoError(t, err)
   141  	require.LogsContain(t, hook, "Successfully created new wallet")
   142  
   143  	openedWallet, err := wallet.OpenWalletOrElseCli(cliCtx, createImportedWallet)
   144  	require.NoError(t, err)
   145  	assert.Equal(t, createdWallet.KeymanagerKind(), openedWallet.KeymanagerKind())
   146  	assert.Equal(t, createdWallet.AccountsDir(), openedWallet.AccountsDir())
   147  }
   148  
   149  func TestCreateWallet_Imported(t *testing.T) {
   150  	walletDir, passwordsDir, walletPasswordFile := setupWalletAndPasswordsDir(t)
   151  	cliCtx := setupWalletCtx(t, &testWalletConfig{
   152  		walletDir:          walletDir,
   153  		passwordsDir:       passwordsDir,
   154  		keymanagerKind:     keymanager.Imported,
   155  		walletPasswordFile: walletPasswordFile,
   156  	})
   157  
   158  	// We attempt to create the wallet.
   159  	_, err := CreateAndSaveWalletCli(cliCtx)
   160  	require.NoError(t, err)
   161  
   162  	// We attempt to open the newly created wallet.
   163  	w, err := wallet.OpenWallet(cliCtx.Context, &wallet.Config{
   164  		WalletDir: walletDir,
   165  	})
   166  	assert.NoError(t, err)
   167  	_, err = w.ReadFileAtPath(cliCtx.Context, imported.AccountsPath, imported.AccountsKeystoreFileName)
   168  	require.NoError(t, err)
   169  }
   170  
   171  func TestCreateWallet_Derived(t *testing.T) {
   172  	walletDir, passwordsDir, passwordFile := setupWalletAndPasswordsDir(t)
   173  	cliCtx := setupWalletCtx(t, &testWalletConfig{
   174  		walletDir:          walletDir,
   175  		passwordsDir:       passwordsDir,
   176  		walletPasswordFile: passwordFile,
   177  		keymanagerKind:     keymanager.Derived,
   178  		numAccounts:        1,
   179  	})
   180  
   181  	// We attempt to create the wallet.
   182  	_, err := CreateAndSaveWalletCli(cliCtx)
   183  	require.NoError(t, err)
   184  
   185  	// We attempt to open the newly created wallet.
   186  	_, err = wallet.OpenWallet(cliCtx.Context, &wallet.Config{
   187  		WalletDir: walletDir,
   188  	})
   189  	assert.NoError(t, err)
   190  }
   191  
   192  // TestCreateWallet_WalletAlreadyExists checks for expected error if trying to create a wallet when there is one already.
   193  func TestCreateWallet_WalletAlreadyExists(t *testing.T) {
   194  	walletDir, passwordsDir, passwordFile := setupWalletAndPasswordsDir(t)
   195  	cliCtx := setupWalletCtx(t, &testWalletConfig{
   196  		walletDir:          walletDir,
   197  		passwordsDir:       passwordsDir,
   198  		walletPasswordFile: passwordFile,
   199  		keymanagerKind:     keymanager.Derived,
   200  		numAccounts:        1,
   201  	})
   202  
   203  	// We attempt to create the wallet.
   204  	_, err := CreateAndSaveWalletCli(cliCtx)
   205  	require.NoError(t, err)
   206  
   207  	// We attempt to create another wallet of the same type at the same location. We expect an error.
   208  	_, err = CreateAndSaveWalletCli(cliCtx)
   209  	require.ErrorContains(t, "already exists", err)
   210  
   211  	cliCtx = setupWalletCtx(t, &testWalletConfig{
   212  		walletDir:          walletDir,
   213  		passwordsDir:       passwordsDir,
   214  		walletPasswordFile: passwordFile,
   215  		keymanagerKind:     keymanager.Imported,
   216  	})
   217  
   218  	// We attempt to create another wallet of different type at the same location. We expect an error.
   219  	_, err = CreateAndSaveWalletCli(cliCtx)
   220  	require.ErrorContains(t, "already exists", err)
   221  }
   222  
   223  func TestCreateWallet_Remote(t *testing.T) {
   224  	walletDir, _, walletPasswordFile := setupWalletAndPasswordsDir(t)
   225  	wantCfg := &remote.KeymanagerOpts{
   226  		RemoteCertificate: &remote.CertificateConfig{
   227  			RequireTls:     true,
   228  			ClientCertPath: "/tmp/client.crt",
   229  			ClientKeyPath:  "/tmp/client.key",
   230  			CACertPath:     "/tmp/ca.crt",
   231  		},
   232  		RemoteAddr: "host.example.com:4000",
   233  	}
   234  	app := cli.App{}
   235  	set := flag.NewFlagSet("test", 0)
   236  	keymanagerKind := "remote"
   237  	set.String(flags.WalletDirFlag.Name, walletDir, "")
   238  	set.String(flags.WalletPasswordFileFlag.Name, walletDir, "")
   239  	set.String(flags.KeymanagerKindFlag.Name, keymanagerKind, "")
   240  	set.String(flags.GrpcRemoteAddressFlag.Name, wantCfg.RemoteAddr, "")
   241  	set.String(flags.RemoteSignerCertPathFlag.Name, wantCfg.RemoteCertificate.ClientCertPath, "")
   242  	set.String(flags.RemoteSignerKeyPathFlag.Name, wantCfg.RemoteCertificate.ClientKeyPath, "")
   243  	set.String(flags.RemoteSignerCACertPathFlag.Name, wantCfg.RemoteCertificate.CACertPath, "")
   244  	assert.NoError(t, set.Set(flags.WalletDirFlag.Name, walletDir))
   245  	assert.NoError(t, set.Set(flags.WalletPasswordFileFlag.Name, walletPasswordFile))
   246  	assert.NoError(t, set.Set(flags.KeymanagerKindFlag.Name, keymanagerKind))
   247  	assert.NoError(t, set.Set(flags.GrpcRemoteAddressFlag.Name, wantCfg.RemoteAddr))
   248  	assert.NoError(t, set.Set(flags.RemoteSignerCertPathFlag.Name, wantCfg.RemoteCertificate.ClientCertPath))
   249  	assert.NoError(t, set.Set(flags.RemoteSignerKeyPathFlag.Name, wantCfg.RemoteCertificate.ClientKeyPath))
   250  	assert.NoError(t, set.Set(flags.RemoteSignerCACertPathFlag.Name, wantCfg.RemoteCertificate.CACertPath))
   251  	cliCtx := cli.NewContext(&app, set, nil)
   252  
   253  	// We attempt to create the wallet.
   254  	_, err := CreateAndSaveWalletCli(cliCtx)
   255  	require.NoError(t, err)
   256  
   257  	// We attempt to open the newly created wallet.
   258  	ctx := context.Background()
   259  	w, err := wallet.OpenWallet(cliCtx.Context, &wallet.Config{
   260  		WalletDir: walletDir,
   261  	})
   262  	assert.NoError(t, err)
   263  
   264  	// We read the keymanager config for the newly created wallet.
   265  	encoded, err := w.ReadKeymanagerConfigFromDisk(ctx)
   266  	assert.NoError(t, err)
   267  	cfg, err := remote.UnmarshalOptionsFile(encoded)
   268  	assert.NoError(t, err)
   269  
   270  	// We assert the created configuration was as desired.
   271  	assert.DeepEqual(t, wantCfg, cfg)
   272  }