mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 15:37:56 -05:00
Resolve Web UI Beta Testing Bugs (#7471)
* more descriptive password validation error * include change wallet password fixes * balance and jwt improvements * allow for different wallet dirs specified on startup * ensure wallet password is always validated * fix up prysm tests * gaz * test pass * pass balances tests * wrap up fixes * radek feedback * fix up tests * cors fix * add tests for validator status * pass tests * fix n * skip content type test * package level cache and send over feed * package level cache for derived * all tests passing * gofmt Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com> Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
This commit is contained in:
@@ -79,6 +79,7 @@ go_test(
|
||||
"//validator/accounts/v2/wallet:go_default_library",
|
||||
"//validator/client:go_default_library",
|
||||
"//validator/db/testing:go_default_library",
|
||||
"//validator/flags:go_default_library",
|
||||
"//validator/keymanager/v2:go_default_library",
|
||||
"//validator/keymanager/v2/derived:go_default_library",
|
||||
"//validator/keymanager/v2/direct:go_default_library",
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
|
||||
@@ -17,10 +18,12 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
v2 "github.com/prysmaticlabs/prysm/validator/accounts/v2"
|
||||
"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"
|
||||
)
|
||||
|
||||
var defaultWalletPath = filepath.Join(flags.DefaultValidatorDir(), flags.WalletDefaultDirName)
|
||||
var _ accountCreator = (*mockAccountCreator)(nil)
|
||||
|
||||
type mockAccountCreator struct {
|
||||
|
||||
@@ -22,30 +22,34 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
tokenExpiryLength = 20 * time.Minute
|
||||
tokenExpiryLength = time.Hour
|
||||
hashCost = 8
|
||||
)
|
||||
|
||||
// Signup to authenticate access to the validator RPC API using bcrypt and
|
||||
// a sufficiently strong password check.
|
||||
func (s *Server) Signup(ctx context.Context, req *pb.AuthRequest) (*pb.AuthResponse, error) {
|
||||
walletDir := s.walletDir
|
||||
if strings.TrimSpace(req.WalletDir) != "" {
|
||||
walletDir = req.WalletDir
|
||||
}
|
||||
// First, we check if the validator already has a password. In this case,
|
||||
// the user should NOT be able to signup and the function will return an error.
|
||||
if fileutil.FileExists(filepath.Join(defaultWalletPath, wallet.HashedPasswordFileName)) {
|
||||
return nil, status.Error(codes.PermissionDenied, "Validator already has a password set, cannot signup")
|
||||
// the user should be logged in as normal.
|
||||
if fileutil.FileExists(filepath.Join(walletDir, wallet.HashedPasswordFileName)) {
|
||||
return s.Login(ctx, req)
|
||||
}
|
||||
// We check the strength of the password to ensure it is high-entropy,
|
||||
// has the required character count, and contains only unicode characters.
|
||||
if err := promptutil.ValidatePasswordInput(req.Password); err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "Could not validate password input")
|
||||
return nil, status.Error(codes.InvalidArgument, "Could not validate wallet password input")
|
||||
}
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), hashCost)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not generate hashed password")
|
||||
}
|
||||
hashFilePath := filepath.Join(defaultWalletPath, wallet.HashedPasswordFileName)
|
||||
hashFilePath := filepath.Join(walletDir, wallet.HashedPasswordFileName)
|
||||
// Write the config file to disk.
|
||||
if err := os.MkdirAll(defaultWalletPath, os.ModePerm); err != nil {
|
||||
if err := os.MkdirAll(walletDir, os.ModePerm); err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
if err := ioutil.WriteFile(hashFilePath, hashedPassword, params.BeaconIoConfig().ReadWritePermissions); err != nil {
|
||||
@@ -56,19 +60,29 @@ func (s *Server) Signup(ctx context.Context, req *pb.AuthRequest) (*pb.AuthRespo
|
||||
|
||||
// Login to authenticate with the validator RPC API using a password.
|
||||
func (s *Server) Login(ctx context.Context, req *pb.AuthRequest) (*pb.AuthResponse, error) {
|
||||
hashedPasswordPath := filepath.Join(defaultWalletPath, wallet.HashedPasswordFileName)
|
||||
if fileutil.FileExists(hashedPasswordPath) {
|
||||
hashedPassword, err := fileutil.ReadFileAsBytes(hashedPasswordPath)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, "Could not retrieve hashed password from disk")
|
||||
}
|
||||
// Compare the stored hashed password, with the hashed version of the password that was received.
|
||||
if err := bcrypt.CompareHashAndPassword(hashedPassword, []byte(req.Password)); err != nil {
|
||||
return nil, status.Error(codes.Unauthenticated, "Incorrect password")
|
||||
}
|
||||
walletDir := s.walletDir
|
||||
if strings.TrimSpace(req.WalletDir) != "" {
|
||||
walletDir = req.WalletDir
|
||||
}
|
||||
// We check the strength of the password to ensure it is high-entropy,
|
||||
// has the required character count, and contains only unicode characters.
|
||||
if err := promptutil.ValidatePasswordInput(req.Password); err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "Could not validate wallet password input")
|
||||
}
|
||||
hashedPasswordPath := filepath.Join(walletDir, wallet.HashedPasswordFileName)
|
||||
if !fileutil.FileExists(hashedPasswordPath) {
|
||||
return nil, status.Error(codes.Internal, "Could not find hashed password on disk")
|
||||
}
|
||||
hashedPassword, err := fileutil.ReadFileAsBytes(hashedPasswordPath)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, "Could not retrieve hashed password from disk")
|
||||
}
|
||||
// Compare the stored hashed password, with the hashed version of the password that was received.
|
||||
if err := bcrypt.CompareHashAndPassword(hashedPassword, []byte(req.Password)); err != nil {
|
||||
return nil, status.Error(codes.Unauthenticated, "Incorrect password")
|
||||
}
|
||||
if err := s.initializeWallet(ctx, &wallet.Config{
|
||||
WalletDir: defaultWalletPath,
|
||||
WalletDir: walletDir,
|
||||
WalletPassword: req.Password,
|
||||
}); err != nil {
|
||||
if strings.Contains(err.Error(), "invalid checksum") {
|
||||
@@ -146,6 +160,15 @@ func (s *Server) initializeWallet(ctx context.Context, cfg *wallet.Config) error
|
||||
}
|
||||
s.keymanager = km
|
||||
s.wallet = w
|
||||
s.walletInitializedFeed.Send(w)
|
||||
s.walletDir = cfg.WalletDir
|
||||
|
||||
// Only send over feed if we have validating keys.
|
||||
validatingPublicKeys, err := km.FetchValidatingPublicKeys(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not check for validating public keys")
|
||||
}
|
||||
if len(validatingPublicKeys) > 0 {
|
||||
s.walletInitializedFeed.Send(w)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -13,7 +12,6 @@ import (
|
||||
pb "github.com/prysmaticlabs/prysm/proto/validator/accounts/v2"
|
||||
"github.com/prysmaticlabs/prysm/shared/event"
|
||||
"github.com/prysmaticlabs/prysm/shared/fileutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
@@ -34,32 +32,6 @@ func setupWalletDir(t testing.TB) string {
|
||||
return walletDir
|
||||
}
|
||||
|
||||
func TestServer_Signup_PasswordAlreadyExists(t *testing.T) {
|
||||
valDB := dbtest.SetupDB(t, [][48]byte{})
|
||||
ctx := context.Background()
|
||||
ss := &Server{
|
||||
valDB: valDB,
|
||||
}
|
||||
|
||||
// Save a hash password preemptively to the database.
|
||||
localWalletDir := setupWalletDir(t)
|
||||
defaultWalletPath = localWalletDir
|
||||
hashedPassword := []byte("2093402934902839489238492")
|
||||
require.NoError(t, ioutil.WriteFile(
|
||||
filepath.Join(defaultWalletPath, wallet.HashedPasswordFileName),
|
||||
hashedPassword,
|
||||
params.BeaconIoConfig().ReadWritePermissions,
|
||||
))
|
||||
|
||||
// Attempt to signup despite already having a hashed password in the DB
|
||||
// which should immediately fail.
|
||||
strongPass := "29384283xasjasd32%%&*@*#*"
|
||||
_, err := ss.Signup(ctx, &pb.AuthRequest{
|
||||
Password: strongPass,
|
||||
})
|
||||
require.ErrorContains(t, "Validator already has a password set, cannot signup", err)
|
||||
}
|
||||
|
||||
func TestServer_SignupAndLogin_RoundTrip(t *testing.T) {
|
||||
valDB := dbtest.SetupDB(t, [][48]byte{})
|
||||
ctx := context.Background()
|
||||
@@ -71,12 +43,13 @@ func TestServer_SignupAndLogin_RoundTrip(t *testing.T) {
|
||||
ss := &Server{
|
||||
valDB: valDB,
|
||||
walletInitializedFeed: new(event.Feed),
|
||||
walletDir: defaultWalletPath,
|
||||
}
|
||||
weakPass := "password"
|
||||
_, err := ss.Signup(ctx, &pb.AuthRequest{
|
||||
Password: weakPass,
|
||||
})
|
||||
require.ErrorContains(t, "Could not validate password input", err)
|
||||
require.ErrorContains(t, "Could not validate wallet password input", err)
|
||||
|
||||
// We assert we are able to signup with a strong password.
|
||||
_, err = ss.Signup(ctx, &pb.AuthRequest{
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/pkg/errors"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
@@ -17,10 +16,11 @@ import (
|
||||
// authentication from our API.
|
||||
var (
|
||||
noAuthPaths = map[string]bool{
|
||||
"/ethereum.validator.accounts.v2.Auth/Signup": true,
|
||||
"/ethereum.validator.accounts.v2.Auth/Login": true,
|
||||
"/ethereum.validator.accounts.v2.Wallet/HasWallet": true,
|
||||
"/ethereum.validator.accounts.v2.Wallet/GenerateMnemonic": true,
|
||||
"/ethereum.validator.accounts.v2.Auth/Signup": true,
|
||||
"/ethereum.validator.accounts.v2.Auth/Login": true,
|
||||
"/ethereum.validator.accounts.v2.Wallet/HasWallet": true,
|
||||
"/ethereum.validator.accounts.v2.Wallet/GenerateMnemonic": true,
|
||||
"/ethereum.validator.accounts.v2.Wallet/DefaultWalletPath": true,
|
||||
}
|
||||
authLock sync.RWMutex
|
||||
)
|
||||
@@ -70,7 +70,7 @@ func (s *Server) authorize(ctx context.Context) error {
|
||||
token := strings.Split(authHeader[0], "Bearer ")[1]
|
||||
_, err := jwt.Parse(token, checkParsedKey)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Could not parse JWT token")
|
||||
return status.Errorf(codes.Unauthenticated, "Could not parse JWT token: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ type Config struct {
|
||||
CertFlag string
|
||||
KeyFlag string
|
||||
ValDB db.Database
|
||||
WalletDir string
|
||||
ValidatorService *client.ValidatorService
|
||||
SyncChecker client.SyncChecker
|
||||
GenesisFetcher client.GenesisFetcher
|
||||
@@ -61,6 +62,7 @@ type Server struct {
|
||||
validatorService *client.ValidatorService
|
||||
syncChecker client.SyncChecker
|
||||
genesisFetcher client.GenesisFetcher
|
||||
walletDir string
|
||||
wallet *v2.Wallet
|
||||
walletInitializedFeed *event.Feed
|
||||
walletInitialized bool
|
||||
@@ -81,6 +83,7 @@ func NewServer(ctx context.Context, cfg *Config) *Server {
|
||||
validatorService: cfg.ValidatorService,
|
||||
syncChecker: cfg.SyncChecker,
|
||||
genesisFetcher: cfg.GenesisFetcher,
|
||||
walletDir: cfg.WalletDir,
|
||||
walletInitializedFeed: cfg.WalletInitializedFeed,
|
||||
walletInitialized: false,
|
||||
nodeGatewayEndpoint: cfg.NodeGatewayEndpoint,
|
||||
|
||||
@@ -6,9 +6,12 @@ import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
ptypes "github.com/gogo/protobuf/types"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/validator/accounts/v2"
|
||||
"github.com/prysmaticlabs/prysm/shared/fileutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/promptutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/rand"
|
||||
v2 "github.com/prysmaticlabs/prysm/validator/accounts/v2"
|
||||
"github.com/prysmaticlabs/prysm/validator/accounts/v2/wallet"
|
||||
@@ -17,12 +20,11 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/validator/keymanager/v2/derived"
|
||||
"github.com/prysmaticlabs/prysm/validator/keymanager/v2/direct"
|
||||
"github.com/tyler-smith/go-bip39"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
var defaultWalletPath = filepath.Join(flags.DefaultValidatorDir(), flags.WalletDefaultDirName)
|
||||
|
||||
const (
|
||||
checkExistsErrMsg = "Could not check if wallet exists"
|
||||
checkValidityErrMsg = "Could not check if wallet is valid"
|
||||
@@ -33,7 +35,7 @@ const (
|
||||
// HasWallet checks if a user has created a wallet before as well as whether or not
|
||||
// they have used the web UI before to set a wallet password.
|
||||
func (s *Server) HasWallet(ctx context.Context, _ *ptypes.Empty) (*pb.HasWalletResponse, error) {
|
||||
exists, err := wallet.Exists(defaultWalletPath)
|
||||
exists, err := wallet.Exists(s.walletDir)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not check if wallet exists: %v", err)
|
||||
}
|
||||
@@ -47,35 +49,50 @@ func (s *Server) HasWallet(ctx context.Context, _ *ptypes.Empty) (*pb.HasWalletR
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DefaultWalletPath for the user, which is dependent on operating system.
|
||||
func (s *Server) DefaultWalletPath(ctx context.Context, _ *ptypes.Empty) (*pb.DefaultWalletResponse, error) {
|
||||
return &pb.DefaultWalletResponse{
|
||||
WalletDir: filepath.Join(flags.DefaultValidatorDir(), flags.WalletDefaultDirName),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreateWallet via an API request, allowing a user to save a new
|
||||
// derived, direct, or remote wallet.
|
||||
func (s *Server) CreateWallet(ctx context.Context, req *pb.CreateWalletRequest) (*pb.CreateWalletResponse, error) {
|
||||
// Currently defaultWalletPath is used as the wallet directory and req's WalletPath is ignored for simplicity
|
||||
exists, err := wallet.Exists(defaultWalletPath)
|
||||
walletDir := s.walletDir
|
||||
if strings.TrimSpace(req.WalletPath) != "" {
|
||||
walletDir = req.WalletPath
|
||||
}
|
||||
exists, err := wallet.Exists(walletDir)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not check for existing wallet: %v", err)
|
||||
}
|
||||
if exists {
|
||||
return nil, status.Error(codes.AlreadyExists, "A wallet already exists at this location.")
|
||||
if err := s.initializeWallet(ctx, &wallet.Config{
|
||||
WalletDir: walletDir,
|
||||
WalletPassword: req.WalletPassword,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keymanagerKind := pb.KeymanagerKind_DIRECT
|
||||
switch s.wallet.KeymanagerKind() {
|
||||
case v2keymanager.Derived:
|
||||
keymanagerKind = pb.KeymanagerKind_DERIVED
|
||||
case v2keymanager.Remote:
|
||||
keymanagerKind = pb.KeymanagerKind_REMOTE
|
||||
}
|
||||
return &pb.CreateWalletResponse{
|
||||
Wallet: &pb.WalletResponse{
|
||||
WalletPath: walletDir,
|
||||
KeymanagerKind: keymanagerKind,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
switch req.Keymanager {
|
||||
case pb.KeymanagerKind_DIRECT:
|
||||
// Needs to unmarshal the keystores from the requests.
|
||||
if req.KeystoresImported == nil || len(req.KeystoresImported) < 1 {
|
||||
return nil, status.Error(codes.InvalidArgument, "No keystores included for import")
|
||||
}
|
||||
keystores := make([]*v2keymanager.Keystore, len(req.KeystoresImported))
|
||||
for i := 0; i < len(req.KeystoresImported); i++ {
|
||||
encoded := req.KeystoresImported[i]
|
||||
keystore := &v2keymanager.Keystore{}
|
||||
if err := json.Unmarshal([]byte(encoded), &keystore); err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "Not a valid EIP-2335 keystore JSON file: %v", err)
|
||||
}
|
||||
keystores[i] = keystore
|
||||
}
|
||||
w, err := v2.CreateWalletWithKeymanager(ctx, &v2.CreateWalletConfig{
|
||||
WalletCfg: &wallet.Config{
|
||||
WalletDir: defaultWalletPath,
|
||||
WalletDir: walletDir,
|
||||
KeymanagerKind: v2keymanager.Direct,
|
||||
WalletPassword: req.WalletPassword,
|
||||
},
|
||||
@@ -84,16 +101,11 @@ func (s *Server) CreateWallet(ctx context.Context, req *pb.CreateWalletRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Import the uploaded accounts.
|
||||
if err := v2.ImportAccounts(ctx, &v2.ImportAccountsConfig{
|
||||
Wallet: w,
|
||||
Keystores: keystores,
|
||||
AccountPassword: req.KeystoresPassword,
|
||||
}); err != nil {
|
||||
if err := w.SaveHashedPassword(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.initializeWallet(ctx, &wallet.Config{
|
||||
WalletDir: defaultWalletPath,
|
||||
WalletDir: walletDir,
|
||||
KeymanagerKind: v2keymanager.Direct,
|
||||
WalletPassword: req.WalletPassword,
|
||||
}); err != nil {
|
||||
@@ -101,7 +113,7 @@ func (s *Server) CreateWallet(ctx context.Context, req *pb.CreateWalletRequest)
|
||||
}
|
||||
return &pb.CreateWalletResponse{
|
||||
Wallet: &pb.WalletResponse{
|
||||
WalletPath: defaultWalletPath,
|
||||
WalletPath: walletDir,
|
||||
KeymanagerKind: pb.KeymanagerKind_DIRECT,
|
||||
},
|
||||
}, nil
|
||||
@@ -113,7 +125,7 @@ func (s *Server) CreateWallet(ctx context.Context, req *pb.CreateWalletRequest)
|
||||
return nil, status.Error(codes.InvalidArgument, "Must include mnemonic in request")
|
||||
}
|
||||
_, depositData, err := v2.RecoverWallet(ctx, &v2.RecoverWalletConfig{
|
||||
WalletDir: defaultWalletPath,
|
||||
WalletDir: walletDir,
|
||||
WalletPassword: req.WalletPassword,
|
||||
Mnemonic: req.Mnemonic,
|
||||
NumAccounts: int64(req.NumAccounts),
|
||||
@@ -122,7 +134,7 @@ func (s *Server) CreateWallet(ctx context.Context, req *pb.CreateWalletRequest)
|
||||
return nil, err
|
||||
}
|
||||
if err := s.initializeWallet(ctx, &wallet.Config{
|
||||
WalletDir: defaultWalletPath,
|
||||
WalletDir: walletDir,
|
||||
KeymanagerKind: v2keymanager.Direct,
|
||||
WalletPassword: req.WalletPassword,
|
||||
}); err != nil {
|
||||
@@ -141,7 +153,7 @@ func (s *Server) CreateWallet(ctx context.Context, req *pb.CreateWalletRequest)
|
||||
}
|
||||
return &pb.CreateWalletResponse{
|
||||
Wallet: &pb.WalletResponse{
|
||||
WalletPath: defaultWalletPath,
|
||||
WalletPath: walletDir,
|
||||
KeymanagerKind: pb.KeymanagerKind_DERIVED,
|
||||
},
|
||||
AccountsCreated: &pb.DepositDataResponse{
|
||||
@@ -162,7 +174,7 @@ func (s *Server) EditConfig(ctx context.Context, req *pb.EditWalletConfigRequest
|
||||
|
||||
// WalletConfig returns the wallet's configuration. If no wallet exists, we return an empty response.
|
||||
func (s *Server) WalletConfig(ctx context.Context, _ *ptypes.Empty) (*pb.WalletResponse, error) {
|
||||
exists, err := wallet.Exists(defaultWalletPath)
|
||||
exists, err := wallet.Exists(s.walletDir)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, checkExistsErrMsg)
|
||||
}
|
||||
@@ -170,7 +182,7 @@ func (s *Server) WalletConfig(ctx context.Context, _ *ptypes.Empty) (*pb.WalletR
|
||||
// If no wallet is found, we simply return an empty response.
|
||||
return &pb.WalletResponse{}, nil
|
||||
}
|
||||
valid, err := wallet.IsValid(defaultWalletPath)
|
||||
valid, err := wallet.IsValid(s.walletDir)
|
||||
if err == wallet.ErrNoWalletFound {
|
||||
return &pb.WalletResponse{}, nil
|
||||
}
|
||||
@@ -207,7 +219,7 @@ func (s *Server) WalletConfig(ctx context.Context, _ *ptypes.Empty) (*pb.WalletR
|
||||
return nil, status.Errorf(codes.Internal, "Could not JSON unmarshal keymanager config: %v", err)
|
||||
}
|
||||
return &pb.WalletResponse{
|
||||
WalletPath: defaultWalletPath,
|
||||
WalletPath: s.walletDir,
|
||||
KeymanagerKind: keymanagerKind,
|
||||
KeymanagerConfig: config,
|
||||
}, nil
|
||||
@@ -235,14 +247,14 @@ func (s *Server) GenerateMnemonic(ctx context.Context, _ *ptypes.Empty) (*pb.Gen
|
||||
// ChangePassword allows changing a wallet password via the API as
|
||||
// an authenticated method.
|
||||
func (s *Server) ChangePassword(ctx context.Context, req *pb.ChangePasswordRequest) (*ptypes.Empty, error) {
|
||||
exists, err := wallet.Exists(defaultWalletPath)
|
||||
exists, err := wallet.Exists(s.walletDir)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, checkExistsErrMsg)
|
||||
}
|
||||
if !exists {
|
||||
return nil, status.Errorf(codes.FailedPrecondition, noWalletMsg)
|
||||
}
|
||||
valid, err := wallet.IsValid(defaultWalletPath)
|
||||
valid, err := wallet.IsValid(s.walletDir)
|
||||
if err == wallet.ErrNoWalletFound {
|
||||
return nil, status.Errorf(codes.FailedPrecondition, noWalletMsg)
|
||||
}
|
||||
@@ -252,12 +264,26 @@ func (s *Server) ChangePassword(ctx context.Context, req *pb.ChangePasswordReque
|
||||
if !valid {
|
||||
return nil, status.Errorf(codes.FailedPrecondition, invalidWalletMsg)
|
||||
}
|
||||
if req.Password == "" {
|
||||
return nil, status.Error(codes.InvalidArgument, "Password cannot be empty")
|
||||
if req.CurrentPassword == "" {
|
||||
return nil, status.Error(codes.InvalidArgument, "Current wallet password cannot be empty")
|
||||
}
|
||||
hashedPasswordPath := filepath.Join(s.walletDir, wallet.HashedPasswordFileName)
|
||||
if !fileutil.FileExists(hashedPasswordPath) {
|
||||
return nil, status.Error(codes.FailedPrecondition, "Could not compare password from disk")
|
||||
}
|
||||
hashedPassword, err := fileutil.ReadFileAsBytes(hashedPasswordPath)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.FailedPrecondition, "Could not retrieve hashed password from disk")
|
||||
}
|
||||
if err := bcrypt.CompareHashAndPassword(hashedPassword, []byte(req.CurrentPassword)); err != nil {
|
||||
return nil, status.Error(codes.Unauthenticated, "Incorrect wallet password")
|
||||
}
|
||||
if req.Password != req.PasswordConfirmation {
|
||||
return nil, status.Error(codes.InvalidArgument, "Password does not match confirmation")
|
||||
}
|
||||
if err := promptutil.ValidatePasswordInput(req.Password); err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "Could not validate wallet password input")
|
||||
}
|
||||
switch s.wallet.KeymanagerKind() {
|
||||
case v2keymanager.Direct:
|
||||
km, ok := s.keymanager.(*direct.Keymanager)
|
||||
@@ -297,7 +323,8 @@ func (s *Server) ImportKeystores(
|
||||
if s.wallet == nil {
|
||||
return nil, status.Error(codes.FailedPrecondition, "No wallet initialized")
|
||||
}
|
||||
if s.wallet.KeymanagerKind() != v2keymanager.Direct {
|
||||
keymanager, ok := s.keymanager.(*direct.Keymanager)
|
||||
if !ok {
|
||||
return nil, status.Error(codes.FailedPrecondition, "Only Non-HD wallets can import keystores")
|
||||
}
|
||||
if req.KeystoresPassword == "" {
|
||||
@@ -324,12 +351,13 @@ func (s *Server) ImportKeystores(
|
||||
}
|
||||
// Import the uploaded accounts.
|
||||
if err := v2.ImportAccounts(ctx, &v2.ImportAccountsConfig{
|
||||
Wallet: s.wallet,
|
||||
Keymanager: keymanager,
|
||||
Keystores: keystores,
|
||||
AccountPassword: req.KeystoresPassword,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.walletInitializedFeed.Send(s.wallet)
|
||||
return &pb.ImportKeystoresResponse{
|
||||
ImportedPublicKeys: importedPubKeys,
|
||||
}, nil
|
||||
|
||||
@@ -39,8 +39,10 @@ func createDirectWalletWithAccounts(t testing.TB, numAccounts int) (*Server, [][
|
||||
km, err := w.InitializeKeymanager(ctx, true /* skip mnemonic confirm */)
|
||||
require.NoError(t, err)
|
||||
ss := &Server{
|
||||
keymanager: km,
|
||||
wallet: w,
|
||||
keymanager: km,
|
||||
wallet: w,
|
||||
walletDir: defaultWalletPath,
|
||||
walletInitializedFeed: new(event.Feed),
|
||||
}
|
||||
// First we import accounts into the wallet.
|
||||
encryptor := keystorev4.New()
|
||||
@@ -82,21 +84,29 @@ func TestServer_CreateWallet_Direct(t *testing.T) {
|
||||
strongPass := "29384283xasjasd32%%&*@*#*"
|
||||
s := &Server{
|
||||
walletInitializedFeed: new(event.Feed),
|
||||
walletDir: defaultWalletPath,
|
||||
}
|
||||
_, err := s.Signup(ctx, &pb.AuthRequest{
|
||||
Password: strongPass,
|
||||
WalletDir: defaultWalletPath,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
req := &pb.CreateWalletRequest{
|
||||
WalletPath: localWalletDir,
|
||||
Keymanager: pb.KeymanagerKind_DIRECT,
|
||||
WalletPassword: strongPass,
|
||||
KeystoresPassword: strongPass,
|
||||
WalletPath: localWalletDir,
|
||||
Keymanager: pb.KeymanagerKind_DIRECT,
|
||||
WalletPassword: strongPass,
|
||||
}
|
||||
// We delete the directory at defaultWalletPath as CreateWallet will return an error if it tries to create a wallet
|
||||
// where a directory already exists
|
||||
require.NoError(t, os.RemoveAll(defaultWalletPath))
|
||||
_, err := s.CreateWallet(ctx, req)
|
||||
require.ErrorContains(t, "No keystores included for import", err)
|
||||
|
||||
req.KeystoresImported = []string{"badjson"}
|
||||
_, err = s.CreateWallet(ctx, req)
|
||||
require.NoError(t, err)
|
||||
|
||||
importReq := &pb.ImportKeystoresRequest{
|
||||
KeystoresPassword: strongPass,
|
||||
KeystoresImported: []string{"badjson"},
|
||||
}
|
||||
_, err = s.ImportKeystores(ctx, importReq)
|
||||
require.ErrorContains(t, "Not a valid EIP-2335 keystore", err)
|
||||
|
||||
encryptor := keystorev4.New()
|
||||
@@ -119,8 +129,8 @@ func TestServer_CreateWallet_Direct(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
keystores[i] = string(encodedFile)
|
||||
}
|
||||
req.KeystoresImported = keystores
|
||||
_, err = s.CreateWallet(ctx, req)
|
||||
importReq.KeystoresImported = keystores
|
||||
_, err = s.ImportKeystores(ctx, importReq)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -154,10 +164,6 @@ func TestServer_CreateWallet_Derived(t *testing.T) {
|
||||
|
||||
_, err = s.CreateWallet(ctx, req)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Now trying to create a wallet where a previous wallet already exists. We expect an error.
|
||||
_, err = s.CreateWallet(ctx, req)
|
||||
require.ErrorContains(t, "wallet already exists at this location", err)
|
||||
}
|
||||
|
||||
func TestServer_WalletConfig_NoWalletFound(t *testing.T) {
|
||||
@@ -174,6 +180,7 @@ func TestServer_WalletConfig(t *testing.T) {
|
||||
strongPass := "29384283xasjasd32%%&*@*#*"
|
||||
s := &Server{
|
||||
walletInitializedFeed: new(event.Feed),
|
||||
walletDir: defaultWalletPath,
|
||||
}
|
||||
// We attempt to create the wallet.
|
||||
w, err := v2.CreateWalletWithKeymanager(ctx, &v2.CreateWalletConfig{
|
||||
@@ -210,9 +217,12 @@ func TestServer_ChangePassword_Preconditions(t *testing.T) {
|
||||
defaultWalletPath = localWalletDir
|
||||
ctx := context.Background()
|
||||
strongPass := "29384283xasjasd32%%&*@*#*"
|
||||
ss := &Server{}
|
||||
ss := &Server{
|
||||
walletDir: defaultWalletPath,
|
||||
}
|
||||
_, err := ss.ChangePassword(ctx, &pb.ChangePasswordRequest{
|
||||
Password: "",
|
||||
CurrentPassword: strongPass,
|
||||
Password: "",
|
||||
})
|
||||
assert.ErrorContains(t, noWalletMsg, err)
|
||||
// We attempt to create the wallet.
|
||||
@@ -231,10 +241,12 @@ func TestServer_ChangePassword_Preconditions(t *testing.T) {
|
||||
ss.walletInitialized = true
|
||||
ss.keymanager = km
|
||||
_, err = ss.ChangePassword(ctx, &pb.ChangePasswordRequest{
|
||||
Password: "",
|
||||
CurrentPassword: strongPass,
|
||||
Password: "",
|
||||
})
|
||||
assert.ErrorContains(t, "cannot be empty", err)
|
||||
assert.ErrorContains(t, "Could not validate wallet password", err)
|
||||
_, err = ss.ChangePassword(ctx, &pb.ChangePasswordRequest{
|
||||
CurrentPassword: strongPass,
|
||||
Password: "abc",
|
||||
PasswordConfirmation: "def",
|
||||
})
|
||||
@@ -245,6 +257,7 @@ func TestServer_ChangePassword_DirectKeymanager(t *testing.T) {
|
||||
ss, _ := createDirectWalletWithAccounts(t, 1)
|
||||
newPassword := "NewPassw0rdz%%%%pass"
|
||||
_, err := ss.ChangePassword(context.Background(), &pb.ChangePasswordRequest{
|
||||
CurrentPassword: ss.wallet.Password(),
|
||||
Password: newPassword,
|
||||
PasswordConfirmation: newPassword,
|
||||
})
|
||||
@@ -269,12 +282,15 @@ func TestServer_ChangePassword_DerivedKeymanager(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
km, err := w.InitializeKeymanager(ctx, true /* skip mnemonic confirm */)
|
||||
require.NoError(t, err)
|
||||
ss := &Server{}
|
||||
ss := &Server{
|
||||
walletDir: defaultWalletPath,
|
||||
}
|
||||
ss.wallet = w
|
||||
ss.walletInitialized = true
|
||||
ss.keymanager = km
|
||||
newPassword := "NewPassw0rdz%%%%pass"
|
||||
_, err = ss.ChangePassword(ctx, &pb.ChangePasswordRequest{
|
||||
CurrentPassword: strongPass,
|
||||
Password: newPassword,
|
||||
PasswordConfirmation: newPassword,
|
||||
})
|
||||
@@ -287,7 +303,9 @@ func TestServer_HasWallet(t *testing.T) {
|
||||
defaultWalletPath = localWalletDir
|
||||
ctx := context.Background()
|
||||
strongPass := "29384283xasjasd32%%&*@*#*"
|
||||
ss := &Server{}
|
||||
ss := &Server{
|
||||
walletDir: defaultWalletPath,
|
||||
}
|
||||
// First delete the created folder and check the response
|
||||
require.NoError(t, os.RemoveAll(defaultWalletPath))
|
||||
resp, err := ss.HasWallet(ctx, &ptypes.Empty{})
|
||||
@@ -383,6 +401,7 @@ func TestServer_ImportKeystores_FailedPreconditions(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestServer_ImportKeystores_OK(t *testing.T) {
|
||||
direct.ResetCaches()
|
||||
localWalletDir := setupWalletDir(t)
|
||||
defaultWalletPath = localWalletDir
|
||||
ctx := context.Background()
|
||||
@@ -400,8 +419,9 @@ func TestServer_ImportKeystores_OK(t *testing.T) {
|
||||
km, err := w.InitializeKeymanager(ctx, true /* skip mnemonic confirm */)
|
||||
require.NoError(t, err)
|
||||
ss := &Server{
|
||||
keymanager: km,
|
||||
wallet: w,
|
||||
keymanager: km,
|
||||
wallet: w,
|
||||
walletInitializedFeed: new(event.Feed),
|
||||
}
|
||||
|
||||
// Create 3 keystores.
|
||||
|
||||
Reference in New Issue
Block a user