prysm-pulse/validator/rpc/accounts.go
terencechain d17996f8b0
Update to V4 🚀 (#12134)
* Update V3 from V4

* Fix build v3 -> v4

* Update ssz

* Update beacon_chain.pb.go

* Fix formatter import

* Update update-mockgen.sh comment to v4

* Fix conflicts. Pass build and tests

* Fix test
2023-03-17 18:52:56 +00:00

198 lines
6.9 KiB
Go

package rpc
import (
"archive/zip"
"bytes"
"context"
"encoding/json"
"fmt"
"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"
"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.
// DEPRECATED: Prysm Web UI and associated endpoints will be fully removed in a future hard fork.
func (s *Server) ListAccounts(ctx context.Context, req *pb.ListAccountsRequest) (*pb.ListAccountsResponse, error) {
if s.validatorService == nil {
return nil, status.Error(codes.FailedPrecondition, "Validator service not yet initialized")
}
if !s.walletInitialized {
return nil, status.Error(codes.FailedPrecondition, "Wallet not yet initialized")
}
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)
}
km, err := s.validatorService.Keymanager()
if err != nil {
return nil, err
}
keys, err := km.FetchValidatingPublicKeys(ctx)
if err != nil {
return nil, err
}
accs := make([]*pb.Account, len(keys))
for i := 0; i < len(keys); i++ {
accs[i] = &pb.Account{
ValidatingPublicKey: keys[i][:],
AccountName: petnames.DeterministicName(keys[i][:], "-"),
}
if s.wallet.KeymanagerKind() == keymanager.Derived {
accs[i].DerivationPath = fmt.Sprintf(derived.ValidatingKeyDerivationPathTemplate, i)
}
}
if req.All {
return &pb.ListAccountsResponse{
Accounts: accs,
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,
)
}
return &pb.ListAccountsResponse{
Accounts: accs[start:end],
TotalSize: int32(len(keys)),
NextPageToken: nextPageToken,
}, nil
}
// BackupAccounts creates a zip file containing EIP-2335 keystores for the user's
// specified public keys by encrypting them with the specified password.
// DEPRECATED: Prysm Web UI and associated endpoints will be fully removed in a future hard fork.
func (s *Server) BackupAccounts(
ctx context.Context, req *pb.BackupAccountsRequest,
) (*pb.BackupAccountsResponse, error) {
if s.validatorService == nil {
return nil, status.Error(codes.FailedPrecondition, "Validator service not yet initialized")
}
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")
}
if s.wallet == nil {
return nil, status.Error(codes.FailedPrecondition, "No wallet found")
}
var err error
km, err := s.validatorService.Keymanager()
if err != nil {
return nil, err
}
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
switch km := km.(type) {
case *local.Keymanager:
keystoresToBackup, err = km.ExtractKeystores(ctx, pubKeys, req.BackupPassword)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not backup accounts for local keymanager: %v", err)
}
case *derived.Keymanager:
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)
}
default:
return nil, status.Error(codes.FailedPrecondition, "Only HD or imported wallets can backup accounts")
}
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
}
// VoluntaryExit performs a voluntary exit for the validator keys specified in a request.
// 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.
func (s *Server) VoluntaryExit(
ctx context.Context, req *pb.VoluntaryExitRequest,
) (*pb.VoluntaryExitResponse, error) {
if s.validatorService == nil {
return nil, status.Error(codes.FailedPrecondition, "Validator service not yet initialized")
}
if len(req.PublicKeys) == 0 {
return nil, status.Error(codes.InvalidArgument, "No public keys specified to delete")
}
if s.wallet == nil {
return nil, status.Error(codes.FailedPrecondition, "No wallet found")
}
km, err := s.validatorService.Keymanager()
if err != nil {
return nil, err
}
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,
Keymanager: km,
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
}