From 93298dfc56f91e15ea9532afe7a28b960a05fb03 Mon Sep 17 00:00:00 2001 From: Manu NALEPA Date: Tue, 14 Feb 2023 05:53:54 +0100 Subject: [PATCH] Fix #11948 and reduces call to `GetFeeRecipientByPubKey` only in `ListFeeRecipientByPubkey` in nothing is defined in VC (#11970) * Fix #11948 and reduces call to `GetFeeRecipientByPubKey` only in `ListFeeRecipientByPubkey` in nothing is defined in VC * Fix typo --- config/validator/service/BUILD.bazel | 1 - config/validator/service/proposer-settings.go | 17 +- crypto/bls/common/mock/BUILD.bazel | 12 + crypto/bls/common/mock/interface_mock.go | 277 +++++++++ hack/update-mockgen.sh | 17 + validator/client/BUILD.bazel | 3 + validator/client/runner.go | 2 +- validator/client/runner_test.go | 8 +- validator/client/validator.go | 72 ++- validator/client/validator_test.go | 491 +++++++++++++++- validator/node/node.go | 8 +- validator/node/node_test.go | 80 ++- validator/rpc/standard_api.go | 170 +++--- validator/rpc/standard_api_test.go | 544 +++++++++++++----- 14 files changed, 1414 insertions(+), 288 deletions(-) create mode 100644 crypto/bls/common/mock/BUILD.bazel create mode 100644 crypto/bls/common/mock/interface_mock.go diff --git a/config/validator/service/BUILD.bazel b/config/validator/service/BUILD.bazel index 179bc899b..595940661 100644 --- a/config/validator/service/BUILD.bazel +++ b/config/validator/service/BUILD.bazel @@ -7,7 +7,6 @@ go_library( visibility = ["//visibility:public"], deps = [ "//config/fieldparams:go_default_library", - "//config/params:go_default_library", "@com_github_ethereum_go_ethereum//common:go_default_library", ], ) diff --git a/config/validator/service/proposer-settings.go b/config/validator/service/proposer-settings.go index 62bf61ed9..8496f8f0e 100644 --- a/config/validator/service/proposer-settings.go +++ b/config/validator/service/proposer-settings.go @@ -5,7 +5,6 @@ import ( "github.com/ethereum/go-ethereum/common" fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams" - "github.com/prysmaticlabs/prysm/v3/config/params" ) // ProposerSettingsPayload is the struct representation of the JSON or YAML payload set in the validator through the CLI. @@ -69,16 +68,12 @@ type ProposerSettings struct { DefaultConfig *ProposerOption } -// ProposerOption is a Prysm internal representation of the ProposerOptionPayload on the validator client in bytes format instead of hex. -type ProposerOption struct { - FeeRecipient common.Address - BuilderConfig *BuilderConfig +type FeeRecipientConfig struct { + FeeRecipient common.Address } -// DefaultProposerOption returns a Proposer Option with defaults filled -func DefaultProposerOption() ProposerOption { - return ProposerOption{ - FeeRecipient: params.BeaconConfig().DefaultFeeRecipient, - BuilderConfig: nil, - } +// ProposerOption is a Prysm internal representation of the ProposerOptionPayload on the validator client in bytes format instead of hex. +type ProposerOption struct { + FeeRecipientConfig *FeeRecipientConfig + BuilderConfig *BuilderConfig } diff --git a/crypto/bls/common/mock/BUILD.bazel b/crypto/bls/common/mock/BUILD.bazel new file mode 100644 index 000000000..fa69fb23f --- /dev/null +++ b/crypto/bls/common/mock/BUILD.bazel @@ -0,0 +1,12 @@ +load("@prysm//tools/go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["interface_mock.go"], + importpath = "github.com/prysmaticlabs/prysm/v3/crypto/bls/common/mock", + visibility = ["//visibility:public"], + deps = [ + "//crypto/bls/common:go_default_library", + "@com_github_golang_mock//gomock:go_default_library", + ], +) diff --git a/crypto/bls/common/mock/interface_mock.go b/crypto/bls/common/mock/interface_mock.go new file mode 100644 index 000000000..13fec4f3b --- /dev/null +++ b/crypto/bls/common/mock/interface_mock.go @@ -0,0 +1,277 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: crypto/bls/common/interface.go + +// Package mock is a generated GoMock package. +package mock + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + common "github.com/prysmaticlabs/prysm/v3/crypto/bls/common" +) + +// MockSecretKey is a mock of SecretKey interface. +type MockSecretKey struct { + ctrl *gomock.Controller + recorder *MockSecretKeyMockRecorder +} + +// MockSecretKeyMockRecorder is the mock recorder for MockSecretKey. +type MockSecretKeyMockRecorder struct { + mock *MockSecretKey +} + +// NewMockSecretKey creates a new mock instance. +func NewMockSecretKey(ctrl *gomock.Controller) *MockSecretKey { + mock := &MockSecretKey{ctrl: ctrl} + mock.recorder = &MockSecretKeyMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockSecretKey) EXPECT() *MockSecretKeyMockRecorder { + return m.recorder +} + +// Marshal mocks base method. +func (m *MockSecretKey) Marshal() []byte { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Marshal") + ret0, _ := ret[0].([]byte) + return ret0 +} + +// Marshal indicates an expected call of Marshal. +func (mr *MockSecretKeyMockRecorder) Marshal() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Marshal", reflect.TypeOf((*MockSecretKey)(nil).Marshal)) +} + +// PublicKey mocks base method. +func (m *MockSecretKey) PublicKey() common.PublicKey { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PublicKey") + ret0, _ := ret[0].(common.PublicKey) + return ret0 +} + +// PublicKey indicates an expected call of PublicKey. +func (mr *MockSecretKeyMockRecorder) PublicKey() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PublicKey", reflect.TypeOf((*MockSecretKey)(nil).PublicKey)) +} + +// Sign mocks base method. +func (m *MockSecretKey) Sign(msg []byte) common.Signature { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Sign", msg) + ret0, _ := ret[0].(common.Signature) + return ret0 +} + +// Sign indicates an expected call of Sign. +func (mr *MockSecretKeyMockRecorder) Sign(msg interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Sign", reflect.TypeOf((*MockSecretKey)(nil).Sign), msg) +} + +// MockPublicKey is a mock of PublicKey interface. +type MockPublicKey struct { + ctrl *gomock.Controller + recorder *MockPublicKeyMockRecorder +} + +// MockPublicKeyMockRecorder is the mock recorder for MockPublicKey. +type MockPublicKeyMockRecorder struct { + mock *MockPublicKey +} + +// NewMockPublicKey creates a new mock instance. +func NewMockPublicKey(ctrl *gomock.Controller) *MockPublicKey { + mock := &MockPublicKey{ctrl: ctrl} + mock.recorder = &MockPublicKeyMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockPublicKey) EXPECT() *MockPublicKeyMockRecorder { + return m.recorder +} + +// Aggregate mocks base method. +func (m *MockPublicKey) Aggregate(p2 common.PublicKey) common.PublicKey { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Aggregate", p2) + ret0, _ := ret[0].(common.PublicKey) + return ret0 +} + +// Aggregate indicates an expected call of Aggregate. +func (mr *MockPublicKeyMockRecorder) Aggregate(p2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Aggregate", reflect.TypeOf((*MockPublicKey)(nil).Aggregate), p2) +} + +// Copy mocks base method. +func (m *MockPublicKey) Copy() common.PublicKey { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Copy") + ret0, _ := ret[0].(common.PublicKey) + return ret0 +} + +// Copy indicates an expected call of Copy. +func (mr *MockPublicKeyMockRecorder) Copy() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Copy", reflect.TypeOf((*MockPublicKey)(nil).Copy)) +} + +// Equals mocks base method. +func (m *MockPublicKey) Equals(p2 common.PublicKey) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Equals", p2) + ret0, _ := ret[0].(bool) + return ret0 +} + +// Equals indicates an expected call of Equals. +func (mr *MockPublicKeyMockRecorder) Equals(p2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Equals", reflect.TypeOf((*MockPublicKey)(nil).Equals), p2) +} + +// IsInfinite mocks base method. +func (m *MockPublicKey) IsInfinite() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsInfinite") + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsInfinite indicates an expected call of IsInfinite. +func (mr *MockPublicKeyMockRecorder) IsInfinite() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsInfinite", reflect.TypeOf((*MockPublicKey)(nil).IsInfinite)) +} + +// Marshal mocks base method. +func (m *MockPublicKey) Marshal() []byte { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Marshal") + ret0, _ := ret[0].([]byte) + return ret0 +} + +// Marshal indicates an expected call of Marshal. +func (mr *MockPublicKeyMockRecorder) Marshal() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Marshal", reflect.TypeOf((*MockPublicKey)(nil).Marshal)) +} + +// MockSignature is a mock of Signature interface. +type MockSignature struct { + ctrl *gomock.Controller + recorder *MockSignatureMockRecorder +} + +// MockSignatureMockRecorder is the mock recorder for MockSignature. +type MockSignatureMockRecorder struct { + mock *MockSignature +} + +// NewMockSignature creates a new mock instance. +func NewMockSignature(ctrl *gomock.Controller) *MockSignature { + mock := &MockSignature{ctrl: ctrl} + mock.recorder = &MockSignatureMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockSignature) EXPECT() *MockSignatureMockRecorder { + return m.recorder +} + +// AggregateVerify mocks base method. +func (m *MockSignature) AggregateVerify(pubKeys []common.PublicKey, msgs [][32]byte) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AggregateVerify", pubKeys, msgs) + ret0, _ := ret[0].(bool) + return ret0 +} + +// AggregateVerify indicates an expected call of AggregateVerify. +func (mr *MockSignatureMockRecorder) AggregateVerify(pubKeys, msgs interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AggregateVerify", reflect.TypeOf((*MockSignature)(nil).AggregateVerify), pubKeys, msgs) +} + +// Copy mocks base method. +func (m *MockSignature) Copy() common.Signature { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Copy") + ret0, _ := ret[0].(common.Signature) + return ret0 +} + +// Copy indicates an expected call of Copy. +func (mr *MockSignatureMockRecorder) Copy() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Copy", reflect.TypeOf((*MockSignature)(nil).Copy)) +} + +// Eth2FastAggregateVerify mocks base method. +func (m *MockSignature) Eth2FastAggregateVerify(pubKeys []common.PublicKey, msg [32]byte) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Eth2FastAggregateVerify", pubKeys, msg) + ret0, _ := ret[0].(bool) + return ret0 +} + +// Eth2FastAggregateVerify indicates an expected call of Eth2FastAggregateVerify. +func (mr *MockSignatureMockRecorder) Eth2FastAggregateVerify(pubKeys, msg interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Eth2FastAggregateVerify", reflect.TypeOf((*MockSignature)(nil).Eth2FastAggregateVerify), pubKeys, msg) +} + +// FastAggregateVerify mocks base method. +func (m *MockSignature) FastAggregateVerify(pubKeys []common.PublicKey, msg [32]byte) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FastAggregateVerify", pubKeys, msg) + ret0, _ := ret[0].(bool) + return ret0 +} + +// FastAggregateVerify indicates an expected call of FastAggregateVerify. +func (mr *MockSignatureMockRecorder) FastAggregateVerify(pubKeys, msg interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FastAggregateVerify", reflect.TypeOf((*MockSignature)(nil).FastAggregateVerify), pubKeys, msg) +} + +// Marshal mocks base method. +func (m *MockSignature) Marshal() []byte { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Marshal") + ret0, _ := ret[0].([]byte) + return ret0 +} + +// Marshal indicates an expected call of Marshal. +func (mr *MockSignatureMockRecorder) Marshal() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Marshal", reflect.TypeOf((*MockSignature)(nil).Marshal)) +} + +// Verify mocks base method. +func (m *MockSignature) Verify(pubKey common.PublicKey, msg []byte) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Verify", pubKey, msg) + ret0, _ := ret[0].(bool) + return ret0 +} + +// Verify indicates an expected call of Verify. +func (mr *MockSignatureMockRecorder) Verify(pubKey, msg interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Verify", reflect.TypeOf((*MockSignature)(nil).Verify), pubKey, msg) +} diff --git a/hack/update-mockgen.sh b/hack/update-mockgen.sh index c7a9fa3ee..72510cf26 100755 --- a/hack/update-mockgen.sh +++ b/hack/update-mockgen.sh @@ -88,3 +88,20 @@ done goimports -w "$beacon_api_mock_path/." gofmt -s -w "$beacon_api_mock_path/." + +# github.com/prysmaticlabs/prysm/v3/crypto/bls +# -------------------------------------------- +crypto_bls_common_mock_path="crypto/bls/common/mock" +crypto_bls_common_mocks=( + "$crypto_bls_common_mock_path/interface_mock.go interface.go" +) + +for ((i = 0; i < ${#crypto_bls_common_mocks[@]}; i++)); do + file=${crypto_bls_common_mocks[i]% *}; + source=${crypto_bls_common_mocks[i]#* }; + echo "generating $file for file: $source"; + GO11MODULE=on mockgen -package=mock -source="crypto/bls/common/$source" -destination="$file" +done + +goimports -w "$crypto_bls_common_mock_path/." +gofmt -s -w "$crypto_bls_common_mock_path/." diff --git a/validator/client/BUILD.bazel b/validator/client/BUILD.bazel index 3fd0e24fd..073209ffa 100644 --- a/validator/client/BUILD.bazel +++ b/validator/client/BUILD.bazel @@ -127,6 +127,7 @@ go_test( "//consensus-types/interfaces:go_default_library", "//consensus-types/primitives:go_default_library", "//crypto/bls:go_default_library", + "//crypto/bls/common/mock:go_default_library", "//encoding/bytesutil:go_default_library", "//io/file:go_default_library", "//proto/eth/service:go_default_library", @@ -165,7 +166,9 @@ go_test( "@in_gopkg_d4l3k_messagediff_v1//:go_default_library", "@io_bazel_rules_go//go/tools/bazel:go_default_library", "@io_bazel_rules_go//proto/wkt:empty_go_proto", + "@org_golang_google_grpc//codes:go_default_library", "@org_golang_google_grpc//metadata:go_default_library", + "@org_golang_google_grpc//status:go_default_library", "@org_golang_google_protobuf//types/known/emptypb:go_default_library", "@org_golang_google_protobuf//types/known/timestamppb:go_default_library", ], diff --git a/validator/client/runner.go b/validator/client/runner.go index 476556131..7339f3cf2 100644 --- a/validator/client/runner.go +++ b/validator/client/runner.go @@ -58,7 +58,7 @@ func run(ctx context.Context, v iface.Validator) { // Set properties on the beacon node like the fee recipient for validators that are being used & active. if v.ProposerSettings() != nil { log.Infof("Validator client started with provided proposer settings that sets options such as fee recipient"+ - " and will periodically update the beacon node and custom builder ( if --%s)", flags.EnableBuilderFlag.Name) + " and will periodically update the beacon node and custom builder (if --%s)", flags.EnableBuilderFlag.Name) if err := v.PushProposerSettings(ctx, km); err != nil { if errors.Is(err, ErrBuilderValidatorRegistration) { log.WithError(err).Warn("Push proposer settings error") diff --git a/validator/client/runner_test.go b/validator/client/runner_test.go index 386910441..51c563ea9 100644 --- a/validator/client/runner_test.go +++ b/validator/client/runner_test.go @@ -253,7 +253,9 @@ func TestUpdateProposerSettingsAt_EpochStart(t *testing.T) { v := &testutil.FakeValidator{Km: &mockKeymanager{accountsChangedFeed: &event.Feed{}}} v.SetProposerSettings(&validatorserviceconfig.ProposerSettings{ DefaultConfig: &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9"), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9"), + }, }, }) ctx, cancel := context.WithCancel(context.Background()) @@ -279,7 +281,9 @@ func TestUpdateProposerSettings_ContinuesAfterValidatorRegistrationFails(t *test } v.SetProposerSettings(&validatorserviceconfig.ProposerSettings{ DefaultConfig: &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9"), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9"), + }, }, }) ctx, cancel := context.WithCancel(context.Background()) diff --git a/validator/client/validator.go b/validator/client/validator.go index f00f24595..441040334 100644 --- a/validator/client/validator.go +++ b/validator/client/validator.go @@ -1037,6 +1037,16 @@ func (v *validator) buildPrepProposerReqs(ctx context.Context, pubkeys [][fieldp var prepareProposerReqs []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer for _, k := range pubkeys { + // Default case: Define fee recipient to burn address + var feeRecipient common.Address + isFeeRecipientDefined := false + + // If fee recipient is defined in default configuration, use it + if v.ProposerSettings() != nil && v.ProposerSettings().DefaultConfig != nil && v.ProposerSettings().DefaultConfig.FeeRecipientConfig != nil { + feeRecipient = v.ProposerSettings().DefaultConfig.FeeRecipientConfig.FeeRecipient // Use cli config for fee recipient. + isFeeRecipientDefined = true + } + validatorIndex, ok := v.pubkeyToValidatorIndex[k] // Get validator index from RPC server if not found. if !ok { @@ -1044,33 +1054,40 @@ func (v *validator) buildPrepProposerReqs(ctx context.Context, pubkeys [][fieldp if err != nil { return nil, err } + if !ok { // Nothing we can do if RPC server doesn't have validator index. continue } + validatorIndex = i v.pubkeyToValidatorIndex[k] = i } - feeRecipient := common.HexToAddress(params.BeaconConfig().EthBurnAddressHex) - if v.ProposerSettings().DefaultConfig != nil { - feeRecipient = v.ProposerSettings().DefaultConfig.FeeRecipient // Use cli config for fee recipient. - } - if v.ProposerSettings().ProposeConfig != nil { + + // If fee recipient is defined for this specific pubkey in proposer configuration, use it + if v.ProposerSettings() != nil && v.ProposerSettings().ProposeConfig != nil { config, ok := v.ProposerSettings().ProposeConfig[k] - if ok && config != nil { - feeRecipient = config.FeeRecipient // Use file config for fee recipient. + + if ok && config != nil && config.FeeRecipientConfig != nil { + feeRecipient = config.FeeRecipientConfig.FeeRecipient // Use file config for fee recipient. + isFeeRecipientDefined = true } } - prepareProposerReqs = append(prepareProposerReqs, ðpb.PrepareBeaconProposerRequest_FeeRecipientContainer{ - ValidatorIndex: validatorIndex, - FeeRecipient: feeRecipient[:], - }) - if hexutil.Encode(feeRecipient.Bytes()) == params.BeaconConfig().EthBurnAddressHex { - log.WithFields(logrus.Fields{ - "validatorIndex": validatorIndex, - "feeRecipient": feeRecipient, - }).Warn("Fee recipient is burn address") + + if isFeeRecipientDefined { + prepareProposerReqs = append(prepareProposerReqs, ðpb.PrepareBeaconProposerRequest_FeeRecipientContainer{ + ValidatorIndex: validatorIndex, + FeeRecipient: feeRecipient[:], + }) + + if hexutil.Encode(feeRecipient.Bytes()) == params.BeaconConfig().EthBurnAddressHex { + log.WithFields(logrus.Fields{ + "validatorIndex": validatorIndex, + "feeRecipient": feeRecipient, + }).Warn("Fee recipient is burn address") + } } } + return prepareProposerReqs, nil } @@ -1081,18 +1098,22 @@ func (v *validator) buildSignedRegReqs(ctx context.Context, pubkeys [][fieldpara feeRecipient := common.HexToAddress(params.BeaconConfig().EthBurnAddressHex) gasLimit := params.BeaconConfig().DefaultBuilderGasLimit enabled := false - if v.ProposerSettings().DefaultConfig != nil { - feeRecipient = v.ProposerSettings().DefaultConfig.FeeRecipient // Use cli config for fee recipient. - config := v.ProposerSettings().DefaultConfig.BuilderConfig - if config != nil && config.Enabled { - gasLimit = uint64(config.GasLimit) // Use cli config for gas limit. + + if v.ProposerSettings().DefaultConfig != nil && v.ProposerSettings().DefaultConfig.FeeRecipientConfig != nil { + defaultConfig := v.ProposerSettings().DefaultConfig + feeRecipient = defaultConfig.FeeRecipientConfig.FeeRecipient // Use cli defaultBuilderConfig for fee recipient. + defaultBuilderConfig := defaultConfig.BuilderConfig + + if defaultBuilderConfig != nil && defaultBuilderConfig.Enabled { + gasLimit = uint64(defaultBuilderConfig.GasLimit) // Use cli config for gas limit. enabled = true } } + if v.ProposerSettings().ProposeConfig != nil { config, ok := v.ProposerSettings().ProposeConfig[k] - if ok && config != nil { - feeRecipient = config.FeeRecipient // Use file config for fee recipient. + if ok && config != nil && config.FeeRecipientConfig != nil { + feeRecipient = config.FeeRecipientConfig.FeeRecipient // Use file config for fee recipient. builderConfig := config.BuilderConfig if builderConfig != nil { if builderConfig.Enabled { @@ -1104,15 +1125,18 @@ func (v *validator) buildSignedRegReqs(ctx context.Context, pubkeys [][fieldpara } } } + if !enabled { continue } + req := ðpb.ValidatorRegistrationV1{ FeeRecipient: feeRecipient[:], GasLimit: gasLimit, Timestamp: uint64(time.Now().UTC().Unix()), Pubkey: pubkeys[i][:], } + signedReq, err := v.SignValidatorRegistrationRequest(ctx, signer, req) if err != nil { log.WithFields(logrus.Fields{ @@ -1121,7 +1145,9 @@ func (v *validator) buildSignedRegReqs(ctx context.Context, pubkeys [][fieldpara }).Error(err) continue } + signedValRegRegs = append(signedValRegRegs, signedReq) + if hexutil.Encode(feeRecipient.Bytes()) == params.BeaconConfig().EthBurnAddressHex { log.WithFields(logrus.Fields{ "pubkey": fmt.Sprintf("%#x", req.Pubkey), diff --git a/validator/client/validator_test.go b/validator/client/validator_test.go index eb19ae664..7429b4622 100644 --- a/validator/client/validator_test.go +++ b/validator/client/validator_test.go @@ -22,6 +22,7 @@ import ( validatorserviceconfig "github.com/prysmaticlabs/prysm/v3/config/validator/service" "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v3/crypto/bls" + blsmock "github.com/prysmaticlabs/prysm/v3/crypto/bls/common/mock" "github.com/prysmaticlabs/prysm/v3/encoding/bytesutil" ethpbservice "github.com/prysmaticlabs/prysm/v3/proto/eth/service" ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1" @@ -38,6 +39,8 @@ import ( remoteweb3signer "github.com/prysmaticlabs/prysm/v3/validator/keymanager/remote-web3signer" "github.com/sirupsen/logrus" logTest "github.com/sirupsen/logrus/hooks/test" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/emptypb" ) @@ -1417,7 +1420,7 @@ func TestValidator_PushProposerSettings(t *testing.T) { doesntContainLogs bool }{ { - name: " Happy Path proposer config not nil", + name: "Happy Path proposer config not nil", validatorSetter: func(t *testing.T) *validator { v := validator{ @@ -1458,7 +1461,9 @@ func TestValidator_PushProposerSettings(t *testing.T) { }, }).Return(nil, nil) config[keys[0]] = &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9"), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9"), + }, BuilderConfig: &validatorserviceconfig.BuilderConfig{ Enabled: true, GasLimit: 40000000, @@ -1467,7 +1472,9 @@ func TestValidator_PushProposerSettings(t *testing.T) { v.SetProposerSettings(&validatorserviceconfig.ProposerSettings{ ProposeConfig: config, DefaultConfig: &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.HexToAddress(defaultFeeHex), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress(defaultFeeHex), + }, BuilderConfig: &validatorserviceconfig.BuilderConfig{ Enabled: true, GasLimit: 35000000, @@ -1538,7 +1545,9 @@ func TestValidator_PushProposerSettings(t *testing.T) { }, }).Return(nil, nil) config[keys[0]] = &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9"), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9"), + }, BuilderConfig: &validatorserviceconfig.BuilderConfig{ Enabled: true, GasLimit: 40000000, @@ -1547,7 +1556,9 @@ func TestValidator_PushProposerSettings(t *testing.T) { v.SetProposerSettings(&validatorserviceconfig.ProposerSettings{ ProposeConfig: config, DefaultConfig: &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.HexToAddress(defaultFeeHex), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress(defaultFeeHex), + }, BuilderConfig: &validatorserviceconfig.BuilderConfig{ Enabled: false, GasLimit: 35000000, @@ -1614,12 +1625,16 @@ func TestValidator_PushProposerSettings(t *testing.T) { }, }).Return(nil, nil) config[keys[0]] = &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9"), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9"), + }, } v.SetProposerSettings(&validatorserviceconfig.ProposerSettings{ ProposeConfig: config, DefaultConfig: &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.HexToAddress(defaultFeeHex), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress(defaultFeeHex), + }, }, }) return &v @@ -1659,7 +1674,9 @@ func TestValidator_PushProposerSettings(t *testing.T) { v.SetProposerSettings(&validatorserviceconfig.ProposerSettings{ ProposeConfig: nil, DefaultConfig: &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.HexToAddress(defaultFeeHex), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress(defaultFeeHex), + }, BuilderConfig: &validatorserviceconfig.BuilderConfig{ Enabled: true, GasLimit: validatorserviceconfig.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), @@ -1715,7 +1732,9 @@ func TestValidator_PushProposerSettings(t *testing.T) { v.SetProposerSettings(&validatorserviceconfig.ProposerSettings{ ProposeConfig: nil, DefaultConfig: &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.HexToAddress(defaultFeeHex), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress(defaultFeeHex), + }, BuilderConfig: &validatorserviceconfig.BuilderConfig{ Enabled: true, GasLimit: 40000000, @@ -1788,12 +1807,16 @@ func TestValidator_PushProposerSettings(t *testing.T) { }, }).Return(nil, nil) config[keys[0]] = &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.Address{}, + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.Address{}, + }, } v.SetProposerSettings(&validatorserviceconfig.ProposerSettings{ ProposeConfig: config, DefaultConfig: &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.HexToAddress(defaultFeeHex), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress(defaultFeeHex), + }, }, }) return &v @@ -1826,12 +1849,16 @@ func TestValidator_PushProposerSettings(t *testing.T) { ðpb.ValidatorIndexRequest{PublicKey: keys[0][:]}, ).Return(nil, errors.New("could not find validator index for public key")) config[keys[0]] = &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9"), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9"), + }, } v.SetProposerSettings(&validatorserviceconfig.ProposerSettings{ ProposeConfig: config, DefaultConfig: &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.HexToAddress(defaultFeeHex), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress(defaultFeeHex), + }, }, }) return &v @@ -1867,7 +1894,9 @@ func TestValidator_PushProposerSettings(t *testing.T) { }, nil) config[keys[0]] = &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.Address{}, + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.Address{}, + }, BuilderConfig: &validatorserviceconfig.BuilderConfig{ Enabled: true, GasLimit: 40000000, @@ -1876,7 +1905,9 @@ func TestValidator_PushProposerSettings(t *testing.T) { v.SetProposerSettings(&validatorserviceconfig.ProposerSettings{ ProposeConfig: config, DefaultConfig: &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.HexToAddress(defaultFeeHex), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress(defaultFeeHex), + }, BuilderConfig: &validatorserviceconfig.BuilderConfig{ Enabled: true, GasLimit: 40000000, @@ -1943,3 +1974,433 @@ func TestValidator_PushProposerSettings(t *testing.T) { }) } } + +func TestValidator_buildPrepProposerReqs_InvalidValidatorIndex(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + ctx := context.Background() + client := mock2.NewMockValidatorClient(ctrl) + v := validator{validatorClient: client} + pubkeys := [][fieldparams.BLSPubkeyLength]byte{{}} + + client.EXPECT().ValidatorIndex( + ctx, + gomock.Any(), + ).Return(nil, errors.New("custom error")) + + _, err := v.buildPrepProposerReqs(ctx, pubkeys) + assert.ErrorContains(t, "custom error", err) +} + +func getPubkeyFromString(t *testing.T, stringPubkey string) [fieldparams.BLSPubkeyLength]byte { + pubkeyTemp, err := hexutil.Decode(stringPubkey) + require.NoError(t, err) + + var pubkey [fieldparams.BLSPubkeyLength]byte + copy(pubkey[:], pubkeyTemp) + + return pubkey +} + +func getFeeRecipientFromString(t *testing.T, stringFeeRecipient string) common.Address { + feeRecipientTemp, err := hexutil.Decode(stringFeeRecipient) + require.NoError(t, err) + + var feeRecipient common.Address + copy(feeRecipient[:], feeRecipientTemp) + + return feeRecipient +} + +func TestValidator_buildPrepProposerReqs_WithoutDefaultConfig(t *testing.T) { + // pubkey1 => feeRecipient1 (already in `v.validatorIndex`) + // pubkey2 => feeRecipient2 (NOT in `v.validatorIndex`, index found by beacon node) + // pubkey3 => feeRecipient3 (NOT in `v.validatorIndex`, index NOT found by beacon node) + // pubkey4 => Nothing (already in `v.validatorIndex`) + + // Public keys + pubkey1 := getPubkeyFromString(t, "0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111") + pubkey2 := getPubkeyFromString(t, "0x222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222") + pubkey3 := getPubkeyFromString(t, "0x333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333") + pubkey4 := getPubkeyFromString(t, "0x444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444") + + // Fee recipients + feeRecipient1 := getFeeRecipientFromString(t, "0x1111111111111111111111111111111111111111") + feeRecipient2 := getFeeRecipientFromString(t, "0x0000000000000000000000000000000000000000") + feeRecipient3 := getFeeRecipientFromString(t, "0x3333333333333333333333333333333333333333") + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + ctx := context.Background() + client := mock2.NewMockValidatorClient(ctrl) + + client.EXPECT().ValidatorIndex( + ctx, + ðpb.ValidatorIndexRequest{ + PublicKey: pubkey2[:], + }, + ).Return(ðpb.ValidatorIndexResponse{ + Index: 2, + }, nil) + + client.EXPECT().ValidatorIndex( + ctx, + ðpb.ValidatorIndexRequest{ + PublicKey: pubkey3[:], + }, + ).Return(nil, status.Error(codes.NotFound, "NOT_FOUND")) + + v := validator{ + validatorClient: client, + proposerSettings: &validatorserviceconfig.ProposerSettings{ + DefaultConfig: nil, + ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{ + pubkey1: { + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: feeRecipient1, + }, + }, + pubkey2: { + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: feeRecipient2, + }, + }, + pubkey3: { + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: feeRecipient3, + }, + }, + }, + }, + pubkeyToValidatorIndex: map[[48]byte]primitives.ValidatorIndex{ + pubkey1: 1, + pubkey4: 4, + }, + } + + pubkeys := [][fieldparams.BLSPubkeyLength]byte{pubkey1, pubkey2, pubkey3, pubkey4} + + expected := []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{ + { + ValidatorIndex: 1, + FeeRecipient: feeRecipient1[:], + }, + { + ValidatorIndex: 2, + FeeRecipient: feeRecipient2[:], + }, + } + + actual, err := v.buildPrepProposerReqs(ctx, pubkeys) + require.NoError(t, err) + assert.DeepEqual(t, expected, actual) +} + +func TestValidator_buildPrepProposerReqs_WithDefaultConfig(t *testing.T) { + // pubkey1 => feeRecipient1 (already in `v.validatorIndex`) + // pubkey2 => feeRecipient2 (NOT in `v.validatorIndex`, index found by beacon node) + // pubkey3 => feeRecipient3 (NOT in `v.validatorIndex`, index NOT found by beacon node) + // pubkey4 => Nothing (already in `v.validatorIndex`) + + // Public keys + pubkey1 := getPubkeyFromString(t, "0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111") + pubkey2 := getPubkeyFromString(t, "0x222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222") + pubkey3 := getPubkeyFromString(t, "0x333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333") + pubkey4 := getPubkeyFromString(t, "0x444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444") + + // Fee recipients + feeRecipient1 := getFeeRecipientFromString(t, "0x1111111111111111111111111111111111111111") + feeRecipient2 := getFeeRecipientFromString(t, "0x0000000000000000000000000000000000000000") + feeRecipient3 := getFeeRecipientFromString(t, "0x3333333333333333333333333333333333333333") + + defaultFeeRecipient := getFeeRecipientFromString(t, "0xdddddddddddddddddddddddddddddddddddddddd") + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + ctx := context.Background() + client := mock2.NewMockValidatorClient(ctrl) + + client.EXPECT().ValidatorIndex( + ctx, + ðpb.ValidatorIndexRequest{ + PublicKey: pubkey2[:], + }, + ).Return(ðpb.ValidatorIndexResponse{ + Index: 2, + }, nil) + + client.EXPECT().ValidatorIndex( + ctx, + ðpb.ValidatorIndexRequest{ + PublicKey: pubkey3[:], + }, + ).Return(nil, status.Error(codes.NotFound, "NOT_FOUND")) + + v := validator{ + validatorClient: client, + proposerSettings: &validatorserviceconfig.ProposerSettings{ + DefaultConfig: &validatorserviceconfig.ProposerOption{ + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: defaultFeeRecipient, + }, + }, + ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{ + pubkey1: { + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: feeRecipient1, + }, + }, + pubkey2: { + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: feeRecipient2, + }, + }, + pubkey3: { + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: feeRecipient3, + }, + }, + }, + }, + pubkeyToValidatorIndex: map[[48]byte]primitives.ValidatorIndex{ + pubkey1: 1, + pubkey4: 4, + }, + } + + pubkeys := [][fieldparams.BLSPubkeyLength]byte{pubkey1, pubkey2, pubkey3, pubkey4} + + expected := []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{ + { + ValidatorIndex: 1, + FeeRecipient: feeRecipient1[:], + }, + { + ValidatorIndex: 2, + FeeRecipient: feeRecipient2[:], + }, + { + ValidatorIndex: 4, + FeeRecipient: defaultFeeRecipient[:], + }, + } + + actual, err := v.buildPrepProposerReqs(ctx, pubkeys) + require.NoError(t, err) + assert.DeepEqual(t, expected, actual) +} + +func TestValidator_buildSignedRegReqs_DefaultConfigDisabled(t *testing.T) { + // pubkey1 => feeRecipient1, builder enabled + // pubkey2 => feeRecipient2, builder disabled + // pubkey3 => Nothing, builder enabled + + // Public keys + pubkey1 := getPubkeyFromString(t, "0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111") + pubkey2 := getPubkeyFromString(t, "0x222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222") + pubkey3 := getPubkeyFromString(t, "0x333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333") + + // Fee recipients + feeRecipient1 := getFeeRecipientFromString(t, "0x0000000000000000000000000000000000000000") + feeRecipient2 := getFeeRecipientFromString(t, "0x2222222222222222222222222222222222222222") + + defaultFeeRecipient := getFeeRecipientFromString(t, "0xdddddddddddddddddddddddddddddddddddddddd") + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + ctx := context.Background() + client := mock2.NewMockValidatorClient(ctrl) + + signature := blsmock.NewMockSignature(ctrl) + signature.EXPECT().Marshal().Return([]byte{}) + + v := validator{ + signedValidatorRegistrations: map[[48]byte]*ethpb.SignedValidatorRegistrationV1{}, + validatorClient: client, + proposerSettings: &validatorserviceconfig.ProposerSettings{ + DefaultConfig: &validatorserviceconfig.ProposerOption{ + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: defaultFeeRecipient, + }, + BuilderConfig: &validatorserviceconfig.BuilderConfig{ + Enabled: false, + GasLimit: 9999, + }, + }, + ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{ + pubkey1: { + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: feeRecipient1, + }, + BuilderConfig: &validatorserviceconfig.BuilderConfig{ + Enabled: true, + GasLimit: 1111, + }, + }, + pubkey2: { + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: feeRecipient2, + }, + BuilderConfig: &validatorserviceconfig.BuilderConfig{ + Enabled: false, + GasLimit: 2222, + }, + }, + pubkey3: { + FeeRecipientConfig: nil, + BuilderConfig: &validatorserviceconfig.BuilderConfig{ + Enabled: true, + GasLimit: 3333, + }, + }, + }, + }, + } + + pubkeys := [][fieldparams.BLSPubkeyLength]byte{pubkey1, pubkey2, pubkey3} + + var signer = func(_ context.Context, _ *validatorpb.SignRequest) (bls.Signature, error) { + return signature, nil + } + + actual, err := v.buildSignedRegReqs(ctx, pubkeys, signer) + require.NoError(t, err) + + assert.Equal(t, 1, len(actual)) + assert.DeepEqual(t, feeRecipient1[:], actual[0].Message.FeeRecipient) + assert.Equal(t, uint64(1111), actual[0].Message.GasLimit) + assert.DeepEqual(t, pubkey1[:], actual[0].Message.Pubkey) +} + +func TestValidator_buildSignedRegReqs_DefaultConfigEnabled(t *testing.T) { + // pubkey1 => feeRecipient1, builder enabled + // pubkey2 => feeRecipient2, builder disabled + // pubkey3 => Nothing, builder enabled + + // Public keys + pubkey1 := getPubkeyFromString(t, "0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111") + pubkey2 := getPubkeyFromString(t, "0x222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222") + pubkey3 := getPubkeyFromString(t, "0x333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333") + + // Fee recipients + feeRecipient1 := getFeeRecipientFromString(t, "0x0000000000000000000000000000000000000000") + feeRecipient2 := getFeeRecipientFromString(t, "0x2222222222222222222222222222222222222222") + + defaultFeeRecipient := getFeeRecipientFromString(t, "0xdddddddddddddddddddddddddddddddddddddddd") + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + ctx := context.Background() + client := mock2.NewMockValidatorClient(ctrl) + + signature := blsmock.NewMockSignature(ctrl) + signature.EXPECT().Marshal().Return([]byte{}).Times(2) + + v := validator{ + signedValidatorRegistrations: map[[48]byte]*ethpb.SignedValidatorRegistrationV1{}, + validatorClient: client, + proposerSettings: &validatorserviceconfig.ProposerSettings{ + DefaultConfig: &validatorserviceconfig.ProposerOption{ + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: defaultFeeRecipient, + }, + BuilderConfig: &validatorserviceconfig.BuilderConfig{ + Enabled: true, + GasLimit: 9999, + }, + }, + ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{ + pubkey1: { + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: feeRecipient1, + }, + BuilderConfig: &validatorserviceconfig.BuilderConfig{ + Enabled: true, + GasLimit: 1111, + }, + }, + pubkey2: { + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: feeRecipient2, + }, + BuilderConfig: &validatorserviceconfig.BuilderConfig{ + Enabled: false, + GasLimit: 2222, + }, + }, + pubkey3: { + FeeRecipientConfig: nil, + BuilderConfig: &validatorserviceconfig.BuilderConfig{ + Enabled: true, + GasLimit: 3333, + }, + }, + }, + }, + } + + pubkeys := [][fieldparams.BLSPubkeyLength]byte{pubkey1, pubkey2, pubkey3} + + var signer = func(_ context.Context, _ *validatorpb.SignRequest) (bls.Signature, error) { + return signature, nil + } + + actual, err := v.buildSignedRegReqs(ctx, pubkeys, signer) + require.NoError(t, err) + + assert.Equal(t, 2, len(actual)) + + assert.DeepEqual(t, feeRecipient1[:], actual[0].Message.FeeRecipient) + assert.Equal(t, uint64(1111), actual[0].Message.GasLimit) + assert.DeepEqual(t, pubkey1[:], actual[0].Message.Pubkey) + + assert.DeepEqual(t, defaultFeeRecipient[:], actual[1].Message.FeeRecipient) + assert.Equal(t, uint64(9999), actual[1].Message.GasLimit) + assert.DeepEqual(t, pubkey3[:], actual[1].Message.Pubkey) +} + +func TestValidator_buildSignedRegReqs_SignerOnError(t *testing.T) { + // Public keys + pubkey1 := getPubkeyFromString(t, "0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111") + + // Fee recipients + defaultFeeRecipient := getFeeRecipientFromString(t, "0xdddddddddddddddddddddddddddddddddddddddd") + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + ctx := context.Background() + client := mock2.NewMockValidatorClient(ctrl) + + v := validator{ + signedValidatorRegistrations: map[[48]byte]*ethpb.SignedValidatorRegistrationV1{}, + validatorClient: client, + proposerSettings: &validatorserviceconfig.ProposerSettings{ + DefaultConfig: &validatorserviceconfig.ProposerOption{ + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: defaultFeeRecipient, + }, + BuilderConfig: &validatorserviceconfig.BuilderConfig{ + Enabled: true, + GasLimit: 9999, + }, + }, + }, + } + + pubkeys := [][fieldparams.BLSPubkeyLength]byte{pubkey1} + + var signer = func(_ context.Context, _ *validatorpb.SignRequest) (bls.Signature, error) { + return nil, errors.New("custom error") + } + + actual, err := v.buildSignedRegReqs(ctx, pubkeys, signer) + require.NoError(t, err) + + assert.Equal(t, 0, len(actual)) +} diff --git a/validator/node/node.go b/validator/node/node.go index effb4a874..ffca167e6 100644 --- a/validator/node/node.go +++ b/validator/node/node.go @@ -543,7 +543,9 @@ func proposerSettings(cliCtx *cli.Context) (*validatorServiceConfig.ProposerSett return nil, err } vpSettings.DefaultConfig = &validatorServiceConfig.ProposerOption{ - FeeRecipient: common.HexToAddress(fileConfig.DefaultConfig.FeeRecipient), + FeeRecipientConfig: &validatorServiceConfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress(fileConfig.DefaultConfig.FeeRecipient), + }, BuilderConfig: fileConfig.DefaultConfig.BuilderConfig, } if vpSettings.DefaultConfig.BuilderConfig == nil { @@ -587,7 +589,9 @@ func proposerSettings(cliCtx *cli.Context) (*validatorServiceConfig.ProposerSett option.BuilderConfig = builderConfig } vpSettings.ProposeConfig[bytesutil.ToBytes48(decodedKey)] = &validatorServiceConfig.ProposerOption{ - FeeRecipient: common.HexToAddress(option.FeeRecipient), + FeeRecipientConfig: &validatorServiceConfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress(option.FeeRecipient), + }, BuilderConfig: option.BuilderConfig, } diff --git a/validator/node/node_test.go b/validator/node/node_test.go index 818b5c51f..bddb3bc51 100644 --- a/validator/node/node_test.go +++ b/validator/node/node_test.go @@ -242,11 +242,15 @@ func TestProposerSettings(t *testing.T) { return &validatorserviceconfig.ProposerSettings{ ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validatorserviceconfig.ProposerOption{ bytesutil.ToBytes48(key1): { - FeeRecipient: common.HexToAddress("0xae967917c465db8578ca9024c205720b1a3651A9"), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress("0xae967917c465db8578ca9024c205720b1a3651A9"), + }, }, }, DefaultConfig: &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.HexToAddress("0xae967917c465db8578ca9024c205720b1a3651A9"), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress("0xae967917c465db8578ca9024c205720b1a3651A9"), + }, }, } }, @@ -270,14 +274,18 @@ func TestProposerSettings(t *testing.T) { return &validatorserviceconfig.ProposerSettings{ ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validatorserviceconfig.ProposerOption{ bytesutil.ToBytes48(key1): { - FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), + }, BuilderConfig: &validatorserviceconfig.BuilderConfig{ Enabled: true, GasLimit: validatorserviceconfig.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), }, }, bytesutil.ToBytes48(key2): { - FeeRecipient: common.HexToAddress("0x60155530FCE8a85ec7055A5F8b2bE214B3DaeFd4"), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress("0x60155530FCE8a85ec7055A5F8b2bE214B3DaeFd4"), + }, BuilderConfig: &validatorserviceconfig.BuilderConfig{ Enabled: true, GasLimit: validatorserviceconfig.Uint64(35000000), @@ -285,7 +293,9 @@ func TestProposerSettings(t *testing.T) { }, }, DefaultConfig: &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), + }, BuilderConfig: &validatorserviceconfig.BuilderConfig{ Enabled: true, GasLimit: validatorserviceconfig.Uint64(40000000), @@ -310,11 +320,15 @@ func TestProposerSettings(t *testing.T) { return &validatorserviceconfig.ProposerSettings{ ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validatorserviceconfig.ProposerOption{ bytesutil.ToBytes48(key1): { - FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), + }, }, }, DefaultConfig: &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), + }, }, } }, @@ -335,7 +349,9 @@ func TestProposerSettings(t *testing.T) { return &validatorserviceconfig.ProposerSettings{ ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validatorserviceconfig.ProposerOption{ bytesutil.ToBytes48(key1): { - FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), + }, BuilderConfig: &validatorserviceconfig.BuilderConfig{ Enabled: true, GasLimit: 40000000, @@ -343,7 +359,9 @@ func TestProposerSettings(t *testing.T) { }, }, DefaultConfig: &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), + }, BuilderConfig: &validatorserviceconfig.BuilderConfig{ Enabled: false, GasLimit: validatorserviceconfig.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), @@ -366,7 +384,9 @@ func TestProposerSettings(t *testing.T) { return &validatorserviceconfig.ProposerSettings{ ProposeConfig: nil, DefaultConfig: &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), + }, }, } }, @@ -385,7 +405,9 @@ func TestProposerSettings(t *testing.T) { return &validatorserviceconfig.ProposerSettings{ ProposeConfig: nil, DefaultConfig: &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), + }, BuilderConfig: &validatorserviceconfig.BuilderConfig{ Enabled: true, GasLimit: validatorserviceconfig.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), @@ -410,7 +432,9 @@ func TestProposerSettings(t *testing.T) { return &validatorserviceconfig.ProposerSettings{ ProposeConfig: nil, DefaultConfig: &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), + }, BuilderConfig: &validatorserviceconfig.BuilderConfig{ Enabled: true, GasLimit: 50000000, @@ -436,11 +460,15 @@ func TestProposerSettings(t *testing.T) { return &validatorserviceconfig.ProposerSettings{ ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validatorserviceconfig.ProposerOption{ bytesutil.ToBytes48(key1): { - FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), + }, }, }, DefaultConfig: &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), + }, }, } }, @@ -461,7 +489,9 @@ func TestProposerSettings(t *testing.T) { return &validatorserviceconfig.ProposerSettings{ ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validatorserviceconfig.ProposerOption{ bytesutil.ToBytes48(key1): { - FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), + }, BuilderConfig: &validatorserviceconfig.BuilderConfig{ Enabled: true, GasLimit: validatorserviceconfig.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), @@ -469,7 +499,9 @@ func TestProposerSettings(t *testing.T) { }, }, DefaultConfig: &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), + }, BuilderConfig: &validatorserviceconfig.BuilderConfig{ Enabled: true, GasLimit: validatorserviceconfig.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), @@ -495,7 +527,9 @@ func TestProposerSettings(t *testing.T) { return &validatorserviceconfig.ProposerSettings{ ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validatorserviceconfig.ProposerOption{ bytesutil.ToBytes48(key1): { - FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), + }, BuilderConfig: &validatorserviceconfig.BuilderConfig{ Enabled: true, GasLimit: validatorserviceconfig.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), @@ -503,7 +537,9 @@ func TestProposerSettings(t *testing.T) { }, }, DefaultConfig: &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), + }, BuilderConfig: &validatorserviceconfig.BuilderConfig{ Enabled: true, GasLimit: validatorserviceconfig.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), @@ -528,7 +564,9 @@ func TestProposerSettings(t *testing.T) { return &validatorserviceconfig.ProposerSettings{ ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validatorserviceconfig.ProposerOption{ bytesutil.ToBytes48(key1): { - FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), + }, BuilderConfig: &validatorserviceconfig.BuilderConfig{ Enabled: true, GasLimit: validatorserviceconfig.Uint64(40000000), @@ -536,7 +574,9 @@ func TestProposerSettings(t *testing.T) { }, }, DefaultConfig: &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), + }, BuilderConfig: &validatorserviceconfig.BuilderConfig{ Enabled: false, GasLimit: validatorserviceconfig.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), diff --git a/validator/rpc/standard_api.go b/validator/rpc/standard_api.go index 309121fda..7f828e136 100644 --- a/validator/rpc/standard_api.go +++ b/validator/rpc/standard_api.go @@ -25,8 +25,6 @@ import ( "google.golang.org/grpc/status" ) -const nonExistantPublicKey = "0x0" - // ListKeystores implements the standard validator key management API. func (s *Server) ListKeystores( ctx context.Context, _ *empty.Empty, @@ -426,52 +424,44 @@ func (s *Server) SetGasLimit(ctx context.Context, req *ethpbservice.SetGasLimitR if s.validatorService == nil { return nil, status.Error(codes.FailedPrecondition, "Validator service not ready") } + validatorKey := req.Pubkey + if err := validatePublicKey(validatorKey); err != nil { return nil, status.Error(codes.FailedPrecondition, err.Error()) } - defaultOption := validatorServiceConfig.DefaultProposerOption() var pBuilderConfig *validatorServiceConfig.BuilderConfig + if s.validatorService.ProposerSettings() != nil && s.validatorService.ProposerSettings().DefaultConfig != nil && s.validatorService.ProposerSettings().DefaultConfig.BuilderConfig != nil { - // Make a copy of BuildConfig from DefaultConfig (thus "*" then "&"), so when we change GasLimit, we do not mess up with + // Make a copy of BuilderConfig from DefaultConfig (thus "*" then "&"), so when we change GasLimit, we do not mess up with // "DefaultConfig.BuilderConfig". bo := *s.validatorService.ProposerSettings().DefaultConfig.BuilderConfig pBuilderConfig = &bo pBuilderConfig.GasLimit = validatorServiceConfig.Uint64(req.GasLimit) } else { - // No default BuildConfig to copy from, just create one and set "GasLimit", but keep "Enabled" to "false". - pBuilderConfig = &validatorServiceConfig.BuilderConfig{Enabled: false, GasLimit: validatorServiceConfig.Uint64(req.GasLimit)} + // No default BuilderConfig to copy from, just create one and set "GasLimit", but keep "Enabled" to "false". + pBuilderConfig = &validatorServiceConfig.BuilderConfig{ + Enabled: false, + GasLimit: validatorServiceConfig.Uint64(req.GasLimit), + Relays: []string{}, + } } - pOption := validatorServiceConfig.DefaultProposerOption() - // "pOption.BuildConfig" is nil from "validatorServiceConfig.DefaultProposerOption()", so set it. - pOption.BuilderConfig = pBuilderConfig + pOption := validatorServiceConfig.ProposerOption{ + FeeRecipientConfig: nil, + BuilderConfig: pBuilderConfig, + } if s.validatorService.ProposerSettings() == nil { - // get the default fee recipient defined with an invalid public key from beacon node - resp, err := s.beaconNodeValidatorClient.GetFeeRecipientByPubKey(ctx, ð.FeeRecipientByPubKeyRequest{ - PublicKey: []byte(nonExistantPublicKey), + s.validatorService.SetProposerSettings(&validatorServiceConfig.ProposerSettings{ + ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validatorServiceConfig.ProposerOption{ + bytesutil.ToBytes48(validatorKey): &pOption, + }, + DefaultConfig: nil, }) - if resp == nil || len(resp.FeeRecipient) == 0 || err != nil { - s.validatorService.SetProposerSettings(&validatorServiceConfig.ProposerSettings{ - ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validatorServiceConfig.ProposerOption{ - bytesutil.ToBytes48(validatorKey): &pOption, - }, - DefaultConfig: &defaultOption, - }) - } else { - s.validatorService.SetProposerSettings(&validatorServiceConfig.ProposerSettings{ - ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validatorServiceConfig.ProposerOption{ - bytesutil.ToBytes48(validatorKey): &pOption, - }, - DefaultConfig: &validatorServiceConfig.ProposerOption{ - FeeRecipient: common.BytesToAddress(resp.FeeRecipient), - }, - }) - } } else if s.validatorService.ProposerSettings().ProposeConfig == nil { settings := s.validatorService.ProposerSettings() settings.ProposeConfig = make(map[[fieldparams.BLSPubkeyLength]byte]*validatorServiceConfig.ProposerOption) @@ -479,6 +469,7 @@ func (s *Server) SetGasLimit(ctx context.Context, req *ethpbservice.SetGasLimitR s.validatorService.SetProposerSettings(settings) } else { proposerOption, found := s.validatorService.ProposerSettings().ProposeConfig[bytesutil.ToBytes48(validatorKey)] + if found { if proposerOption.BuilderConfig == nil { proposerOption.BuilderConfig = pBuilderConfig @@ -489,10 +480,12 @@ func (s *Server) SetGasLimit(ctx context.Context, req *ethpbservice.SetGasLimitR s.validatorService.ProposerSettings().ProposeConfig[bytesutil.ToBytes48(validatorKey)] = &pOption } } + // override the 200 success with 202 according to the specs if err := grpc.SetHeader(ctx, metadata.Pairs("x-http-code", "202")); err != nil { return &empty.Empty{}, status.Errorf(codes.Internal, "Could not set custom success code header: %v", err) } + return &empty.Empty{}, nil } @@ -534,38 +527,49 @@ func (s *Server) ListFeeRecipientByPubkey(ctx context.Context, req *ethpbservice if s.validatorService == nil { return nil, status.Error(codes.FailedPrecondition, "Validator service not ready") } + validatorKey := req.Pubkey if err := validatePublicKey(validatorKey); err != nil { return nil, status.Error(codes.FailedPrecondition, err.Error()) } - defaultFeeRecipient := params.BeaconConfig().DefaultFeeRecipient.Bytes() + finalResp := ðpbservice.GetFeeRecipientByPubkeyResponse{ Data: ðpbservice.GetFeeRecipientByPubkeyResponse_FeeRecipient{ Pubkey: validatorKey, - Ethaddress: defaultFeeRecipient, + Ethaddress: params.BeaconConfig().DefaultFeeRecipient.Bytes(), }, } + + // If no proposer settings is set, use beacon node default (if possible) if s.validatorService.ProposerSettings() == nil { resp, err := s.beaconNodeValidatorClient.GetFeeRecipientByPubKey(ctx, ð.FeeRecipientByPubKeyRequest{ PublicKey: validatorKey, }) + if resp != nil && len(resp.FeeRecipient) != 0 && err == nil { finalResp.Data.Ethaddress = resp.FeeRecipient } + return finalResp, nil } + + // If fee recipient is defined for this specific pubkey in proposer configuration, use it if s.validatorService.ProposerSettings().ProposeConfig != nil { - hexv := hexutil.Encode(validatorKey) - fmt.Println(hexv) proposerOption, found := s.validatorService.ProposerSettings().ProposeConfig[bytesutil.ToBytes48(validatorKey)] - if found { - finalResp.Data.Ethaddress = proposerOption.FeeRecipient.Bytes() + + if found && proposerOption.FeeRecipientConfig != nil { + finalResp.Data.Ethaddress = proposerOption.FeeRecipientConfig.FeeRecipient.Bytes() return finalResp, nil } } - if s.validatorService.ProposerSettings().DefaultConfig != nil { - finalResp.Data.Ethaddress = s.validatorService.ProposerSettings().DefaultConfig.FeeRecipient.Bytes() + + // If fee recipient is defined in default configuration, use it + if s.validatorService.ProposerSettings().DefaultConfig != nil && s.validatorService.ProposerSettings().DefaultConfig.FeeRecipientConfig != nil { + finalResp.Data.Ethaddress = s.validatorService.ProposerSettings().DefaultConfig.FeeRecipientConfig.FeeRecipient.Bytes() + return finalResp, nil } + + // Default case return finalResp, nil } @@ -574,53 +578,75 @@ func (s *Server) SetFeeRecipientByPubkey(ctx context.Context, req *ethpbservice. if s.validatorService == nil { return nil, status.Error(codes.FailedPrecondition, "Validator service not ready") } + validatorKey := req.Pubkey + feeRecipient := common.BytesToAddress(req.Ethaddress) + if err := validatePublicKey(validatorKey); err != nil { return nil, status.Error(codes.FailedPrecondition, err.Error()) } - defaultOption := validatorServiceConfig.DefaultProposerOption() + encoded := hexutil.Encode(req.Ethaddress) + if !common.IsHexAddress(encoded) { return nil, status.Error( codes.InvalidArgument, "Fee recipient is not a valid Ethereum address") } - pOption := validatorServiceConfig.DefaultProposerOption() - pOption.FeeRecipient = common.BytesToAddress(req.Ethaddress) switch { case s.validatorService.ProposerSettings() == nil: - // get the default fee recipient defined with an invalid public key from beacon node - resp, err := s.beaconNodeValidatorClient.GetFeeRecipientByPubKey(ctx, ð.FeeRecipientByPubKeyRequest{ - PublicKey: []byte(nonExistantPublicKey), - }) - settings := &validatorServiceConfig.ProposerSettings{ + s.validatorService.SetProposerSettings(&validatorServiceConfig.ProposerSettings{ ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validatorServiceConfig.ProposerOption{ - bytesutil.ToBytes48(validatorKey): &pOption, + bytesutil.ToBytes48(validatorKey): { + FeeRecipientConfig: &validatorServiceConfig.FeeRecipientConfig{ + FeeRecipient: feeRecipient, + }, + BuilderConfig: nil, + }, + }, + DefaultConfig: nil, + }) + case s.validatorService.ProposerSettings().ProposeConfig == nil: + builderConfig := &validatorServiceConfig.BuilderConfig{} + settings := s.validatorService.ProposerSettings() + + if settings.DefaultConfig != nil { + builderConfig = settings.DefaultConfig.BuilderConfig + } + + settings.ProposeConfig = map[[fieldparams.BLSPubkeyLength]byte]*validatorServiceConfig.ProposerOption{ + bytesutil.ToBytes48(validatorKey): { + FeeRecipientConfig: &validatorServiceConfig.FeeRecipientConfig{ + FeeRecipient: feeRecipient, + }, + BuilderConfig: builderConfig, }, } - if resp == nil || len(resp.FeeRecipient) == 0 || err != nil { - settings.DefaultConfig = &defaultOption - } else { - settings.DefaultConfig = &validatorServiceConfig.ProposerOption{ - FeeRecipient: common.BytesToAddress(resp.FeeRecipient), - } - } - s.validatorService.SetProposerSettings(settings) - case s.validatorService.ProposerSettings().ProposeConfig == nil: - settings := s.validatorService.ProposerSettings() - pOption.BuilderConfig = settings.DefaultConfig.BuilderConfig - settings.ProposeConfig = map[[fieldparams.BLSPubkeyLength]byte]*validatorServiceConfig.ProposerOption{ - bytesutil.ToBytes48(validatorKey): &pOption, - } + s.validatorService.SetProposerSettings(settings) default: proposerOption, found := s.validatorService.ProposerSettings().ProposeConfig[bytesutil.ToBytes48(validatorKey)] - if found { - proposerOption.FeeRecipient = common.BytesToAddress(req.Ethaddress) + + if found && proposerOption != nil { + proposerOption.FeeRecipientConfig = &validatorServiceConfig.FeeRecipientConfig{ + FeeRecipient: feeRecipient, + } } else { settings := s.validatorService.ProposerSettings() - pOption.BuilderConfig = settings.DefaultConfig.BuilderConfig - settings.ProposeConfig[bytesutil.ToBytes48(validatorKey)] = &pOption + + var builderConfig = &validatorServiceConfig.BuilderConfig{} + + if settings.DefaultConfig != nil { + builderConfig = settings.DefaultConfig.BuilderConfig + } + + settings.ProposeConfig[bytesutil.ToBytes48(validatorKey)] = &validatorServiceConfig.ProposerOption{ + FeeRecipientConfig: &validatorServiceConfig.FeeRecipientConfig{ + FeeRecipient: feeRecipient, + }, + BuilderConfig: builderConfig, + } + s.validatorService.SetProposerSettings(settings) } } @@ -636,20 +662,22 @@ func (s *Server) DeleteFeeRecipientByPubkey(ctx context.Context, req *ethpbservi if s.validatorService == nil { return nil, status.Error(codes.FailedPrecondition, "Validator service not ready") } + validatorKey := req.Pubkey + if err := validatePublicKey(validatorKey); err != nil { return nil, status.Error(codes.FailedPrecondition, err.Error()) } - defaultFeeRecipient := params.BeaconConfig().DefaultFeeRecipient - if s.validatorService.ProposerSettings() != nil && s.validatorService.ProposerSettings().DefaultConfig != nil { - defaultFeeRecipient = s.validatorService.ProposerSettings().DefaultConfig.FeeRecipient - } - if s.validatorService.ProposerSettings() != nil && s.validatorService.ProposerSettings().ProposeConfig != nil { - proposerOption, found := s.validatorService.ProposerSettings().ProposeConfig[bytesutil.ToBytes48(validatorKey)] + + settings := s.validatorService.ProposerSettings() + + if settings != nil && settings.ProposeConfig != nil { + proposerOption, found := settings.ProposeConfig[bytesutil.ToBytes48(validatorKey)] if found { - proposerOption.FeeRecipient = defaultFeeRecipient + proposerOption.FeeRecipientConfig = nil } } + // override the 200 success with 204 according to the specs if err := grpc.SetHeader(ctx, metadata.Pairs("x-http-code", "204")); err != nil { return &empty.Empty{}, status.Errorf(codes.Internal, "Could not set custom success code header: %v", err) diff --git a/validator/rpc/standard_api_test.go b/validator/rpc/standard_api_test.go index 1701833eb..5c754e280 100644 --- a/validator/rpc/standard_api_test.go +++ b/validator/rpc/standard_api_test.go @@ -13,6 +13,7 @@ import ( "github.com/golang/protobuf/ptypes/empty" "github.com/google/uuid" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "github.com/pkg/errors" fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams" "github.com/prysmaticlabs/prysm/v3/config/params" validatorserviceconfig "github.com/prysmaticlabs/prysm/v3/config/validator/service" @@ -711,34 +712,8 @@ func TestServer_ListFeeRecipientByPubkey(t *testing.T) { wantErr bool }{ { - name: "Happy Path Test", - args: &validatorserviceconfig.ProposerSettings{ - ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{ - bytesutil.ToBytes48(byteval): { - FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9"), - }, - }, - DefaultConfig: &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9"), - }, - }, - want: &want{ - EthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9", - }, - wantErr: false, - }, - { - name: "happy path test cached", - args: &validatorserviceconfig.ProposerSettings{ - ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{ - bytesutil.ToBytes48(byteval): { - FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9"), - }, - }, - DefaultConfig: &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9"), - }, - }, + name: "ProposerSettings is nil", + args: nil, want: &want{ EthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9", }, @@ -748,57 +723,167 @@ func TestServer_ListFeeRecipientByPubkey(t *testing.T) { wantErr: false, }, { - name: "empty settings non cached", + name: "ProposerSettings is nil - Beacon node error", args: nil, want: &want{ - EthAddress: params.BeaconConfig().DefaultFeeRecipient.Hex(), + EthAddress: "0x0000000000000000000000000000000000000000", + }, + cached: nil, + wantErr: true, + }, + { + name: "ProposerSettings.ProposeConfig.FeeRecipientConfig defined for pubkey", + 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", }, wantErr: false, }, { - name: "empty settings cached", - args: nil, + 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: common.HexToAddress("0x055Fb65722E7b2455012BFEBf6177F1D2e97387").Hex(), + EthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9", }, wantErr: false, - cached: ð.FeeRecipientByPubKeyResponse{ - FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455012BFEBf6177F1D2e97387").Bytes(), + }, + { + name: "ProposerSettings.ProposeConfig.FeeRecipientConfig NOT defined for pubkey and ProposerSettings.DefaultConfig is nil", + args: &validatorserviceconfig.ProposerSettings{ + ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{}, + DefaultConfig: nil, }, + want: &want{ + EthAddress: "0x0000000000000000000000000000000000000000", + }, + wantErr: false, + }, + { + name: "ProposerSettings.ProposeConfig is nil and ProposerSettings.DefaultConfig is nil", + args: &validatorserviceconfig.ProposerSettings{ + ProposeConfig: nil, + DefaultConfig: nil, + }, + want: &want{ + EthAddress: "0x0000000000000000000000000000000000000000", + }, + wantErr: false, + }, + { + name: "ProposerSettings.ProposeConfig is nil and ProposerSettings.DefaultConfig.FeeRecipientConfig is nil", + args: &validatorserviceconfig.ProposerSettings{ + ProposeConfig: nil, + DefaultConfig: &validatorserviceconfig.ProposerOption{ + FeeRecipientConfig: nil, + }, + }, + want: &want{ + EthAddress: "0x0000000000000000000000000000000000000000", + }, + wantErr: false, + }, + { + name: "ProposerSettings.ProposerConfig.FeeRecipientConfig NOT defined for pubkey, ProposerSettings.DefaultConfig.FeeRecipientConfig defined", + args: &validatorserviceconfig.ProposerSettings{ + DefaultConfig: &validatorserviceconfig.ProposerOption{ + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9"), + }, + }, + }, + want: &want{ + EthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9", + }, + wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) mockValidatorClient := mock2.NewMockValidatorClient(ctrl) + m := &mock.MockValidator{} m.SetProposerSettings(tt.args) + + if tt.args == nil { + var err error = nil + if tt.wantErr { + err = errors.New("custom error") + } + mockValidatorClient.EXPECT().GetFeeRecipientByPubKey(gomock.Any(), gomock.Any()).Return(tt.cached, err) + } + vs, err := client.NewValidatorService(ctx, &client.Config{ Validator: m, }) require.NoError(t, err) - if tt.args == nil || tt.args.ProposeConfig == nil { - mockValidatorClient.EXPECT().GetFeeRecipientByPubKey(gomock.Any(), gomock.Any()).Return(tt.cached, nil) - } + 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_SetFeeRecipientByPubkey(t *testing.T) { + +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 := mock2.NewMockValidatorClient(ctrl) ctx := grpc.NewContextWithServerTransportStream(context.Background(), &runtime.ServerTransportStream{}) + byteval, err := hexutil.Decode("0xaf2e7ba294e03438ea819bd4033c6c1bf6b04320ee2075b77273c08d02f8a61bcc303c2c06bd3713cb442072ae591493") - wantAddress := "0x055Fb65722e7b2455012Bfebf6177f1d2e9738d7" - cachedAddress := "0x055Fb65722E7b2455012BFEBf6177F1D2e97387" require.NoError(t, err) + type want struct { valEthAddress string defaultEthaddress string @@ -816,11 +901,11 @@ func TestServer_SetFeeRecipientByPubkey(t *testing.T) { beaconReturn *beaconResp }{ { - name: "Happy Path Test", - args: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9", + name: "ProposerSetting is nil", + args: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9", + proposerSettings: nil, want: &want{ - valEthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9", - defaultEthaddress: params.BeaconConfig().DefaultFeeRecipient.Hex(), + valEthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9", }, wantErr: false, beaconReturn: &beaconResp{ @@ -829,80 +914,168 @@ func TestServer_SetFeeRecipientByPubkey(t *testing.T) { }, }, { - name: "Happy Path Test Beacon Cached", + name: "ProposerSetting.ProposeConfig is nil", args: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9", + proposerSettings: &validatorserviceconfig.ProposerSettings{ + ProposeConfig: nil, + }, want: &want{ - valEthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9", - defaultEthaddress: common.HexToAddress(cachedAddress).Hex(), + valEthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9", }, wantErr: false, beaconReturn: &beaconResp{ - resp: ð.FeeRecipientByPubKeyResponse{ - FeeRecipient: common.HexToAddress(cachedAddress).Bytes(), - }, + resp: nil, error: nil, }, }, { - name: "Happy Path Test Beacon Cached preexisting proposer data", - args: wantAddress, - want: &want{ - valEthAddress: wantAddress, - defaultEthaddress: common.HexToAddress(cachedAddress).Hex(), - }, + name: "ProposerSetting.ProposeConfig is nil AND ProposerSetting.Defaultconfig is defined", + args: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9", proposerSettings: &validatorserviceconfig.ProposerSettings{ - ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{ - bytesutil.ToBytes48(byteval): { - FeeRecipient: common.HexToAddress("0x055Fb65722e7b2455012Bfebf6177f1d2e9738d8"), - }, - }, - DefaultConfig: &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.HexToAddress(cachedAddress), - }, + ProposeConfig: nil, + DefaultConfig: &validatorserviceconfig.ProposerOption{}, + }, + want: &want{ + valEthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9", }, wantErr: false, + beaconReturn: &beaconResp{ + resp: nil, + error: nil, + }, }, { - name: "Happy Path Test Beacon Cached preexisting default data", - args: wantAddress, - want: &want{ - valEthAddress: wantAddress, - defaultEthaddress: common.HexToAddress(cachedAddress).Hex(), - }, + name: "ProposerSetting.ProposeConfig is defined for pubkey", + args: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9", proposerSettings: &validatorserviceconfig.ProposerSettings{ - DefaultConfig: &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.HexToAddress(cachedAddress), + 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{} m.SetProposerSettings(tt.proposerSettings) + vs, err := client.NewValidatorService(ctx, &client.Config{ Validator: m, }) - if tt.beaconReturn != nil { - beaconClient.EXPECT().GetFeeRecipientByPubKey( - gomock.Any(), - gomock.Any(), - ).Return(tt.beaconReturn.resp, tt.beaconReturn.error) - } + require.NoError(t, err) s := &Server{ validatorService: vs, beaconNodeValidatorClient: beaconClient, } + _, 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)].FeeRecipient.Hex()) - assert.Equal(t, tt.want.defaultEthaddress, s.validatorService.ProposerSettings().DefaultConfig.FeeRecipient.Hex()) + + 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") @@ -921,11 +1094,15 @@ func TestServer_DeleteFeeRecipientByPubkey(t *testing.T) { proposerSettings: &validatorserviceconfig.ProposerSettings{ ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{ bytesutil.ToBytes48(byteval): { - FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455012BFEBf6177F1D2e9738D5"), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455012BFEBf6177F1D2e9738D5"), + }, }, }, DefaultConfig: &validatorserviceconfig.ProposerOption{ - FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9"), + FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9"), + }, }, }, want: &want{ @@ -947,11 +1124,35 @@ func TestServer_DeleteFeeRecipientByPubkey(t *testing.T) { } _, err = s.DeleteFeeRecipientByPubkey(ctx, ðpbservice.PubkeyRequest{Pubkey: byteval}) require.NoError(t, err) - assert.Equal(t, tt.want.EthAddress, s.validatorService.ProposerSettings().ProposeConfig[bytesutil.ToBytes48(byteval)].FeeRecipient.Hex()) + + 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") @@ -1024,16 +1225,20 @@ func TestServer_GetGasLimit(t *testing.T) { func TestServer_SetGasLimit(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() + beaconClient := mock2.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 @@ -1048,17 +1253,40 @@ func TestServer_SetGasLimit(t *testing.T) { beaconReturn *beaconResp }{ { - name: "update existing gas limit", + name: "ProposerSettings is nil", + pubkey: pubkey1, + newGasLimit: 9999, + proposerSettings: nil, + w: []want{ + { + pubkey: pubkey1, + gaslimit: 9999, + }, + }, + }, + { + name: "ProposerSettings.ProposeConfig is nil AND ProposerSettings.DefaultConfig is nil", pubkey: pubkey1, newGasLimit: 9999, proposerSettings: &validatorserviceconfig.ProposerSettings{ - ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{ - bytesutil.ToBytes48(pubkey1): { - BuilderConfig: &validatorserviceconfig.BuilderConfig{GasLimit: 123456789}, - }, + ProposeConfig: nil, + DefaultConfig: nil, + }, + w: []want{ + { + pubkey: pubkey1, + gaslimit: 9999, }, + }, + }, + { + name: "ProposerSettings.ProposeConfig is nil AND ProposerSettings.DefaultConfig.BuilderConfig is nil", + pubkey: pubkey1, + newGasLimit: 9999, + proposerSettings: &validatorserviceconfig.ProposerSettings{ + ProposeConfig: nil, DefaultConfig: &validatorserviceconfig.ProposerOption{ - BuilderConfig: &validatorserviceconfig.BuilderConfig{GasLimit: 987654321}, + BuilderConfig: nil, }, }, w: []want{ @@ -1069,113 +1297,145 @@ func TestServer_SetGasLimit(t *testing.T) { }, }, { - name: "insert a new gas limit", - pubkey: pubkey2, - newGasLimit: 8888, + 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: &validatorserviceconfig.BuilderConfig{GasLimit: 123456789}, + BuilderConfig: nil, }, }, - DefaultConfig: &validatorserviceconfig.ProposerOption{ - BuilderConfig: &validatorserviceconfig.BuilderConfig{GasLimit: 987654321}, - }, + DefaultConfig: nil, }, w: []want{ { pubkey: pubkey1, - gaslimit: 123456789, + gaslimit: 9999, + }, + }, + }, + { + 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, + }, + w: []want{ + { + pubkey: pubkey1, + gaslimit: 9999, + }, + }, + }, + { + 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(pubkey1): { + BuilderConfig: &validatorserviceconfig.BuilderConfig{ + GasLimit: 12345, + }, + }, + }, + DefaultConfig: nil, + }, + w: []want{ + { + pubkey: pubkey1, + gaslimit: 12345, }, { pubkey: pubkey2, - gaslimit: 8888, + gaslimit: 9999, }, }, }, { - name: "create new gas limit value for nil ProposerSettings.ProposeConfig", + name: "ProposerSettings.ProposeConfig is defined for pubkey, BuilderConfig is nil AND ProposerSettings.DefaultConfig.BuilderConfig is defined", pubkey: pubkey1, - newGasLimit: 8888, + newGasLimit: 9999, proposerSettings: &validatorserviceconfig.ProposerSettings{ + ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{ + bytesutil.ToBytes48(pubkey1): { + BuilderConfig: nil, + }, + }, DefaultConfig: &validatorserviceconfig.ProposerOption{ - BuilderConfig: &validatorserviceconfig.BuilderConfig{GasLimit: 987654321}, + BuilderConfig: &validatorserviceconfig.BuilderConfig{}, }, }, w: []want{ { pubkey: pubkey1, - gaslimit: 8888, + gaslimit: 9999, }, }, }, - { - name: "create new gas limit value for nil proposerSettings with beacon node fee recipient", - pubkey: pubkey1, - newGasLimit: 7777, - // proposerSettings is not set - we need to create proposerSettings and set gaslimit properly - w: []want{ - { - pubkey: pubkey1, - gaslimit: 7777, - }, - }, - beaconReturn: &beaconResp{ - resp: ð.FeeRecipientByPubKeyResponse{ - FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455012BFEBf6177F1D2e97387").Bytes(), - }, - error: nil, - }, - }, - { - name: "create new gas limit value for nil proposerSettings without beacon node fee recipient", - pubkey: pubkey1, - newGasLimit: 7777, - // proposerSettings is not set - we need to create proposerSettings and set gaslimit properly - w: []want{ - { - pubkey: pubkey1, - gaslimit: 7777, - }, - }, - beaconReturn: &beaconResp{ - resp: nil, - error: nil, - }, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - feeRecipient := params.BeaconConfig().DefaultFeeRecipient.Hex() m := &mock.MockValidator{} m.SetProposerSettings(tt.proposerSettings) + vs, err := client.NewValidatorService(ctx, &client.Config{ Validator: m, }) require.NoError(t, err) + s := &Server{ validatorService: vs, beaconNodeValidatorClient: beaconClient, } + if tt.beaconReturn != nil { beaconClient.EXPECT().GetFeeRecipientByPubKey( gomock.Any(), gomock.Any(), ).Return(tt.beaconReturn.resp, tt.beaconReturn.error) - if tt.beaconReturn.resp != nil { - feeRecipient = common.BytesToAddress(tt.beaconReturn.resp.FeeRecipient).Hex() - } } + _, err = s.SetGasLimit(ctx, ðpbservice.SetGasLimitRequest{Pubkey: tt.pubkey, GasLimit: tt.newGasLimit}) 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)) } - assert.Equal(t, s.validatorService.ProposerSettings().DefaultConfig.FeeRecipient.Hex(), feeRecipient) }) } } +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")