mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 15:37:56 -05:00
Implement Wallet Config and HasWallet Endpoints (#7341)
* fix the wallet config implementation * implemented wallet config endpoint and tests * better error messages * fix nil pointer * more nil issues fix * gazelle * fix interceptor * revise Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
This commit is contained in:
@@ -77,6 +77,7 @@ go_test(
|
||||
"//validator/db/testing:go_default_library",
|
||||
"//validator/keymanager/v2:go_default_library",
|
||||
"//validator/keymanager/v2/derived:go_default_library",
|
||||
"//validator/keymanager/v2/direct:go_default_library",
|
||||
"@com_github_gogo_protobuf//types:go_default_library",
|
||||
"@com_github_google_uuid//:go_default_library",
|
||||
"@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library",
|
||||
|
||||
@@ -56,13 +56,16 @@ 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) {
|
||||
hashedPassword, err := fileutil.ReadFileAsBytes(filepath.Join(defaultWalletPath, wallet.HashedPasswordFileName))
|
||||
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")
|
||||
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")
|
||||
}
|
||||
}
|
||||
if err := s.initializeWallet(ctx, &wallet.Config{
|
||||
WalletDir: defaultWalletPath,
|
||||
|
||||
@@ -19,7 +19,7 @@ var (
|
||||
noAuthPaths = map[string]bool{
|
||||
"/ethereum.validator.accounts.v2.Auth/Signup": true,
|
||||
"/ethereum.validator.accounts.v2.Auth/Login": true,
|
||||
"/ethereum.validator.accounts.v2.Wallet/WalletConfig": true,
|
||||
"/ethereum.validator.accounts.v2.Wallet/HasWallet": true,
|
||||
"/ethereum.validator.accounts.v2.Wallet/GenerateMnemonic": true,
|
||||
}
|
||||
authLock sync.RWMutex
|
||||
|
||||
@@ -3,6 +3,7 @@ package rpc
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
|
||||
ptypes "github.com/gogo/protobuf/types"
|
||||
@@ -22,11 +23,28 @@ import (
|
||||
|
||||
var defaultWalletPath = filepath.Join(flags.DefaultValidatorDir(), flags.WalletDefaultDirName)
|
||||
|
||||
// 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) {
|
||||
err := wallet.Exists(defaultWalletPath)
|
||||
if err != nil && errors.Is(err, wallet.ErrNoWalletFound) {
|
||||
return &pb.HasWalletResponse{
|
||||
WalletExists: false,
|
||||
}, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not check if wallet exists: %v", err)
|
||||
}
|
||||
return &pb.HasWalletResponse{
|
||||
WalletExists: true,
|
||||
}, 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.WalletResponse, error) {
|
||||
switch req.Keymanager {
|
||||
case pb.CreateWalletRequest_DIRECT:
|
||||
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")
|
||||
@@ -69,7 +87,7 @@ func (s *Server) CreateWallet(ctx context.Context, req *pb.CreateWalletRequest)
|
||||
return &pb.WalletResponse{
|
||||
WalletPath: defaultWalletPath,
|
||||
}, nil
|
||||
case pb.CreateWalletRequest_DERIVED:
|
||||
case pb.KeymanagerKind_DERIVED:
|
||||
if req.NumAccounts < 1 {
|
||||
return nil, status.Error(codes.InvalidArgument, "Must create at least 1 validator account")
|
||||
}
|
||||
@@ -95,7 +113,7 @@ func (s *Server) CreateWallet(ctx context.Context, req *pb.CreateWalletRequest)
|
||||
return &pb.WalletResponse{
|
||||
WalletPath: defaultWalletPath,
|
||||
}, nil
|
||||
case pb.CreateWalletRequest_REMOTE:
|
||||
case pb.KeymanagerKind_REMOTE:
|
||||
return nil, status.Error(codes.Unimplemented, "Remote keymanager not yet supported")
|
||||
default:
|
||||
return nil, status.Errorf(codes.InvalidArgument, "Keymanager type %T not yet supported", req.Keymanager)
|
||||
@@ -117,9 +135,35 @@ func (s *Server) WalletConfig(ctx context.Context, _ *ptypes.Empty) (*pb.WalletR
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not check if wallet exists: %v", err)
|
||||
}
|
||||
if s.wallet == nil || s.keymanager == nil {
|
||||
// If no wallet is found, we simply return an empty response.
|
||||
return &pb.WalletResponse{}, nil
|
||||
}
|
||||
var keymanagerKind pb.KeymanagerKind
|
||||
switch s.wallet.KeymanagerKind() {
|
||||
case v2keymanager.Derived:
|
||||
keymanagerKind = pb.KeymanagerKind_DERIVED
|
||||
case v2keymanager.Direct:
|
||||
keymanagerKind = pb.KeymanagerKind_DIRECT
|
||||
case v2keymanager.Remote:
|
||||
keymanagerKind = pb.KeymanagerKind_REMOTE
|
||||
}
|
||||
f, err := s.wallet.ReadKeymanagerConfigFromDisk(ctx)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not read keymanager config from disk: %v", err)
|
||||
}
|
||||
encoded, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not parse keymanager config: %v", err)
|
||||
}
|
||||
var config map[string]string
|
||||
if err := json.Unmarshal(encoded, &config); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not JSON unmarshal keymanager config: %v", err)
|
||||
}
|
||||
return &pb.WalletResponse{
|
||||
WalletPath: defaultWalletPath,
|
||||
KeymanagerConfig: nil, // Fill in by reading from disk.
|
||||
KeymanagerKind: keymanagerKind,
|
||||
KeymanagerConfig: config,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
v2 "github.com/prysmaticlabs/prysm/validator/accounts/v2"
|
||||
"github.com/prysmaticlabs/prysm/validator/accounts/v2/wallet"
|
||||
v2keymanager "github.com/prysmaticlabs/prysm/validator/keymanager/v2"
|
||||
"github.com/prysmaticlabs/prysm/validator/keymanager/v2/direct"
|
||||
keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4"
|
||||
)
|
||||
|
||||
@@ -29,7 +30,7 @@ func TestServer_CreateWallet_Direct(t *testing.T) {
|
||||
}
|
||||
req := &pb.CreateWalletRequest{
|
||||
WalletPath: localWalletDir,
|
||||
Keymanager: pb.CreateWalletRequest_DIRECT,
|
||||
Keymanager: pb.KeymanagerKind_DIRECT,
|
||||
WalletPassword: strongPass,
|
||||
KeystoresPassword: strongPass,
|
||||
}
|
||||
@@ -75,7 +76,7 @@ func TestServer_CreateWallet_Derived(t *testing.T) {
|
||||
}
|
||||
req := &pb.CreateWalletRequest{
|
||||
WalletPath: localWalletDir,
|
||||
Keymanager: pb.CreateWalletRequest_DERIVED,
|
||||
Keymanager: pb.KeymanagerKind_DERIVED,
|
||||
WalletPassword: strongPass,
|
||||
NumAccounts: 0,
|
||||
}
|
||||
@@ -110,7 +111,7 @@ func TestServer_WalletConfig(t *testing.T) {
|
||||
walletInitializedFeed: new(event.Feed),
|
||||
}
|
||||
// We attempt to create the wallet.
|
||||
_, err := v2.CreateWalletWithKeymanager(ctx, &v2.CreateWalletConfig{
|
||||
w, err := v2.CreateWalletWithKeymanager(ctx, &v2.CreateWalletConfig{
|
||||
WalletCfg: &wallet.Config{
|
||||
WalletDir: defaultWalletPath,
|
||||
KeymanagerKind: v2keymanager.Direct,
|
||||
@@ -119,10 +120,22 @@ func TestServer_WalletConfig(t *testing.T) {
|
||||
SkipMnemonicConfirm: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, w.SaveHashedPassword(ctx))
|
||||
km, err := w.InitializeKeymanager(ctx, true /* skip mnemonic confirm */)
|
||||
require.NoError(t, err)
|
||||
s.wallet = w
|
||||
s.keymanager = km
|
||||
resp, err := s.WalletConfig(ctx, &ptypes.Empty{})
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedConfig := direct.DefaultKeymanagerOpts()
|
||||
enc, err := json.Marshal(expectedConfig)
|
||||
var jsonMap map[string]string
|
||||
require.NoError(t, json.Unmarshal(enc, &jsonMap))
|
||||
assert.DeepEqual(t, resp, &pb.WalletResponse{
|
||||
WalletPath: localWalletDir,
|
||||
WalletPath: localWalletDir,
|
||||
KeymanagerKind: pb.KeymanagerKind_DIRECT,
|
||||
KeymanagerConfig: jsonMap,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -222,3 +235,31 @@ func TestServer_ChangePassword_DerivedKeymanager(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, w.Password(), newPassword)
|
||||
}
|
||||
|
||||
func TestServer_HasWallet(t *testing.T) {
|
||||
localWalletDir := setupWalletDir(t)
|
||||
defaultWalletPath = localWalletDir
|
||||
ctx := context.Background()
|
||||
strongPass := "29384283xasjasd32%%&*@*#*"
|
||||
ss := &Server{}
|
||||
resp, err := ss.HasWallet(ctx, &ptypes.Empty{})
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, &pb.HasWalletResponse{
|
||||
WalletExists: false,
|
||||
}, resp)
|
||||
// We attempt to create the wallet.
|
||||
_, err = v2.CreateWalletWithKeymanager(ctx, &v2.CreateWalletConfig{
|
||||
WalletCfg: &wallet.Config{
|
||||
WalletDir: defaultWalletPath,
|
||||
KeymanagerKind: v2keymanager.Direct,
|
||||
WalletPassword: strongPass,
|
||||
},
|
||||
SkipMnemonicConfirm: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
resp, err = ss.HasWallet(ctx, &ptypes.Empty{})
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, &pb.HasWalletResponse{
|
||||
WalletExists: true,
|
||||
}, resp)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user