mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 15:37:56 -05:00
Implement Validator Standard Key Manager API Delete Keystores (#9886)
* begin * implement delete and filter export history * rem deleted code * delete keystores all tests * gaz * test * double import fix * test * surface errors to user * add in changes * edit proto * edit * del * tests * gaz * slice * duplicate key found in request
This commit is contained in:
@@ -91,6 +91,7 @@ go_test(
|
||||
"//crypto/rand:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//io/file:go_default_library",
|
||||
"//proto/eth/service:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//proto/prysm/v1alpha1/validator-client:go_default_library",
|
||||
"//testing/assert:go_default_library",
|
||||
|
||||
@@ -2,18 +2,20 @@ package rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
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"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// ListKeystores implements the standard validator key management API.
|
||||
func (s Server) ListKeystores(
|
||||
func (s *Server) ListKeystores(
|
||||
ctx context.Context, _ *empty.Empty,
|
||||
) (*ethpbservice.ListKeystoresResponse, error) {
|
||||
if !s.walletInitialized {
|
||||
@@ -36,3 +38,41 @@ func (s Server) ListKeystores(
|
||||
Keystores: keystoreResponse,
|
||||
}, 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")
|
||||
}
|
||||
deleter, ok := s.keymanager.(keymanager.Deleter)
|
||||
if !ok {
|
||||
return nil, status.Error(codes.Internal, "Keymanager kind cannot delete keys")
|
||||
}
|
||||
statuses, err := deleter.DeleteKeystores(ctx, req.PublicKeys)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "Could not delete keys: %v", err)
|
||||
}
|
||||
keysToFilter := req.PublicKeys
|
||||
exportedHistory, err := slashingprotection.ExportStandardProtectionJSON(ctx, s.valDB, keysToFilter...)
|
||||
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 export slashing protection history: %v",
|
||||
err,
|
||||
)
|
||||
}
|
||||
return ðpbservice.DeleteKeystoresResponse{
|
||||
Statuses: statuses,
|
||||
SlashingProtection: string(jsonHist),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -2,17 +2,21 @@ package rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
ethpbservice "github.com/prysmaticlabs/prysm/proto/eth/service"
|
||||
validatorpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/validator-client"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/validator/accounts"
|
||||
"github.com/prysmaticlabs/prysm/validator/accounts/iface"
|
||||
"github.com/prysmaticlabs/prysm/validator/accounts/wallet"
|
||||
"github.com/prysmaticlabs/prysm/validator/db/kv"
|
||||
"github.com/prysmaticlabs/prysm/validator/keymanager"
|
||||
"github.com/prysmaticlabs/prysm/validator/keymanager/derived"
|
||||
constant "github.com/prysmaticlabs/prysm/validator/testing"
|
||||
mocks "github.com/prysmaticlabs/prysm/validator/testing"
|
||||
)
|
||||
|
||||
func TestServer_ListKeystores(t *testing.T) {
|
||||
@@ -52,7 +56,7 @@ func TestServer_ListKeystores(t *testing.T) {
|
||||
numAccounts := 50
|
||||
dr, ok := km.(*derived.Keymanager)
|
||||
require.Equal(t, true, ok)
|
||||
err = dr.RecoverAccountsFromMnemonic(ctx, constant.TestMnemonic, "", numAccounts)
|
||||
err = dr.RecoverAccountsFromMnemonic(ctx, mocks.TestMnemonic, "", numAccounts)
|
||||
require.NoError(t, err)
|
||||
expectedKeys, err := dr.FetchValidatingPublicKeys(ctx)
|
||||
require.NoError(t, err)
|
||||
@@ -71,3 +75,99 @@ func TestServer_ListKeystores(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestServer_DeleteKeystores(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
t.Run("wallet not ready", func(t *testing.T) {
|
||||
s := Server{}
|
||||
_, err := s.DeleteKeystores(context.Background(), nil)
|
||||
require.ErrorContains(t, "Wallet not ready", err)
|
||||
})
|
||||
localWalletDir := setupWalletDir(t)
|
||||
defaultWalletPath = localWalletDir
|
||||
w, err := accounts.CreateWalletWithKeymanager(ctx, &accounts.CreateWalletConfig{
|
||||
WalletCfg: &wallet.Config{
|
||||
WalletDir: defaultWalletPath,
|
||||
KeymanagerKind: keymanager.Derived,
|
||||
WalletPassword: strongPass,
|
||||
},
|
||||
SkipMnemonicConfirm: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false})
|
||||
require.NoError(t, err)
|
||||
|
||||
s := &Server{
|
||||
keymanager: km,
|
||||
walletInitialized: true,
|
||||
wallet: w,
|
||||
}
|
||||
numAccounts := 50
|
||||
dr, ok := km.(*derived.Keymanager)
|
||||
require.Equal(t, true, ok)
|
||||
err = dr.RecoverAccountsFromMnemonic(ctx, mocks.TestMnemonic, "", numAccounts)
|
||||
require.NoError(t, err)
|
||||
|
||||
publicKeys, err := km.FetchValidatingPublicKeys(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, numAccounts, len(publicKeys))
|
||||
|
||||
// Create a validator database.
|
||||
validatorDB, err := kv.NewKVStore(ctx, defaultWalletPath, &kv.Config{
|
||||
PubKeys: publicKeys,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
s.valDB = validatorDB
|
||||
|
||||
// Have to close it after import is done otherwise it complains db is not open.
|
||||
defer func() {
|
||||
require.NoError(t, validatorDB.Close())
|
||||
}()
|
||||
|
||||
// Generate mock slashing history.
|
||||
attestingHistory := make([][]*kv.AttestationRecord, 0)
|
||||
proposalHistory := make([]kv.ProposalHistoryForPubkey, len(publicKeys))
|
||||
for i := 0; i < len(publicKeys); i++ {
|
||||
proposalHistory[i].Proposals = make([]kv.Proposal, 0)
|
||||
}
|
||||
mockJSON, err := mocks.MockSlashingProtectionJSON(publicKeys, attestingHistory, proposalHistory)
|
||||
require.NoError(t, err)
|
||||
|
||||
// JSON encode the protection JSON and save it.
|
||||
encoded, err := json.Marshal(mockJSON)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = s.ImportSlashingProtection(ctx, &validatorpb.ImportSlashingProtectionRequest{
|
||||
SlashingProtectionJson: string(encoded),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
rawPubKeys := make([][]byte, numAccounts)
|
||||
for i := 0; i < numAccounts; i++ {
|
||||
rawPubKeys[i] = publicKeys[i][:]
|
||||
}
|
||||
|
||||
// Deletes properly and returns slashing protection history.
|
||||
resp, err := s.DeleteKeystores(ctx, ðpbservice.DeleteKeystoresRequest{
|
||||
PublicKeys: rawPubKeys,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, numAccounts, len(resp.Statuses))
|
||||
for _, status := range resp.Statuses {
|
||||
require.Equal(t, ethpbservice.DeletedKeystoreStatus_DELETED, status.Status)
|
||||
}
|
||||
publicKeys, err = km.FetchValidatingPublicKeys(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 0, len(publicKeys))
|
||||
require.Equal(t, numAccounts, len(mockJSON.Data))
|
||||
|
||||
// Returns slashing protection history if already deleted.
|
||||
resp, err = s.DeleteKeystores(ctx, ðpbservice.DeleteKeystoresRequest{
|
||||
PublicKeys: rawPubKeys,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, numAccounts, len(resp.Statuses))
|
||||
for _, status := range resp.Statuses {
|
||||
require.Equal(t, ethpbservice.DeletedKeystoreStatus_NOT_FOUND, status.Status)
|
||||
}
|
||||
require.Equal(t, numAccounts, len(mockJSON.Data))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user