prysm-pulse/validator/rpc/standard_api.go
james-prysm aed6e13498
Web3Signer: CLI implementation (#10056)
* initial commit for cli integration of web3signer

* resolving conflicts and execution

* remove aggregation slot from proto

* rem aggregation slot

* define a sync message block root struct

* fix sync message name

* sync message block root struct

* amend where sync committee block root is used

* altered switch statement to return correct json request by type

* fixing fork data import, types, and unit tests

* reverting unwanted changes

* reverting more unwanted changes

* fixing deepsource issues

* fixing formatting

* more fixes for deepsource and code clean up

* only want to fetch once for fetch validating public keys

* adding more comments

* new unit tests for requests and fixing a mapper issue

* Update validator/client/validator.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* Update validator/accounts/wallet/wallet.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* adjusting comment

* adjusting comment

* fixing import organization

* including more unit tests

* adding new cli edit

* adding in checks for wallet initialize

* adding web3signer flags to main.go

* some how resolved files did not save correctly

* adding in check to make sure web flag only works with types imported and derived

* Update validator/client/sync_committee.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* Update validator/client/aggregate.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* Update validator/accounts/wallet/wallet.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* Update cmd/validator/wallet/wallet.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* Update cmd/validator/wallet/wallet.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* Update cmd/validator/main.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* Update cmd/validator/flags/flags.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* Update cmd/validator/flags/flags.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* Update cmd/validator/wallet/wallet.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* Update cmd/validator/wallet/wallet.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* reverting changes that accidently got checked in

* reverting

* reverting

* continuing to revert unintenteded changes

* reverting

* removing more unneeded changes

* addressing review comment

* initial refactor

* adding in more clarifying comments

* fixing mock

* resolving desource issues

* addressing gosec scan for helper go file

* addressing gosec

* trying to fix bazel build

* removal of interface to fix build

* fixing maligned struct

* addressing deepsource

* fixing deepsource

* addressing efficiency of type checking

* fixing bazel test failure

* fixing go linter errors

* gaz

* web changes

* add w3signer

* new kind

* proper use

* align

* adding prysm validator flags to help flags list

* addressing root comment

* ci lint

* fixing standardapi tests

* fixing accounts_test after removal of keymanager from rpc server

* fixing more unit tests

* Update cmd/validator/flags/flags.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* Update cmd/validator/flags/flags.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* Update validator/client/service.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* Update validator/client/service.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* addressing missed err checks

* fixing mock tests

* fixing gofmt

* unskipping minimal e2e test and removing related TODOs

* Update testing/endtoend/components/validator.go

Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>

* Update testing/endtoend/components/validator.go

Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>

* adding some error wrapers to clarify failure point

* fixing bazel build with new error checks

* taking preston's advice to make test fail faster to understand what's going on with the test

* checking if genesis validators root is not zero hash

* adding check for genesis validators root giving zero hash

* fixing missing dependency

* adding check for wallet

* log all

* fixing errors for http responses

* switching marshal to pretty print

* adding pretty sign request test

* fixing base url setting

* adding in check for web3signer and temporary wallet instead of having to open the wallet

* refactoring web3signer to not require wallet

* bazel build fix

* fixing gazelle build

* adding content type of request

* fixing more bazel

* removing unused code

* removing unused comments

* adding skip test back in

* addressing a validation and error message

* fix parse

* body

* fixing logic for datadir

* improving error handling

* show resp

* fix

* sign resp as str

* point of pointer remove

* sign resp

* unmarshal sig resp

* read body as str

* adding more verbose logging

* removing unused result

* fixing unit test

* reconfiguring files to properly nest code and mocks

* fix build issue

* using context when using client function calls

* fixing based on suggestion

* addressing comments

* gaz

* removing defined max timeout

* reverting json print pretty

* Update validator/accounts/wallet_edit.go

Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>

* removing unneeded code restrictions

* should not introduce new code that may impact existing key manager types

* adjusting comments

* adding in json validation

* running go mod tidy

* some logging

* more logs

* fixing typo

* remove logs

* testing without byte trim

* fixing order or properties

* gaz

* tidy

* reverting some logs

* removing the confusing comments

* Update validator/client/aggregate.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* Update validator/client/aggregate.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* addressing pr comments

* editing bytes test

* Run gazelle update-repos

* run gazelle

* improving unit test coverage

* fixing text

* fixing a potential escaped error

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>
2022-01-31 10:44:17 -06:00

227 lines
8.4 KiB
Go

package rpc
import (
"bytes"
"context"
"encoding/json"
"fmt"
"github.com/golang/protobuf/ptypes/empty"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpbservice "github.com/prysmaticlabs/prysm/proto/eth/service"
"github.com/prysmaticlabs/prysm/validator/keymanager"
"github.com/prysmaticlabs/prysm/validator/keymanager/derived"
slashingprotection "github.com/prysmaticlabs/prysm/validator/slashing-protection-history"
"github.com/prysmaticlabs/prysm/validator/slashing-protection-history/format"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// ListKeystores implements the standard validator key management API.
func (s *Server) ListKeystores(
ctx context.Context, _ *empty.Empty,
) (*ethpbservice.ListKeystoresResponse, error) {
if !s.walletInitialized {
return nil, status.Error(codes.Internal, "Wallet not ready")
}
if s.validatorService == nil {
return nil, status.Error(codes.Internal, "Validator service not ready")
}
km, err := s.validatorService.Keymanager()
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get keymanager: %v", err)
}
pubKeys, err := km.FetchValidatingPublicKeys(ctx)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not list keystores: %v", err)
}
keystoreResponse := make([]*ethpbservice.ListKeystoresResponse_Keystore, len(pubKeys))
for i := 0; i < len(pubKeys); i++ {
keystoreResponse[i] = &ethpbservice.ListKeystoresResponse_Keystore{
ValidatingPubkey: pubKeys[i][:],
}
if s.wallet.KeymanagerKind() == keymanager.Derived {
keystoreResponse[i].DerivationPath = fmt.Sprintf(derived.ValidatingKeyDerivationPathTemplate, i)
}
}
return &ethpbservice.ListKeystoresResponse{
Keystores: keystoreResponse,
}, nil
}
// ImportKeystores allows for importing keystores into Prysm with their slashing protection history.
func (s *Server) ImportKeystores(
ctx context.Context, req *ethpbservice.ImportKeystoresRequest,
) (*ethpbservice.ImportKeystoresResponse, error) {
if !s.walletInitialized {
return nil, status.Error(codes.Internal, "Wallet not ready")
}
if s.validatorService == nil {
return nil, status.Error(codes.Internal, "Validator service not ready")
}
km, err := s.validatorService.Keymanager()
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get keymanager: %v", err)
}
importer, ok := km.(keymanager.Importer)
if !ok {
return nil, status.Error(codes.Internal, "Keymanager kind cannot import keys")
}
if len(req.Passwords) == 0 {
return nil, status.Error(codes.Internal, "No passwords provided for keystores")
}
if len(req.Passwords) != len(req.Keystores) {
return nil, status.Error(codes.Internal, "Number of passwords does not match number of keystores")
}
keystores := make([]*keymanager.Keystore, len(req.Keystores))
for i := 0; i < len(req.Keystores); i++ {
k := &keymanager.Keystore{}
if err := json.Unmarshal([]byte(req.Keystores[i]), k); err != nil {
return nil, status.Errorf(
codes.Internal, "Invalid keystore at index %d in request: %v", i, err,
)
}
keystores[i] = k
}
if req.SlashingProtection != "" {
if err := slashingprotection.ImportStandardProtectionJSON(
ctx, s.valDB, bytes.NewBuffer([]byte(req.SlashingProtection)),
); err != nil {
statuses := make([]*ethpbservice.ImportedKeystoreStatus, len(req.Keystores))
for i := range statuses {
statuses[i] = &ethpbservice.ImportedKeystoreStatus{
Status: ethpbservice.ImportedKeystoreStatus_ERROR,
Message: fmt.Sprintf("could not import slashing protection: %v", err),
}
}
return &ethpbservice.ImportKeystoresResponse{Statuses: statuses}, nil
}
}
statuses, err := importer.ImportKeystores(ctx, keystores, req.Passwords)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not import keystores: %v", err)
}
// If any of the keys imported had a slashing protection history before, we
// stop marking them as deleted from our validator database.
return &ethpbservice.ImportKeystoresResponse{Statuses: statuses}, nil
}
// DeleteKeystores allows for deleting specified public keys from Prysm.
func (s *Server) DeleteKeystores(
ctx context.Context, req *ethpbservice.DeleteKeystoresRequest,
) (*ethpbservice.DeleteKeystoresResponse, error) {
if !s.walletInitialized {
return nil, status.Error(codes.Internal, "Wallet not ready")
}
if s.validatorService == nil {
return nil, status.Error(codes.Internal, "Validator service not ready")
}
km, err := s.validatorService.Keymanager()
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get keymanager: %v", err)
}
deleter, ok := km.(keymanager.Deleter)
if !ok {
return nil, status.Error(codes.Internal, "Keymanager kind cannot delete keys")
}
if len(req.PublicKeys) == 0 {
return &ethpbservice.DeleteKeystoresResponse{Statuses: make([]*ethpbservice.DeletedKeystoreStatus, 0)}, nil
}
statuses, err := deleter.DeleteKeystores(ctx, req.PublicKeys)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not delete keys: %v", err)
}
if len(statuses) != len(req.PublicKeys) {
return nil, status.Errorf(
codes.Internal,
"Wanted same amount of statuses %d as public keys %d",
len(statuses),
len(req.PublicKeys),
)
}
statuses, err = s.transformDeletedKeysStatuses(ctx, req.PublicKeys, statuses)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not transform deleted keys statuses: %v", err)
}
exportedHistory, err := s.slashingProtectionHistoryForDeletedKeys(ctx, req.PublicKeys, statuses)
if err != nil {
return nil, status.Errorf(
codes.Internal,
"Could not export slashing protection history: %v",
err,
)
}
jsonHist, err := json.Marshal(exportedHistory)
if err != nil {
return nil, status.Errorf(
codes.Internal,
"Could not JSON marshal slashing protection history: %v",
err,
)
}
return &ethpbservice.DeleteKeystoresResponse{
Statuses: statuses,
SlashingProtection: string(jsonHist),
}, nil
}
// For a list of deleted keystore statuses, we check if any NOT_FOUND status actually
// has a corresponding public key in the database. In this case, we transform the status
// to NOT_ACTIVE, as we do have slashing protection history for it and should not mark it
// as NOT_FOUND when returning a response to the caller.
func (s *Server) transformDeletedKeysStatuses(
ctx context.Context, pubKeys [][]byte, statuses []*ethpbservice.DeletedKeystoreStatus,
) ([]*ethpbservice.DeletedKeystoreStatus, error) {
pubKeysInDB, err := s.publicKeysInDB(ctx)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get public keys from DB: %v", err)
}
if len(pubKeysInDB) > 0 {
for i := 0; i < len(pubKeys); i++ {
keyExistsInDB := pubKeysInDB[bytesutil.ToBytes48(pubKeys[i])]
if keyExistsInDB && statuses[i].Status == ethpbservice.DeletedKeystoreStatus_NOT_FOUND {
statuses[i].Status = ethpbservice.DeletedKeystoreStatus_NOT_ACTIVE
}
}
}
return statuses, nil
}
// Gets a map of all public keys in the database, useful for O(1) lookups.
func (s *Server) publicKeysInDB(ctx context.Context) (map[[fieldparams.BLSPubkeyLength]byte]bool, error) {
pubKeysInDB := make(map[[fieldparams.BLSPubkeyLength]byte]bool)
attestedPublicKeys, err := s.valDB.AttestedPublicKeys(ctx)
if err != nil {
return nil, fmt.Errorf("could not get attested public keys from DB: %v", err)
}
proposedPublicKeys, err := s.valDB.ProposedPublicKeys(ctx)
if err != nil {
return nil, fmt.Errorf("could not get proposed public keys from DB: %v", err)
}
for _, pk := range append(attestedPublicKeys, proposedPublicKeys...) {
pubKeysInDB[pk] = true
}
return pubKeysInDB, nil
}
// Exports slashing protection data for a list of DELETED or NOT_ACTIVE keys only to be used
// as part of the DeleteKeystores endpoint.
func (s *Server) slashingProtectionHistoryForDeletedKeys(
ctx context.Context, pubKeys [][]byte, statuses []*ethpbservice.DeletedKeystoreStatus,
) (*format.EIPSlashingProtectionFormat, error) {
// We select the keys that were DELETED or NOT_ACTIVE from the previous action
// and use that to filter our slashing protection export.
filteredKeys := make([][]byte, 0, len(pubKeys))
for i, pk := range pubKeys {
if statuses[i].Status == ethpbservice.DeletedKeystoreStatus_DELETED ||
statuses[i].Status == ethpbservice.DeletedKeystoreStatus_NOT_ACTIVE {
filteredKeys = append(filteredKeys, pk)
}
}
return slashingprotection.ExportStandardProtectionJSON(ctx, s.valDB, filteredKeys...)
}