Prepare Validator Client for Web Usage (#7158)

* prepare validator for web usage
* wallet exists func in rpc server
* only initialize wallet if feed exists and wallet also exists
* enabling web via a feed
* wallet creation works but needs to be able to recover from mnemonic via api
* ensure logic works
* Merge branch 'master' into validator-web
* fix up tests
* test file
* wallet rpc tests
* add all tests to create wallet
* Merge branch 'master' into validator-web
* imports
* Merge branch 'validator-web' of github.com:prysmaticlabs/prysm into validator-web
* Merge refs/heads/master into validator-web
* Merge refs/heads/master into validator-web
This commit is contained in:
Raul Jordan
2020-09-03 10:11:17 -05:00
committed by GitHub
parent c1a7c65e05
commit f4a6864343
27 changed files with 1010 additions and 521 deletions

View File

@@ -8,24 +8,32 @@ go_library(
"health.go",
"intercepter.go",
"server.go",
"wallet.go",
],
importpath = "github.com/prysmaticlabs/prysm/validator/rpc",
visibility = ["//validator:__subpackages__"],
deps = [
"//proto/validator/accounts/v2:go_default_library",
"//shared/bytesutil:go_default_library",
"//shared/event:go_default_library",
"//shared/promptutil:go_default_library",
"//shared/rand:go_default_library",
"//shared/roughtime:go_default_library",
"//shared/traceutil:go_default_library",
"//validator/accounts/v2:go_default_library",
"//validator/client:go_default_library",
"//validator/db:go_default_library",
"//validator/flags:go_default_library",
"//validator/keymanager/v2:go_default_library",
"@com_github_dgrijalva_jwt_go//:go_default_library",
"@com_github_gogo_protobuf//types:go_default_library",
"@com_github_grpc_ecosystem_go_grpc_middleware//:go_default_library",
"@com_github_grpc_ecosystem_go_grpc_middleware//recovery:go_default_library",
"@com_github_grpc_ecosystem_go_grpc_middleware//tracing/opentracing:go_default_library",
"@com_github_grpc_ecosystem_go_grpc_prometheus//:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_tyler_smith_go_bip39//:go_default_library",
"@io_opencensus_go//plugin/ocgrpc:go_default_library",
"@org_golang_google_grpc//:go_default_library",
"@org_golang_google_grpc//codes:go_default_library",
@@ -44,16 +52,25 @@ go_test(
"health_test.go",
"intercepter_test.go",
"server_test.go",
"wallet_test.go",
],
embed = [":go_default_library"],
deps = [
"//proto/validator/accounts/v2:go_default_library",
"//shared/bls:go_default_library",
"//shared/bytesutil:go_default_library",
"//shared/event:go_default_library",
"//shared/testutil:go_default_library",
"//shared/testutil/assert:go_default_library",
"//shared/testutil/require:go_default_library",
"//validator/accounts/v2:go_default_library",
"//validator/client:go_default_library",
"//validator/db/testing:go_default_library",
"//validator/keymanager/v2: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",
"@com_github_wealdtech_go_eth2_wallet_encryptor_keystorev4//:go_default_library",
"@org_golang_google_grpc//:go_default_library",
"@org_golang_google_grpc//metadata:go_default_library",
],

View File

@@ -5,12 +5,15 @@ import (
"time"
"github.com/dgrijalva/jwt-go"
pb "github.com/prysmaticlabs/prysm/proto/validator/accounts/v2"
"github.com/prysmaticlabs/prysm/shared/promptutil"
"github.com/prysmaticlabs/prysm/shared/roughtime"
"github.com/pkg/errors"
"golang.org/x/crypto/bcrypt"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
pb "github.com/prysmaticlabs/prysm/proto/validator/accounts/v2"
"github.com/prysmaticlabs/prysm/shared/promptutil"
"github.com/prysmaticlabs/prysm/shared/roughtime"
v2 "github.com/prysmaticlabs/prysm/validator/accounts/v2"
)
var (
@@ -46,6 +49,12 @@ func (s *Server) Signup(ctx context.Context, req *pb.AuthRequest) (*pb.AuthRespo
if err := s.valDB.SaveHashedPasswordForAPI(ctx, hashedPassword); err != nil {
return nil, status.Error(codes.Internal, "Could not save hashed password to database")
}
if err := s.initializeWallet(ctx, &v2.WalletConfig{
WalletDir: defaultWalletPath,
WalletPassword: req.Password,
}); err != nil {
return nil, status.Error(codes.Internal, "Could not initialize wallet")
}
return s.sendAuthResponse()
}
@@ -60,6 +69,12 @@ func (s *Server) Login(ctx context.Context, req *pb.AuthRequest) (*pb.AuthRespon
if err := bcrypt.CompareHashAndPassword(hashedPassword, []byte(req.Password)); err != nil {
return nil, status.Error(codes.Unauthenticated, "Incorrect password")
}
if err := s.initializeWallet(ctx, &v2.WalletConfig{
WalletDir: defaultWalletPath,
WalletPassword: req.Password,
}); err != nil {
return nil, status.Error(codes.Internal, "Could not initialize wallet")
}
return s.sendAuthResponse()
}
@@ -91,3 +106,26 @@ func (s *Server) createTokenString() (string, uint64, error) {
}
return tokenString, uint64(expirationTime.Unix()), nil
}
// Initialize a wallet and send it over a global feed.
func (s *Server) initializeWallet(ctx context.Context, cfg *v2.WalletConfig) error {
// We first ensure the user has a wallet.
if err := v2.WalletExists(cfg.WalletDir); err != nil {
if errors.Is(err, v2.ErrNoWalletFound) {
return v2.ErrNoWalletFound
}
return errors.Wrap(err, "could not check if wallet exists")
}
// We fire an event with the opened wallet over
// a global feed signifying wallet initialization.
wallet, err := v2.OpenWallet(ctx, &v2.WalletConfig{
WalletDir: cfg.WalletDir,
WalletPassword: cfg.WalletPassword,
})
if err != nil {
return errors.Wrap(err, "could not open wallet")
}
s.walletInitialized = true
s.walletInitializedFeed.Send(wallet)
return nil
}

View File

@@ -2,14 +2,34 @@ package rpc
import (
"context"
"crypto/rand"
"fmt"
"math/big"
"os"
"path/filepath"
"testing"
pb "github.com/prysmaticlabs/prysm/proto/validator/accounts/v2"
"github.com/prysmaticlabs/prysm/shared/event"
"github.com/prysmaticlabs/prysm/shared/testutil"
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
"github.com/prysmaticlabs/prysm/shared/testutil/require"
v2 "github.com/prysmaticlabs/prysm/validator/accounts/v2"
dbtest "github.com/prysmaticlabs/prysm/validator/db/testing"
v2keymanager "github.com/prysmaticlabs/prysm/validator/keymanager/v2"
)
func setupWalletDir(t testing.TB) string {
randPath, err := rand.Int(rand.Reader, big.NewInt(1000000))
require.NoError(t, err, "Could not generate random file path")
walletDir := filepath.Join(testutil.TempDir(), fmt.Sprintf("/%d", randPath), "wallet")
require.NoError(t, os.RemoveAll(walletDir), "Failed to remove directory")
t.Cleanup(func() {
require.NoError(t, os.RemoveAll(walletDir), "Failed to remove directory")
})
return walletDir
}
func TestServer_Signup_PasswordAlreadyExists(t *testing.T) {
valDB := dbtest.SetupDB(t, [][48]byte{})
ctx := context.Background()
@@ -33,17 +53,32 @@ func TestServer_Signup_PasswordAlreadyExists(t *testing.T) {
func TestServer_SignupAndLogin_RoundTrip(t *testing.T) {
valDB := dbtest.SetupDB(t, [][48]byte{})
ctx := context.Background()
localWalletDir := setupWalletDir(t)
defaultWalletPath = localWalletDir
strongPass := "29384283xasjasd32%%&*@*#*"
// We attempt to create the wallet.
_, err := v2.CreateWalletWithKeymanager(ctx, &v2.CreateWalletConfig{
WalletCfg: &v2.WalletConfig{
WalletDir: defaultWalletPath,
KeymanagerKind: v2keymanager.Direct,
WalletPassword: strongPass,
},
SkipMnemonicConfirm: true,
})
require.NoError(t, err)
ss := &Server{
valDB: valDB,
valDB: valDB,
walletInitializedFeed: new(event.Feed),
}
weakPass := "password"
_, err := ss.Signup(ctx, &pb.AuthRequest{
_, err = ss.Signup(ctx, &pb.AuthRequest{
Password: weakPass,
})
require.ErrorContains(t, "Could not validate password input", err)
// We assert we are able to signup with a strong password.
strongPass := "29384283xasjasd32%%&*@*#*"
_, err = ss.Signup(ctx, &pb.AuthRequest{
Password: strongPass,
})

View File

@@ -56,11 +56,15 @@ func (g *Gateway) Start() {
),
)
opts := []grpc.DialOption{grpc.WithInsecure()}
if err := pb.RegisterAuthHandlerFromEndpoint(ctx, gwmux, g.remoteAddr, opts); err != nil {
log.Fatalf("Could not register API handler with grpc endpoint: %v", err)
handlers := []func(context.Context, *gwruntime.ServeMux, string, []grpc.DialOption) error{
pb.RegisterAuthHandlerFromEndpoint,
pb.RegisterWalletHandlerFromEndpoint,
pb.RegisterHealthHandlerFromEndpoint,
}
if err := pb.RegisterHealthHandlerFromEndpoint(ctx, gwmux, g.remoteAddr, opts); err != nil {
log.Fatalf("Could not register API handler with grpc endpoint: %v", err)
for _, h := range handlers {
if err := h(ctx, gwmux, g.remoteAddr, opts); err != nil {
log.Fatalf("Could not register API handler with grpc endpoint: %v", err)
}
}
g.mux.Handle("/", g.corsMiddleware(gwmux))
g.server = &http.Server{

View File

@@ -12,6 +12,9 @@ import (
// ListBalances lists the validator balances.
func (s *Server) ListBalances(ctx context.Context, req *pb.AccountRequest) (*pb.ListBalancesResponse, error) {
if !s.walletInitialized {
return nil, status.Error(codes.FailedPrecondition, "No wallet found")
}
filteredIndices := s.filteredIndices(ctx, req)
returnedKeys := make([][]byte, 0, len(filteredIndices))
returnedIndices := make([]uint64, 0, len(filteredIndices))
@@ -42,6 +45,9 @@ func (s *Server) ListBalances(ctx context.Context, req *pb.AccountRequest) (*pb.
// ListStatuses lists the validator current statuses.
func (s *Server) ListStatuses(ctx context.Context, req *pb.AccountRequest) (*pb.ListStatusesResponse, error) {
if !s.walletInitialized {
return nil, status.Error(codes.FailedPrecondition, "No wallet found")
}
filteredIndices := s.filteredIndices(ctx, req)
returnedKeys := make([][]byte, 0, len(filteredIndices))
returnedIndices := make([]uint64, 0, len(filteredIndices))
@@ -72,6 +78,9 @@ func (s *Server) ListStatuses(ctx context.Context, req *pb.AccountRequest) (*pb.
// ListPerformance lists the validator current performances.
func (s *Server) ListPerformance(ctx context.Context, req *pb.AccountRequest) (*pb.ListPerformanceResponse, error) {
if !s.walletInitialized {
return nil, status.Error(codes.FailedPrecondition, "No wallet found")
}
return nil, status.Error(codes.Unimplemented, "Unimplemented")
}

View File

@@ -16,7 +16,7 @@ func TestServer_ListBalancesHappy(t *testing.T) {
fv := setupFakeClient()
vs, err := client.NewValidatorService(ctx, &client.Config{Validator: fv})
require.NoError(t, err)
s := &Server{validatorService: vs}
s := &Server{validatorService: vs, walletInitialized: true}
req := &pb.AccountRequest{
PublicKeys: [][]byte{{'a'}, {'b'}, {'c'}},
Indices: []uint64{4, 5, 6},
@@ -43,7 +43,7 @@ func TestServer_ListBalancesOverlaps(t *testing.T) {
fv := setupFakeClient()
vs, err := client.NewValidatorService(ctx, &client.Config{Validator: fv})
require.NoError(t, err)
s := &Server{validatorService: vs}
s := &Server{validatorService: vs, walletInitialized: true}
req := &pb.AccountRequest{
PublicKeys: [][]byte{{'a'}, {'b'}, {'c'}},
Indices: []uint64{1, 2, 4},
@@ -68,7 +68,7 @@ func TestServer_ListBalancesMissing(t *testing.T) {
fv := setupFakeClient()
vs, err := client.NewValidatorService(ctx, &client.Config{Validator: fv})
require.NoError(t, err)
s := &Server{validatorService: vs}
s := &Server{validatorService: vs, walletInitialized: true}
req := &pb.AccountRequest{
PublicKeys: [][]byte{{'a'}, {'x'}, {'y'}},
Indices: []uint64{1, 200, 400},
@@ -90,7 +90,7 @@ func TestServer_ListStatusesHappy(t *testing.T) {
fv := setupFakeClient()
vs, err := client.NewValidatorService(ctx, &client.Config{Validator: fv})
require.NoError(t, err)
s := &Server{validatorService: vs}
s := &Server{validatorService: vs, walletInitialized: true}
req := &pb.AccountRequest{
PublicKeys: [][]byte{{'a'}, {'b'}, {'c'}},
Indices: []uint64{4, 5, 6},
@@ -117,7 +117,7 @@ func TestServer_ListStatusesOverlaps(t *testing.T) {
fv := setupFakeClient()
vs, err := client.NewValidatorService(ctx, &client.Config{Validator: fv})
require.NoError(t, err)
s := &Server{validatorService: vs}
s := &Server{validatorService: vs, walletInitialized: true}
req := &pb.AccountRequest{
PublicKeys: [][]byte{{'a'}, {'b'}, {'c'}},
Indices: []uint64{1, 2, 4},
@@ -142,7 +142,7 @@ func TestServer_ListStatusesMissing(t *testing.T) {
fv := setupFakeClient()
vs, err := client.NewValidatorService(ctx, &client.Config{Validator: fv})
require.NoError(t, err)
s := &Server{validatorService: vs}
s := &Server{validatorService: vs, walletInitialized: true}
req := &pb.AccountRequest{
PublicKeys: [][]byte{{'a'}, {'x'}, {'y'}},
Indices: []uint64{1, 200, 400},

View File

@@ -15,8 +15,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.Auth/Signup": true,
"/ethereum.validator.accounts.v2.Auth/Login": true,
"/ethereum.validator.accounts.v2.Wallet/WalletConfig": true,
"/ethereum.validator.accounts.v2.Wallet/GenerateMnemonic": true,
"/ethereum.validator.accounts.v2.Wallet/CreateWallet": true,
}
authLock sync.RWMutex
)

View File

@@ -10,6 +10,7 @@ import (
grpc_opentracing "github.com/grpc-ecosystem/go-grpc-middleware/tracing/opentracing"
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
pb "github.com/prysmaticlabs/prysm/proto/validator/accounts/v2"
"github.com/prysmaticlabs/prysm/shared/event"
"github.com/prysmaticlabs/prysm/shared/rand"
"github.com/prysmaticlabs/prysm/shared/traceutil"
"github.com/prysmaticlabs/prysm/validator/client"
@@ -29,42 +30,50 @@ func init() {
// Config options for the gRPC server.
type Config struct {
Host string
Port string
CertFlag string
KeyFlag string
ValDB db.Database
ValidatorService *client.ValidatorService
Host string
Port string
CertFlag string
KeyFlag string
ValDB db.Database
ValidatorService *client.ValidatorService
WalletInitializedFeed *event.Feed
WalletDir string
}
// Server defining a gRPC server for the remote signer API.
type Server struct {
valDB db.Database
ctx context.Context
cancel context.CancelFunc
host string
port string
listener net.Listener
withCert string
withKey string
credentialError error
grpcServer *grpc.Server
jwtKey []byte
validatorService *client.ValidatorService
valDB db.Database
ctx context.Context
cancel context.CancelFunc
host string
port string
listener net.Listener
withCert string
withKey string
credentialError error
grpcServer *grpc.Server
jwtKey []byte
validatorService *client.ValidatorService
walletDir string
walletInitializedFeed *event.Feed
walletInitialized bool
}
// NewServer instantiates a new gRPC server.
func NewServer(ctx context.Context, cfg *Config) *Server {
ctx, cancel := context.WithCancel(ctx)
return &Server{
ctx: ctx,
cancel: cancel,
host: cfg.Host,
port: cfg.Port,
withCert: cfg.CertFlag,
withKey: cfg.KeyFlag,
valDB: cfg.ValDB,
validatorService: cfg.ValidatorService,
ctx: ctx,
cancel: cancel,
host: cfg.Host,
port: cfg.Port,
withCert: cfg.CertFlag,
withKey: cfg.KeyFlag,
valDB: cfg.ValDB,
validatorService: cfg.ValidatorService,
walletDir: cfg.WalletDir,
walletInitializedFeed: cfg.WalletInitializedFeed,
walletInitialized: false,
}
}
@@ -122,6 +131,7 @@ func (s *Server) Start() {
// Register services available for the gRPC server.
reflection.Register(s.grpcServer)
pb.RegisterAuthServer(s.grpcServer, s)
pb.RegisterWalletServer(s.grpcServer, s)
pb.RegisterHealthServer(s.grpcServer, s)
go func() {

126
validator/rpc/wallet.go Normal file
View File

@@ -0,0 +1,126 @@
package rpc
import (
"context"
"encoding/json"
"path/filepath"
ptypes "github.com/gogo/protobuf/types"
"github.com/pkg/errors"
pb "github.com/prysmaticlabs/prysm/proto/validator/accounts/v2"
"github.com/prysmaticlabs/prysm/shared/rand"
v2 "github.com/prysmaticlabs/prysm/validator/accounts/v2"
"github.com/prysmaticlabs/prysm/validator/flags"
v2keymanager "github.com/prysmaticlabs/prysm/validator/keymanager/v2"
"github.com/tyler-smith/go-bip39"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
var defaultWalletPath = filepath.Join(flags.DefaultValidatorDir(), flags.WalletDefaultDirName)
// 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:
// 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
}
wallet, err := v2.CreateWalletWithKeymanager(ctx, &v2.CreateWalletConfig{
WalletCfg: &v2.WalletConfig{
WalletDir: defaultWalletPath,
KeymanagerKind: v2keymanager.Direct,
WalletPassword: req.WalletPassword,
},
SkipMnemonicConfirm: true,
})
if err != nil {
return nil, err
}
// Import the uploaded accounts.
if err := v2.ImportAccounts(ctx, &v2.ImportAccountsConfig{
Wallet: wallet,
Keystores: keystores,
AccountPassword: req.KeystoresPassword,
}); err != nil {
return nil, err
}
return &pb.WalletResponse{
WalletPath: defaultWalletPath,
}, nil
case pb.CreateWalletRequest_DERIVED:
if req.NumAccounts < 1 {
return nil, status.Error(codes.InvalidArgument, "Must create at least 1 validator account")
}
if req.Mnemonic == "" {
return nil, status.Error(codes.InvalidArgument, "Must include mnemonic in request")
}
_, err := v2.RecoverWallet(ctx, &v2.RecoverWalletConfig{
WalletDir: defaultWalletPath,
WalletPassword: req.WalletPassword,
Mnemonic: req.Mnemonic,
NumAccounts: int64(req.NumAccounts),
})
if err != nil {
return nil, err
}
return &pb.WalletResponse{
WalletPath: defaultWalletPath,
}, nil
case pb.CreateWalletRequest_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)
}
}
// EditConfig allows the user to edit their wallet's keymanageropts.
func (s *Server) EditConfig(ctx context.Context, req *pb.EditWalletConfigRequest) (*pb.WalletResponse, error) {
return nil, status.Error(codes.Unimplemented, "Unimplemented")
}
// 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) {
err := v2.WalletExists(defaultWalletPath)
if err != nil && errors.Is(err, v2.ErrNoWalletFound) {
// If no wallet is found, we simply return an empty response.
return &pb.WalletResponse{}, nil
}
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not check if wallet exists: %v", err)
}
return &pb.WalletResponse{
WalletPath: defaultWalletPath,
KeymanagerConfig: nil, // Fill in by reading from disk.
}, nil
}
// GenerateMnemonic creates a new, random bip39 mnemonic phrase.
func (s *Server) GenerateMnemonic(ctx context.Context, _ *ptypes.Empty) (*pb.GenerateMnemonicResponse, error) {
mnemonicRandomness := make([]byte, 32)
if _, err := rand.NewGenerator().Read(mnemonicRandomness); err != nil {
return nil, status.Errorf(
codes.FailedPrecondition,
"Could not initialize mnemonic source of randomness: %v",
err,
)
}
mnemonic, err := bip39.NewMnemonic(mnemonicRandomness)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not generate wallet seed: %v", err)
}
return &pb.GenerateMnemonicResponse{
Mnemonic: mnemonic,
}, nil
}

View File

@@ -0,0 +1,119 @@
package rpc
import (
"context"
"encoding/json"
"fmt"
"testing"
ptypes "github.com/gogo/protobuf/types"
"github.com/google/uuid"
pb "github.com/prysmaticlabs/prysm/proto/validator/accounts/v2"
"github.com/prysmaticlabs/prysm/shared/bls"
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
"github.com/prysmaticlabs/prysm/shared/testutil/require"
v2 "github.com/prysmaticlabs/prysm/validator/accounts/v2"
v2keymanager "github.com/prysmaticlabs/prysm/validator/keymanager/v2"
keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4"
)
func TestServer_CreateWallet_Direct(t *testing.T) {
localWalletDir := setupWalletDir(t)
defaultWalletPath = localWalletDir
ctx := context.Background()
strongPass := "29384283xasjasd32%%&*@*#*"
s := &Server{}
req := &pb.CreateWalletRequest{
WalletPath: localWalletDir,
Keymanager: pb.CreateWalletRequest_DIRECT,
WalletPassword: strongPass,
KeystoresPassword: strongPass,
}
_, err := s.CreateWallet(ctx, req)
require.ErrorContains(t, "No keystores included for import", err)
req.KeystoresImported = []string{"badjson"}
_, err = s.CreateWallet(ctx, req)
require.ErrorContains(t, "Not a valid EIP-2335 keystore", err)
encryptor := keystorev4.New()
keystores := make([]string, 3)
for i := 0; i < len(keystores); i++ {
privKey := bls.RandKey()
pubKey := fmt.Sprintf("%x", privKey.PublicKey().Marshal())
id, err := uuid.NewRandom()
require.NoError(t, err)
cryptoFields, err := encryptor.Encrypt(privKey.Marshal(), strongPass)
require.NoError(t, err)
item := &v2keymanager.Keystore{
Crypto: cryptoFields,
ID: id.String(),
Version: encryptor.Version(),
Pubkey: pubKey,
Name: encryptor.Name(),
}
encodedFile, err := json.MarshalIndent(item, "", "\t")
require.NoError(t, err)
keystores[i] = string(encodedFile)
}
req.KeystoresImported = keystores
_, err = s.CreateWallet(ctx, req)
require.NoError(t, err)
}
func TestServer_CreateWallet_Derived(t *testing.T) {
localWalletDir := setupWalletDir(t)
defaultWalletPath = localWalletDir
ctx := context.Background()
strongPass := "29384283xasjasd32%%&*@*#*"
s := &Server{}
req := &pb.CreateWalletRequest{
WalletPath: localWalletDir,
Keymanager: pb.CreateWalletRequest_DERIVED,
WalletPassword: strongPass,
NumAccounts: 0,
}
_, err := s.CreateWallet(ctx, req)
require.ErrorContains(t, "Must create at least 1 validator account", err)
req.NumAccounts = 2
_, err = s.CreateWallet(ctx, req)
require.ErrorContains(t, "Must include mnemonic", err)
mnemonicResp, err := s.GenerateMnemonic(ctx, &ptypes.Empty{})
require.NoError(t, err)
req.Mnemonic = mnemonicResp.Mnemonic
_, err = s.CreateWallet(ctx, req)
require.NoError(t, err)
}
func TestServer_WalletConfig_NoWalletFound(t *testing.T) {
s := &Server{}
resp, err := s.WalletConfig(context.Background(), &ptypes.Empty{})
require.NoError(t, err)
assert.DeepEqual(t, resp, &pb.WalletResponse{})
}
func TestServer_WalletConfig(t *testing.T) {
localWalletDir := setupWalletDir(t)
defaultWalletPath = localWalletDir
ctx := context.Background()
strongPass := "29384283xasjasd32%%&*@*#*"
s := &Server{}
// We attempt to create the wallet.
_, err := v2.CreateWalletWithKeymanager(ctx, &v2.CreateWalletConfig{
WalletCfg: &v2.WalletConfig{
WalletDir: defaultWalletPath,
KeymanagerKind: v2keymanager.Direct,
WalletPassword: strongPass,
},
SkipMnemonicConfirm: true,
})
require.NoError(t, err)
resp, err := s.WalletConfig(ctx, &ptypes.Empty{})
require.NoError(t, err)
assert.DeepEqual(t, resp, &pb.WalletResponse{
WalletPath: localWalletDir,
})
}