mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 15:37:56 -05:00
Accounts V2: Refactor Wallet Open / Close and Keymanager Initialization (#6668)
* refactoring create account * dep * much easier, create a derived account by simply unlocking wallet * revert changes to new * make open wallet smarter and utilize cli ctx * remove the wallet config * successfully build * simplify ctx creation for tests * tests should pass individually * tests pass * fixed up to allow for wallet password file input * fix broken tests * formatting * fmt * simplify recover * fixed up tests * implicit use of default wallet path working Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
This commit is contained in:
2
go.sum
2
go.sum
@@ -1003,6 +1003,7 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.0.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/src-d/envconfig v1.0.0 h1:/AJi6DtjFhZKNx3OB2qMsq7y4yT5//AeSZIe7rk+PX8=
|
||||
github.com/src-d/envconfig v1.0.0/go.mod h1:Q9YQZ7BKITldTBnoxsE5gOeB5y66RyPXeue/R4aaNBc=
|
||||
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q=
|
||||
github.com/status-im/keycard-go v0.0.0-20200402102358-957c09536969 h1:Oo2KZNP70KE0+IUJSidPj/BFS/RXNHmKIJOdckzml2E=
|
||||
@@ -1464,6 +1465,7 @@ gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200316214253-d7b0ff38cac9 h1:ITeyKbRetr
|
||||
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200316214253-d7b0ff38cac9/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns=
|
||||
gopkg.in/redis.v4 v4.2.4/go.mod h1:8KREHdypkCEojGKQcjMqAODMICIVwZAONWq8RowTITA=
|
||||
gopkg.in/src-d/go-cli.v0 v0.0.0-20181105080154-d492247bbc0d/go.mod h1:z+K8VcOYVYcSwSjGebuDL6176A1XskgbtNl64NSg+n8=
|
||||
gopkg.in/src-d/go-log.v1 v1.0.1 h1:heWvX7J6qbGWbeFS/aRmiy1eYaT+QMV6wNvHDyMjQV4=
|
||||
gopkg.in/src-d/go-log.v1 v1.0.1/go.mod h1:GN34hKP0g305ysm2/hctJ0Y8nWP3zxXXJ8GFabTyABE=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
|
||||
@@ -64,7 +64,6 @@ go_test(
|
||||
"//validator/keymanager/v2/derived:go_default_library",
|
||||
"//validator/keymanager/v2/direct:go_default_library",
|
||||
"//validator/keymanager/v2/remote:go_default_library",
|
||||
"//validator/keymanager/v2/testing:go_default_library",
|
||||
"@com_github_dustin_go_humanize//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_urfave_cli_v2//:go_default_library",
|
||||
|
||||
@@ -23,6 +23,7 @@ var WalletCommands = &cli.Command{
|
||||
flags.RemoteSignerCertPathFlag,
|
||||
flags.RemoteSignerKeyPathFlag,
|
||||
flags.RemoteSignerCACertPathFlag,
|
||||
flags.PasswordFileFlag,
|
||||
},
|
||||
Action: func(cliCtx *cli.Context) error {
|
||||
if err := CreateWallet(cliCtx); err != nil {
|
||||
|
||||
@@ -2,7 +2,6 @@ package v2
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
@@ -23,23 +22,12 @@ 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 {
|
||||
// Read a wallet's directory from user input.
|
||||
walletDir, err := inputWalletDir(cliCtx)
|
||||
if errors.Is(err, ErrNoWalletFound) {
|
||||
return errors.New("no wallet found, create a new one with ./prysm.sh validator wallet-v2 create")
|
||||
} else if err != nil {
|
||||
return errors.Wrap(err, "could not parse wallet directory")
|
||||
}
|
||||
|
||||
outputDir, err := inputExportDir(cliCtx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not parse output directory")
|
||||
}
|
||||
|
||||
wallet, err := OpenWallet(context.Background(), &WalletConfig{
|
||||
CanUnlockAccounts: false,
|
||||
WalletDir: walletDir,
|
||||
})
|
||||
wallet, err := OpenWallet(cliCtx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not open wallet")
|
||||
}
|
||||
@@ -57,7 +45,7 @@ func ExportAccount(cliCtx *cli.Context) error {
|
||||
}
|
||||
|
||||
if err := wallet.zipAccounts(accounts, outputDir); err != nil {
|
||||
log.WithError(err).Error("Could not export accounts")
|
||||
return errors.Wrap(err, "could not export accounts")
|
||||
}
|
||||
|
||||
if err := logAccountsExported(wallet, accounts); err != nil {
|
||||
|
||||
@@ -2,61 +2,49 @@ package v2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/validator/flags"
|
||||
v2 "github.com/prysmaticlabs/prysm/validator/keymanager/v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
v2keymanager "github.com/prysmaticlabs/prysm/validator/keymanager/v2"
|
||||
"github.com/prysmaticlabs/prysm/validator/keymanager/v2/direct"
|
||||
)
|
||||
|
||||
func setupWallet(t *testing.T, testDir string) *Wallet {
|
||||
walletDir := filepath.Join(testDir, walletDirName)
|
||||
passwordsDir := filepath.Join(testDir, passwordDirName)
|
||||
func TestZipAndUnzip(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 := path.Join(testutil.TempDir(), fmt.Sprintf("/%d", randPath), "export")
|
||||
importDir := path.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)
|
||||
require.NoError(t, err)
|
||||
ctx := context.Background()
|
||||
|
||||
app := cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.String(flags.WalletPasswordsDirFlag.Name, passwordsDir, "")
|
||||
assert.NoError(t, set.Set(flags.WalletPasswordsDirFlag.Name, passwordsDir))
|
||||
cliCtx := cli.NewContext(&app, set, nil)
|
||||
assert.NoError(t, createDirectWallet(cliCtx, walletDir))
|
||||
cfg := &WalletConfig{
|
||||
WalletDir: walletDir,
|
||||
PasswordsDir: passwordsDir,
|
||||
KeymanagerKind: v2.Direct,
|
||||
}
|
||||
w, err := NewWallet(ctx, cfg)
|
||||
keymanager, err := direct.NewKeymanager(
|
||||
ctx,
|
||||
wallet,
|
||||
direct.DefaultConfig(),
|
||||
true, /* skip mnemonic */
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
keymanager, err := w.InitializeKeymanager(ctx, true)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = keymanager.CreateAccount(ctx, password)
|
||||
require.NoError(t, err)
|
||||
return w
|
||||
}
|
||||
|
||||
func TestZipAndUnzip(t *testing.T) {
|
||||
testDir := testutil.TempDir()
|
||||
walletDir := filepath.Join(testDir, walletDirName)
|
||||
passwordsDir := filepath.Join(testDir, passwordDirName)
|
||||
exportDir := filepath.Join(testDir, exportDirName)
|
||||
importDir := filepath.Join(testDir, importDirName)
|
||||
defer func() {
|
||||
assert.NoError(t, os.RemoveAll(walletDir))
|
||||
assert.NoError(t, os.RemoveAll(passwordsDir))
|
||||
assert.NoError(t, os.RemoveAll(exportDir))
|
||||
assert.NoError(t, os.RemoveAll(importDir))
|
||||
}()
|
||||
wallet := setupWallet(t, testDir)
|
||||
|
||||
accounts, err := wallet.AccountNames()
|
||||
require.NoError(t, err)
|
||||
@@ -83,29 +71,33 @@ func TestZipAndUnzip(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExport_Noninteractive(t *testing.T) {
|
||||
testDir := testutil.TempDir()
|
||||
walletDir := filepath.Join(testDir, walletDirName)
|
||||
passwordsDir := filepath.Join(testDir, passwordDirName)
|
||||
exportDir := filepath.Join(testDir, exportDirName)
|
||||
walletDir, passwordsDir := setupWalletAndPasswordsDir(t)
|
||||
randPath, err := rand.Int(rand.Reader, big.NewInt(1000000))
|
||||
require.NoError(t, err, "Could not generate random file path")
|
||||
exportDir := path.Join(testutil.TempDir(), fmt.Sprintf("/%d", randPath), "export")
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, os.RemoveAll(exportDir), "Failed to remove directory")
|
||||
})
|
||||
accounts := "all"
|
||||
defer func() {
|
||||
assert.NoError(t, os.RemoveAll(walletDir))
|
||||
assert.NoError(t, os.RemoveAll(passwordsDir))
|
||||
assert.NoError(t, os.RemoveAll(exportDir))
|
||||
}()
|
||||
setupWallet(t, testDir)
|
||||
app := cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.String(flags.WalletDirFlag.Name, walletDir, "")
|
||||
set.String(flags.WalletPasswordsDirFlag.Name, passwordsDir, "")
|
||||
set.String(flags.BackupPathFlag.Name, exportDir, "")
|
||||
set.String(flags.AccountsFlag.Name, accounts, "")
|
||||
assert.NoError(t, set.Set(flags.WalletDirFlag.Name, walletDir))
|
||||
assert.NoError(t, set.Set(flags.WalletPasswordsDirFlag.Name, passwordsDir))
|
||||
assert.NoError(t, set.Set(flags.BackupPathFlag.Name, exportDir))
|
||||
assert.NoError(t, set.Set(flags.AccountsFlag.Name, accounts))
|
||||
cliCtx := cli.NewContext(&app, set, nil)
|
||||
|
||||
cliCtx := setupWalletCtx(t, &testWalletConfig{
|
||||
walletDir: walletDir,
|
||||
passwordsDir: passwordsDir,
|
||||
exportDir: exportDir,
|
||||
accountsToExport: accounts,
|
||||
keymanagerKind: v2keymanager.Direct,
|
||||
})
|
||||
wallet, err := NewWallet(cliCtx)
|
||||
require.NoError(t, err)
|
||||
ctx := context.Background()
|
||||
keymanager, err := direct.NewKeymanager(
|
||||
ctx,
|
||||
wallet,
|
||||
direct.DefaultConfig(),
|
||||
true, /* skip mnemonic */
|
||||
)
|
||||
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")
|
||||
|
||||
@@ -2,7 +2,6 @@ package v2
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
@@ -21,10 +20,9 @@ import (
|
||||
// ImportAccount uses the archived account made from ExportAccount to import an account and
|
||||
// asks the users for account passwords.
|
||||
func ImportAccount(cliCtx *cli.Context) error {
|
||||
// Read a wallet's directory from user input.
|
||||
walletDir, err := inputWalletDir(cliCtx)
|
||||
wallet, err := OpenWallet(cliCtx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not parse wallet directory")
|
||||
return errors.Wrap(err, "could not open wallet")
|
||||
}
|
||||
|
||||
backupDir, err := inputImportDir(cliCtx)
|
||||
@@ -32,7 +30,7 @@ func ImportAccount(cliCtx *cli.Context) error {
|
||||
return errors.Wrap(err, "could not parse output directory")
|
||||
}
|
||||
|
||||
accountsImported, err := unzipArchiveToTarget(backupDir, walletDir)
|
||||
accountsImported, err := unzipArchiveToTarget(backupDir, wallet.AccountsDir())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not unzip archive")
|
||||
}
|
||||
@@ -44,18 +42,6 @@ func ImportAccount(cliCtx *cli.Context) error {
|
||||
}
|
||||
fmt.Printf("Importing accounts: %s\n", strings.Join(loggedAccounts, ", "))
|
||||
|
||||
// Read the directory for password storage from user input.
|
||||
passwordsDirPath := inputPasswordsDirectory(cliCtx)
|
||||
|
||||
wallet, err := OpenWallet(context.Background(), &WalletConfig{
|
||||
CanUnlockAccounts: true,
|
||||
PasswordsDir: passwordsDirPath,
|
||||
WalletDir: walletDir,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not open wallet")
|
||||
}
|
||||
|
||||
for _, accountName := range accountsImported {
|
||||
if err := wallet.enterPasswordForAccount(cliCtx, accountName); err != nil {
|
||||
return errors.Wrap(err, "could not set account password")
|
||||
|
||||
@@ -1,38 +1,59 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
"github.com/prysmaticlabs/prysm/validator/flags"
|
||||
"github.com/urfave/cli/v2"
|
||||
v2keymanager "github.com/prysmaticlabs/prysm/validator/keymanager/v2"
|
||||
"github.com/prysmaticlabs/prysm/validator/keymanager/v2/direct"
|
||||
)
|
||||
|
||||
func TestImport_Noninteractive(t *testing.T) {
|
||||
testDir := testutil.TempDir()
|
||||
walletDir := filepath.Join(testDir, walletDirName)
|
||||
passwordsDir := filepath.Join(testDir, passwordDirName)
|
||||
exportDir := filepath.Join(testDir, exportDirName)
|
||||
importDir := filepath.Join(testDir, importDirName)
|
||||
importPasswordDir := filepath.Join(testDir, importPasswordDirName)
|
||||
|
||||
passwordFilePath := filepath.Join(testDir, passwordFileName)
|
||||
walletDir, passwordsDir := setupWalletAndPasswordsDir(t)
|
||||
randPath, err := rand.Int(rand.Reader, big.NewInt(1000000))
|
||||
require.NoError(t, err, "Could not generate random file path")
|
||||
exportDir := path.Join(testutil.TempDir(), fmt.Sprintf("/%d", randPath), "export")
|
||||
importDir := path.Join(testutil.TempDir(), fmt.Sprintf("/%d", randPath), "import")
|
||||
importPasswordDir := path.Join(testutil.TempDir(), fmt.Sprintf("/%d", randPath), "importpassword")
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, os.RemoveAll(exportDir), "Failed to remove directory")
|
||||
require.NoError(t, os.RemoveAll(importDir), "Failed to remove directory")
|
||||
require.NoError(t, os.RemoveAll(importPasswordDir), "Failed to remove directory")
|
||||
})
|
||||
require.NoError(t, os.MkdirAll(importPasswordDir, os.ModePerm))
|
||||
passwordFilePath := filepath.Join(importPasswordDir, passwordFileName)
|
||||
require.NoError(t, ioutil.WriteFile(passwordFilePath, []byte(password), os.ModePerm))
|
||||
defer func() {
|
||||
assert.NoError(t, os.RemoveAll(walletDir))
|
||||
assert.NoError(t, os.RemoveAll(passwordsDir))
|
||||
assert.NoError(t, os.RemoveAll(exportDir))
|
||||
assert.NoError(t, os.RemoveAll(importDir))
|
||||
assert.NoError(t, os.RemoveAll(importPasswordDir))
|
||||
}()
|
||||
|
||||
wallet := setupWallet(t, testDir)
|
||||
cliCtx := setupWalletCtx(t, &testWalletConfig{
|
||||
walletDir: walletDir,
|
||||
passwordsDir: passwordsDir,
|
||||
exportDir: exportDir,
|
||||
keymanagerKind: v2keymanager.Direct,
|
||||
passwordFile: passwordFilePath,
|
||||
})
|
||||
wallet, err := NewWallet(cliCtx)
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := context.Background()
|
||||
keymanager, err := direct.NewKeymanager(
|
||||
ctx,
|
||||
wallet,
|
||||
direct.DefaultConfig(),
|
||||
true, /* skip mnemonic */
|
||||
)
|
||||
require.NoError(t, err)
|
||||
_, err = keymanager.CreateAccount(ctx, password)
|
||||
require.NoError(t, err)
|
||||
|
||||
accounts, err := wallet.AccountNames()
|
||||
require.NoError(t, err)
|
||||
@@ -42,18 +63,5 @@ func TestImport_Noninteractive(t *testing.T) {
|
||||
if _, err := os.Stat(filepath.Join(exportDir, archiveFilename)); os.IsNotExist(err) {
|
||||
t.Fatal("Expected file to exist")
|
||||
}
|
||||
|
||||
app := cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.String(flags.WalletDirFlag.Name, importDir, "")
|
||||
set.String(flags.WalletPasswordsDirFlag.Name, importPasswordDir, "")
|
||||
set.String(flags.BackupPathFlag.Name, exportDir, "")
|
||||
set.String(flags.PasswordFileFlag.Name, passwordFilePath, "")
|
||||
assert.NoError(t, set.Set(flags.WalletDirFlag.Name, importDir))
|
||||
assert.NoError(t, set.Set(flags.WalletPasswordsDirFlag.Name, importPasswordDir))
|
||||
assert.NoError(t, set.Set(flags.BackupPathFlag.Name, exportDir))
|
||||
assert.NoError(t, set.Set(flags.PasswordFileFlag.Name, passwordFilePath))
|
||||
cliCtx := cli.NewContext(&app, set, nil)
|
||||
|
||||
require.NoError(t, ImportAccount(cliCtx))
|
||||
}
|
||||
|
||||
@@ -19,22 +19,13 @@ import (
|
||||
|
||||
// ListAccounts displays all available validator accounts in a Prysm wallet.
|
||||
func ListAccounts(cliCtx *cli.Context) error {
|
||||
walletDir, err := inputWalletDir(cliCtx)
|
||||
if errors.Is(err, ErrNoWalletFound) {
|
||||
return errors.New("no wallet found, create a new one with ./prysm.sh validator wallet-v2 create")
|
||||
} else if err != nil {
|
||||
return errors.Wrap(err, "could not parse wallet directory")
|
||||
}
|
||||
// Read the wallet from the specified path.
|
||||
ctx := context.Background()
|
||||
wallet, err := OpenWallet(ctx, &WalletConfig{
|
||||
WalletDir: walletDir,
|
||||
CanUnlockAccounts: false,
|
||||
})
|
||||
wallet, err := OpenWallet(cliCtx)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not read wallet at specified path %s", walletDir)
|
||||
return errors.Wrapf(err, "could not read wallet at specified path %s", wallet.AccountsDir())
|
||||
}
|
||||
keymanager, err := wallet.InitializeKeymanager(ctx, false /* skipMnemonicConfirm */)
|
||||
keymanager, err := wallet.InitializeKeymanager(ctx, true /* skip mnemonic confirm */)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not initialize keymanager")
|
||||
}
|
||||
@@ -84,8 +75,6 @@ func listDirectKeymanagerAccounts(
|
||||
au.BrightRed("View the eth1 deposit transaction data for your accounts " +
|
||||
"by running `validator accounts-v2 list --show-deposit-data"),
|
||||
)
|
||||
dirPath := au.BrightCyan("(wallet dir)")
|
||||
fmt.Printf("%s %s\n", dirPath, wallet.AccountsDir())
|
||||
fmt.Printf("Keymanager kind: %s\n", au.BrightGreen(wallet.KeymanagerKind().String()).Bold())
|
||||
|
||||
pubKeys, err := keymanager.FetchValidatingPublicKeys(context.Background())
|
||||
@@ -142,8 +131,6 @@ func listDerivedKeymanagerAccounts(
|
||||
au.BrightRed("View the eth1 deposit transaction data for your accounts " +
|
||||
"by running `validator accounts-v2 list --show-deposit-data"),
|
||||
)
|
||||
dirPath := au.BrightCyan("(wallet dir)")
|
||||
fmt.Printf("%s %s\n", dirPath, wallet.AccountsDir())
|
||||
fmt.Printf("(keymanager kind) %s\n", au.BrightGreen("derived, (HD) hierarchical-deterministic").Bold())
|
||||
fmt.Printf("(derivation format) %s\n", au.BrightGreen(keymanager.Config().DerivedPathStructure).Bold())
|
||||
ctx := context.Background()
|
||||
@@ -164,7 +151,6 @@ func listDerivedKeymanagerAccounts(
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info(currentAccountNumber)
|
||||
for i := uint64(0); i <= currentAccountNumber; i++ {
|
||||
fmt.Println("")
|
||||
validatingKeyPath := fmt.Sprintf(derived.ValidatingKeyDerivationPathTemplate, i)
|
||||
|
||||
@@ -19,17 +19,23 @@ import (
|
||||
)
|
||||
|
||||
func TestListAccounts_DirectKeymanager(t *testing.T) {
|
||||
walletDir, passwordsDir := setupWalletDir(t)
|
||||
keymanagerKind := v2keymanager.Direct
|
||||
ctx := context.Background()
|
||||
wallet, err := NewWallet(ctx, &WalletConfig{
|
||||
PasswordsDir: passwordsDir,
|
||||
WalletDir: walletDir,
|
||||
KeymanagerKind: keymanagerKind,
|
||||
walletDir, passwordsDir := setupWalletAndPasswordsDir(t)
|
||||
cliCtx := setupWalletCtx(t, &testWalletConfig{
|
||||
walletDir: walletDir,
|
||||
passwordsDir: passwordsDir,
|
||||
keymanagerKind: v2keymanager.Direct,
|
||||
})
|
||||
wallet, err := NewWallet(cliCtx)
|
||||
require.NoError(t, err)
|
||||
keymanager, err := direct.NewKeymanager(ctx, wallet, direct.DefaultConfig(), true /* skip confirm */)
|
||||
ctx := context.Background()
|
||||
keymanager, err := direct.NewKeymanager(
|
||||
ctx,
|
||||
wallet,
|
||||
direct.DefaultConfig(),
|
||||
true, /* skip confirm */
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
numAccounts := 5
|
||||
depositDataForAccounts := make([][]byte, numAccounts)
|
||||
accountCreationTimestamps := make([][]byte, numAccounts)
|
||||
@@ -100,17 +106,17 @@ func TestListAccounts_DirectKeymanager(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListAccounts_DerivedKeymanager(t *testing.T) {
|
||||
walletDir, passwordsDir := setupWalletDir(t)
|
||||
keymanagerKind := v2keymanager.Derived
|
||||
ctx := context.Background()
|
||||
wallet, err := NewWallet(ctx, &WalletConfig{
|
||||
PasswordsDir: passwordsDir,
|
||||
WalletDir: walletDir,
|
||||
KeymanagerKind: keymanagerKind,
|
||||
walletDir, passwordsDir := setupWalletAndPasswordsDir(t)
|
||||
cliCtx := setupWalletCtx(t, &testWalletConfig{
|
||||
walletDir: walletDir,
|
||||
passwordsDir: passwordsDir,
|
||||
keymanagerKind: v2keymanager.Derived,
|
||||
})
|
||||
wallet, err := NewWallet(cliCtx)
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := context.Background()
|
||||
password := "hello world"
|
||||
|
||||
seedConfig, err := derived.InitializeWalletSeedFile(ctx, password, true /* skip confirm */)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -127,11 +133,12 @@ func TestListAccounts_DerivedKeymanager(t *testing.T) {
|
||||
password,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
numAccounts := 5
|
||||
depositDataForAccounts := make([][]byte, numAccounts)
|
||||
accountCreationTimestamps := make([][]byte, numAccounts)
|
||||
for i := 0; i < numAccounts; i++ {
|
||||
_, err := keymanager.CreateAccount(ctx, password)
|
||||
_, err := keymanager.CreateAccount(ctx)
|
||||
require.NoError(t, err)
|
||||
withdrawalKeyPath := fmt.Sprintf(derived.WithdrawalKeyDerivationPathTemplate, i)
|
||||
depositData, err := wallet.ReadFileAtPath(ctx, withdrawalKeyPath, direct.DepositTransactionFileName)
|
||||
|
||||
@@ -7,11 +7,14 @@ import (
|
||||
"path"
|
||||
"unicode"
|
||||
|
||||
"github.com/logrusorgru/aurora"
|
||||
"github.com/manifoldco/promptui"
|
||||
strongPasswords "github.com/nbutton23/zxcvbn-go"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/validator/flags"
|
||||
v2keymanager "github.com/prysmaticlabs/prysm/validator/keymanager/v2"
|
||||
"github.com/prysmaticlabs/prysm/validator/keymanager/v2/derived"
|
||||
"github.com/prysmaticlabs/prysm/validator/keymanager/v2/direct"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
@@ -25,60 +28,45 @@ const (
|
||||
minPasswordScore = 3
|
||||
)
|
||||
|
||||
var keymanagerKindSelections = map[v2keymanager.Kind]string{
|
||||
v2keymanager.Derived: "HD Wallet (Recommended)",
|
||||
v2keymanager.Direct: "Non-HD Wallet (Most Basic)",
|
||||
v2keymanager.Remote: "Remote Signing Wallet (Advanced)",
|
||||
}
|
||||
|
||||
// NewAccount creates a new validator account from user input by opening
|
||||
// a wallet from the user's specified path.
|
||||
func NewAccount(cliCtx *cli.Context) error {
|
||||
// Read a wallet's directory from user input.
|
||||
walletDir, err := inputWalletDir(cliCtx)
|
||||
if errors.Is(err, ErrNoWalletFound) {
|
||||
return errors.New("no wallet found, create a new one with ./prysm.sh validator wallet-v2 create")
|
||||
} else if err != nil {
|
||||
return errors.Wrap(err, "could not get wallet directory")
|
||||
}
|
||||
ctx := context.Background()
|
||||
keymanagerKind, err := readKeymanagerKindFromWalletPath(walletDir)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get keymanager kind")
|
||||
}
|
||||
|
||||
// Only direct keymanagers can create accounts for now.
|
||||
if keymanagerKind == v2keymanager.Remote {
|
||||
return errors.New("cannot create a new account for a remote keymanager")
|
||||
}
|
||||
// Read the directory for password storage from user input.
|
||||
passwordsDirPath := inputPasswordsDirectory(cliCtx)
|
||||
wallet, err := OpenWallet(ctx, &WalletConfig{
|
||||
PasswordsDir: passwordsDirPath,
|
||||
WalletDir: walletDir,
|
||||
CanUnlockAccounts: true,
|
||||
KeymanagerKind: keymanagerKind,
|
||||
})
|
||||
wallet, err := OpenWallet(cliCtx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not open wallet")
|
||||
}
|
||||
|
||||
// We initialize a new keymanager depending on the wallet's keymanager kind.
|
||||
skipMnemonicConfirm := cliCtx.Bool(flags.SkipMnemonicConfirmFlag.Name)
|
||||
keymanager, err := wallet.InitializeKeymanager(ctx, skipMnemonicConfirm)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not initialize keymanager")
|
||||
}
|
||||
|
||||
// Read the new account's password from user input.
|
||||
password, err := inputNewAccountPassword(cliCtx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not read password")
|
||||
}
|
||||
|
||||
// Create a new validator account using the specified keymanager.
|
||||
if _, err := keymanager.CreateAccount(ctx, password); err != nil {
|
||||
return errors.Wrap(err, "could not create account in wallet")
|
||||
switch wallet.KeymanagerKind() {
|
||||
case v2keymanager.Remote:
|
||||
return errors.New("cannot create a new account for a remote keymanager")
|
||||
case v2keymanager.Direct:
|
||||
km, ok := keymanager.(*direct.Keymanager)
|
||||
if !ok {
|
||||
return errors.New("not a direct keymanager")
|
||||
}
|
||||
password, err := inputNewAccountPassword(cliCtx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not input new account password")
|
||||
}
|
||||
// Create a new validator account using the specified keymanager.
|
||||
if _, err := km.CreateAccount(ctx, password); err != nil {
|
||||
return errors.Wrap(err, "could not create account in wallet")
|
||||
}
|
||||
case v2keymanager.Derived:
|
||||
km, ok := keymanager.(*derived.Keymanager)
|
||||
if !ok {
|
||||
return errors.New("not a derived keymanager")
|
||||
}
|
||||
if _, err := km.CreateAccount(ctx); err != nil {
|
||||
return errors.Wrap(err, "could not create account in wallet")
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("keymanager kind %s not supported", wallet.KeymanagerKind())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -91,6 +79,15 @@ func inputWalletDir(cliCtx *cli.Context) (string, error) {
|
||||
|
||||
if walletDir == flags.DefaultValidatorDir() {
|
||||
walletDir = path.Join(walletDir, WalletDefaultDirName)
|
||||
ok, err := hasDir(walletDir)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "could not check if wallet dir %s exists", walletDir)
|
||||
}
|
||||
if ok {
|
||||
au := aurora.NewAurora(true)
|
||||
log.Infof("%s %s", au.BrightMagenta("(wallet path)"), walletDir)
|
||||
return walletDir, nil
|
||||
}
|
||||
}
|
||||
prompt := promptui.Prompt{
|
||||
Label: "Enter a wallet directory",
|
||||
@@ -118,8 +115,8 @@ func inputKeymanagerKind(cliCtx *cli.Context) (v2keymanager.Kind, error) {
|
||||
promptSelect := promptui.Select{
|
||||
Label: "Select a type of wallet",
|
||||
Items: []string{
|
||||
keymanagerKindSelections[v2keymanager.Direct],
|
||||
keymanagerKindSelections[v2keymanager.Derived],
|
||||
keymanagerKindSelections[v2keymanager.Direct],
|
||||
keymanagerKindSelections[v2keymanager.Remote],
|
||||
},
|
||||
}
|
||||
@@ -176,7 +173,15 @@ func inputNewWalletPassword(cliCtx *cli.Context) (string, error) {
|
||||
return walletPassword, nil
|
||||
}
|
||||
|
||||
func inputExistingWalletPassword() (string, error) {
|
||||
func inputExistingWalletPassword(cliCtx *cli.Context) (string, error) {
|
||||
if cliCtx.IsSet(flags.PasswordFileFlag.Name) {
|
||||
passwordFilePath := cliCtx.String(flags.PasswordFileFlag.Name)
|
||||
data, err := ioutil.ReadFile(passwordFilePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
prompt := promptui.Prompt{
|
||||
Label: "Wallet password",
|
||||
Validate: validatePasswordInput,
|
||||
@@ -249,14 +254,23 @@ func inputPasswordForAccount(_ *cli.Context, accountName string) (string, error)
|
||||
return walletPassword, nil
|
||||
}
|
||||
|
||||
func inputPasswordsDirectory(cliCtx *cli.Context) string {
|
||||
func inputPasswordsDirectory(cliCtx *cli.Context) (string, error) {
|
||||
passwordsDir := cliCtx.String(flags.WalletPasswordsDirFlag.Name)
|
||||
if cliCtx.IsSet(flags.WalletPasswordsDirFlag.Name) {
|
||||
return passwordsDir
|
||||
return passwordsDir, nil
|
||||
}
|
||||
|
||||
if passwordsDir == flags.DefaultValidatorDir() {
|
||||
passwordsDir = path.Join(passwordsDir, PasswordsDefaultDirName)
|
||||
ok, err := hasDir(passwordsDir)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "could not check if passwords directory exists")
|
||||
}
|
||||
if ok {
|
||||
au := aurora.NewAurora(true)
|
||||
log.Infof("%s %s", au.BrightMagenta("(account passwords path)"), passwordsDir)
|
||||
return passwordsDir, nil
|
||||
}
|
||||
}
|
||||
prompt := promptui.Prompt{
|
||||
Label: "Directory where passwords will be stored",
|
||||
@@ -265,9 +279,9 @@ func inputPasswordsDirectory(cliCtx *cli.Context) string {
|
||||
}
|
||||
passwordsPath, err := prompt.Run()
|
||||
if err != nil {
|
||||
log.Fatalf("Could not determine passwords directory: %v", formatPromptError(err))
|
||||
return "", fmt.Errorf("could not determine passwords directory: %v", formatPromptError(err))
|
||||
}
|
||||
return passwordsPath
|
||||
return passwordsPath, nil
|
||||
}
|
||||
|
||||
// Validate a strong password input for new accounts,
|
||||
|
||||
@@ -49,17 +49,13 @@ var (
|
||||
ErrNoWalletFound = errors.New(
|
||||
"no wallet found at path, please create a new wallet using `./prysm.sh validator wallet-v2 create`",
|
||||
)
|
||||
keymanagerKindSelections = map[v2keymanager.Kind]string{
|
||||
v2keymanager.Derived: "HD Wallet (Recommended)",
|
||||
v2keymanager.Direct: "Non-HD Wallet (Most Basic)",
|
||||
v2keymanager.Remote: "Remote Signing Wallet (Advanced)",
|
||||
}
|
||||
)
|
||||
|
||||
// WalletConfig for a wallet struct, containing important information
|
||||
// such as the passwords directory, the wallet's directory, and keymanager.
|
||||
type WalletConfig struct {
|
||||
WalletDir string
|
||||
PasswordsDir string
|
||||
KeymanagerKind v2keymanager.Kind
|
||||
CanUnlockAccounts bool
|
||||
}
|
||||
|
||||
// Wallet is a primitive in Prysm's v2 account management which
|
||||
// has the capability of creating new accounts, reading existing accounts,
|
||||
// and providing secure access to eth2 secrets depending on an
|
||||
@@ -69,6 +65,7 @@ type Wallet struct {
|
||||
passwordsDir string
|
||||
canUnlockAccounts bool
|
||||
keymanagerKind v2keymanager.Kind
|
||||
walletPassword string
|
||||
}
|
||||
|
||||
func init() {
|
||||
@@ -77,24 +74,48 @@ func init() {
|
||||
|
||||
// NewWallet given a set of configuration options, will leverage
|
||||
// create and write a new wallet to disk for a Prysm validator.
|
||||
func NewWallet(ctx context.Context, cfg *WalletConfig) (*Wallet, error) {
|
||||
if cfg.WalletDir == "" || (cfg.CanUnlockAccounts && cfg.PasswordsDir == "") {
|
||||
return nil, errors.New("wallet dir and passwords dir cannot be nil")
|
||||
func NewWallet(
|
||||
cliCtx *cli.Context,
|
||||
) (*Wallet, error) {
|
||||
walletDir, err := inputWalletDir(cliCtx)
|
||||
if err != nil && !errors.Is(err, ErrNoWalletFound) {
|
||||
return nil, errors.Wrap(err, "could not parse wallet directory")
|
||||
}
|
||||
accountsPath := path.Join(cfg.WalletDir, cfg.KeymanagerKind.String())
|
||||
// Check if the user has a wallet at the specified path.
|
||||
// If a user does not have a wallet, we instantiate one
|
||||
// based on specified options.
|
||||
walletExists, err := hasDir(walletDir)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not check if wallet exists")
|
||||
}
|
||||
if walletExists {
|
||||
return nil, errors.New(
|
||||
"you already have a wallet at the specified path. You can " +
|
||||
"edit your wallet configuration by running ./prysm.sh validator wallet-v2 edit",
|
||||
)
|
||||
}
|
||||
keymanagerKind, err := inputKeymanagerKind(cliCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
accountsPath := path.Join(walletDir, keymanagerKind.String())
|
||||
if err := os.MkdirAll(accountsPath, DirectoryPermissions); err != nil {
|
||||
return nil, errors.Wrap(err, "could not create wallet directory")
|
||||
}
|
||||
if cfg.PasswordsDir != "" {
|
||||
if err := os.MkdirAll(cfg.PasswordsDir, DirectoryPermissions); err != nil {
|
||||
w := &Wallet{
|
||||
accountsPath: accountsPath,
|
||||
keymanagerKind: keymanagerKind,
|
||||
}
|
||||
if keymanagerKind == v2keymanager.Direct {
|
||||
passwordsDir, err := inputPasswordsDirectory(cliCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := os.MkdirAll(passwordsDir, DirectoryPermissions); err != nil {
|
||||
return nil, errors.Wrap(err, "could not create passwords directory")
|
||||
}
|
||||
}
|
||||
w := &Wallet{
|
||||
accountsPath: accountsPath,
|
||||
passwordsDir: cfg.PasswordsDir,
|
||||
keymanagerKind: cfg.KeymanagerKind,
|
||||
canUnlockAccounts: cfg.CanUnlockAccounts,
|
||||
w.passwordsDir = passwordsDir
|
||||
w.canUnlockAccounts = true
|
||||
}
|
||||
return w, nil
|
||||
}
|
||||
@@ -102,18 +123,39 @@ func NewWallet(ctx context.Context, cfg *WalletConfig) (*Wallet, error) {
|
||||
// OpenWallet instantiates a wallet from a specified path. It checks the
|
||||
// type of keymanager associated with the wallet by reading files in the wallet
|
||||
// path, if applicable. If a wallet does not exist, returns an appropriate error.
|
||||
func OpenWallet(ctx context.Context, cfg *WalletConfig) (*Wallet, error) {
|
||||
keymanagerKind, err := readKeymanagerKindFromWalletPath(cfg.WalletDir)
|
||||
func OpenWallet(cliCtx *cli.Context) (*Wallet, error) {
|
||||
// Read a wallet's directory from user input.
|
||||
walletDir, err := inputWalletDir(cliCtx)
|
||||
if errors.Is(err, ErrNoWalletFound) {
|
||||
return nil, errors.New("no wallet found, create a new one with ./prysm.sh validator wallet-v2 create")
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keymanagerKind, err := readKeymanagerKindFromWalletPath(walletDir)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not read keymanager kind for wallet")
|
||||
}
|
||||
walletPath := path.Join(cfg.WalletDir, keymanagerKind.String())
|
||||
return &Wallet{
|
||||
accountsPath: walletPath,
|
||||
passwordsDir: cfg.PasswordsDir,
|
||||
keymanagerKind: keymanagerKind,
|
||||
canUnlockAccounts: cfg.CanUnlockAccounts,
|
||||
}, nil
|
||||
walletPath := path.Join(walletDir, keymanagerKind.String())
|
||||
w := &Wallet{
|
||||
accountsPath: walletPath,
|
||||
keymanagerKind: keymanagerKind,
|
||||
}
|
||||
if keymanagerKind == v2keymanager.Derived {
|
||||
walletPassword, err := inputExistingWalletPassword(cliCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
w.walletPassword = walletPassword
|
||||
}
|
||||
if keymanagerKind == v2keymanager.Direct {
|
||||
passwordsDir, err := inputPasswordsDirectory(cliCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
w.passwordsDir = passwordsDir
|
||||
w.canUnlockAccounts = true
|
||||
}
|
||||
return w, nil
|
||||
}
|
||||
|
||||
// ReadKeymanagerConfigFromDisk opens a keymanager config file
|
||||
@@ -195,15 +237,11 @@ func (w *Wallet) InitializeKeymanager(
|
||||
return nil, errors.Wrap(err, "could not initialize direct keymanager")
|
||||
}
|
||||
case v2keymanager.Derived:
|
||||
seedPassword, err := inputExistingWalletPassword()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cfg, err := derived.UnmarshalConfigFile(configFile)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not unmarshal keymanager config file")
|
||||
}
|
||||
keymanager, err = derived.NewKeymanager(ctx, w, cfg, skipMnemonicConfirm, seedPassword)
|
||||
keymanager, err = derived.NewKeymanager(ctx, w, cfg, skipMnemonicConfirm, w.walletPassword)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not initialize derived keymanager")
|
||||
}
|
||||
|
||||
@@ -19,96 +19,58 @@ import (
|
||||
// wallet already exists in the path, it suggests the user alternatives
|
||||
// such as how to edit their existing wallet configuration.
|
||||
func CreateWallet(cliCtx *cli.Context) error {
|
||||
// Read a wallet's directory from user input.
|
||||
walletDir, err := inputWalletDir(cliCtx)
|
||||
if err != nil && !errors.Is(err, ErrNoWalletFound) {
|
||||
return errors.Wrap(err, "could not parse wallet directory")
|
||||
}
|
||||
// Check if the user has a wallet at the specified path.
|
||||
// If a user does not have a wallet, we instantiate one
|
||||
// based on specified options.
|
||||
walletExists, err := hasDir(walletDir)
|
||||
w, err := NewWallet(cliCtx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not check if wallet directory exists")
|
||||
}
|
||||
if walletExists {
|
||||
return errors.New(
|
||||
"You already have a wallet at the specified path. You can " +
|
||||
"edit your wallet configuration by running ./prysm.sh validator wallet-v2 edit",
|
||||
)
|
||||
}
|
||||
// Determine the desired keymanager kind for the wallet from user input.
|
||||
keymanagerKind, err := inputKeymanagerKind(cliCtx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not select keymanager kind")
|
||||
}
|
||||
switch keymanagerKind {
|
||||
switch w.KeymanagerKind() {
|
||||
case v2keymanager.Direct:
|
||||
if err = createDirectWallet(cliCtx, walletDir); err != nil {
|
||||
if err = createDirectKeymanagerWallet(cliCtx, w); err != nil {
|
||||
return errors.Wrap(err, "could not initialize wallet with direct keymanager")
|
||||
}
|
||||
log.WithField("wallet-path", walletDir).Infof(
|
||||
log.WithField("wallet-path", w.accountsPath).Infof(
|
||||
"Successfully created wallet with on-disk keymanager configuration. " +
|
||||
"Make a new validator account with ./prysm.sh validator accounts-2 new",
|
||||
)
|
||||
case v2keymanager.Derived:
|
||||
if err = createDerivedWallet(cliCtx, walletDir); err != nil {
|
||||
if err = createDerivedKeymanagerWallet(cliCtx, w); err != nil {
|
||||
return errors.Wrap(err, "could not initialize wallet with derived keymanager")
|
||||
}
|
||||
log.WithField("wallet-path", walletDir).Infof(
|
||||
log.WithField("wallet-path", w.accountsPath).Infof(
|
||||
"Successfully created HD wallet and saved configuration to disk. " +
|
||||
"Make a new validator account with ./prysm.sh validator accounts-2 new",
|
||||
)
|
||||
case v2keymanager.Remote:
|
||||
if err = createRemoteWallet(cliCtx, walletDir); err != nil {
|
||||
if err = createRemoteKeymanagerWallet(cliCtx, w); err != nil {
|
||||
return errors.Wrap(err, "could not initialize wallet with remote keymanager")
|
||||
}
|
||||
log.WithField("wallet-path", walletDir).Infof(
|
||||
log.WithField("wallet-path", w.accountsPath).Infof(
|
||||
"Successfully created wallet with remote keymanager configuration",
|
||||
)
|
||||
default:
|
||||
return errors.Wrap(err, "keymanager type %s is not supported")
|
||||
return errors.Wrapf(err, "keymanager type %s is not supported", w.KeymanagerKind())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createDirectWallet(cliCtx *cli.Context, walletDir string) error {
|
||||
passwordsDirPath := inputPasswordsDirectory(cliCtx)
|
||||
walletConfig := &WalletConfig{
|
||||
WalletDir: walletDir,
|
||||
PasswordsDir: passwordsDirPath,
|
||||
KeymanagerKind: v2keymanager.Direct,
|
||||
CanUnlockAccounts: true,
|
||||
}
|
||||
ctx := context.Background()
|
||||
wallet, err := NewWallet(ctx, walletConfig)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not create new wallet")
|
||||
}
|
||||
keymanagerConfig, err := direct.MarshalConfigFile(ctx, direct.DefaultConfig())
|
||||
func createDirectKeymanagerWallet(cliCtx *cli.Context, wallet *Wallet) error {
|
||||
keymanagerConfig, err := direct.MarshalConfigFile(context.Background(), direct.DefaultConfig())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not marshal keymanager config file")
|
||||
}
|
||||
if err := wallet.WriteKeymanagerConfigToDisk(ctx, keymanagerConfig); err != nil {
|
||||
if err := wallet.WriteKeymanagerConfigToDisk(context.Background(), keymanagerConfig); err != nil {
|
||||
return errors.Wrap(err, "could not write keymanager config to disk")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createDerivedWallet(cliCtx *cli.Context, walletDir string) error {
|
||||
passwordsDirPath := inputPasswordsDirectory(cliCtx)
|
||||
walletConfig := &WalletConfig{
|
||||
PasswordsDir: passwordsDirPath,
|
||||
WalletDir: walletDir,
|
||||
KeymanagerKind: v2keymanager.Derived,
|
||||
CanUnlockAccounts: true,
|
||||
}
|
||||
func createDerivedKeymanagerWallet(cliCtx *cli.Context, wallet *Wallet) error {
|
||||
skipMnemonicConfirm := cliCtx.Bool(flags.SkipMnemonicConfirmFlag.Name)
|
||||
ctx := context.Background()
|
||||
walletPassword, err := inputNewWalletPassword(cliCtx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not input new wallet password")
|
||||
return err
|
||||
}
|
||||
skipMnemonicConfirm := cliCtx.Bool(flags.SkipMnemonicConfirmFlag.Name)
|
||||
seedConfig, err := derived.InitializeWalletSeedFile(ctx, walletPassword, skipMnemonicConfirm)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not initialize new wallet seed file")
|
||||
@@ -117,10 +79,6 @@ func createDerivedWallet(cliCtx *cli.Context, walletDir string) error {
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not marshal encrypted wallet seed file")
|
||||
}
|
||||
wallet, err := NewWallet(ctx, walletConfig)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not create new wallet")
|
||||
}
|
||||
keymanagerConfig, err := derived.MarshalConfigFile(ctx, derived.DefaultConfig())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not marshal keymanager config file")
|
||||
@@ -134,7 +92,7 @@ func createDerivedWallet(cliCtx *cli.Context, walletDir string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func createRemoteWallet(cliCtx *cli.Context, walletDir string) error {
|
||||
func createRemoteKeymanagerWallet(cliCtx *cli.Context, wallet *Wallet) error {
|
||||
conf, err := inputRemoteKeymanagerConfig(cliCtx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not input remote keymanager config")
|
||||
@@ -144,14 +102,6 @@ func createRemoteWallet(cliCtx *cli.Context, walletDir string) error {
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not marshal config file")
|
||||
}
|
||||
walletConfig := &WalletConfig{
|
||||
WalletDir: walletDir,
|
||||
KeymanagerKind: v2keymanager.Remote,
|
||||
}
|
||||
wallet, err := NewWallet(ctx, walletConfig)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not create new wallet")
|
||||
}
|
||||
if err := wallet.WriteKeymanagerConfigToDisk(ctx, keymanagerConfig); err != nil {
|
||||
return errors.Wrap(err, "could not write keymanager config to disk")
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"flag"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
@@ -18,34 +17,19 @@ import (
|
||||
)
|
||||
|
||||
func TestCreateWallet_Direct(t *testing.T) {
|
||||
walletDir := filepath.Join(testutil.TempDir(), walletDirName)
|
||||
passwordsDir := filepath.Join(testutil.TempDir(), passwordDirName)
|
||||
defer func() {
|
||||
assert.NoError(t, os.RemoveAll(walletDir))
|
||||
assert.NoError(t, os.RemoveAll(passwordsDir))
|
||||
}()
|
||||
wantCfg := direct.DefaultConfig()
|
||||
app := cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
keymanagerKind := "direct"
|
||||
set.String(flags.WalletDirFlag.Name, walletDir, "")
|
||||
set.String(flags.KeymanagerKindFlag.Name, keymanagerKind, "")
|
||||
set.String(flags.WalletPasswordsDirFlag.Name, passwordsDir, "")
|
||||
assert.NoError(t, set.Set(flags.WalletDirFlag.Name, walletDir))
|
||||
assert.NoError(t, set.Set(flags.KeymanagerKindFlag.Name, keymanagerKind))
|
||||
assert.NoError(t, set.Set(flags.WalletPasswordsDirFlag.Name, passwordsDir))
|
||||
cliCtx := cli.NewContext(&app, set, nil)
|
||||
walletDir, passwordsDir := setupWalletAndPasswordsDir(t)
|
||||
cliCtx := setupWalletCtx(t, &testWalletConfig{
|
||||
walletDir: walletDir,
|
||||
passwordsDir: passwordsDir,
|
||||
keymanagerKind: v2keymanager.Direct,
|
||||
})
|
||||
|
||||
// We attempt to create the wallet.
|
||||
require.NoError(t, CreateWallet(cliCtx))
|
||||
|
||||
// We attempt to open the newly created wallet.
|
||||
ctx := context.Background()
|
||||
wallet, err := OpenWallet(ctx, &WalletConfig{
|
||||
WalletDir: walletDir,
|
||||
KeymanagerKind: v2keymanager.Direct,
|
||||
CanUnlockAccounts: false,
|
||||
})
|
||||
wallet, err := OpenWallet(cliCtx)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// We read the keymanager config for the newly created wallet.
|
||||
@@ -55,7 +39,7 @@ func TestCreateWallet_Direct(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
|
||||
// We assert the created configuration was as desired.
|
||||
assert.DeepEqual(t, wantCfg, cfg)
|
||||
assert.DeepEqual(t, direct.DefaultConfig(), cfg)
|
||||
}
|
||||
|
||||
func TestCreateWallet_Remote(t *testing.T) {
|
||||
@@ -93,11 +77,7 @@ func TestCreateWallet_Remote(t *testing.T) {
|
||||
|
||||
// We attempt to open the newly created wallet.
|
||||
ctx := context.Background()
|
||||
wallet, err := OpenWallet(ctx, &WalletConfig{
|
||||
WalletDir: walletDir,
|
||||
KeymanagerKind: v2keymanager.Remote,
|
||||
CanUnlockAccounts: false,
|
||||
})
|
||||
wallet, err := OpenWallet(cliCtx)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// We read the keymanager config for the newly created wallet.
|
||||
|
||||
@@ -14,28 +14,12 @@ import (
|
||||
// things such as remote gRPC credentials for remote signing, derivation paths
|
||||
// for HD wallets, and more.
|
||||
func EditWalletConfiguration(cliCtx *cli.Context) error {
|
||||
// Read a wallet's directory from user input.
|
||||
walletDir, err := inputWalletDir(cliCtx)
|
||||
if errors.Is(err, ErrNoWalletFound) {
|
||||
return errors.New("no wallet found, create a new one with ./prysm.sh validator wallet-v2 create")
|
||||
} else if err != nil {
|
||||
return errors.Wrap(err, "could not parse wallet directory")
|
||||
}
|
||||
// Determine the keymanager kind for the wallet.
|
||||
keymanagerKind, err := readKeymanagerKindFromWalletPath(walletDir)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not select keymanager kind")
|
||||
}
|
||||
ctx := context.Background()
|
||||
wallet, err := OpenWallet(ctx, &WalletConfig{
|
||||
CanUnlockAccounts: false,
|
||||
WalletDir: walletDir,
|
||||
KeymanagerKind: keymanagerKind,
|
||||
})
|
||||
wallet, err := OpenWallet(cliCtx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not open wallet")
|
||||
}
|
||||
switch keymanagerKind {
|
||||
switch wallet.KeymanagerKind() {
|
||||
case v2keymanager.Direct:
|
||||
return errors.New("no configuration options available to edit for direct keymanager")
|
||||
case v2keymanager.Derived:
|
||||
@@ -63,7 +47,7 @@ func EditWalletConfiguration(cliCtx *cli.Context) error {
|
||||
return errors.Wrap(err, "could not write config to disk")
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("keymanager type %s is not supported", keymanagerKind)
|
||||
return fmt.Errorf("keymanager type %s is not supported", wallet.KeymanagerKind())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,12 +3,10 @@ package v2
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
"github.com/prysmaticlabs/prysm/validator/flags"
|
||||
v2keymanager "github.com/prysmaticlabs/prysm/validator/keymanager/v2"
|
||||
"github.com/prysmaticlabs/prysm/validator/keymanager/v2/remote"
|
||||
@@ -16,11 +14,15 @@ import (
|
||||
)
|
||||
|
||||
func TestEditWalletConfiguration(t *testing.T) {
|
||||
walletDir := filepath.Join(testutil.TempDir(), walletDirName)
|
||||
defer func() {
|
||||
assert.NoError(t, os.RemoveAll(walletDir))
|
||||
}()
|
||||
walletDir, _ := setupWalletAndPasswordsDir(t)
|
||||
cliCtx := setupWalletCtx(t, &testWalletConfig{
|
||||
walletDir: walletDir,
|
||||
keymanagerKind: v2keymanager.Remote,
|
||||
})
|
||||
wallet, err := NewWallet(cliCtx)
|
||||
require.NoError(t, err)
|
||||
ctx := context.Background()
|
||||
|
||||
originalCfg := &remote.Config{
|
||||
RemoteCertificate: &remote.CertificateConfig{
|
||||
ClientCertPath: "/tmp/a.crt",
|
||||
@@ -31,12 +33,6 @@ func TestEditWalletConfiguration(t *testing.T) {
|
||||
}
|
||||
encodedCfg, err := remote.MarshalConfigFile(ctx, originalCfg)
|
||||
assert.NoError(t, err)
|
||||
walletConfig := &WalletConfig{
|
||||
WalletDir: walletDir,
|
||||
KeymanagerKind: v2keymanager.Remote,
|
||||
}
|
||||
wallet, err := NewWallet(ctx, walletConfig)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, wallet.WriteKeymanagerConfigToDisk(ctx, encodedCfg))
|
||||
|
||||
wantCfg := &remote.Config{
|
||||
@@ -59,7 +55,7 @@ func TestEditWalletConfiguration(t *testing.T) {
|
||||
assert.NoError(t, set.Set(flags.RemoteSignerCertPathFlag.Name, wantCfg.RemoteCertificate.ClientCertPath))
|
||||
assert.NoError(t, set.Set(flags.RemoteSignerKeyPathFlag.Name, wantCfg.RemoteCertificate.ClientKeyPath))
|
||||
assert.NoError(t, set.Set(flags.RemoteSignerCACertPathFlag.Name, wantCfg.RemoteCertificate.CACertPath))
|
||||
cliCtx := cli.NewContext(&app, set, nil)
|
||||
cliCtx = cli.NewContext(&app, set, nil)
|
||||
|
||||
assert.NoError(t, EditWalletConfiguration(cliCtx))
|
||||
encoded, err := wallet.ReadKeymanagerConfigFromDisk(ctx)
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"github.com/manifoldco/promptui"
|
||||
"github.com/pkg/errors"
|
||||
"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"
|
||||
)
|
||||
@@ -18,54 +17,16 @@ const phraseWordCount = 24
|
||||
|
||||
// RecoverWallet uses a menmonic seed phrase to recover a wallet into the path provided.
|
||||
func RecoverWallet(cliCtx *cli.Context) error {
|
||||
// Read a wallet's directory from user input.
|
||||
walletDir, err := inputWalletDir(cliCtx)
|
||||
if err != nil && !errors.Is(err, ErrNoWalletFound) {
|
||||
log.Fatalf("Could not parse wallet directory: %v", err)
|
||||
}
|
||||
|
||||
// Check if the user has a wallet at the specified path.
|
||||
// If a user does not have a wallet, we instantiate one
|
||||
// based on specified options.
|
||||
walletExists, err := hasDir(walletDir)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if walletExists {
|
||||
log.Fatal(
|
||||
"You already have a wallet at the specified path. You can " +
|
||||
"edit your wallet configuration by running ./prysm.sh validator wallet-v2 edit",
|
||||
)
|
||||
}
|
||||
if err = recoverDerivedWallet(cliCtx, walletDir); err != nil {
|
||||
log.Fatalf("Could not initialize wallet with derived keymanager: %v", err)
|
||||
}
|
||||
log.WithField("wallet-path", walletDir).Infof(
|
||||
"Successfully recovered HD wallet and saved configuration to disk. " +
|
||||
"Make a new validator account with ./prysm.sh validator accounts-2 new",
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
func recoverDerivedWallet(cliCtx *cli.Context, walletDir string) error {
|
||||
mnemonic, err := inputMnemonic(cliCtx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get mnemonic phrase")
|
||||
}
|
||||
passwordsDirPath := inputPasswordsDirectory(cliCtx)
|
||||
walletConfig := &WalletConfig{
|
||||
PasswordsDir: passwordsDirPath,
|
||||
WalletDir: walletDir,
|
||||
KeymanagerKind: v2keymanager.Derived,
|
||||
CanUnlockAccounts: true,
|
||||
wallet, err := NewWallet(cliCtx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not create new wallet")
|
||||
}
|
||||
ctx := context.Background()
|
||||
walletPassword, err := inputNewWalletPassword(cliCtx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not input new wallet password")
|
||||
}
|
||||
|
||||
seedConfig, err := derived.SeedFileFromMnemonic(ctx, mnemonic, walletPassword)
|
||||
seedConfig, err := derived.SeedFileFromMnemonic(ctx, mnemonic, wallet.walletPassword)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not initialize new wallet seed file")
|
||||
}
|
||||
@@ -73,11 +34,6 @@ func recoverDerivedWallet(cliCtx *cli.Context, walletDir string) error {
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not marshal encrypted wallet seed file")
|
||||
}
|
||||
wallet, err := NewWallet(ctx, walletConfig)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not create new wallet")
|
||||
}
|
||||
|
||||
keymanagerConfig, err := derived.MarshalConfigFile(ctx, derived.DefaultConfig())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not marshal keymanager config file")
|
||||
@@ -88,6 +44,10 @@ func recoverDerivedWallet(cliCtx *cli.Context, walletDir string) error {
|
||||
if err := wallet.WriteEncryptedSeedToDisk(ctx, seedConfigFile); err != nil {
|
||||
return errors.Wrap(err, "could not write encrypted wallet seed config to disk")
|
||||
}
|
||||
log.WithField("wallet-path", wallet.AccountsDir()).Infof(
|
||||
"Successfully recovered HD wallet and saved configuration to disk. " +
|
||||
"Make a new validator account with ./prysm.sh validator accounts-2 new",
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -38,24 +38,21 @@ func TestRecoverDerivedWallet(t *testing.T) {
|
||||
set.String(flags.WalletDirFlag.Name, walletDir, "")
|
||||
set.String(flags.WalletPasswordsDirFlag.Name, passwordsDir, "")
|
||||
set.String(flags.PasswordFileFlag.Name, passwordFilePath, "")
|
||||
set.String(flags.KeymanagerKindFlag.Name, v2keymanager.Derived.String(), "")
|
||||
set.String(flags.MnemonicFileFlag.Name, mnemonicFilePath, "")
|
||||
assert.NoError(t, set.Set(flags.WalletDirFlag.Name, walletDir))
|
||||
assert.NoError(t, set.Set(flags.WalletPasswordsDirFlag.Name, passwordsDir))
|
||||
assert.NoError(t, set.Set(flags.PasswordFileFlag.Name, passwordFilePath))
|
||||
assert.NoError(t, set.Set(flags.KeymanagerKindFlag.Name, v2keymanager.Derived.String()))
|
||||
assert.NoError(t, set.Set(flags.MnemonicFileFlag.Name, mnemonicFilePath))
|
||||
cliCtx := cli.NewContext(&app, set, nil)
|
||||
|
||||
if err := recoverDerivedWallet(cliCtx, walletDir); err != nil {
|
||||
if err := RecoverWallet(cliCtx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
wallet, err := OpenWallet(ctx, &WalletConfig{
|
||||
WalletDir: walletDir,
|
||||
PasswordsDir: passwordsDir,
|
||||
KeymanagerKind: v2keymanager.Derived,
|
||||
CanUnlockAccounts: false,
|
||||
})
|
||||
wallet, err := OpenWallet(cliCtx)
|
||||
assert.NoError(t, err)
|
||||
|
||||
encoded, err := wallet.ReadKeymanagerConfigFromDisk(ctx)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
@@ -11,10 +11,12 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
"github.com/prysmaticlabs/prysm/validator/flags"
|
||||
v2keymanager "github.com/prysmaticlabs/prysm/validator/keymanager/v2"
|
||||
mock "github.com/prysmaticlabs/prysm/validator/keymanager/v2/testing"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -22,12 +24,42 @@ func init() {
|
||||
logrus.SetOutput(ioutil.Discard)
|
||||
}
|
||||
|
||||
func setupWalletDir(t testing.TB) (string, string) {
|
||||
type testWalletConfig struct {
|
||||
walletDir string
|
||||
passwordsDir string
|
||||
exportDir string
|
||||
accountsToExport string
|
||||
passwordFile string
|
||||
keymanagerKind v2keymanager.Kind
|
||||
}
|
||||
|
||||
func setupWalletCtx(
|
||||
tb testing.TB,
|
||||
cfg *testWalletConfig,
|
||||
) *cli.Context {
|
||||
app := cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.String(flags.WalletDirFlag.Name, cfg.walletDir, "")
|
||||
set.String(flags.WalletPasswordsDirFlag.Name, cfg.passwordsDir, "")
|
||||
set.String(flags.KeymanagerKindFlag.Name, cfg.keymanagerKind.String(), "")
|
||||
set.String(flags.BackupPathFlag.Name, cfg.exportDir, "")
|
||||
set.String(flags.AccountsFlag.Name, cfg.accountsToExport, "")
|
||||
set.String(flags.PasswordFileFlag.Name, cfg.passwordFile, "")
|
||||
assert.NoError(tb, set.Set(flags.WalletDirFlag.Name, cfg.walletDir))
|
||||
assert.NoError(tb, set.Set(flags.WalletPasswordsDirFlag.Name, cfg.passwordsDir))
|
||||
assert.NoError(tb, set.Set(flags.KeymanagerKindFlag.Name, cfg.keymanagerKind.String()))
|
||||
assert.NoError(tb, set.Set(flags.BackupPathFlag.Name, cfg.exportDir))
|
||||
assert.NoError(tb, set.Set(flags.AccountsFlag.Name, cfg.accountsToExport))
|
||||
assert.NoError(tb, set.Set(flags.PasswordFileFlag.Name, cfg.passwordFile))
|
||||
return cli.NewContext(&app, set, nil)
|
||||
}
|
||||
|
||||
func setupWalletAndPasswordsDir(t testing.TB) (string, string) {
|
||||
randPath, err := rand.Int(rand.Reader, big.NewInt(1000000))
|
||||
require.NoError(t, err, "Could not generate random file path")
|
||||
walletDir := path.Join(testutil.TempDir(), fmt.Sprintf("/%d", randPath))
|
||||
walletDir := path.Join(testutil.TempDir(), fmt.Sprintf("/%d", randPath), "wallet")
|
||||
require.NoError(t, os.RemoveAll(walletDir), "Failed to remove directory")
|
||||
passwordsDir := path.Join(testutil.TempDir(), fmt.Sprintf("/%d", randPath))
|
||||
passwordsDir := path.Join(testutil.TempDir(), fmt.Sprintf("/%d", randPath), "passwords")
|
||||
require.NoError(t, os.RemoveAll(passwordsDir), "Failed to remove directory")
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, os.RemoveAll(walletDir), "Failed to remove directory")
|
||||
@@ -37,37 +69,15 @@ func setupWalletDir(t testing.TB) (string, string) {
|
||||
}
|
||||
|
||||
func TestCreateAndReadWallet(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
if _, err := NewWallet(ctx, &WalletConfig{
|
||||
PasswordsDir: "",
|
||||
WalletDir: "",
|
||||
}); err == nil {
|
||||
t.Error("Expected error when passing in empty directories, received nil")
|
||||
}
|
||||
walletDir, passwordsDir := setupWalletDir(t)
|
||||
keymanagerKind := v2keymanager.Direct
|
||||
wallet, err := NewWallet(ctx, &WalletConfig{
|
||||
PasswordsDir: passwordsDir,
|
||||
WalletDir: walletDir,
|
||||
KeymanagerKind: keymanagerKind,
|
||||
walletDir, passwordsDir := setupWalletAndPasswordsDir(t)
|
||||
cliCtx := setupWalletCtx(t, &testWalletConfig{
|
||||
walletDir: walletDir,
|
||||
passwordsDir: passwordsDir,
|
||||
keymanagerKind: v2keymanager.Direct,
|
||||
})
|
||||
_, err := NewWallet(cliCtx)
|
||||
require.NoError(t, err)
|
||||
|
||||
keymanager := &mock.MockKeymanager{
|
||||
ConfigFileContents: []byte("hello-world"),
|
||||
}
|
||||
keymanagerConfig, err := keymanager.MarshalConfigFile(ctx)
|
||||
require.NoError(t, err, "Could not marshal keymanager config file")
|
||||
require.NoError(t, wallet.WriteKeymanagerConfigToDisk(ctx, keymanagerConfig), "Could not write keymanager config file to disk")
|
||||
|
||||
walletPath := path.Join(walletDir, keymanagerKind.String())
|
||||
configFilePath := path.Join(walletPath, KeymanagerConfigFileName)
|
||||
require.Equal(t, true, fileExists(configFilePath), "Expected config file to have been created at path: %s", configFilePath)
|
||||
|
||||
// We should be able to now read the wallet as well.
|
||||
_, err = NewWallet(ctx, &WalletConfig{
|
||||
PasswordsDir: passwordsDir,
|
||||
WalletDir: walletDir,
|
||||
})
|
||||
_, err = OpenWallet(cliCtx)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -73,6 +73,7 @@ type Keymanager struct {
|
||||
lock sync.RWMutex
|
||||
seedCfg *SeedConfig
|
||||
seed []byte
|
||||
walletPassword string
|
||||
}
|
||||
|
||||
// SeedConfig json file representation as a Go struct.
|
||||
@@ -128,9 +129,10 @@ func NewKeymanager(
|
||||
mnemonicGenerator: &EnglishMnemonicGenerator{
|
||||
skipMnemonicConfirm: skipMnemonicConfirm,
|
||||
},
|
||||
seedCfg: seedConfig,
|
||||
seed: seed,
|
||||
keysCache: make(map[[48]byte]bls.SecretKey),
|
||||
seedCfg: seedConfig,
|
||||
seed: seed,
|
||||
walletPassword: password,
|
||||
keysCache: make(map[[48]byte]bls.SecretKey),
|
||||
}
|
||||
// We initialize a cache of public key -> secret keys
|
||||
// used to retrieve secrets keys for the accounts via the unlocked wallet.
|
||||
@@ -248,8 +250,12 @@ func (dr *Keymanager) NextAccountNumber(ctx context.Context) uint64 {
|
||||
func (dr *Keymanager) ValidatingAccountNames(ctx context.Context) ([]string, error) {
|
||||
names := make([]string, 0)
|
||||
for i := uint64(0); i < dr.seedCfg.NextAccount; i++ {
|
||||
withdrawalKeyPath := fmt.Sprintf(WithdrawalKeyDerivationPathTemplate, i)
|
||||
names = append(names, petnames.DeterministicName([]byte(withdrawalKeyPath), "-"))
|
||||
validatingKeyPath := fmt.Sprintf(ValidatingKeyDerivationPathTemplate, i)
|
||||
validatingKey, err := util.PrivateKeyFromSeedAndPath(dr.seed, validatingKeyPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not derive validating key")
|
||||
}
|
||||
names = append(names, petnames.DeterministicName(validatingKey.Marshal(), "-"))
|
||||
}
|
||||
return names, nil
|
||||
}
|
||||
@@ -259,7 +265,7 @@ func (dr *Keymanager) ValidatingAccountNames(ctx context.Context) ([]string, err
|
||||
// for hierarchical derivation of BLS secret keys and a common derivation path structure for
|
||||
// persisting accounts to disk. Each account stores the generated keystore.json file.
|
||||
// The entire derived wallet seed phrase can be recovered from a BIP-39 english mnemonic.
|
||||
func (dr *Keymanager) CreateAccount(ctx context.Context, password string) (string, error) {
|
||||
func (dr *Keymanager) CreateAccount(ctx context.Context) (string, error) {
|
||||
withdrawalKeyPath := fmt.Sprintf(WithdrawalKeyDerivationPathTemplate, dr.seedCfg.NextAccount)
|
||||
validatingKeyPath := fmt.Sprintf(ValidatingKeyDerivationPathTemplate, dr.seedCfg.NextAccount)
|
||||
withdrawalKey, err := util.PrivateKeyFromSeedAndPath(dr.seed, withdrawalKeyPath)
|
||||
@@ -275,7 +281,7 @@ func (dr *Keymanager) CreateAccount(ctx context.Context, password string) (strin
|
||||
encodedWithdrawalKeystore, err := dr.generateKeystoreFile(
|
||||
withdrawalKey.Marshal(),
|
||||
withdrawalKey.PublicKey().Marshal(),
|
||||
password,
|
||||
dr.walletPassword,
|
||||
)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "could not generate keystore file for withdrawal account")
|
||||
@@ -283,7 +289,7 @@ func (dr *Keymanager) CreateAccount(ctx context.Context, password string) (strin
|
||||
encodedValidatingKeystore, err := dr.generateKeystoreFile(
|
||||
validatingKey.Marshal(),
|
||||
validatingKey.PublicKey().Marshal(),
|
||||
password,
|
||||
dr.walletPassword,
|
||||
)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "could not generate keystore file for validating account")
|
||||
|
||||
@@ -29,16 +29,17 @@ func TestDerivedKeymanager_CreateAccount(t *testing.T) {
|
||||
}
|
||||
seed := make([]byte, 32)
|
||||
copy(seed, "hello world")
|
||||
password := "secretPassw0rd$1999"
|
||||
dr := &Keymanager{
|
||||
wallet: wallet,
|
||||
seed: seed,
|
||||
seedCfg: &SeedConfig{
|
||||
NextAccount: 0,
|
||||
},
|
||||
walletPassword: password,
|
||||
}
|
||||
ctx := context.Background()
|
||||
password := "secretPassw0rd$1999"
|
||||
accountName, err := dr.CreateAccount(ctx, password)
|
||||
accountName, err := dr.CreateAccount(ctx)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "0", accountName)
|
||||
|
||||
@@ -108,17 +109,17 @@ func TestDerivedKeymanager_FetchValidatingPublicKeys(t *testing.T) {
|
||||
seedCfg: &SeedConfig{
|
||||
NextAccount: 0,
|
||||
},
|
||||
seed: make([]byte, 32),
|
||||
seed: make([]byte, 32),
|
||||
walletPassword: "hello world",
|
||||
}
|
||||
// First, generate accounts and their keystore.json files.
|
||||
ctx := context.Background()
|
||||
numAccounts := 20
|
||||
password := "hello world"
|
||||
wantedPublicKeys := make([][48]byte, numAccounts)
|
||||
var err error
|
||||
var accountName string
|
||||
for i := 0; i < numAccounts; i++ {
|
||||
accountName, err = dr.CreateAccount(ctx, password)
|
||||
accountName, err = dr.CreateAccount(ctx)
|
||||
require.NoError(t, err)
|
||||
validatingKeyPath := fmt.Sprintf(ValidatingKeyDerivationPathTemplate, i)
|
||||
enc, err := wallet.ReadFileAtPath(ctx, validatingKeyPath, KeystoreFileName)
|
||||
@@ -161,16 +162,16 @@ func TestDerivedKeymanager_Sign(t *testing.T) {
|
||||
seedCfg: &SeedConfig{
|
||||
NextAccount: 0,
|
||||
},
|
||||
walletPassword: "hello world",
|
||||
}
|
||||
|
||||
// First, generate some accounts.
|
||||
numAccounts := 2
|
||||
ctx := context.Background()
|
||||
password := "hello world"
|
||||
var err error
|
||||
var accountName string
|
||||
for i := 0; i < numAccounts; i++ {
|
||||
accountName, err = dr.CreateAccount(ctx, password)
|
||||
accountName, err = dr.CreateAccount(ctx)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
assert.Equal(t, fmt.Sprintf("%d", numAccounts-1), accountName)
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
testonly = 1,
|
||||
srcs = ["mock.go"],
|
||||
importpath = "github.com/prysmaticlabs/prysm/validator/keymanager/v2/testing",
|
||||
visibility = [
|
||||
"//validator:__pkg__",
|
||||
"//validator:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//proto/validator/accounts/v2:go_default_library",
|
||||
"//shared/bls:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -1,42 +0,0 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
validatorpb "github.com/prysmaticlabs/prysm/proto/validator/accounts/v2"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
)
|
||||
|
||||
// MockKeymanager --
|
||||
type MockKeymanager struct {
|
||||
ConfigFileContents []byte
|
||||
PublicKeys [][48]byte
|
||||
PubkeystoSecretKeys map[[48]byte]bls.SecretKey
|
||||
}
|
||||
|
||||
// CreateAccount --
|
||||
func (m *MockKeymanager) CreateAccount(ctx context.Context, password string) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// MarshalConfigFile --
|
||||
func (m *MockKeymanager) MarshalConfigFile(ctx context.Context) ([]byte, error) {
|
||||
return m.ConfigFileContents, nil
|
||||
}
|
||||
|
||||
// FetchValidatingPublicKeys --
|
||||
func (m *MockKeymanager) FetchValidatingPublicKeys(ctx context.Context) ([][48]byte, error) {
|
||||
return m.PublicKeys, nil
|
||||
}
|
||||
|
||||
// Sign --
|
||||
func (m *MockKeymanager) Sign(ctx context.Context, req *validatorpb.SignRequest) (bls.Signature, error) {
|
||||
pubKey := bytesutil.ToBytes48(req.PublicKey)
|
||||
secretKey, ok := m.PubkeystoSecretKeys[pubKey]
|
||||
if !ok {
|
||||
return nil, errors.New("no secret key found")
|
||||
}
|
||||
return secretKey.Sign(req.SigningRoot), nil
|
||||
}
|
||||
@@ -10,8 +10,6 @@ import (
|
||||
|
||||
// IKeymanager defines a general keymanager-v2 interface for Prysm wallets.
|
||||
type IKeymanager interface {
|
||||
// CreateAccount based on the keymanager's logic. Returns the account name.
|
||||
CreateAccount(ctx context.Context, password string) (string, error)
|
||||
// FetchValidatingKeys fetches the list of public keys that should be used to validate with.
|
||||
FetchValidatingPublicKeys(ctx context.Context) ([][48]byte, error)
|
||||
// Sign signs a message using a validator key.
|
||||
@@ -32,10 +30,10 @@ type Keystore struct {
|
||||
type Kind int
|
||||
|
||||
const (
|
||||
// Direct keymanager defines an on-disk, encrypted keystore-capable store.
|
||||
Direct Kind = iota
|
||||
// Derived keymanager using a hierarchical-deterministic algorithm.
|
||||
Derived
|
||||
Derived Kind = iota
|
||||
// Direct keymanager defines an on-disk, encrypted keystore-capable store.
|
||||
Direct
|
||||
// Remote keymanager capable of remote-signing data.
|
||||
Remote
|
||||
)
|
||||
@@ -43,10 +41,10 @@ const (
|
||||
// String marshals a keymanager kind to a string value.
|
||||
func (k Kind) String() string {
|
||||
switch k {
|
||||
case Direct:
|
||||
return "direct"
|
||||
case Derived:
|
||||
return "derived"
|
||||
case Direct:
|
||||
return "direct"
|
||||
case Remote:
|
||||
return "remote"
|
||||
default:
|
||||
@@ -57,10 +55,10 @@ func (k Kind) String() string {
|
||||
// ParseKind from a raw string, returning a keymanager kind.
|
||||
func ParseKind(k string) (Kind, error) {
|
||||
switch k {
|
||||
case "direct":
|
||||
return Direct, nil
|
||||
case "derived":
|
||||
return Derived, nil
|
||||
case "direct":
|
||||
return Direct, nil
|
||||
case "remote":
|
||||
return Remote, nil
|
||||
default:
|
||||
|
||||
@@ -95,11 +95,7 @@ func NewValidatorClient(cliCtx *cli.Context) (*ValidatorClient, error) {
|
||||
passwordsDir = path.Join(passwordsDir, accountsv2.PasswordsDefaultDirName)
|
||||
}
|
||||
// Read the wallet from the specified path.
|
||||
wallet, err := accountsv2.OpenWallet(context.Background(), &accountsv2.WalletConfig{
|
||||
PasswordsDir: passwordsDir,
|
||||
WalletDir: walletDir,
|
||||
CanUnlockAccounts: true,
|
||||
})
|
||||
wallet, err := accountsv2.OpenWallet(cliCtx)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not open wallet: %v", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user