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(ðpb.ValidatorIndexResponse{Index: 0}, nil) 252 253 mockValidatorClient.EXPECT(). 254 ValidatorIndex(gomock.Any(), gomock.Any()). 255 Return(ðpb.ValidatorIndexResponse{Index: 1}, nil) 256 257 // Any time in the past will suffice 258 genesisTime := ×tamppb.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(ðpb.Genesis{GenesisTime: genesisTime}, nil) 266 267 mockValidatorClient.EXPECT(). 268 DomainData(gomock.Any(), gomock.Any()). 269 Times(2). 270 Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil) 271 272 mockValidatorClient.EXPECT(). 273 ProposeExit(gomock.Any(), gomock.AssignableToTypeOf(ðpb.SignedVoluntaryExit{})). 274 Times(2). 275 Return(ðpb.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 }