mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2025-01-03 08:37:37 +00:00
5a66807989
* First take at updating everything to v5 * Patch gRPC gateway to use prysm v5 Fix patch * Update go ssz --------- Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com>
275 lines
9.2 KiB
Go
275 lines
9.2 KiB
Go
package rpc
|
|
|
|
import (
|
|
"archive/zip"
|
|
"bytes"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
"github.com/pkg/errors"
|
|
"github.com/prysmaticlabs/prysm/v5/api/pagination"
|
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/shared"
|
|
"github.com/prysmaticlabs/prysm/v5/cmd"
|
|
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
|
|
"github.com/prysmaticlabs/prysm/v5/crypto/bls"
|
|
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
|
|
"github.com/prysmaticlabs/prysm/v5/network/httputil"
|
|
"github.com/prysmaticlabs/prysm/v5/validator/accounts"
|
|
"github.com/prysmaticlabs/prysm/v5/validator/accounts/petnames"
|
|
"github.com/prysmaticlabs/prysm/v5/validator/keymanager"
|
|
"github.com/prysmaticlabs/prysm/v5/validator/keymanager/derived"
|
|
"github.com/prysmaticlabs/prysm/v5/validator/keymanager/local"
|
|
"go.opencensus.io/trace"
|
|
)
|
|
|
|
// ListAccounts allows retrieval of validating keys and their petnames
|
|
// for a user's wallet via RPC.
|
|
func (s *Server) ListAccounts(w http.ResponseWriter, r *http.Request) {
|
|
ctx, span := trace.StartSpan(r.Context(), "validator.web.accounts.ListAccounts")
|
|
defer span.End()
|
|
if s.validatorService == nil {
|
|
httputil.HandleError(w, "Validator service not ready.", http.StatusServiceUnavailable)
|
|
return
|
|
}
|
|
if !s.walletInitialized {
|
|
httputil.HandleError(w, "Prysm Wallet not initialized. Please create a new wallet.", http.StatusServiceUnavailable)
|
|
return
|
|
}
|
|
pageSize := r.URL.Query().Get("page_size")
|
|
var ps int64
|
|
if pageSize != "" {
|
|
psi, err := strconv.ParseInt(pageSize, 10, 32)
|
|
if err != nil {
|
|
httputil.HandleError(w, errors.Wrap(err, "Failed to parse page_size").Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
ps = psi
|
|
}
|
|
pageToken := r.URL.Query().Get("page_token")
|
|
publicKeys := r.URL.Query()["public_keys"]
|
|
pubkeys := make([][]byte, len(publicKeys))
|
|
for i, key := range publicKeys {
|
|
k, ok := shared.ValidateHex(w, fmt.Sprintf("PublicKeys[%d]", i), key, fieldparams.BLSPubkeyLength)
|
|
if !ok {
|
|
return
|
|
}
|
|
pubkeys[i] = bytesutil.SafeCopyBytes(k)
|
|
}
|
|
if int(ps) > cmd.Get().MaxRPCPageSize {
|
|
httputil.HandleError(w, fmt.Sprintf("Requested page size %d can not be greater than max size %d",
|
|
ps, cmd.Get().MaxRPCPageSize), http.StatusBadRequest)
|
|
return
|
|
}
|
|
km, err := s.validatorService.Keymanager()
|
|
if err != nil {
|
|
httputil.HandleError(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
keys, err := km.FetchValidatingPublicKeys(ctx)
|
|
if err != nil {
|
|
httputil.HandleError(w, errors.Errorf("Could not retrieve public keys: %v", err).Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
accs := make([]*Account, len(keys))
|
|
for i := 0; i < len(keys); i++ {
|
|
accs[i] = &Account{
|
|
ValidatingPublicKey: hexutil.Encode(keys[i][:]),
|
|
AccountName: petnames.DeterministicName(keys[i][:], "-"),
|
|
}
|
|
if s.wallet.KeymanagerKind() == keymanager.Derived {
|
|
accs[i].DerivationPath = fmt.Sprintf(derived.ValidatingKeyDerivationPathTemplate, i)
|
|
}
|
|
}
|
|
if r.URL.Query().Get("all") == "true" {
|
|
httputil.WriteJson(w, &ListAccountsResponse{
|
|
Accounts: accs,
|
|
TotalSize: int32(len(keys)),
|
|
NextPageToken: "",
|
|
})
|
|
return
|
|
}
|
|
start, end, nextPageToken, err := pagination.StartAndEndPage(pageToken, int(ps), len(keys))
|
|
if err != nil {
|
|
httputil.HandleError(w, fmt.Errorf("Could not paginate results: %v",
|
|
err).Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
httputil.WriteJson(w, &ListAccountsResponse{
|
|
Accounts: accs[start:end],
|
|
TotalSize: int32(len(keys)),
|
|
NextPageToken: nextPageToken,
|
|
})
|
|
}
|
|
|
|
// BackupAccounts creates a zip file containing EIP-2335 keystores for the user's
|
|
// specified public keys by encrypting them with the specified password.
|
|
func (s *Server) BackupAccounts(w http.ResponseWriter, r *http.Request) {
|
|
ctx, span := trace.StartSpan(r.Context(), "validator.web.accounts.ListAccounts")
|
|
defer span.End()
|
|
if s.validatorService == nil {
|
|
httputil.HandleError(w, "Validator service not ready.", http.StatusServiceUnavailable)
|
|
return
|
|
}
|
|
if !s.walletInitialized {
|
|
httputil.HandleError(w, "Prysm Wallet not initialized. Please create a new wallet.", http.StatusServiceUnavailable)
|
|
return
|
|
}
|
|
|
|
var req BackupAccountsRequest
|
|
err := json.NewDecoder(r.Body).Decode(&req)
|
|
switch {
|
|
case err == io.EOF:
|
|
httputil.HandleError(w, "No data submitted", http.StatusBadRequest)
|
|
return
|
|
case err != nil:
|
|
httputil.HandleError(w, "Could not decode request body: "+err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if req.PublicKeys == nil || len(req.PublicKeys) < 1 {
|
|
httputil.HandleError(w, "No public keys specified to backup", http.StatusBadRequest)
|
|
return
|
|
}
|
|
if req.BackupPassword == "" {
|
|
httputil.HandleError(w, "Backup password cannot be empty", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
km, err := s.validatorService.Keymanager()
|
|
if err != nil {
|
|
httputil.HandleError(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
pubKeys := make([]bls.PublicKey, len(req.PublicKeys))
|
|
for i, key := range req.PublicKeys {
|
|
byteskey, ok := shared.ValidateHex(w, "pubkey", key, fieldparams.BLSPubkeyLength)
|
|
if !ok {
|
|
return
|
|
}
|
|
pubKey, err := bls.PublicKeyFromBytes(byteskey)
|
|
if err != nil {
|
|
httputil.HandleError(w, errors.Wrap(err, fmt.Sprintf("%s Not a valid BLS public key", key)).Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
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 {
|
|
httputil.HandleError(w, errors.Wrap(err, "Could not backup accounts for local keymanager").Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
case *derived.Keymanager:
|
|
keystoresToBackup, err = km.ExtractKeystores(ctx, pubKeys, req.BackupPassword)
|
|
if err != nil {
|
|
httputil.HandleError(w, errors.Wrap(err, "Could not backup accounts for derived keymanager").Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
default:
|
|
httputil.HandleError(w, "Only HD or IMPORTED wallets can backup accounts", http.StatusBadRequest)
|
|
return
|
|
}
|
|
if len(keystoresToBackup) == 0 {
|
|
httputil.HandleError(w, "No keystores to backup", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
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")
|
|
}
|
|
httputil.HandleError(w, "could not marshal keystore to JSON file", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
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")
|
|
}
|
|
httputil.HandleError(w, "Could not write keystore file to zip", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if _, err = f.Write(encodedFile); err != nil {
|
|
if err := writer.Close(); err != nil {
|
|
log.WithError(err).Error("Could not close zip file after writing")
|
|
}
|
|
httputil.HandleError(w, "Could not write keystore file contents", http.StatusBadRequest)
|
|
return
|
|
}
|
|
}
|
|
if err := writer.Close(); err != nil {
|
|
log.WithError(err).Error("Could not close zip file after writing")
|
|
}
|
|
httputil.WriteJson(w, &BackupAccountsResponse{
|
|
ZipFile: base64.StdEncoding.EncodeToString(buf.Bytes()), // convert to base64 string for processing
|
|
})
|
|
}
|
|
|
|
// VoluntaryExit performs a voluntary exit for the validator keys specified in a request.
|
|
func (s *Server) VoluntaryExit(w http.ResponseWriter, r *http.Request) {
|
|
ctx, span := trace.StartSpan(r.Context(), "validator.web.accounts.VoluntaryExit")
|
|
defer span.End()
|
|
if s.validatorService == nil {
|
|
httputil.HandleError(w, "Validator service not ready.", http.StatusServiceUnavailable)
|
|
return
|
|
}
|
|
if !s.walletInitialized {
|
|
httputil.HandleError(w, "Prysm Wallet not initialized. Please create a new wallet.", http.StatusServiceUnavailable)
|
|
return
|
|
}
|
|
var req VoluntaryExitRequest
|
|
err := json.NewDecoder(r.Body).Decode(&req)
|
|
switch {
|
|
case err == io.EOF:
|
|
httputil.HandleError(w, "No data submitted", http.StatusBadRequest)
|
|
return
|
|
case err != nil:
|
|
httputil.HandleError(w, "Could not decode request body: "+err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
if len(req.PublicKeys) == 0 {
|
|
httputil.HandleError(w, "No public keys specified to delete", http.StatusBadRequest)
|
|
return
|
|
}
|
|
km, err := s.validatorService.Keymanager()
|
|
if err != nil {
|
|
httputil.HandleError(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
pubKeys := make([][]byte, len(req.PublicKeys))
|
|
for i, key := range req.PublicKeys {
|
|
byteskey, ok := shared.ValidateHex(w, "pubkey", key, fieldparams.BLSPubkeyLength)
|
|
if !ok {
|
|
return
|
|
}
|
|
pubKeys[i] = byteskey
|
|
}
|
|
cfg := accounts.PerformExitCfg{
|
|
ValidatorClient: s.beaconNodeValidatorClient,
|
|
NodeClient: s.beaconNodeClient,
|
|
Keymanager: km,
|
|
RawPubKeys: pubKeys,
|
|
FormattedPubKeys: req.PublicKeys,
|
|
}
|
|
rawExitedKeys, _, err := accounts.PerformVoluntaryExit(ctx, cfg)
|
|
if err != nil {
|
|
httputil.HandleError(w, errors.Wrap(err, "Could not perform voluntary exit").Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
httputil.WriteJson(w, &VoluntaryExitResponse{
|
|
ExitedKeys: rawExitedKeys,
|
|
})
|
|
}
|