github.com/prysmaticlabs/prysm@v1.4.4/validator/rpc/accounts_test.go (about)

     1  package rpc
     2  
     3  import (
     4  	"archive/zip"
     5  	"bytes"
     6  	"context"
     7  	"encoding/json"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"path/filepath"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/golang/mock/gomock"
    15  	"github.com/prysmaticlabs/prysm/cmd/validator/flags"
    16  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    17  	pb "github.com/prysmaticlabs/prysm/proto/validator/accounts/v2"
    18  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    19  	"github.com/prysmaticlabs/prysm/shared/mock"
    20  	"github.com/prysmaticlabs/prysm/shared/testutil/assert"
    21  	"github.com/prysmaticlabs/prysm/shared/testutil/require"
    22  	"github.com/prysmaticlabs/prysm/validator/accounts"
    23  	"github.com/prysmaticlabs/prysm/validator/accounts/iface"
    24  	"github.com/prysmaticlabs/prysm/validator/accounts/wallet"
    25  	"github.com/prysmaticlabs/prysm/validator/keymanager"
    26  	"github.com/prysmaticlabs/prysm/validator/keymanager/derived"
    27  	constant "github.com/prysmaticlabs/prysm/validator/testing"
    28  	"google.golang.org/protobuf/types/known/timestamppb"
    29  )
    30  
    31  var (
    32  	defaultWalletPath = filepath.Join(flags.DefaultValidatorDir(), flags.WalletDefaultDirName)
    33  )
    34  
    35  func TestServer_ListAccounts(t *testing.T) {
    36  	ctx := context.Background()
    37  	localWalletDir := setupWalletDir(t)
    38  	defaultWalletPath = localWalletDir
    39  	// We attempt to create the wallet.
    40  	w, err := accounts.CreateWalletWithKeymanager(ctx, &accounts.CreateWalletConfig{
    41  		WalletCfg: &wallet.Config{
    42  			WalletDir:      defaultWalletPath,
    43  			KeymanagerKind: keymanager.Derived,
    44  			WalletPassword: strongPass,
    45  		},
    46  		SkipMnemonicConfirm: true,
    47  	})
    48  	require.NoError(t, err)
    49  	km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false})
    50  	require.NoError(t, err)
    51  	s := &Server{
    52  		keymanager:        km,
    53  		walletInitialized: true,
    54  		wallet:            w,
    55  	}
    56  	numAccounts := 50
    57  	dr, ok := km.(*derived.Keymanager)
    58  	require.Equal(t, true, ok)
    59  	err = dr.RecoverAccountsFromMnemonic(ctx, constant.TestMnemonic, "", numAccounts)
    60  	require.NoError(t, err)
    61  	resp, err := s.ListAccounts(ctx, &pb.ListAccountsRequest{
    62  		PageSize: int32(numAccounts),
    63  	})
    64  	require.NoError(t, err)
    65  	require.Equal(t, len(resp.Accounts), numAccounts)
    66  
    67  	tests := []struct {
    68  		req *pb.ListAccountsRequest
    69  		res *pb.ListAccountsResponse
    70  	}{
    71  		{
    72  			req: &pb.ListAccountsRequest{
    73  				PageSize: 5,
    74  			},
    75  			res: &pb.ListAccountsResponse{
    76  				Accounts:      resp.Accounts[0:5],
    77  				NextPageToken: "1",
    78  				TotalSize:     int32(numAccounts),
    79  			},
    80  		},
    81  		{
    82  			req: &pb.ListAccountsRequest{
    83  				PageSize:  5,
    84  				PageToken: "1",
    85  			},
    86  			res: &pb.ListAccountsResponse{
    87  				Accounts:      resp.Accounts[5:10],
    88  				NextPageToken: "2",
    89  				TotalSize:     int32(numAccounts),
    90  			},
    91  		},
    92  	}
    93  	for _, test := range tests {
    94  		res, err := s.ListAccounts(context.Background(), test.req)
    95  		require.NoError(t, err)
    96  		assert.DeepEqual(t, res, test.res)
    97  	}
    98  }
    99  
   100  func TestServer_BackupAccounts(t *testing.T) {
   101  	ctx := context.Background()
   102  	localWalletDir := setupWalletDir(t)
   103  	defaultWalletPath = localWalletDir
   104  	// We attempt to create the wallet.
   105  	w, err := accounts.CreateWalletWithKeymanager(ctx, &accounts.CreateWalletConfig{
   106  		WalletCfg: &wallet.Config{
   107  			WalletDir:      defaultWalletPath,
   108  			KeymanagerKind: keymanager.Derived,
   109  			WalletPassword: strongPass,
   110  		},
   111  		SkipMnemonicConfirm: true,
   112  	})
   113  	require.NoError(t, err)
   114  	km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false})
   115  	require.NoError(t, err)
   116  	s := &Server{
   117  		keymanager:        km,
   118  		walletInitialized: true,
   119  		wallet:            w,
   120  	}
   121  	numAccounts := 50
   122  	dr, ok := km.(*derived.Keymanager)
   123  	require.Equal(t, true, ok)
   124  	err = dr.RecoverAccountsFromMnemonic(ctx, constant.TestMnemonic, "", numAccounts)
   125  	require.NoError(t, err)
   126  	resp, err := s.ListAccounts(ctx, &pb.ListAccountsRequest{
   127  		PageSize: int32(numAccounts),
   128  	})
   129  	require.NoError(t, err)
   130  	require.Equal(t, len(resp.Accounts), numAccounts)
   131  
   132  	pubKeys := make([][]byte, numAccounts)
   133  	for i, aa := range resp.Accounts {
   134  		pubKeys[i] = aa.ValidatingPublicKey
   135  	}
   136  	// We now attempt to backup all public keys from the wallet.
   137  	res, err := s.BackupAccounts(context.Background(), &pb.BackupAccountsRequest{
   138  		PublicKeys:     pubKeys,
   139  		BackupPassword: s.wallet.Password(),
   140  	})
   141  	require.NoError(t, err)
   142  	require.NotNil(t, res.ZipFile)
   143  
   144  	// Open a zip archive for reading.
   145  	buf := bytes.NewReader(res.ZipFile)
   146  	r, err := zip.NewReader(buf, int64(len(res.ZipFile)))
   147  	require.NoError(t, err)
   148  	require.Equal(t, len(pubKeys), len(r.File))
   149  
   150  	// Iterate through the files in the archive, checking they
   151  	// match the keystores we wanted to backup.
   152  	for i, f := range r.File {
   153  		keystoreFile, err := f.Open()
   154  		require.NoError(t, err)
   155  		encoded, err := ioutil.ReadAll(keystoreFile)
   156  		if err != nil {
   157  			require.NoError(t, keystoreFile.Close())
   158  			t.Fatal(err)
   159  		}
   160  		keystore := &keymanager.Keystore{}
   161  		if err := json.Unmarshal(encoded, &keystore); err != nil {
   162  			require.NoError(t, keystoreFile.Close())
   163  			t.Fatal(err)
   164  		}
   165  		assert.Equal(t, keystore.Pubkey, fmt.Sprintf("%x", pubKeys[i]))
   166  		require.NoError(t, keystoreFile.Close())
   167  	}
   168  }
   169  
   170  func TestServer_DeleteAccounts_FailedPreconditions_DerivedWallet(t *testing.T) {
   171  	ctx := context.Background()
   172  	localWalletDir := setupWalletDir(t)
   173  	defaultWalletPath = localWalletDir
   174  	// We attempt to create the wallet.
   175  	w, err := accounts.CreateWalletWithKeymanager(ctx, &accounts.CreateWalletConfig{
   176  		WalletCfg: &wallet.Config{
   177  			WalletDir:      defaultWalletPath,
   178  			KeymanagerKind: keymanager.Derived,
   179  			WalletPassword: strongPass,
   180  		},
   181  		SkipMnemonicConfirm: true,
   182  	})
   183  	require.NoError(t, err)
   184  	km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false})
   185  	require.NoError(t, err)
   186  	s := &Server{
   187  		keymanager:        km,
   188  		walletInitialized: true,
   189  		wallet:            w,
   190  	}
   191  	numAccounts := 5
   192  	dr, ok := km.(*derived.Keymanager)
   193  	require.Equal(t, true, ok)
   194  	err = dr.RecoverAccountsFromMnemonic(ctx, constant.TestMnemonic, "", numAccounts)
   195  	require.NoError(t, err)
   196  
   197  	_, err = s.DeleteAccounts(ctx, &pb.DeleteAccountsRequest{
   198  		PublicKeysToDelete: nil,
   199  	})
   200  	assert.ErrorContains(t, "No public keys specified to delete", err)
   201  
   202  	keys, err := s.keymanager.FetchValidatingPublicKeys(ctx)
   203  	require.NoError(t, err)
   204  	_, err = s.DeleteAccounts(ctx, &pb.DeleteAccountsRequest{
   205  		PublicKeysToDelete: bytesutil.FromBytes48Array(keys),
   206  	})
   207  	require.NoError(t, err)
   208  }
   209  
   210  func TestServer_DeleteAccounts_FailedPreconditions_NoWallet(t *testing.T) {
   211  	s := &Server{}
   212  	ctx := context.Background()
   213  	_, err := s.DeleteAccounts(ctx, &pb.DeleteAccountsRequest{})
   214  	assert.ErrorContains(t, "No public keys specified to delete", err)
   215  	_, err = s.DeleteAccounts(ctx, &pb.DeleteAccountsRequest{
   216  		PublicKeysToDelete: make([][]byte, 1),
   217  	})
   218  	assert.ErrorContains(t, "No wallet found", err)
   219  }
   220  
   221  func TestServer_DeleteAccounts_OK_ImportedWallet(t *testing.T) {
   222  	s, pubKeys := createImportedWalletWithAccounts(t, 3)
   223  	ctx := context.Background()
   224  	keys, err := s.keymanager.FetchValidatingPublicKeys(ctx)
   225  	require.NoError(t, err)
   226  	require.Equal(t, len(pubKeys), len(keys))
   227  
   228  	// Next, we attempt to delete one of the keystores.
   229  	_, err = s.DeleteAccounts(ctx, &pb.DeleteAccountsRequest{
   230  		PublicKeysToDelete: pubKeys[:1], // Delete the 0th public key
   231  	})
   232  	require.NoError(t, err)
   233  	s.keymanager, err = s.wallet.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false})
   234  	require.NoError(t, err)
   235  
   236  	// We expect one of the keys to have been deleted.
   237  	keys, err = s.keymanager.FetchValidatingPublicKeys(ctx)
   238  	require.NoError(t, err)
   239  	assert.Equal(t, len(pubKeys)-1, len(keys))
   240  }
   241  
   242  func TestServer_VoluntaryExit(t *testing.T) {
   243  	ctrl := gomock.NewController(t)
   244  	defer ctrl.Finish()
   245  	ctx := context.Background()
   246  	mockValidatorClient := mock.NewMockBeaconNodeValidatorClient(ctrl)
   247  	mockNodeClient := mock.NewMockNodeClient(ctrl)
   248  
   249  	mockValidatorClient.EXPECT().
   250  		ValidatorIndex(gomock.Any(), gomock.Any()).
   251  		Return(&ethpb.ValidatorIndexResponse{Index: 0}, nil)
   252  
   253  	mockValidatorClient.EXPECT().
   254  		ValidatorIndex(gomock.Any(), gomock.Any()).
   255  		Return(&ethpb.ValidatorIndexResponse{Index: 1}, nil)
   256  
   257  	// Any time in the past will suffice
   258  	genesisTime := &timestamppb.Timestamp{
   259  		Seconds: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC).Unix(),
   260  	}
   261  
   262  	mockNodeClient.EXPECT().
   263  		GetGenesis(gomock.Any(), gomock.Any()).
   264  		Times(2).
   265  		Return(&ethpb.Genesis{GenesisTime: genesisTime}, nil)
   266  
   267  	mockValidatorClient.EXPECT().
   268  		DomainData(gomock.Any(), gomock.Any()).
   269  		Times(2).
   270  		Return(&ethpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil)
   271  
   272  	mockValidatorClient.EXPECT().
   273  		ProposeExit(gomock.Any(), gomock.AssignableToTypeOf(&ethpb.SignedVoluntaryExit{})).
   274  		Times(2).
   275  		Return(&ethpb.ProposeExitResponse{}, nil)
   276  
   277  	localWalletDir := setupWalletDir(t)
   278  	defaultWalletPath = localWalletDir
   279  	// We attempt to create the wallet.
   280  	w, err := accounts.CreateWalletWithKeymanager(ctx, &accounts.CreateWalletConfig{
   281  		WalletCfg: &wallet.Config{
   282  			WalletDir:      defaultWalletPath,
   283  			KeymanagerKind: keymanager.Derived,
   284  			WalletPassword: strongPass,
   285  		},
   286  		SkipMnemonicConfirm: true,
   287  	})
   288  	require.NoError(t, err)
   289  	km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false})
   290  	require.NoError(t, err)
   291  	s := &Server{
   292  		keymanager:                km,
   293  		walletInitialized:         true,
   294  		wallet:                    w,
   295  		beaconNodeClient:          mockNodeClient,
   296  		beaconNodeValidatorClient: mockValidatorClient,
   297  	}
   298  	numAccounts := 2
   299  	dr, ok := km.(*derived.Keymanager)
   300  	require.Equal(t, true, ok)
   301  	err = dr.RecoverAccountsFromMnemonic(ctx, constant.TestMnemonic, "", numAccounts)
   302  	require.NoError(t, err)
   303  	pubKeys, err := dr.FetchValidatingPublicKeys(ctx)
   304  	require.NoError(t, err)
   305  
   306  	rawPubKeys := make([][]byte, len(pubKeys))
   307  	for i, key := range pubKeys {
   308  		rawPubKeys[i] = key[:]
   309  	}
   310  	res, err := s.VoluntaryExit(ctx, &pb.VoluntaryExitRequest{
   311  		PublicKeys: rawPubKeys,
   312  	})
   313  	require.NoError(t, err)
   314  	require.DeepEqual(t, rawPubKeys, res.ExitedKeys)
   315  }