From 6ed0539723b263d358dfe35e42f7eca05c4053ad Mon Sep 17 00:00:00 2001 From: Ivan Martinez Date: Thu, 13 Aug 2020 15:45:56 -0400 Subject: [PATCH] Acconuts-V2: Allow importing from any valid keystore, regardless of name (#6992) * Allow importing from any file * Fix log * Add test * Fix test --- validator/accounts/v2/accounts_import.go | 18 +++-- validator/accounts/v2/accounts_import_test.go | 79 +++++++++++++++++++ 2 files changed, 89 insertions(+), 8 deletions(-) diff --git a/validator/accounts/v2/accounts_import.go b/validator/accounts/v2/accounts_import.go index 99a18dbf0..735d3684d 100644 --- a/validator/accounts/v2/accounts_import.go +++ b/validator/accounts/v2/accounts_import.go @@ -130,22 +130,21 @@ func ImportAccounts(cliCtx *cli.Context) error { if len(files) == 0 { return fmt.Errorf("directory %s has no files, cannot import from it", keysDir) } - keystoreFileNames := make([]string, 0) + filesInDir := make([]string, 0) for i := 0; i < len(files); i++ { if files[i].IsDir() { continue } - if !strings.HasPrefix(files[i].Name(), "keystore") { - continue - } - keystoreFileNames = append(keystoreFileNames, files[i].Name()) + filesInDir = append(filesInDir, files[i].Name()) } // Sort the imported keystores by derivation path if they // specify this value in their filename. - sort.Sort(byDerivationPath(keystoreFileNames)) - for _, name := range keystoreFileNames { + sort.Sort(byDerivationPath(filesInDir)) + for _, name := range filesInDir { keystore, err := readKeystoreFile(ctx, filepath.Join(keysDir, name)) - if err != nil { + if err != nil && strings.Contains(err.Error(), "could not decode keystore json") { + continue + } else if err != nil { return errors.Wrapf(err, "could not import keystore at path: %s", name) } keystoresImported = append(keystoresImported, keystore) @@ -228,6 +227,9 @@ func readKeystoreFile(ctx context.Context, keystoreFilePath string) (*v2keymanag if err := json.Unmarshal(keystoreBytes, keystoreFile); err != nil { return nil, errors.Wrap(err, "could not decode keystore json") } + if keystoreFile.Pubkey == "" { + return nil, errors.New("could not decode keystore json") + } return keystoreFile, nil } diff --git a/validator/accounts/v2/accounts_import_test.go b/validator/accounts/v2/accounts_import_test.go index fc50fb439..c55244ec1 100644 --- a/validator/accounts/v2/accounts_import_test.go +++ b/validator/accounts/v2/accounts_import_test.go @@ -80,6 +80,60 @@ func TestImport_Noninteractive(t *testing.T) { assert.Equal(t, 2, len(keys)) } +func TestImport_Noninteractive_RandomName(t *testing.T) { + walletDir, passwordsDir, passwordFilePath := setupWalletAndPasswordsDir(t) + randPath, err := rand.Int(rand.Reader, big.NewInt(1000000)) + require.NoError(t, err, "Could not generate random file path") + keysDir := filepath.Join(testutil.TempDir(), fmt.Sprintf("/%d", randPath), "keysDir") + require.NoError(t, os.MkdirAll(keysDir, os.ModePerm)) + t.Cleanup(func() { + require.NoError(t, os.RemoveAll(keysDir), "Failed to remove directory") + }) + + cliCtx := setupWalletCtx(t, &testWalletConfig{ + 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() + encodedCfg, err := direct.MarshalConfigFile(ctx, direct.DefaultConfig()) + require.NoError(t, err) + require.NoError(t, wallet.WriteKeymanagerConfigToDisk(ctx, encodedCfg)) + keymanager, err := direct.NewKeymanager( + cliCtx, + wallet, + direct.DefaultConfig(), + ) + require.NoError(t, err) + + // Make sure there are no accounts at the start. + accounts, err := keymanager.ValidatingAccountNames() + require.NoError(t, err) + assert.Equal(t, len(accounts), 0) + + // Create 2 keys. + createRandomNameKeystore(t, keysDir) + time.Sleep(time.Second) + createRandomNameKeystore(t, keysDir) + + require.NoError(t, ImportAccounts(cliCtx)) + + wallet, err = OpenWallet(cliCtx) + require.NoError(t, err) + km, err := wallet.InitializeKeymanager(cliCtx, true) + require.NoError(t, err) + keys, err := km.FetchValidatingPublicKeys(ctx) + require.NoError(t, err) + + assert.Equal(t, 2, len(keys)) +} + func TestImport_Noninteractive_Filepath(t *testing.T) { walletDir, passwordsDir, passwordFilePath := setupWalletAndPasswordsDir(t) randPath, err := rand.Int(rand.Reader, big.NewInt(1000000)) @@ -272,3 +326,28 @@ func createKeystore(t *testing.T, path string) (*v2keymanager.Keystore, string) require.NoError(t, ioutil.WriteFile(fullPath, encoded, os.ModePerm)) return keystoreFile, fullPath } + +// Returns the fullPath to the newly created keystore file. +func createRandomNameKeystore(t *testing.T, path string) (*v2keymanager.Keystore, string) { + validatingKey := bls.RandKey() + encryptor := keystorev4.New() + cryptoFields, err := encryptor.Encrypt(validatingKey.Marshal(), password) + require.NoError(t, err) + id, err := uuid.NewRandom() + require.NoError(t, err) + keystoreFile := &v2keymanager.Keystore{ + Crypto: cryptoFields, + ID: id.String(), + Pubkey: fmt.Sprintf("%x", validatingKey.PublicKey().Marshal()), + Version: encryptor.Version(), + Name: encryptor.Name(), + } + encoded, err := json.MarshalIndent(keystoreFile, "", "\t") + require.NoError(t, err) + // Write the encoded keystore to disk with the timestamp appended + random, err := rand.Int(rand.Reader, big.NewInt(1000000)) + require.NoError(t, err) + fullPath := filepath.Join(path, fmt.Sprintf("test-%d-keystore", random.Int64())) + require.NoError(t, ioutil.WriteFile(fullPath, encoded, os.ModePerm)) + return keystoreFile, fullPath +}