2020-09-04 00:58:36 +00:00
package rpc
import (
2021-01-30 00:30:44 +00:00
"archive/zip"
"bytes"
2020-09-04 00:58:36 +00:00
"context"
2021-01-30 00:30:44 +00:00
"encoding/json"
2020-09-04 00:58:36 +00:00
"fmt"
2023-03-17 18:52:56 +00:00
"github.com/prysmaticlabs/prysm/v4/api/pagination"
"github.com/prysmaticlabs/prysm/v4/cmd"
"github.com/prysmaticlabs/prysm/v4/crypto/bls"
pb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1/validator-client"
"github.com/prysmaticlabs/prysm/v4/validator/accounts"
"github.com/prysmaticlabs/prysm/v4/validator/accounts/petnames"
"github.com/prysmaticlabs/prysm/v4/validator/keymanager"
"github.com/prysmaticlabs/prysm/v4/validator/keymanager/derived"
"github.com/prysmaticlabs/prysm/v4/validator/keymanager/local"
2020-09-04 00:58:36 +00:00
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// ListAccounts allows retrieval of validating keys and their petnames
// for a user's wallet via RPC.
2023-02-23 15:24:06 +00:00
// DEPRECATED: Prysm Web UI and associated endpoints will be fully removed in a future hard fork.
2020-09-04 00:58:36 +00:00
func ( s * Server ) ListAccounts ( ctx context . Context , req * pb . ListAccountsRequest ) ( * pb . ListAccountsResponse , error ) {
2022-01-31 16:44:17 +00:00
if s . validatorService == nil {
return nil , status . Error ( codes . FailedPrecondition , "Validator service not yet initialized" )
}
2020-09-04 00:58:36 +00:00
if ! s . walletInitialized {
return nil , status . Error ( codes . FailedPrecondition , "Wallet not yet initialized" )
}
2020-10-03 22:26:39 +00:00
if int ( req . PageSize ) > cmd . Get ( ) . MaxRPCPageSize {
return nil , status . Errorf ( codes . InvalidArgument , "Requested page size %d can not be greater than max size %d" ,
req . PageSize , cmd . Get ( ) . MaxRPCPageSize )
}
2022-01-31 16:44:17 +00:00
km , err := s . validatorService . Keymanager ( )
if err != nil {
return nil , err
}
keys , err := km . FetchValidatingPublicKeys ( ctx )
2020-09-04 00:58:36 +00:00
if err != nil {
return nil , err
}
2020-10-20 01:22:36 +00:00
accs := make ( [ ] * pb . Account , len ( keys ) )
2020-09-04 00:58:36 +00:00
for i := 0 ; i < len ( keys ) ; i ++ {
2020-10-20 01:22:36 +00:00
accs [ i ] = & pb . Account {
2020-09-04 00:58:36 +00:00
ValidatingPublicKey : keys [ i ] [ : ] ,
AccountName : petnames . DeterministicName ( keys [ i ] [ : ] , "-" ) ,
}
2020-10-15 22:31:52 +00:00
if s . wallet . KeymanagerKind ( ) == keymanager . Derived {
2020-10-20 01:22:36 +00:00
accs [ i ] . DerivationPath = fmt . Sprintf ( derived . ValidatingKeyDerivationPathTemplate , i )
2020-09-04 00:58:36 +00:00
}
}
2020-10-03 22:26:39 +00:00
if req . All {
return & pb . ListAccountsResponse {
2020-10-20 01:22:36 +00:00
Accounts : accs ,
2020-10-03 22:26:39 +00:00
TotalSize : int32 ( len ( keys ) ) ,
NextPageToken : "" ,
} , nil
}
start , end , nextPageToken , err := pagination . StartAndEndPage ( req . PageToken , int ( req . PageSize ) , len ( keys ) )
if err != nil {
return nil , status . Errorf (
codes . Internal ,
"Could not paginate results: %v" ,
err ,
)
}
2020-09-04 00:58:36 +00:00
return & pb . ListAccountsResponse {
2020-10-20 01:22:36 +00:00
Accounts : accs [ start : end ] ,
2020-10-03 22:26:39 +00:00
TotalSize : int32 ( len ( keys ) ) ,
NextPageToken : nextPageToken ,
2020-09-04 00:58:36 +00:00
} , nil
}
2021-01-30 00:30:44 +00:00
// BackupAccounts creates a zip file containing EIP-2335 keystores for the user's
// specified public keys by encrypting them with the specified password.
2023-02-23 15:24:06 +00:00
// DEPRECATED: Prysm Web UI and associated endpoints will be fully removed in a future hard fork.
2021-01-30 00:30:44 +00:00
func ( s * Server ) BackupAccounts (
ctx context . Context , req * pb . BackupAccountsRequest ,
) ( * pb . BackupAccountsResponse , error ) {
2022-01-31 16:44:17 +00:00
if s . validatorService == nil {
return nil , status . Error ( codes . FailedPrecondition , "Validator service not yet initialized" )
}
2021-01-30 00:30:44 +00:00
if req . PublicKeys == nil || len ( req . PublicKeys ) < 1 {
return nil , status . Error ( codes . InvalidArgument , "No public keys specified to backup" )
}
if req . BackupPassword == "" {
return nil , status . Error ( codes . InvalidArgument , "Backup password cannot be empty" )
}
2022-01-31 16:44:17 +00:00
if s . wallet == nil {
return nil , status . Error ( codes . FailedPrecondition , "No wallet found" )
2021-01-30 00:30:44 +00:00
}
2022-01-31 16:44:17 +00:00
var err error
km , err := s . validatorService . Keymanager ( )
if err != nil {
return nil , err
2021-01-30 00:30:44 +00:00
}
pubKeys := make ( [ ] bls . PublicKey , len ( req . PublicKeys ) )
for i , key := range req . PublicKeys {
pubKey , err := bls . PublicKeyFromBytes ( key )
if err != nil {
return nil , status . Errorf ( codes . InvalidArgument , "%#x Not a valid BLS public key: %v" , key , err )
}
pubKeys [ i ] = pubKey
}
var keystoresToBackup [ ] * keymanager . Keystore
2022-01-31 16:44:17 +00:00
switch km := km . ( type ) {
2022-02-01 19:54:19 +00:00
case * local . Keymanager :
2021-01-30 00:30:44 +00:00
keystoresToBackup , err = km . ExtractKeystores ( ctx , pubKeys , req . BackupPassword )
if err != nil {
2022-02-01 19:54:19 +00:00
return nil , status . Errorf ( codes . Internal , "Could not backup accounts for local keymanager: %v" , err )
2021-01-30 00:30:44 +00:00
}
2022-01-31 16:44:17 +00:00
case * derived . Keymanager :
2021-01-30 00:30:44 +00:00
keystoresToBackup , err = km . ExtractKeystores ( ctx , pubKeys , req . BackupPassword )
if err != nil {
return nil , status . Errorf ( codes . Internal , "Could not backup accounts for derived keymanager: %v" , err )
}
2022-01-31 16:44:17 +00:00
default :
return nil , status . Error ( codes . FailedPrecondition , "Only HD or imported wallets can backup accounts" )
2021-01-30 00:30:44 +00:00
}
if len ( keystoresToBackup ) == 0 {
return nil , status . Error ( codes . InvalidArgument , "No keystores to backup" )
}
buf := new ( bytes . Buffer )
writer := zip . NewWriter ( buf )
for i , k := range keystoresToBackup {
encodedFile , err := json . MarshalIndent ( k , "" , "\t" )
if err != nil {
if err := writer . Close ( ) ; err != nil {
log . WithError ( err ) . Error ( "Could not close zip file after writing" )
}
return nil , status . Errorf ( codes . Internal , "could not marshal keystore to JSON file: %v" , err )
}
f , err := writer . Create ( fmt . Sprintf ( "keystore-%d.json" , i ) )
if err != nil {
if err := writer . Close ( ) ; err != nil {
log . WithError ( err ) . Error ( "Could not close zip file after writing" )
}
return nil , status . Errorf ( codes . Internal , "Could not write keystore file to zip: %v" , err )
}
if _ , err = f . Write ( encodedFile ) ; err != nil {
if err := writer . Close ( ) ; err != nil {
log . WithError ( err ) . Error ( "Could not close zip file after writing" )
}
return nil , status . Errorf ( codes . Internal , "Could not write keystore file contents" )
}
}
if err := writer . Close ( ) ; err != nil {
log . WithError ( err ) . Error ( "Could not close zip file after writing" )
}
return & pb . BackupAccountsResponse {
ZipFile : buf . Bytes ( ) ,
} , nil
}
2021-04-02 09:33:42 +00:00
2021-04-16 07:46:38 +00:00
// VoluntaryExit performs a voluntary exit for the validator keys specified in a request.
2023-02-23 15:24:06 +00:00
// DEPRECATE: Prysm Web UI and associated endpoints will be fully removed in a future hard fork. There is a similar endpoint that is still used /eth/v1alpha1/validator/exit.
2021-04-16 07:46:38 +00:00
func ( s * Server ) VoluntaryExit (
ctx context . Context , req * pb . VoluntaryExitRequest ,
) ( * pb . VoluntaryExitResponse , error ) {
2022-01-31 16:44:17 +00:00
if s . validatorService == nil {
return nil , status . Error ( codes . FailedPrecondition , "Validator service not yet initialized" )
}
2021-04-16 07:46:38 +00:00
if len ( req . PublicKeys ) == 0 {
return nil , status . Error ( codes . InvalidArgument , "No public keys specified to delete" )
}
2022-01-31 16:44:17 +00:00
if s . wallet == nil {
2021-04-16 07:46:38 +00:00
return nil , status . Error ( codes . FailedPrecondition , "No wallet found" )
}
2022-01-31 16:44:17 +00:00
km , err := s . validatorService . Keymanager ( )
if err != nil {
return nil , err
2021-04-16 07:46:38 +00:00
}
formattedKeys := make ( [ ] string , len ( req . PublicKeys ) )
for i , key := range req . PublicKeys {
formattedKeys [ i ] = fmt . Sprintf ( "%#x" , key )
}
cfg := accounts . PerformExitCfg {
ValidatorClient : s . beaconNodeValidatorClient ,
NodeClient : s . beaconNodeClient ,
2022-01-31 16:44:17 +00:00
Keymanager : km ,
2021-04-16 07:46:38 +00:00
RawPubKeys : req . PublicKeys ,
FormattedPubKeys : formattedKeys ,
}
rawExitedKeys , _ , err := accounts . PerformVoluntaryExit ( ctx , cfg )
if err != nil {
return nil , status . Errorf ( codes . Internal , "Could not perform voluntary exit: %v" , err )
}
return & pb . VoluntaryExitResponse {
ExitedKeys : rawExitedKeys ,
} , nil
}