mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-23 03:51:29 +00:00
d0c740f477
* WIP * WIP * adding in migration function * updating mock validator and gaz * adding descriptive logs * fixing mocking * fixing tests * fixing mock * adding changes to handle enable builder settings * fixing tests and edge case * reduce cognative complexity of function * further reducing cognative complexity on function * WIP * fixing unit test on migration * adding more tests * gaz and fix unit test * fixing deepsource issues * fixing more deesource issues missed previously * removing unused reciever name * WIP fix to migration logic * fixing loging info * reverting migration logic, converting logic to address issues discussed on slack, adding unit tests * adding test for builder setting only not saved to db * addressing comment * fixing flag * removing accidently missed deprecated flags * rolling back mock on pr * fixing fmt linting * updating comments based on feedback * Update config/features/flags.go Co-authored-by: Sammy Rosso <15244892+saolyn@users.noreply.github.com> * fixing based on feedback on PR * Update config/validator/service/proposer_settings.go Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com> * Update validator/client/runner.go Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com> * Update validator/db/kv/proposer_settings.go Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com> * adding additional logs to clear up some steps based on feedback * fixing log * deepsource * adding comments based on review feedback --------- Co-authored-by: Raul Jordan <raul@prysmaticlabs.com> Co-authored-by: Sammy Rosso <15244892+saolyn@users.noreply.github.com> Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com>
1667 lines
55 KiB
Go
1667 lines
55 KiB
Go
package rpc
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
"github.com/golang/mock/gomock"
|
|
"github.com/golang/protobuf/ptypes/empty"
|
|
"github.com/google/uuid"
|
|
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
|
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
|
|
"github.com/prysmaticlabs/prysm/v4/config/params"
|
|
validatorserviceconfig "github.com/prysmaticlabs/prysm/v4/config/validator/service"
|
|
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
|
"github.com/prysmaticlabs/prysm/v4/consensus-types/validator"
|
|
"github.com/prysmaticlabs/prysm/v4/crypto/bls"
|
|
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
|
|
ethpbservice "github.com/prysmaticlabs/prysm/v4/proto/eth/service"
|
|
eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
|
validatorpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1/validator-client"
|
|
"github.com/prysmaticlabs/prysm/v4/testing/assert"
|
|
"github.com/prysmaticlabs/prysm/v4/testing/require"
|
|
validatormock "github.com/prysmaticlabs/prysm/v4/testing/validator-mock"
|
|
"github.com/prysmaticlabs/prysm/v4/validator/accounts"
|
|
"github.com/prysmaticlabs/prysm/v4/validator/accounts/iface"
|
|
mock "github.com/prysmaticlabs/prysm/v4/validator/accounts/testing"
|
|
"github.com/prysmaticlabs/prysm/v4/validator/accounts/wallet"
|
|
"github.com/prysmaticlabs/prysm/v4/validator/client"
|
|
"github.com/prysmaticlabs/prysm/v4/validator/db/kv"
|
|
dbtest "github.com/prysmaticlabs/prysm/v4/validator/db/testing"
|
|
"github.com/prysmaticlabs/prysm/v4/validator/keymanager"
|
|
"github.com/prysmaticlabs/prysm/v4/validator/keymanager/derived"
|
|
remoteweb3signer "github.com/prysmaticlabs/prysm/v4/validator/keymanager/remote-web3signer"
|
|
"github.com/prysmaticlabs/prysm/v4/validator/slashing-protection-history/format"
|
|
mocks "github.com/prysmaticlabs/prysm/v4/validator/testing"
|
|
keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4"
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/protobuf/types/known/emptypb"
|
|
"google.golang.org/protobuf/types/known/timestamppb"
|
|
)
|
|
|
|
func TestServer_ListKeystores(t *testing.T) {
|
|
t.Run("wallet not ready", func(t *testing.T) {
|
|
s := Server{}
|
|
_, err := s.ListKeystores(context.Background(), &empty.Empty{})
|
|
require.ErrorContains(t, "Prysm Wallet not initialized. Please create a new wallet.", err)
|
|
})
|
|
ctx := context.Background()
|
|
localWalletDir := setupWalletDir(t)
|
|
defaultWalletPath = localWalletDir
|
|
opts := []accounts.Option{
|
|
accounts.WithWalletDir(defaultWalletPath),
|
|
accounts.WithKeymanagerType(keymanager.Derived),
|
|
accounts.WithWalletPassword(strongPass),
|
|
accounts.WithSkipMnemonicConfirm(true),
|
|
}
|
|
acc, err := accounts.NewCLIManager(opts...)
|
|
require.NoError(t, err)
|
|
w, err := acc.WalletCreate(ctx)
|
|
require.NoError(t, err)
|
|
km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false})
|
|
require.NoError(t, err)
|
|
vs, err := client.NewValidatorService(ctx, &client.Config{
|
|
Wallet: w,
|
|
Validator: &mock.MockValidator{
|
|
Km: km,
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
s := &Server{
|
|
walletInitialized: true,
|
|
wallet: w,
|
|
validatorService: vs,
|
|
}
|
|
numAccounts := 50
|
|
dr, ok := km.(*derived.Keymanager)
|
|
require.Equal(t, true, ok)
|
|
err = dr.RecoverAccountsFromMnemonic(ctx, mocks.TestMnemonic, derived.DefaultMnemonicLanguage, "", numAccounts)
|
|
require.NoError(t, err)
|
|
expectedKeys, err := dr.FetchValidatingPublicKeys(ctx)
|
|
require.NoError(t, err)
|
|
|
|
t.Run("returns proper data with existing keystores", func(t *testing.T) {
|
|
resp, err := s.ListKeystores(context.Background(), &empty.Empty{})
|
|
require.NoError(t, err)
|
|
require.Equal(t, numAccounts, len(resp.Data))
|
|
for i := 0; i < numAccounts; i++ {
|
|
require.DeepEqual(t, expectedKeys[i][:], resp.Data[i].ValidatingPubkey)
|
|
require.Equal(
|
|
t,
|
|
fmt.Sprintf(derived.ValidatingKeyDerivationPathTemplate, i),
|
|
resp.Data[i].DerivationPath,
|
|
)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestServer_ImportKeystores(t *testing.T) {
|
|
t.Run("wallet not ready", func(t *testing.T) {
|
|
s := Server{}
|
|
response, err := s.ImportKeystores(context.Background(), ðpbservice.ImportKeystoresRequest{})
|
|
require.NoError(t, err)
|
|
require.Equal(t, 0, len(response.Data))
|
|
})
|
|
ctx := context.Background()
|
|
localWalletDir := setupWalletDir(t)
|
|
defaultWalletPath = localWalletDir
|
|
opts := []accounts.Option{
|
|
accounts.WithWalletDir(defaultWalletPath),
|
|
accounts.WithKeymanagerType(keymanager.Derived),
|
|
accounts.WithWalletPassword(strongPass),
|
|
accounts.WithSkipMnemonicConfirm(true),
|
|
}
|
|
acc, err := accounts.NewCLIManager(opts...)
|
|
require.NoError(t, err)
|
|
w, err := acc.WalletCreate(ctx)
|
|
require.NoError(t, err)
|
|
km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false})
|
|
require.NoError(t, err)
|
|
vs, err := client.NewValidatorService(ctx, &client.Config{
|
|
Wallet: w,
|
|
Validator: &mock.MockValidator{
|
|
Km: km,
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
s := &Server{
|
|
walletInitialized: true,
|
|
wallet: w,
|
|
validatorService: vs,
|
|
}
|
|
t.Run("200 response even if faulty keystore in request", func(t *testing.T) {
|
|
response, err := s.ImportKeystores(context.Background(), ðpbservice.ImportKeystoresRequest{
|
|
Keystores: []string{"hi"},
|
|
Passwords: []string{"hi"},
|
|
})
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(response.Data))
|
|
require.Equal(t, ethpbservice.ImportedKeystoreStatus_ERROR, response.Data[0].Status)
|
|
})
|
|
t.Run("200 response even if no passwords in request", func(t *testing.T) {
|
|
response, err := s.ImportKeystores(context.Background(), ðpbservice.ImportKeystoresRequest{
|
|
Keystores: []string{"hi"},
|
|
Passwords: []string{},
|
|
})
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(response.Data))
|
|
require.Equal(t, ethpbservice.ImportedKeystoreStatus_ERROR, response.Data[0].Status)
|
|
})
|
|
t.Run("200 response even if keystores more than passwords in request", func(t *testing.T) {
|
|
response, err := s.ImportKeystores(context.Background(), ðpbservice.ImportKeystoresRequest{
|
|
Keystores: []string{"hi", "hi"},
|
|
Passwords: []string{"hi"},
|
|
})
|
|
require.NoError(t, err)
|
|
require.Equal(t, 2, len(response.Data))
|
|
require.Equal(t, ethpbservice.ImportedKeystoreStatus_ERROR, response.Data[0].Status)
|
|
})
|
|
t.Run("200 response even if number of passwords does not match number of keystores", func(t *testing.T) {
|
|
response, err := s.ImportKeystores(context.Background(), ðpbservice.ImportKeystoresRequest{
|
|
Keystores: []string{"hi"},
|
|
Passwords: []string{"hi", "hi"},
|
|
})
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(response.Data))
|
|
require.Equal(t, ethpbservice.ImportedKeystoreStatus_ERROR, response.Data[0].Status)
|
|
})
|
|
t.Run("200 response even if faulty slashing protection data", func(t *testing.T) {
|
|
numKeystores := 5
|
|
password := "12345678"
|
|
encodedKeystores := make([]string, numKeystores)
|
|
passwords := make([]string, numKeystores)
|
|
for i := 0; i < numKeystores; i++ {
|
|
enc, err := json.Marshal(createRandomKeystore(t, password))
|
|
encodedKeystores[i] = string(enc)
|
|
require.NoError(t, err)
|
|
passwords[i] = password
|
|
}
|
|
resp, err := s.ImportKeystores(context.Background(), ðpbservice.ImportKeystoresRequest{
|
|
Keystores: encodedKeystores,
|
|
Passwords: passwords,
|
|
SlashingProtection: "foobar",
|
|
})
|
|
require.NoError(t, err)
|
|
require.Equal(t, numKeystores, len(resp.Data))
|
|
for _, st := range resp.Data {
|
|
require.Equal(t, ethpbservice.ImportedKeystoreStatus_ERROR, st.Status)
|
|
}
|
|
})
|
|
t.Run("returns proper statuses for keystores in request", func(t *testing.T) {
|
|
numKeystores := 5
|
|
password := "12345678"
|
|
keystores := make([]*keymanager.Keystore, numKeystores)
|
|
passwords := make([]string, numKeystores)
|
|
publicKeys := make([][fieldparams.BLSPubkeyLength]byte, numKeystores)
|
|
for i := 0; i < numKeystores; i++ {
|
|
keystores[i] = createRandomKeystore(t, password)
|
|
pubKey, err := hex.DecodeString(keystores[i].Pubkey)
|
|
require.NoError(t, err)
|
|
publicKeys[i] = bytesutil.ToBytes48(pubKey)
|
|
passwords[i] = password
|
|
}
|
|
|
|
// 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())
|
|
}()
|
|
encodedKeystores := make([]string, numKeystores)
|
|
for i := 0; i < numKeystores; i++ {
|
|
enc, err := json.Marshal(keystores[i])
|
|
require.NoError(t, err)
|
|
encodedKeystores[i] = string(enc)
|
|
}
|
|
|
|
// 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.
|
|
encodedSlashingProtection, err := json.Marshal(mockJSON)
|
|
require.NoError(t, err)
|
|
|
|
resp, err := s.ImportKeystores(context.Background(), ðpbservice.ImportKeystoresRequest{
|
|
Keystores: encodedKeystores,
|
|
Passwords: passwords,
|
|
SlashingProtection: string(encodedSlashingProtection),
|
|
})
|
|
require.NoError(t, err)
|
|
require.Equal(t, numKeystores, len(resp.Data))
|
|
for _, status := range resp.Data {
|
|
require.Equal(t, ethpbservice.ImportedKeystoreStatus_IMPORTED, status.Status)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestServer_ImportKeystores_WrongKeymanagerKind(t *testing.T) {
|
|
ctx := context.Background()
|
|
w := wallet.NewWalletForWeb3Signer()
|
|
root := make([]byte, fieldparams.RootLength)
|
|
root[0] = 1
|
|
km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false, Web3SignerConfig: &remoteweb3signer.SetupConfig{
|
|
BaseEndpoint: "http://example.com",
|
|
GenesisValidatorsRoot: root,
|
|
PublicKeysURL: "http://example.com/public_keys",
|
|
}})
|
|
require.NoError(t, err)
|
|
vs, err := client.NewValidatorService(ctx, &client.Config{
|
|
Wallet: w,
|
|
Validator: &mock.MockValidator{
|
|
Km: km,
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
s := &Server{
|
|
walletInitialized: true,
|
|
wallet: w,
|
|
validatorService: vs,
|
|
}
|
|
response, err := s.ImportKeystores(context.Background(), ðpbservice.ImportKeystoresRequest{
|
|
Keystores: []string{"hi"},
|
|
Passwords: []string{"hi"},
|
|
})
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(response.Data))
|
|
require.Equal(t, ethpbservice.ImportedKeystoreStatus_ERROR, response.Data[0].Status)
|
|
require.Equal(t, "Keymanager kind cannot import keys", response.Data[0].Message)
|
|
}
|
|
|
|
func TestServer_DeleteKeystores(t *testing.T) {
|
|
t.Run("wallet not ready", func(t *testing.T) {
|
|
s := Server{}
|
|
response, err := s.DeleteKeystores(context.Background(), ðpbservice.DeleteKeystoresRequest{})
|
|
require.NoError(t, err)
|
|
require.Equal(t, 0, len(response.Data))
|
|
})
|
|
ctx := context.Background()
|
|
srv := setupServerWithWallet(t)
|
|
|
|
// We recover 3 accounts from a test mnemonic.
|
|
numAccounts := 3
|
|
km, er := srv.validatorService.Keymanager()
|
|
require.NoError(t, er)
|
|
dr, ok := km.(*derived.Keymanager)
|
|
require.Equal(t, true, ok)
|
|
err := dr.RecoverAccountsFromMnemonic(ctx, mocks.TestMnemonic, derived.DefaultMnemonicLanguage, "", numAccounts)
|
|
require.NoError(t, err)
|
|
publicKeys, err := dr.FetchValidatingPublicKeys(ctx)
|
|
require.NoError(t, err)
|
|
|
|
// Create a validator database.
|
|
validatorDB, err := kv.NewKVStore(ctx, defaultWalletPath, &kv.Config{
|
|
PubKeys: publicKeys,
|
|
})
|
|
require.NoError(t, err)
|
|
srv.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 = srv.ImportSlashingProtection(ctx, &validatorpb.ImportSlashingProtectionRequest{
|
|
SlashingProtectionJson: string(encoded),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
t.Run("no slashing protection response if no keys in request even if we have a history in DB", func(t *testing.T) {
|
|
resp, err := srv.DeleteKeystores(context.Background(), ðpbservice.DeleteKeystoresRequest{
|
|
Pubkeys: nil,
|
|
})
|
|
require.NoError(t, err)
|
|
require.Equal(t, "", resp.SlashingProtection)
|
|
})
|
|
|
|
// For ease of test setup, we'll give each public key a string identifier.
|
|
publicKeysWithId := map[string][fieldparams.BLSPubkeyLength]byte{
|
|
"a": publicKeys[0],
|
|
"b": publicKeys[1],
|
|
"c": publicKeys[2],
|
|
}
|
|
|
|
type keyCase struct {
|
|
id string
|
|
wantProtectionData bool
|
|
}
|
|
tests := []struct {
|
|
keys []*keyCase
|
|
wantStatuses []ethpbservice.DeletedKeystoreStatus_Status
|
|
}{
|
|
{
|
|
keys: []*keyCase{
|
|
{id: "a", wantProtectionData: true},
|
|
{id: "a", wantProtectionData: true},
|
|
{id: "d"},
|
|
{id: "c", wantProtectionData: true},
|
|
},
|
|
wantStatuses: []ethpbservice.DeletedKeystoreStatus_Status{
|
|
ethpbservice.DeletedKeystoreStatus_DELETED,
|
|
ethpbservice.DeletedKeystoreStatus_NOT_ACTIVE,
|
|
ethpbservice.DeletedKeystoreStatus_NOT_FOUND,
|
|
ethpbservice.DeletedKeystoreStatus_DELETED,
|
|
},
|
|
},
|
|
{
|
|
keys: []*keyCase{
|
|
{id: "a", wantProtectionData: true},
|
|
{id: "c", wantProtectionData: true},
|
|
},
|
|
wantStatuses: []ethpbservice.DeletedKeystoreStatus_Status{
|
|
ethpbservice.DeletedKeystoreStatus_NOT_ACTIVE,
|
|
ethpbservice.DeletedKeystoreStatus_NOT_ACTIVE,
|
|
},
|
|
},
|
|
{
|
|
keys: []*keyCase{
|
|
{id: "x"},
|
|
},
|
|
wantStatuses: []ethpbservice.DeletedKeystoreStatus_Status{
|
|
ethpbservice.DeletedKeystoreStatus_NOT_FOUND,
|
|
},
|
|
},
|
|
}
|
|
for _, tc := range tests {
|
|
keys := make([][]byte, len(tc.keys))
|
|
for i := 0; i < len(tc.keys); i++ {
|
|
pk := publicKeysWithId[tc.keys[i].id]
|
|
keys[i] = pk[:]
|
|
}
|
|
resp, err := srv.DeleteKeystores(ctx, ðpbservice.DeleteKeystoresRequest{Pubkeys: keys})
|
|
require.NoError(t, err)
|
|
require.Equal(t, len(keys), len(resp.Data))
|
|
slashingProtectionData := &format.EIPSlashingProtectionFormat{}
|
|
require.NoError(t, json.Unmarshal([]byte(resp.SlashingProtection), slashingProtectionData))
|
|
require.Equal(t, true, len(slashingProtectionData.Data) > 0)
|
|
|
|
for i := 0; i < len(tc.keys); i++ {
|
|
require.Equal(
|
|
t,
|
|
tc.wantStatuses[i],
|
|
resp.Data[i].Status,
|
|
fmt.Sprintf("Checking status for key %s", tc.keys[i].id),
|
|
)
|
|
if tc.keys[i].wantProtectionData {
|
|
// We check that we can find the key in the slashing protection data.
|
|
var found bool
|
|
for _, dt := range slashingProtectionData.Data {
|
|
if dt.Pubkey == fmt.Sprintf("%#x", keys[i]) {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
require.Equal(t, true, found)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestServer_DeleteKeystores_FailedSlashingProtectionExport(t *testing.T) {
|
|
ctx := context.Background()
|
|
srv := setupServerWithWallet(t)
|
|
|
|
// We recover 3 accounts from a test mnemonic.
|
|
numAccounts := 3
|
|
km, er := srv.validatorService.Keymanager()
|
|
require.NoError(t, er)
|
|
dr, ok := km.(*derived.Keymanager)
|
|
require.Equal(t, true, ok)
|
|
err := dr.RecoverAccountsFromMnemonic(ctx, mocks.TestMnemonic, derived.DefaultMnemonicLanguage, "", numAccounts)
|
|
require.NoError(t, err)
|
|
publicKeys, err := dr.FetchValidatingPublicKeys(ctx)
|
|
require.NoError(t, err)
|
|
|
|
// Create a validator database.
|
|
validatorDB, err := kv.NewKVStore(ctx, defaultWalletPath, &kv.Config{
|
|
PubKeys: publicKeys,
|
|
})
|
|
require.NoError(t, err)
|
|
err = validatorDB.SaveGenesisValidatorsRoot(ctx, make([]byte, fieldparams.RootLength))
|
|
require.NoError(t, err)
|
|
srv.valDB = validatorDB
|
|
|
|
// Have to close it after import is done otherwise it complains db is not open.
|
|
defer func() {
|
|
require.NoError(t, validatorDB.Close())
|
|
}()
|
|
|
|
response, err := srv.DeleteKeystores(context.Background(), ðpbservice.DeleteKeystoresRequest{
|
|
Pubkeys: [][]byte{[]byte("a")},
|
|
})
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, len(response.Data))
|
|
require.Equal(t, ethpbservice.DeletedKeystoreStatus_ERROR, response.Data[0].Status)
|
|
require.Equal(t, "Non duplicate keys that were existing were deleted, but could not export slashing protection history.",
|
|
response.Data[0].Message,
|
|
)
|
|
}
|
|
|
|
func TestServer_DeleteKeystores_WrongKeymanagerKind(t *testing.T) {
|
|
ctx := context.Background()
|
|
w := wallet.NewWalletForWeb3Signer()
|
|
root := make([]byte, fieldparams.RootLength)
|
|
root[0] = 1
|
|
km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false,
|
|
Web3SignerConfig: &remoteweb3signer.SetupConfig{
|
|
BaseEndpoint: "http://example.com",
|
|
GenesisValidatorsRoot: root,
|
|
PublicKeysURL: "http://example.com/public_keys",
|
|
}})
|
|
require.NoError(t, err)
|
|
vs, err := client.NewValidatorService(ctx, &client.Config{
|
|
Wallet: w,
|
|
Validator: &mock.MockValidator{
|
|
Km: km,
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
s := &Server{
|
|
walletInitialized: true,
|
|
wallet: w,
|
|
validatorService: vs,
|
|
}
|
|
_, err = s.DeleteKeystores(ctx, ðpbservice.DeleteKeystoresRequest{Pubkeys: [][]byte{[]byte("a")}})
|
|
require.ErrorContains(t, "Wrong wallet type", err)
|
|
require.ErrorContains(t, "Only Imported or Derived wallets can delete accounts", err)
|
|
}
|
|
|
|
func setupServerWithWallet(t testing.TB) *Server {
|
|
ctx := context.Background()
|
|
localWalletDir := setupWalletDir(t)
|
|
defaultWalletPath = localWalletDir
|
|
opts := []accounts.Option{
|
|
accounts.WithWalletDir(defaultWalletPath),
|
|
accounts.WithKeymanagerType(keymanager.Derived),
|
|
accounts.WithWalletPassword(strongPass),
|
|
accounts.WithSkipMnemonicConfirm(true),
|
|
}
|
|
acc, err := accounts.NewCLIManager(opts...)
|
|
require.NoError(t, err)
|
|
w, err := acc.WalletCreate(ctx)
|
|
require.NoError(t, err)
|
|
km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false})
|
|
require.NoError(t, err)
|
|
vs, err := client.NewValidatorService(ctx, &client.Config{
|
|
Wallet: w,
|
|
Validator: &mock.MockValidator{
|
|
Km: km,
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
return &Server{
|
|
walletInitialized: true,
|
|
wallet: w,
|
|
validatorService: vs,
|
|
}
|
|
}
|
|
|
|
func createRandomKeystore(t testing.TB, password string) *keymanager.Keystore {
|
|
encryptor := keystorev4.New()
|
|
id, err := uuid.NewRandom()
|
|
require.NoError(t, err)
|
|
validatingKey, err := bls.RandKey()
|
|
require.NoError(t, err)
|
|
pubKey := validatingKey.PublicKey().Marshal()
|
|
cryptoFields, err := encryptor.Encrypt(validatingKey.Marshal(), password)
|
|
require.NoError(t, err)
|
|
return &keymanager.Keystore{
|
|
Crypto: cryptoFields,
|
|
Pubkey: fmt.Sprintf("%x", pubKey),
|
|
ID: id.String(),
|
|
Version: encryptor.Version(),
|
|
Description: encryptor.Name(),
|
|
}
|
|
}
|
|
|
|
func TestServer_ListRemoteKeys(t *testing.T) {
|
|
t.Run("wallet not ready", func(t *testing.T) {
|
|
s := Server{}
|
|
_, err := s.ListKeystores(context.Background(), &empty.Empty{})
|
|
require.ErrorContains(t, "Prysm Wallet not initialized. Please create a new wallet.", err)
|
|
})
|
|
ctx := context.Background()
|
|
w := wallet.NewWalletForWeb3Signer()
|
|
root := make([]byte, fieldparams.RootLength)
|
|
root[0] = 1
|
|
bytevalue, err := hexutil.Decode("0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a")
|
|
require.NoError(t, err)
|
|
pubkeys := [][fieldparams.BLSPubkeyLength]byte{bytesutil.ToBytes48(bytevalue)}
|
|
config := &remoteweb3signer.SetupConfig{
|
|
BaseEndpoint: "http://example.com",
|
|
GenesisValidatorsRoot: root,
|
|
ProvidedPublicKeys: pubkeys,
|
|
}
|
|
km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false, Web3SignerConfig: config})
|
|
require.NoError(t, err)
|
|
vs, err := client.NewValidatorService(ctx, &client.Config{
|
|
Wallet: w,
|
|
Validator: &mock.MockValidator{
|
|
Km: km,
|
|
},
|
|
Web3SignerConfig: config,
|
|
})
|
|
require.NoError(t, err)
|
|
s := &Server{
|
|
walletInitialized: true,
|
|
wallet: w,
|
|
validatorService: vs,
|
|
}
|
|
expectedKeys, err := km.FetchValidatingPublicKeys(ctx)
|
|
require.NoError(t, err)
|
|
|
|
t.Run("returns proper data with existing pub keystores", func(t *testing.T) {
|
|
resp, err := s.ListRemoteKeys(context.Background(), &empty.Empty{})
|
|
require.NoError(t, err)
|
|
for i := 0; i < len(resp.Data); i++ {
|
|
require.DeepEqual(t, expectedKeys[i][:], resp.Data[i].Pubkey)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestServer_ImportRemoteKeys(t *testing.T) {
|
|
t.Run("wallet not ready", func(t *testing.T) {
|
|
s := Server{}
|
|
_, err := s.ListKeystores(context.Background(), &empty.Empty{})
|
|
require.ErrorContains(t, "Prysm Wallet not initialized. Please create a new wallet.", err)
|
|
})
|
|
ctx := context.Background()
|
|
w := wallet.NewWalletForWeb3Signer()
|
|
root := make([]byte, fieldparams.RootLength)
|
|
root[0] = 1
|
|
config := &remoteweb3signer.SetupConfig{
|
|
BaseEndpoint: "http://example.com",
|
|
GenesisValidatorsRoot: root,
|
|
ProvidedPublicKeys: nil,
|
|
}
|
|
km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false, Web3SignerConfig: config})
|
|
require.NoError(t, err)
|
|
vs, err := client.NewValidatorService(ctx, &client.Config{
|
|
Wallet: w,
|
|
Validator: &mock.MockValidator{
|
|
Km: km,
|
|
},
|
|
Web3SignerConfig: config,
|
|
})
|
|
require.NoError(t, err)
|
|
s := &Server{
|
|
walletInitialized: true,
|
|
wallet: w,
|
|
validatorService: vs,
|
|
}
|
|
bytevalue, err := hexutil.Decode("0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a")
|
|
require.NoError(t, err)
|
|
remoteKeys := []*ethpbservice.ImportRemoteKeysRequest_Keystore{
|
|
{
|
|
Pubkey: bytevalue,
|
|
},
|
|
}
|
|
|
|
t.Run("returns proper data with existing pub keystores", func(t *testing.T) {
|
|
resp, err := s.ImportRemoteKeys(context.Background(), ðpbservice.ImportRemoteKeysRequest{
|
|
RemoteKeys: remoteKeys,
|
|
})
|
|
expectedStatuses := []*ethpbservice.ImportedRemoteKeysStatus{
|
|
{
|
|
Status: ethpbservice.ImportedRemoteKeysStatus_IMPORTED,
|
|
Message: fmt.Sprintf("Successfully added pubkey: %v", hexutil.Encode(bytevalue)),
|
|
},
|
|
}
|
|
require.NoError(t, err)
|
|
for i := 0; i < len(resp.Data); i++ {
|
|
require.DeepEqual(t, expectedStatuses[i], resp.Data[i])
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestServer_DeleteRemoteKeys(t *testing.T) {
|
|
t.Run("wallet not ready", func(t *testing.T) {
|
|
s := Server{}
|
|
_, err := s.ListKeystores(context.Background(), &empty.Empty{})
|
|
require.ErrorContains(t, "Prysm Wallet not initialized. Please create a new wallet.", err)
|
|
})
|
|
ctx := context.Background()
|
|
w := wallet.NewWalletForWeb3Signer()
|
|
root := make([]byte, fieldparams.RootLength)
|
|
root[0] = 1
|
|
bytevalue, err := hexutil.Decode("0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a")
|
|
require.NoError(t, err)
|
|
pubkeys := [][fieldparams.BLSPubkeyLength]byte{bytesutil.ToBytes48(bytevalue)}
|
|
config := &remoteweb3signer.SetupConfig{
|
|
BaseEndpoint: "http://example.com",
|
|
GenesisValidatorsRoot: root,
|
|
ProvidedPublicKeys: pubkeys,
|
|
}
|
|
km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false, Web3SignerConfig: config})
|
|
require.NoError(t, err)
|
|
vs, err := client.NewValidatorService(ctx, &client.Config{
|
|
Wallet: w,
|
|
Validator: &mock.MockValidator{
|
|
Km: km,
|
|
},
|
|
Web3SignerConfig: config,
|
|
})
|
|
require.NoError(t, err)
|
|
s := &Server{
|
|
walletInitialized: true,
|
|
wallet: w,
|
|
validatorService: vs,
|
|
}
|
|
|
|
t.Run("returns proper data with existing pub keystores", func(t *testing.T) {
|
|
resp, err := s.DeleteRemoteKeys(context.Background(), ðpbservice.DeleteRemoteKeysRequest{
|
|
Pubkeys: [][]byte{bytevalue},
|
|
})
|
|
expectedStatuses := []*ethpbservice.DeletedRemoteKeysStatus{
|
|
{
|
|
Status: ethpbservice.DeletedRemoteKeysStatus_DELETED,
|
|
Message: fmt.Sprintf("Successfully deleted pubkey: %v", hexutil.Encode(bytevalue)),
|
|
},
|
|
}
|
|
require.NoError(t, err)
|
|
for i := 0; i < len(resp.Data); i++ {
|
|
require.DeepEqual(t, expectedStatuses[i], resp.Data[i])
|
|
|
|
}
|
|
expectedKeys, err := km.FetchValidatingPublicKeys(ctx)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 0, len(expectedKeys))
|
|
})
|
|
}
|
|
|
|
func TestServer_ListFeeRecipientByPubkey(t *testing.T) {
|
|
ctx := context.Background()
|
|
byteval, err := hexutil.Decode("0xaf2e7ba294e03438ea819bd4033c6c1bf6b04320ee2075b77273c08d02f8a61bcc303c2c06bd3713cb442072ae591493")
|
|
require.NoError(t, err)
|
|
|
|
type want struct {
|
|
EthAddress string
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
args *validatorserviceconfig.ProposerSettings
|
|
want *want
|
|
cached *eth.FeeRecipientByPubKeyResponse
|
|
}{
|
|
{
|
|
name: "ProposerSettings.ProposeConfig.FeeRecipientConfig defined for pubkey (and ProposerSettings.DefaultConfig.FeeRecipientConfig defined)",
|
|
args: &validatorserviceconfig.ProposerSettings{
|
|
ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{
|
|
bytesutil.ToBytes48(byteval): {
|
|
FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{
|
|
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9"),
|
|
},
|
|
},
|
|
},
|
|
DefaultConfig: &validatorserviceconfig.ProposerOption{
|
|
FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{
|
|
FeeRecipient: common.HexToAddress("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"),
|
|
},
|
|
},
|
|
},
|
|
want: &want{
|
|
EthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
|
},
|
|
},
|
|
{
|
|
name: "ProposerSettings.ProposeConfig.FeeRecipientConfig NOT defined for pubkey and ProposerSettings.DefaultConfig.FeeRecipientConfig defined",
|
|
args: &validatorserviceconfig.ProposerSettings{
|
|
ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{},
|
|
DefaultConfig: &validatorserviceconfig.ProposerOption{
|
|
FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{
|
|
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9"),
|
|
},
|
|
},
|
|
},
|
|
want: &want{
|
|
EthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
|
},
|
|
},
|
|
{
|
|
name: "ProposerSettings is nil and beacon node response is correct",
|
|
args: nil,
|
|
want: &want{
|
|
EthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
|
},
|
|
cached: ð.FeeRecipientByPubKeyResponse{
|
|
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9").Bytes(),
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
mockValidatorClient := validatormock.NewMockValidatorClient(ctrl)
|
|
|
|
m := &mock.MockValidator{}
|
|
err := m.SetProposerSettings(ctx, tt.args)
|
|
require.NoError(t, err)
|
|
|
|
if tt.args == nil {
|
|
mockValidatorClient.EXPECT().GetFeeRecipientByPubKey(gomock.Any(), gomock.Any()).Return(tt.cached, nil)
|
|
}
|
|
|
|
vs, err := client.NewValidatorService(ctx, &client.Config{
|
|
Validator: m,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
s := &Server{
|
|
validatorService: vs,
|
|
beaconNodeValidatorClient: mockValidatorClient,
|
|
}
|
|
|
|
got, err := s.ListFeeRecipientByPubkey(ctx, ðpbservice.PubkeyRequest{Pubkey: byteval})
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, tt.want.EthAddress, common.BytesToAddress(got.Data.Ethaddress).Hex())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestServer_ListFeeRecipientByPubKey_BeaconNodeError(t *testing.T) {
|
|
ctx := context.Background()
|
|
byteval, err := hexutil.Decode("0xaf2e7ba294e03438ea819bd4033c6c1bf6b04320ee2075b77273c08d02f8a61bcc303c2c06bd3713cb442072ae591493")
|
|
require.NoError(t, err)
|
|
|
|
ctrl := gomock.NewController(t)
|
|
mockValidatorClient := validatormock.NewMockValidatorClient(ctrl)
|
|
|
|
mockValidatorClient.EXPECT().GetFeeRecipientByPubKey(gomock.Any(), gomock.Any()).Return(nil, errors.New("custom error"))
|
|
|
|
vs, err := client.NewValidatorService(ctx, &client.Config{
|
|
Validator: &mock.MockValidator{},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
s := &Server{
|
|
validatorService: vs,
|
|
beaconNodeValidatorClient: mockValidatorClient,
|
|
}
|
|
|
|
_, err = s.ListFeeRecipientByPubkey(ctx, ðpbservice.PubkeyRequest{Pubkey: byteval})
|
|
require.ErrorContains(t, "Failed to retrieve default fee recipient from beacon node", err)
|
|
}
|
|
|
|
func TestServer_ListFeeRecipientByPubKey_NoFeeRecipientSet(t *testing.T) {
|
|
ctx := context.Background()
|
|
byteval, err := hexutil.Decode("0xaf2e7ba294e03438ea819bd4033c6c1bf6b04320ee2075b77273c08d02f8a61bcc303c2c06bd3713cb442072ae591493")
|
|
require.NoError(t, err)
|
|
|
|
ctrl := gomock.NewController(t)
|
|
mockValidatorClient := validatormock.NewMockValidatorClient(ctrl)
|
|
|
|
mockValidatorClient.EXPECT().GetFeeRecipientByPubKey(gomock.Any(), gomock.Any()).Return(nil, nil)
|
|
|
|
vs, err := client.NewValidatorService(ctx, &client.Config{
|
|
Validator: &mock.MockValidator{},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
s := &Server{
|
|
validatorService: vs,
|
|
beaconNodeValidatorClient: mockValidatorClient,
|
|
}
|
|
|
|
_, err = s.ListFeeRecipientByPubkey(ctx, ðpbservice.PubkeyRequest{Pubkey: byteval})
|
|
require.ErrorContains(t, "No fee recipient set", err)
|
|
}
|
|
|
|
func TestServer_ListFeeRecipientByPubkey_ValidatorServiceNil(t *testing.T) {
|
|
ctx := grpc.NewContextWithServerTransportStream(context.Background(), &runtime.ServerTransportStream{})
|
|
|
|
s := &Server{}
|
|
|
|
_, err := s.ListFeeRecipientByPubkey(ctx, nil)
|
|
require.ErrorContains(t, "Validator service not ready", err)
|
|
}
|
|
|
|
func TestServer_ListFeeRecipientByPubkey_InvalidPubKey(t *testing.T) {
|
|
ctx := grpc.NewContextWithServerTransportStream(context.Background(), &runtime.ServerTransportStream{})
|
|
s := &Server{
|
|
validatorService: &client.ValidatorService{},
|
|
}
|
|
|
|
req := ðpbservice.PubkeyRequest{
|
|
Pubkey: []byte{},
|
|
}
|
|
|
|
_, err := s.ListFeeRecipientByPubkey(ctx, req)
|
|
require.ErrorContains(t, "not a valid bls public key", err)
|
|
}
|
|
|
|
func TestServer_FeeRecipientByPubkey(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
beaconClient := validatormock.NewMockValidatorClient(ctrl)
|
|
ctx := grpc.NewContextWithServerTransportStream(context.Background(), &runtime.ServerTransportStream{})
|
|
|
|
byteval, err := hexutil.Decode("0xaf2e7ba294e03438ea819bd4033c6c1bf6b04320ee2075b77273c08d02f8a61bcc303c2c06bd3713cb442072ae591493")
|
|
require.NoError(t, err)
|
|
|
|
type want struct {
|
|
valEthAddress string
|
|
defaultEthaddress string
|
|
}
|
|
type beaconResp struct {
|
|
resp *eth.FeeRecipientByPubKeyResponse
|
|
error error
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args string
|
|
proposerSettings *validatorserviceconfig.ProposerSettings
|
|
want *want
|
|
wantErr bool
|
|
beaconReturn *beaconResp
|
|
}{
|
|
{
|
|
name: "ProposerSetting is nil",
|
|
args: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
|
proposerSettings: nil,
|
|
want: &want{
|
|
valEthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
|
},
|
|
wantErr: false,
|
|
beaconReturn: &beaconResp{
|
|
resp: nil,
|
|
error: nil,
|
|
},
|
|
},
|
|
{
|
|
name: "ProposerSetting.ProposeConfig is nil",
|
|
args: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
|
proposerSettings: &validatorserviceconfig.ProposerSettings{
|
|
ProposeConfig: nil,
|
|
},
|
|
want: &want{
|
|
valEthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
|
},
|
|
wantErr: false,
|
|
beaconReturn: &beaconResp{
|
|
resp: nil,
|
|
error: nil,
|
|
},
|
|
},
|
|
{
|
|
name: "ProposerSetting.ProposeConfig is nil AND ProposerSetting.Defaultconfig is defined",
|
|
args: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
|
proposerSettings: &validatorserviceconfig.ProposerSettings{
|
|
ProposeConfig: nil,
|
|
DefaultConfig: &validatorserviceconfig.ProposerOption{},
|
|
},
|
|
want: &want{
|
|
valEthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
|
},
|
|
wantErr: false,
|
|
beaconReturn: &beaconResp{
|
|
resp: nil,
|
|
error: nil,
|
|
},
|
|
},
|
|
{
|
|
name: "ProposerSetting.ProposeConfig is defined for pubkey",
|
|
args: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
|
proposerSettings: &validatorserviceconfig.ProposerSettings{
|
|
ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{
|
|
bytesutil.ToBytes48(byteval): {},
|
|
},
|
|
},
|
|
want: &want{
|
|
valEthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
|
},
|
|
wantErr: false,
|
|
beaconReturn: &beaconResp{
|
|
resp: nil,
|
|
error: nil,
|
|
},
|
|
},
|
|
{
|
|
name: "ProposerSetting.ProposeConfig not defined for pubkey",
|
|
args: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
|
proposerSettings: &validatorserviceconfig.ProposerSettings{
|
|
ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{},
|
|
},
|
|
want: &want{
|
|
valEthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
|
},
|
|
wantErr: false,
|
|
beaconReturn: &beaconResp{
|
|
resp: nil,
|
|
error: nil,
|
|
},
|
|
},
|
|
{
|
|
name: "ProposerSetting.ProposeConfig is nil for pubkey",
|
|
args: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
|
proposerSettings: &validatorserviceconfig.ProposerSettings{
|
|
ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{
|
|
bytesutil.ToBytes48(byteval): nil,
|
|
},
|
|
},
|
|
want: &want{
|
|
valEthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
|
},
|
|
wantErr: false,
|
|
beaconReturn: &beaconResp{
|
|
resp: nil,
|
|
error: nil,
|
|
},
|
|
},
|
|
{
|
|
name: "ProposerSetting.ProposeConfig is nil for pubkey AND DefaultConfig is not nil",
|
|
args: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
|
proposerSettings: &validatorserviceconfig.ProposerSettings{
|
|
ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{
|
|
bytesutil.ToBytes48(byteval): nil,
|
|
},
|
|
DefaultConfig: &validatorserviceconfig.ProposerOption{},
|
|
},
|
|
want: &want{
|
|
valEthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
|
|
},
|
|
wantErr: false,
|
|
beaconReturn: &beaconResp{
|
|
resp: nil,
|
|
error: nil,
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
m := &mock.MockValidator{}
|
|
err := m.SetProposerSettings(ctx, tt.proposerSettings)
|
|
require.NoError(t, err)
|
|
validatorDB := dbtest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{})
|
|
|
|
// save a default here
|
|
vs, err := client.NewValidatorService(ctx, &client.Config{
|
|
Validator: m,
|
|
ValDB: validatorDB,
|
|
})
|
|
require.NoError(t, err)
|
|
s := &Server{
|
|
validatorService: vs,
|
|
beaconNodeValidatorClient: beaconClient,
|
|
valDB: validatorDB,
|
|
}
|
|
|
|
_, err = s.SetFeeRecipientByPubkey(ctx, ðpbservice.SetFeeRecipientByPubkeyRequest{Pubkey: byteval, Ethaddress: common.HexToAddress(tt.args).Bytes()})
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, tt.want.valEthAddress, s.validatorService.ProposerSettings().ProposeConfig[bytesutil.ToBytes48(byteval)].FeeRecipientConfig.FeeRecipient.Hex())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestServer_SetFeeRecipientByPubkey_ValidatorServiceNil(t *testing.T) {
|
|
ctx := grpc.NewContextWithServerTransportStream(context.Background(), &runtime.ServerTransportStream{})
|
|
|
|
s := &Server{}
|
|
|
|
_, err := s.SetFeeRecipientByPubkey(ctx, nil)
|
|
require.ErrorContains(t, "Validator service not ready", err)
|
|
}
|
|
|
|
func TestServer_SetFeeRecipientByPubkey_InvalidPubKey(t *testing.T) {
|
|
ctx := grpc.NewContextWithServerTransportStream(context.Background(), &runtime.ServerTransportStream{})
|
|
s := &Server{
|
|
validatorService: &client.ValidatorService{},
|
|
}
|
|
|
|
req := ðpbservice.SetFeeRecipientByPubkeyRequest{
|
|
Pubkey: []byte{},
|
|
}
|
|
|
|
_, err := s.SetFeeRecipientByPubkey(ctx, req)
|
|
require.ErrorContains(t, "not a valid bls public key", err)
|
|
}
|
|
|
|
func TestServer_SetGasLimit_InvalidFeeRecipient(t *testing.T) {
|
|
ctx := grpc.NewContextWithServerTransportStream(context.Background(), &runtime.ServerTransportStream{})
|
|
|
|
byteval, err := hexutil.Decode("0xaf2e7ba294e03438ea819bd4033c6c1bf6b04320ee2075b77273c08d02f8a61bcc303c2c06bd3713cb442072ae591493")
|
|
require.NoError(t, err)
|
|
|
|
s := &Server{
|
|
validatorService: &client.ValidatorService{},
|
|
}
|
|
|
|
req := ðpbservice.SetFeeRecipientByPubkeyRequest{
|
|
Pubkey: byteval,
|
|
}
|
|
|
|
_, err = s.SetFeeRecipientByPubkey(ctx, req)
|
|
require.ErrorContains(t, "Fee recipient is not a valid Ethereum address", err)
|
|
}
|
|
|
|
func TestServer_DeleteFeeRecipientByPubkey(t *testing.T) {
|
|
ctx := grpc.NewContextWithServerTransportStream(context.Background(), &runtime.ServerTransportStream{})
|
|
byteval, err := hexutil.Decode("0xaf2e7ba294e03438ea819bd4033c6c1bf6b04320ee2075b77273c08d02f8a61bcc303c2c06bd3713cb442072ae591493")
|
|
require.NoError(t, err)
|
|
type want struct {
|
|
EthAddress string
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
proposerSettings *validatorserviceconfig.ProposerSettings
|
|
want *want
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "Happy Path Test",
|
|
proposerSettings: &validatorserviceconfig.ProposerSettings{
|
|
ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{
|
|
bytesutil.ToBytes48(byteval): {
|
|
FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{
|
|
FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455012BFEBf6177F1D2e9738D5"),
|
|
},
|
|
},
|
|
},
|
|
DefaultConfig: &validatorserviceconfig.ProposerOption{
|
|
FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{
|
|
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9"),
|
|
},
|
|
},
|
|
},
|
|
want: &want{
|
|
EthAddress: common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9").Hex(),
|
|
},
|
|
wantErr: false,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
m := &mock.MockValidator{}
|
|
err := m.SetProposerSettings(ctx, tt.proposerSettings)
|
|
require.NoError(t, err)
|
|
validatorDB := dbtest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{})
|
|
vs, err := client.NewValidatorService(ctx, &client.Config{
|
|
Validator: m,
|
|
ValDB: validatorDB,
|
|
})
|
|
require.NoError(t, err)
|
|
s := &Server{
|
|
validatorService: vs,
|
|
valDB: validatorDB,
|
|
}
|
|
_, err = s.DeleteFeeRecipientByPubkey(ctx, ðpbservice.PubkeyRequest{Pubkey: byteval})
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, true, s.validatorService.ProposerSettings().ProposeConfig[bytesutil.ToBytes48(byteval)].FeeRecipientConfig == nil)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestServer_DeleteFeeRecipientByPubkey_ValidatorServiceNil(t *testing.T) {
|
|
ctx := grpc.NewContextWithServerTransportStream(context.Background(), &runtime.ServerTransportStream{})
|
|
|
|
s := &Server{}
|
|
|
|
_, err := s.DeleteFeeRecipientByPubkey(ctx, nil)
|
|
require.ErrorContains(t, "Validator service not ready", err)
|
|
}
|
|
|
|
func TestServer_DeleteFeeRecipientByPubkey_InvalidPubKey(t *testing.T) {
|
|
ctx := grpc.NewContextWithServerTransportStream(context.Background(), &runtime.ServerTransportStream{})
|
|
s := &Server{
|
|
validatorService: &client.ValidatorService{},
|
|
}
|
|
|
|
req := ðpbservice.PubkeyRequest{
|
|
Pubkey: []byte{},
|
|
}
|
|
|
|
_, err := s.DeleteFeeRecipientByPubkey(ctx, req)
|
|
require.ErrorContains(t, "not a valid bls public key", err)
|
|
}
|
|
|
|
func TestServer_GetGasLimit(t *testing.T) {
|
|
ctx := context.Background()
|
|
byteval, err := hexutil.Decode("0xaf2e7ba294e03438ea819bd4033c6c1bf6b04320ee2075b77273c08d02f8a61bcc303c2c06bd3713cb442072ae591493")
|
|
byteval2, err2 := hexutil.Decode("0x1234567878903438ea819bd4033c6c1bf6b04320ee2075b77273c08d02f8a61bcc303c2c06bd3713cb442072ae591493")
|
|
require.NoError(t, err)
|
|
require.NoError(t, err2)
|
|
|
|
tests := []struct {
|
|
name string
|
|
args *validatorserviceconfig.ProposerSettings
|
|
pubkey [48]byte
|
|
want uint64
|
|
}{
|
|
{
|
|
name: "ProposerSetting for specific pubkey exists",
|
|
args: &validatorserviceconfig.ProposerSettings{
|
|
ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{
|
|
bytesutil.ToBytes48(byteval): {
|
|
BuilderConfig: &validatorserviceconfig.BuilderConfig{GasLimit: 123456789},
|
|
},
|
|
},
|
|
DefaultConfig: &validatorserviceconfig.ProposerOption{
|
|
BuilderConfig: &validatorserviceconfig.BuilderConfig{GasLimit: 987654321},
|
|
},
|
|
},
|
|
pubkey: bytesutil.ToBytes48(byteval),
|
|
want: 123456789,
|
|
},
|
|
{
|
|
name: "ProposerSetting for specific pubkey does not exist",
|
|
args: &validatorserviceconfig.ProposerSettings{
|
|
ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{
|
|
bytesutil.ToBytes48(byteval): {
|
|
BuilderConfig: &validatorserviceconfig.BuilderConfig{GasLimit: 123456789},
|
|
},
|
|
},
|
|
DefaultConfig: &validatorserviceconfig.ProposerOption{
|
|
BuilderConfig: &validatorserviceconfig.BuilderConfig{GasLimit: 987654321},
|
|
},
|
|
},
|
|
// no settings for the following validator, so the gaslimit returned is the default value.
|
|
pubkey: bytesutil.ToBytes48(byteval2),
|
|
want: 987654321,
|
|
},
|
|
{
|
|
name: "No proposerSetting at all",
|
|
args: nil,
|
|
pubkey: bytesutil.ToBytes48(byteval),
|
|
want: params.BeaconConfig().DefaultBuilderGasLimit,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
m := &mock.MockValidator{}
|
|
err := m.SetProposerSettings(ctx, tt.args)
|
|
require.NoError(t, err)
|
|
vs, err := client.NewValidatorService(ctx, &client.Config{
|
|
Validator: m,
|
|
})
|
|
require.NoError(t, err)
|
|
s := &Server{
|
|
validatorService: vs,
|
|
}
|
|
got, err := s.GetGasLimit(ctx, ðpbservice.PubkeyRequest{Pubkey: tt.pubkey[:]})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, tt.want, got.Data.GasLimit)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestServer_SetGasLimit(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
beaconClient := validatormock.NewMockValidatorClient(ctrl)
|
|
ctx := grpc.NewContextWithServerTransportStream(context.Background(), &runtime.ServerTransportStream{})
|
|
|
|
pubkey1, err := hexutil.Decode("0xaf2e7ba294e03438ea819bd4033c6c1bf6b04320ee2075b77273c08d02f8a61bcc303c2c06bd3713cb442072ae591493")
|
|
pubkey2, err2 := hexutil.Decode("0xbedefeaa94e03438ea819bd4033c6c1bf6b04320ee2075b77273c08d02f8a61bcc303c2cdddddddddddddddddddddddd")
|
|
require.NoError(t, err)
|
|
require.NoError(t, err2)
|
|
|
|
type beaconResp struct {
|
|
resp *eth.FeeRecipientByPubKeyResponse
|
|
error error
|
|
}
|
|
|
|
type want struct {
|
|
pubkey []byte
|
|
gaslimit uint64
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
pubkey []byte
|
|
newGasLimit uint64
|
|
proposerSettings *validatorserviceconfig.ProposerSettings
|
|
w []*want
|
|
beaconReturn *beaconResp
|
|
wantErr string
|
|
}{
|
|
{
|
|
name: "ProposerSettings is nil",
|
|
pubkey: pubkey1,
|
|
newGasLimit: 9999,
|
|
proposerSettings: nil,
|
|
wantErr: "no proposer settings were found to update",
|
|
},
|
|
{
|
|
name: "ProposerSettings.ProposeConfig is nil AND ProposerSettings.DefaultConfig is nil",
|
|
pubkey: pubkey1,
|
|
newGasLimit: 9999,
|
|
proposerSettings: &validatorserviceconfig.ProposerSettings{
|
|
ProposeConfig: nil,
|
|
DefaultConfig: nil,
|
|
},
|
|
wantErr: "gas limit changes only apply when builder is enabled",
|
|
},
|
|
{
|
|
name: "ProposerSettings.ProposeConfig is nil AND ProposerSettings.DefaultConfig.BuilderConfig is nil",
|
|
pubkey: pubkey1,
|
|
newGasLimit: 9999,
|
|
proposerSettings: &validatorserviceconfig.ProposerSettings{
|
|
ProposeConfig: nil,
|
|
DefaultConfig: &validatorserviceconfig.ProposerOption{
|
|
BuilderConfig: nil,
|
|
},
|
|
},
|
|
wantErr: "gas limit changes only apply when builder is enabled",
|
|
},
|
|
{
|
|
name: "ProposerSettings.ProposeConfig is defined for pubkey, BuilderConfig is nil AND ProposerSettings.DefaultConfig is nil",
|
|
pubkey: pubkey1,
|
|
newGasLimit: 9999,
|
|
proposerSettings: &validatorserviceconfig.ProposerSettings{
|
|
ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{
|
|
bytesutil.ToBytes48(pubkey1): {
|
|
BuilderConfig: nil,
|
|
},
|
|
},
|
|
DefaultConfig: nil,
|
|
},
|
|
wantErr: "gas limit changes only apply when builder is enabled",
|
|
},
|
|
{
|
|
name: "ProposerSettings.ProposeConfig is defined for pubkey, BuilderConfig is defined AND ProposerSettings.DefaultConfig is nil",
|
|
pubkey: pubkey1,
|
|
newGasLimit: 9999,
|
|
proposerSettings: &validatorserviceconfig.ProposerSettings{
|
|
ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{
|
|
bytesutil.ToBytes48(pubkey1): {
|
|
BuilderConfig: &validatorserviceconfig.BuilderConfig{},
|
|
},
|
|
},
|
|
DefaultConfig: nil,
|
|
},
|
|
wantErr: "gas limit changes only apply when builder is enabled",
|
|
},
|
|
{
|
|
name: "ProposerSettings.ProposeConfig is NOT defined for pubkey, BuilderConfig is defined AND ProposerSettings.DefaultConfig is nil",
|
|
pubkey: pubkey2,
|
|
newGasLimit: 9999,
|
|
proposerSettings: &validatorserviceconfig.ProposerSettings{
|
|
ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{
|
|
bytesutil.ToBytes48(pubkey2): {
|
|
BuilderConfig: &validatorserviceconfig.BuilderConfig{
|
|
Enabled: true,
|
|
GasLimit: 12345,
|
|
},
|
|
},
|
|
},
|
|
DefaultConfig: nil,
|
|
},
|
|
w: []*want{{
|
|
pubkey2,
|
|
9999,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "ProposerSettings.ProposeConfig is defined for pubkey, BuilderConfig is nil AND ProposerSettings.DefaultConfig.BuilderConfig is defined",
|
|
pubkey: pubkey1,
|
|
newGasLimit: 9999,
|
|
proposerSettings: &validatorserviceconfig.ProposerSettings{
|
|
ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{
|
|
bytesutil.ToBytes48(pubkey2): {
|
|
BuilderConfig: nil,
|
|
},
|
|
},
|
|
DefaultConfig: &validatorserviceconfig.ProposerOption{
|
|
BuilderConfig: &validatorserviceconfig.BuilderConfig{
|
|
Enabled: true,
|
|
},
|
|
},
|
|
},
|
|
w: []*want{{
|
|
pubkey1,
|
|
9999,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
m := &mock.MockValidator{}
|
|
err := m.SetProposerSettings(ctx, tt.proposerSettings)
|
|
require.NoError(t, err)
|
|
validatorDB := dbtest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{})
|
|
vs, err := client.NewValidatorService(ctx, &client.Config{
|
|
Validator: m,
|
|
ValDB: validatorDB,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
s := &Server{
|
|
validatorService: vs,
|
|
beaconNodeValidatorClient: beaconClient,
|
|
valDB: validatorDB,
|
|
}
|
|
|
|
if tt.beaconReturn != nil {
|
|
beaconClient.EXPECT().GetFeeRecipientByPubKey(
|
|
gomock.Any(),
|
|
gomock.Any(),
|
|
).Return(tt.beaconReturn.resp, tt.beaconReturn.error)
|
|
}
|
|
|
|
_, err = s.SetGasLimit(ctx, ðpbservice.SetGasLimitRequest{Pubkey: tt.pubkey, GasLimit: tt.newGasLimit})
|
|
if tt.wantErr != "" {
|
|
require.ErrorContains(t, tt.wantErr, err)
|
|
} else {
|
|
require.NoError(t, err)
|
|
for _, w := range tt.w {
|
|
assert.Equal(t, w.gaslimit, uint64(s.validatorService.ProposerSettings().ProposeConfig[bytesutil.ToBytes48(w.pubkey)].BuilderConfig.GasLimit))
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestServer_SetGasLimit_ValidatorServiceNil(t *testing.T) {
|
|
ctx := grpc.NewContextWithServerTransportStream(context.Background(), &runtime.ServerTransportStream{})
|
|
|
|
s := &Server{}
|
|
|
|
_, err := s.SetGasLimit(ctx, nil)
|
|
require.ErrorContains(t, "Validator service not ready", err)
|
|
}
|
|
|
|
func TestServer_SetGasLimit_InvalidPubKey(t *testing.T) {
|
|
ctx := grpc.NewContextWithServerTransportStream(context.Background(), &runtime.ServerTransportStream{})
|
|
s := &Server{
|
|
validatorService: &client.ValidatorService{},
|
|
}
|
|
|
|
req := ðpbservice.SetGasLimitRequest{
|
|
Pubkey: []byte{},
|
|
}
|
|
|
|
_, err := s.SetGasLimit(ctx, req)
|
|
require.ErrorContains(t, "not a valid bls public key", err)
|
|
}
|
|
|
|
func TestServer_DeleteGasLimit(t *testing.T) {
|
|
ctx := grpc.NewContextWithServerTransportStream(context.Background(), &runtime.ServerTransportStream{})
|
|
pubkey1, err := hexutil.Decode("0xaf2e7ba294e03438ea819bd4033c6c1bf6b04320ee2075b77273c08d02f8a61bcc303c2c06bd3713cb442072ae591493")
|
|
pubkey2, err2 := hexutil.Decode("0xbedefeaa94e03438ea819bd4033c6c1bf6b04320ee2075b77273c08d02f8a61bcc303c2cdddddddddddddddddddddddd")
|
|
require.NoError(t, err)
|
|
require.NoError(t, err2)
|
|
|
|
// This test changes global default values, we do not want this to side-affect other
|
|
// tests, so store the origin global default and then restore after tests are done.
|
|
originBeaconChainGasLimit := params.BeaconConfig().DefaultBuilderGasLimit
|
|
defer func() {
|
|
params.BeaconConfig().DefaultBuilderGasLimit = originBeaconChainGasLimit
|
|
}()
|
|
|
|
globalDefaultGasLimit := validator.Uint64(0xbbdd)
|
|
|
|
type want struct {
|
|
pubkey []byte
|
|
gaslimit validator.Uint64
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
pubkey []byte
|
|
proposerSettings *validatorserviceconfig.ProposerSettings
|
|
wantError error
|
|
w []want
|
|
}{
|
|
{
|
|
name: "delete existing gas limit with default config",
|
|
pubkey: pubkey1,
|
|
proposerSettings: &validatorserviceconfig.ProposerSettings{
|
|
ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{
|
|
bytesutil.ToBytes48(pubkey1): {
|
|
BuilderConfig: &validatorserviceconfig.BuilderConfig{GasLimit: validator.Uint64(987654321)},
|
|
},
|
|
bytesutil.ToBytes48(pubkey2): {
|
|
BuilderConfig: &validatorserviceconfig.BuilderConfig{GasLimit: validator.Uint64(123456789)},
|
|
},
|
|
},
|
|
DefaultConfig: &validatorserviceconfig.ProposerOption{
|
|
BuilderConfig: &validatorserviceconfig.BuilderConfig{GasLimit: validator.Uint64(5555)},
|
|
},
|
|
},
|
|
wantError: nil,
|
|
w: []want{
|
|
{
|
|
pubkey: pubkey1,
|
|
// After deletion, use DefaultConfig.BuilderConfig.GasLimit.
|
|
gaslimit: validator.Uint64(5555),
|
|
},
|
|
{
|
|
pubkey: pubkey2,
|
|
gaslimit: validator.Uint64(123456789),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "delete existing gas limit with no default config",
|
|
pubkey: pubkey1,
|
|
proposerSettings: &validatorserviceconfig.ProposerSettings{
|
|
ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{
|
|
bytesutil.ToBytes48(pubkey1): {
|
|
BuilderConfig: &validatorserviceconfig.BuilderConfig{GasLimit: validator.Uint64(987654321)},
|
|
},
|
|
bytesutil.ToBytes48(pubkey2): {
|
|
BuilderConfig: &validatorserviceconfig.BuilderConfig{GasLimit: validator.Uint64(123456789)},
|
|
},
|
|
},
|
|
},
|
|
wantError: nil,
|
|
w: []want{
|
|
{
|
|
pubkey: pubkey1,
|
|
// After deletion, use global default, because DefaultConfig is not set at all.
|
|
gaslimit: globalDefaultGasLimit,
|
|
},
|
|
{
|
|
pubkey: pubkey2,
|
|
gaslimit: validator.Uint64(123456789),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "delete nonexist gas limit",
|
|
pubkey: pubkey2,
|
|
proposerSettings: &validatorserviceconfig.ProposerSettings{
|
|
ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{
|
|
bytesutil.ToBytes48(pubkey1): {
|
|
BuilderConfig: &validatorserviceconfig.BuilderConfig{GasLimit: validator.Uint64(987654321)},
|
|
},
|
|
},
|
|
},
|
|
wantError: fmt.Errorf("%s", codes.NotFound.String()),
|
|
w: []want{
|
|
// pubkey1's gaslimit is unaffected
|
|
{
|
|
pubkey: pubkey1,
|
|
gaslimit: validator.Uint64(987654321),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "delete nonexist gas limit 2",
|
|
pubkey: pubkey2,
|
|
wantError: fmt.Errorf("%s", codes.NotFound.String()),
|
|
w: []want{},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
m := &mock.MockValidator{}
|
|
err := m.SetProposerSettings(ctx, tt.proposerSettings)
|
|
require.NoError(t, err)
|
|
validatorDB := dbtest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{})
|
|
vs, err := client.NewValidatorService(ctx, &client.Config{
|
|
Validator: m,
|
|
ValDB: validatorDB,
|
|
})
|
|
require.NoError(t, err)
|
|
s := &Server{
|
|
validatorService: vs,
|
|
valDB: validatorDB,
|
|
}
|
|
// Set up global default value for builder gas limit.
|
|
params.BeaconConfig().DefaultBuilderGasLimit = uint64(globalDefaultGasLimit)
|
|
_, err = s.DeleteGasLimit(ctx, ðpbservice.DeleteGasLimitRequest{Pubkey: tt.pubkey})
|
|
if tt.wantError != nil {
|
|
assert.ErrorContains(t, fmt.Sprintf("code = %s", tt.wantError.Error()), err)
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
for _, w := range tt.w {
|
|
assert.Equal(t, w.gaslimit, s.validatorService.ProposerSettings().ProposeConfig[bytesutil.ToBytes48(w.pubkey)].BuilderConfig.GasLimit)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestServer_SetVoluntaryExit(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
ctx := grpc.NewContextWithServerTransportStream(context.Background(), &runtime.ServerTransportStream{})
|
|
defaultWalletPath = setupWalletDir(t)
|
|
opts := []accounts.Option{
|
|
accounts.WithWalletDir(defaultWalletPath),
|
|
accounts.WithKeymanagerType(keymanager.Derived),
|
|
accounts.WithWalletPassword(strongPass),
|
|
accounts.WithSkipMnemonicConfirm(true),
|
|
}
|
|
acc, err := accounts.NewCLIManager(opts...)
|
|
require.NoError(t, err)
|
|
w, err := acc.WalletCreate(ctx)
|
|
require.NoError(t, err)
|
|
km, err := w.InitializeKeymanager(ctx, iface.InitKeymanagerConfig{ListenForChanges: false})
|
|
require.NoError(t, err)
|
|
|
|
m := &mock.MockValidator{Km: km}
|
|
vs, err := client.NewValidatorService(ctx, &client.Config{
|
|
Validator: m,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
dr, ok := km.(*derived.Keymanager)
|
|
require.Equal(t, true, ok)
|
|
err = dr.RecoverAccountsFromMnemonic(ctx, mocks.TestMnemonic, derived.DefaultMnemonicLanguage, "", 1)
|
|
require.NoError(t, err)
|
|
pubKeys, err := dr.FetchValidatingPublicKeys(ctx)
|
|
require.NoError(t, err)
|
|
|
|
beaconClient := validatormock.NewMockValidatorClient(ctrl)
|
|
mockNodeClient := validatormock.NewMockNodeClient(ctrl)
|
|
// Any time in the past will suffice
|
|
genesisTime := ×tamppb.Timestamp{
|
|
Seconds: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC).Unix(),
|
|
}
|
|
|
|
beaconClient.EXPECT().ValidatorIndex(gomock.Any(), ð.ValidatorIndexRequest{PublicKey: pubKeys[0][:]}).
|
|
Times(3).
|
|
Return(ð.ValidatorIndexResponse{Index: 2}, nil)
|
|
|
|
beaconClient.EXPECT().DomainData(
|
|
gomock.Any(), // ctx
|
|
gomock.Any(), // epoch
|
|
).Times(3).
|
|
Return(ð.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/)
|
|
|
|
mockNodeClient.EXPECT().
|
|
GetGenesis(gomock.Any(), gomock.Any()).
|
|
Times(3).
|
|
Return(ð.Genesis{GenesisTime: genesisTime}, nil)
|
|
|
|
s := &Server{
|
|
validatorService: vs,
|
|
beaconNodeValidatorClient: beaconClient,
|
|
wallet: w,
|
|
beaconNodeClient: mockNodeClient,
|
|
}
|
|
|
|
type want struct {
|
|
epoch primitives.Epoch
|
|
validatorIndex uint64
|
|
signature []byte
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
pubkey []byte
|
|
epoch primitives.Epoch
|
|
w want
|
|
}{
|
|
{
|
|
name: "Ok: with epoch",
|
|
epoch: 30000000,
|
|
w: want{
|
|
epoch: 30000000,
|
|
validatorIndex: 2,
|
|
signature: []uint8{175, 157, 5, 134, 253, 2, 193, 35, 176, 43, 217, 36, 39, 240, 24, 79, 207, 133, 150, 7, 237, 16, 54, 244, 64, 27, 244, 17, 8, 225, 140, 1, 172, 24, 35, 95, 178, 116, 172, 213, 113, 182, 193, 61, 192, 65, 162, 253, 19, 202, 111, 164, 195, 215, 0, 205, 95, 7, 30, 251, 244, 157, 210, 155, 238, 30, 35, 219, 177, 232, 174, 62, 218, 69, 23, 249, 180, 140, 60, 29, 190, 249, 229, 95, 235, 236, 81, 33, 60, 4, 201, 227, 70, 239, 167, 2},
|
|
},
|
|
},
|
|
{
|
|
name: "Ok: epoch not set",
|
|
w: want{
|
|
epoch: 0,
|
|
validatorIndex: 2,
|
|
signature: []uint8{},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
resp, err := s.SetVoluntaryExit(ctx, ðpbservice.SetVoluntaryExitRequest{Pubkey: pubKeys[0][:], Epoch: tt.epoch})
|
|
require.NoError(t, err)
|
|
if tt.w.epoch == 0 {
|
|
genesisResponse, err := s.beaconNodeClient.GetGenesis(ctx, &emptypb.Empty{})
|
|
require.NoError(t, err)
|
|
tt.w.epoch, err = client.CurrentEpoch(genesisResponse.GenesisTime)
|
|
require.NoError(t, err)
|
|
resp2, err := s.SetVoluntaryExit(ctx, ðpbservice.SetVoluntaryExitRequest{Pubkey: pubKeys[0][:], Epoch: tt.epoch})
|
|
require.NoError(t, err)
|
|
tt.w.signature = resp2.Data.Signature
|
|
}
|
|
require.Equal(t, uint64(tt.w.epoch), resp.Data.Message.Epoch)
|
|
require.Equal(t, tt.w.validatorIndex, resp.Data.Message.ValidatorIndex)
|
|
require.NotEmpty(t, resp.Data.Signature)
|
|
ok = bytes.Equal(tt.w.signature, resp.Data.Signature)
|
|
require.Equal(t, true, ok)
|
|
})
|
|
}
|
|
}
|