Improvement to Fee Recipient UX (#11307)

* updating mock

* adding new internal api

* adding generated code

* converting validator index to pubkey

* adding new API endpoint

* fixing mock related new API

* fixing unit tests

* preventing failure on startup, replacing with warnings

* improving log message

* changing warn log to error log

* fixing error formatting

* improve log on beacon node side on startup

* fixing deepsource issue

* improving logs

* fixing unit tests

* fixing more tests

* adding error check

* adding in new case for fee recipient to avoid conflicts on changing beacon node suggested fee recipient

* adding default fee recipient check for gas limit as well

* adding improved unit tests

* accidently checked in tmp folder

* adding more unit tests

* fixing gas limit unit test

* Update validator/rpc/standard_api_test.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Update validator/rpc/standard_api_test.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Update validator/rpc/standard_api_test.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Update beacon-chain/node/config.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Update beacon-chain/node/config.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Update proto/prysm/v1alpha1/validator.proto

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Update validator/rpc/standard_api.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Update validator/client/runner.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* addressing comments

* updating proto generated files

* fixing linting and addressign review comments

* fixing unit test

* improve error handling

* accidently added tmp folder

* improving function error returns

* realizing i am wrapping error incorrectly

* fixing unit test

* addressing review comment

* fixing linting

* fixing unit test

* improving ux around enable builder

Co-authored-by: Radosław Kapka <rkapka@wp.pl>
Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
This commit is contained in:
james-prysm
2022-09-22 11:35:35 -05:00
committed by GitHub
parent 61f6b27548
commit 20e99fd1f9
20 changed files with 1113 additions and 471 deletions

View File

@@ -4,7 +4,6 @@ import (
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/pkg/errors"
fastssz "github.com/prysmaticlabs/fastssz"
"github.com/prysmaticlabs/prysm/v3/cmd"
"github.com/prysmaticlabs/prysm/v3/cmd/beacon-chain/flags"
@@ -158,17 +157,22 @@ func configureExecutionSetting(cliCtx *cli.Context) error {
}
if !cliCtx.IsSet(flags.SuggestedFeeRecipient.Name) {
log.Warnf("In order to receive transaction fees from proposing blocks, " +
"you must provide flag --" + flags.SuggestedFeeRecipient.Name + " with a valid ethereum address when starting your beacon node. " +
"Please see our documentation for more information on this requirement (https://docs.prylabs.network/docs/execution-node/fee-recipient).")
return nil
}
c := params.BeaconConfig().Copy()
ha := cliCtx.String(flags.SuggestedFeeRecipient.Name)
if !common.IsHexAddress(ha) {
return fmt.Errorf("%s is not a valid fee recipient address", ha)
log.Warnf("%s is not a valid fee recipient address, setting suggested-fee-recipient failed", ha)
return nil
}
mixedcaseAddress, err := common.NewMixedcaseAddressFromString(ha)
if err != nil {
return errors.Wrapf(err, "could not decode fee recipient %s", ha)
log.WithError(err).Error(fmt.Sprintf("Could not decode fee recipient %s, setting suggested-fee-recipient failed", ha))
return nil
}
checksumAddress := common.HexToAddress(ha)
if !mixedcaseAddress.ValidChecksum() {
@@ -178,6 +182,8 @@ func configureExecutionSetting(cliCtx *cli.Context) error {
"to prevent spelling mistakes in your fee recipient Ethereum address", ha, checksumAddress.Hex())
}
c.DefaultFeeRecipient = checksumAddress
log.Infof("Default fee recipient is set to %s, recipient may be overwritten from validator client and persist in db."+
" Default fee recipient will be used as a fall back", checksumAddress.Hex())
return params.SetActive(c)
}

View File

@@ -90,7 +90,8 @@ func TestConfigureExecutionSetting(t *testing.T) {
require.NoError(t, set.Set(flags.SuggestedFeeRecipient.Name, "0xB"))
cliCtx := cli.NewContext(&app, set, nil)
err := configureExecutionSetting(cliCtx)
require.ErrorContains(t, "0xB is not a valid fee recipient address", err)
assert.LogsContain(t, hook, "0xB is not a valid fee recipient address")
require.NoError(t, err)
require.NoError(t, set.Set(flags.SuggestedFeeRecipient.Name, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"))
cliCtx = cli.NewContext(&app, set, nil)

View File

@@ -42,6 +42,8 @@ func TestNodeClose_OK(t *testing.T) {
set.String("p2p-encoding", "ssz", "p2p encoding scheme")
set.Bool("demo-config", true, "demo configuration")
set.String("deposit-contract", "0x0000000000000000000000000000000000000000", "deposit contract address")
set.String("suggested-fee-recipient", "0x6e35733c5af9B61374A128e6F85f553aF09ff89A", "fee recipient")
require.NoError(t, set.Set("suggested-fee-recipient", "0x6e35733c5af9B61374A128e6F85f553aF09ff89A"))
cmd.ValidatorMonitorIndicesFlag.Value = &cli.IntSlice{}
cmd.ValidatorMonitorIndicesFlag.Value.SetInt(1)
ctx := cli.NewContext(&app, set, nil)
@@ -60,6 +62,9 @@ func TestNodeStart_Ok(t *testing.T) {
tmp := fmt.Sprintf("%s/datadirtest2", t.TempDir())
set := flag.NewFlagSet("test", 0)
set.String("datadir", tmp, "node data directory")
set.String("suggested-fee-recipient", "0x6e35733c5af9B61374A128e6F85f553aF09ff89A", "fee recipient")
require.NoError(t, set.Set("suggested-fee-recipient", "0x6e35733c5af9B61374A128e6F85f553aF09ff89A"))
ctx := cli.NewContext(&app, set, nil)
node, err := New(ctx, WithBlockchainFlagOptions([]blockchain.Option{}),
WithBuilderFlagOptions([]builder.Option{}),
@@ -83,6 +88,8 @@ func TestNodeStart_Ok_registerDeterministicGenesisService(t *testing.T) {
set := flag.NewFlagSet("test", 0)
set.String("datadir", tmp, "node data directory")
set.Uint64(flags.InteropNumValidatorsFlag.Name, numValidators, "")
set.String("suggested-fee-recipient", "0x6e35733c5af9B61374A128e6F85f553aF09ff89A", "fee recipient")
require.NoError(t, set.Set("suggested-fee-recipient", "0x6e35733c5af9B61374A128e6F85f553aF09ff89A"))
genesisState, _, err := interop.GenerateGenesisState(context.Background(), 0, numValidators)
require.NoError(t, err, "Could not generate genesis beacon state")
for i := uint64(1); i < 2; i++ {
@@ -136,7 +143,8 @@ func TestClearDB(t *testing.T) {
set := flag.NewFlagSet("test", 0)
set.String("datadir", tmp, "node data directory")
set.Bool(cmd.ForceClearDB.Name, true, "force clear db")
set.String("suggested-fee-recipient", "0x6e35733c5af9B61374A128e6F85f553aF09ff89A", "fee recipient")
require.NoError(t, set.Set("suggested-fee-recipient", "0x6e35733c5af9B61374A128e6F85f553aF09ff89A"))
context := cli.NewContext(&app, set, nil)
_, err = New(context, WithExecutionChainOptions([]execution.Option{
execution.WithHttpEndpoint(endpoint),

View File

@@ -4,6 +4,7 @@ import (
"context"
"encoding/hex"
"fmt"
"strings"
"time"
"github.com/ethereum/go-ethereum/common"
@@ -118,6 +119,42 @@ func (vs *Server) PrepareBeaconProposer(
return &emptypb.Empty{}, nil
}
// GetFeeRecipientByPubKey returns a fee recipient from the beacon node's settings or db based on a given public key
func (vs *Server) GetFeeRecipientByPubKey(ctx context.Context, request *ethpb.FeeRecipientByPubKeyRequest) (*ethpb.FeeRecipientByPubKeyResponse, error) {
ctx, span := trace.StartSpan(ctx, "validator.GetFeeRecipientByPublicKey")
defer span.End()
if request == nil {
return nil, status.Errorf(codes.InvalidArgument, "request was empty")
}
resp, err := vs.ValidatorIndex(ctx, &ethpb.ValidatorIndexRequest{PublicKey: request.PublicKey})
if err != nil {
if strings.Contains(err.Error(), "Could not find validator index") {
return &ethpb.FeeRecipientByPubKeyResponse{
FeeRecipient: params.BeaconConfig().DefaultFeeRecipient.Bytes(),
}, nil
} else {
log.WithError(err).Error("An error occurred while retrieving validator index")
return nil, err
}
}
address, err := vs.BeaconDB.FeeRecipientByValidatorID(ctx, resp.GetIndex())
if err != nil {
if errors.Is(err, kv.ErrNotFoundFeeRecipient) {
return &ethpb.FeeRecipientByPubKeyResponse{
FeeRecipient: params.BeaconConfig().DefaultFeeRecipient.Bytes(),
}, nil
} else {
log.WithError(err).Error("An error occurred while retrieving fee recipient from db")
return nil, status.Errorf(codes.Internal, err.Error())
}
}
return &ethpb.FeeRecipientByPubKeyResponse{
FeeRecipient: address.Bytes(),
}, nil
}
func (vs *Server) proposeGenericBeaconBlock(ctx context.Context, blk interfaces.SignedBeaconBlock) (*ethpb.ProposeResponse, error) {
ctx, span := trace.StartSpan(ctx, "ProposerServer.proposeGenericBeaconBlock")
defer span.End()

View File

@@ -7,6 +7,7 @@ import (
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/pkg/errors"
"github.com/prysmaticlabs/go-bitfield"
mock "github.com/prysmaticlabs/prysm/v3/beacon-chain/blockchain/testing"
@@ -2453,3 +2454,43 @@ func majorityVoteBoundaryTime(slot types.Slot) (uint64, uint64) {
return earliestValidTime, latestValidTime
}
func TestProposer_GetFeeRecipientByPubKey(t *testing.T) {
db := dbutil.SetupDB(t)
ctx := context.Background()
numDeposits := uint64(64)
beaconState, _ := util.DeterministicGenesisState(t, numDeposits)
bsRoot, err := beaconState.HashTreeRoot(ctx)
require.NoError(t, err)
proposerServer := &Server{
BeaconDB: db,
HeadFetcher: &mock.ChainService{Root: bsRoot[:], State: beaconState},
}
pubkey, err := hexutil.Decode("0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a")
require.NoError(t, err)
resp, err := proposerServer.GetFeeRecipientByPubKey(ctx, &ethpb.FeeRecipientByPubKeyRequest{
PublicKey: pubkey,
})
require.NoError(t, err)
require.Equal(t, params.BeaconConfig().DefaultFeeRecipient.Hex(), hexutil.Encode(resp.FeeRecipient))
params.BeaconConfig().DefaultFeeRecipient = common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9728D9")
resp, err = proposerServer.GetFeeRecipientByPubKey(ctx, &ethpb.FeeRecipientByPubKeyRequest{
PublicKey: beaconState.Validators()[0].PublicKey,
})
require.NoError(t, err)
require.Equal(t, params.BeaconConfig().DefaultFeeRecipient.Hex(), common.BytesToAddress(resp.FeeRecipient).Hex())
index, err := proposerServer.ValidatorIndex(ctx, &ethpb.ValidatorIndexRequest{
PublicKey: beaconState.Validators()[0].PublicKey,
})
require.NoError(t, err)
err = proposerServer.BeaconDB.SaveFeeRecipientsByValidatorIDs(ctx, []types.ValidatorIndex{index.Index}, []common.Address{common.HexToAddress("0x055Fb65722E7b2455012BFEBf6177F1D2e9728D8")})
require.NoError(t, err)
resp, err = proposerServer.GetFeeRecipientByPubKey(ctx, &ethpb.FeeRecipientByPubKeyRequest{
PublicKey: beaconState.Validators()[0].PublicKey,
})
require.NoError(t, err)
require.Equal(t, common.HexToAddress("0x055Fb65722E7b2455012BFEBf6177F1D2e9728D8").Hex(), common.BytesToAddress(resp.FeeRecipient).Hex())
}

File diff suppressed because it is too large Load Diff

View File

@@ -394,6 +394,40 @@ func local_request_BeaconNodeValidator_PrepareBeaconProposer_0(ctx context.Conte
}
func request_BeaconNodeValidator_GetFeeRecipientByPubKey_0(ctx context.Context, marshaler runtime.Marshaler, client BeaconNodeValidatorClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq FeeRecipientByPubKeyRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.GetFeeRecipientByPubKey(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_BeaconNodeValidator_GetFeeRecipientByPubKey_0(ctx context.Context, marshaler runtime.Marshaler, server BeaconNodeValidatorServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq FeeRecipientByPubKeyRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.GetFeeRecipientByPubKey(ctx, &protoReq)
return msg, metadata, err
}
var (
filter_BeaconNodeValidator_GetAttestationData_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
@@ -1065,6 +1099,29 @@ func RegisterBeaconNodeValidatorHandlerServer(ctx context.Context, mux *runtime.
})
mux.Handle("POST", pattern_BeaconNodeValidator_GetFeeRecipientByPubKey_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/ethereum.eth.v1alpha1.BeaconNodeValidator/GetFeeRecipientByPubKey")
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_BeaconNodeValidator_GetFeeRecipientByPubKey_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_BeaconNodeValidator_GetFeeRecipientByPubKey_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_BeaconNodeValidator_GetAttestationData_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
@@ -1632,6 +1689,26 @@ func RegisterBeaconNodeValidatorHandlerClient(ctx context.Context, mux *runtime.
})
mux.Handle("POST", pattern_BeaconNodeValidator_GetFeeRecipientByPubKey_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req, "/ethereum.eth.v1alpha1.BeaconNodeValidator/GetFeeRecipientByPubKey")
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_BeaconNodeValidator_GetFeeRecipientByPubKey_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_BeaconNodeValidator_GetFeeRecipientByPubKey_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_BeaconNodeValidator_GetAttestationData_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
@@ -1938,6 +2015,8 @@ var (
pattern_BeaconNodeValidator_PrepareBeaconProposer_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"eth", "v1alpha1", "validator", "prepare_beacon_proposer"}, ""))
pattern_BeaconNodeValidator_GetFeeRecipientByPubKey_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"eth", "v1alpha1", "validator", "fee_recipient_by_pub_key"}, ""))
pattern_BeaconNodeValidator_GetAttestationData_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"eth", "v1alpha1", "validator", "attestation"}, ""))
pattern_BeaconNodeValidator_ProposeAttestation_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"eth", "v1alpha1", "validator", "attestation"}, ""))
@@ -1990,6 +2069,8 @@ var (
forward_BeaconNodeValidator_PrepareBeaconProposer_0 = runtime.ForwardResponseMessage
forward_BeaconNodeValidator_GetFeeRecipientByPubKey_0 = runtime.ForwardResponseMessage
forward_BeaconNodeValidator_GetAttestationData_0 = runtime.ForwardResponseMessage
forward_BeaconNodeValidator_ProposeAttestation_0 = runtime.ForwardResponseMessage

View File

@@ -168,6 +168,14 @@ service BeaconNodeValidator {
};
}
// GetFeeRecipientByPubKey returns a fee recipient from the beacon node's settings or db based on a given public key
rpc GetFeeRecipientByPubKey(FeeRecipientByPubKeyRequest) returns (FeeRecipientByPubKeyResponse){
option (google.api.http) = {
post: "/eth/v1alpha1/validator/fee_recipient_by_pub_key"
body: "*"
};
}
// Retrieves the latest valid attestation data to be attested on the beacon chain.
//
// The server returns the latest valid data which represents the correct vote
@@ -764,4 +772,12 @@ message PrepareBeaconProposerRequest {
uint64 validator_index = 2 [(ethereum.eth.ext.cast_type) = "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives.ValidatorIndex"];
}
repeated FeeRecipientContainer recipients = 1;
}
}
message FeeRecipientByPubKeyRequest {
bytes public_key = 1 [(ethereum.eth.ext.ssz_size) = "48", (ethereum.eth.ext.spec_name) = "pubkey"];
}
message FeeRecipientByPubKeyResponse {
bytes fee_recipient = 1 [(ethereum.eth.ext.ssz_size) = "20"];
}

View File

@@ -238,6 +238,26 @@ func (mr *MockBeaconNodeValidatorClientMockRecorder) PrepareBeaconProposer(arg0,
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PrepareBeaconProposer", reflect.TypeOf((*MockBeaconNodeValidatorClient)(nil).PrepareBeaconProposer), varargs...)
}
// GetFeeRecipientByPubKey mocks base method.
func (m *MockBeaconNodeValidatorClient) GetFeeRecipientByPubKey(arg0 context.Context, arg1 *eth.FeeRecipientByPubKeyRequest, arg2 ...grpc.CallOption) (*eth.FeeRecipientByPubKeyResponse, error) {
m.ctrl.T.Helper()
varargs := []interface{}{arg0, arg1}
for _, a := range arg2 {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "GetFeeRecipientByPublicKey", varargs...)
ret0, _ := ret[0].(*eth.FeeRecipientByPubKeyResponse)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetFeeRecipientByPubKey indicates an expected call of GetFeeRecipientByPubKey.
func (mr *MockBeaconNodeValidatorClientMockRecorder) GetFeeRecipientByPubKey(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{arg0, arg1}, arg2...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFeeRecipientByPublicKey", reflect.TypeOf((*MockBeaconNodeValidatorClient)(nil).GetFeeRecipientByPubKey), varargs...)
}
// ProposeAttestation mocks base method.
func (m *MockBeaconNodeValidatorClient) ProposeAttestation(arg0 context.Context, arg1 *eth.Attestation, arg2 ...grpc.CallOption) (*eth.AttestResponse, error) {
m.ctrl.T.Helper()

View File

@@ -178,6 +178,11 @@ func (_ MockValidator) CheckDoppelGanger(_ context.Context) error {
panic("implement me")
}
// PushProposerSettings for mocking
func (MockValidator) HasProposerSettings() bool {
panic("implement me")
}
// PushProposerSettings for mocking
func (_ MockValidator) PushProposerSettings(_ context.Context, _ keymanager.IKeymanager) error {
panic("implement me")

View File

@@ -32,6 +32,7 @@ go_library(
"//beacon-chain/core/altair:go_default_library",
"//beacon-chain/core/signing:go_default_library",
"//cache/lru:go_default_library",
"//cmd/validator/flags:go_default_library",
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",

View File

@@ -60,6 +60,7 @@ type Validator interface {
ReceiveBlocks(ctx context.Context, connectionErrorChannel chan<- error)
HandleKeyReload(ctx context.Context, newKeys [][fieldparams.BLSPubkeyLength]byte) (bool, error)
CheckDoppelGanger(ctx context.Context) error
HasProposerSettings() bool
PushProposerSettings(ctx context.Context, km keymanager.IKeymanager) error
SignValidatorRegistrationRequest(ctx context.Context, signer SigningFunc, newValidatorRegistration *ethpb.ValidatorRegistrationV1) (*ethpb.SignedValidatorRegistrationV1, error)
}

View File

@@ -7,6 +7,7 @@ import (
"time"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v3/cmd/validator/flags"
fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams"
"github.com/prysmaticlabs/prysm/v3/config/params"
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
@@ -55,13 +56,21 @@ func run(ctx context.Context, v iface.Validator) {
}
sub := km.SubscribeAccountChanges(accountsChangedChan)
// Set properties on the beacon node like the fee recipient for validators that are being used & active.
if err := v.PushProposerSettings(ctx, km); err != nil {
if errors.Is(err, ErrBuilderValidatorRegistration) {
log.WithError(err).Warn("Push proposer settings error")
} else {
log.WithError(err).Fatal("Failed to update proposer settings") // allow fatal. skipcq
if v.HasProposerSettings() {
log.Infof("Provided proposer settings will periodically update settings such as fee recipient"+
" in 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")
} else {
log.WithError(err).Fatal("Failed to update proposer settings") // allow fatal. skipcq
}
}
} else {
log.Info("Proposer settings such as fee recipient are not defined in the validator client" +
" and will continue to use settings provided in the beacon node.")
}
for {
_, cancel := context.WithCancel(ctx)
ctx, span := trace.StartSpan(ctx, "validator.processSlot")
@@ -118,7 +127,7 @@ func run(ctx context.Context, v iface.Validator) {
continue
}
if slots.IsEpochStart(slot) {
if slots.IsEpochStart(slot) && v.HasProposerSettings() {
go func() {
//deadline set for next epoch rounded up
if err := v.PushProposerSettings(ctx, km); err != nil {

View File

@@ -247,6 +247,11 @@ func (fv *FakeValidator) HandleKeyReload(_ context.Context, newKeys [][fieldpara
func (_ *FakeValidator) SubmitSignedContributionAndProof(_ context.Context, _ types.Slot, _ [fieldparams.BLSPubkeyLength]byte) {
}
// HasProposerSettings for mocking
func (*FakeValidator) HasProposerSettings() bool {
return true
}
// PushProposerSettings for mocking
func (fv *FakeValidator) PushProposerSettings(_ context.Context, _ keymanager.IKeymanager) error {
if fv.ProposerSettingsErr != nil {

View File

@@ -956,21 +956,12 @@ func (v *validator) logDuties(slot types.Slot, duties []*ethpb.DutiesResponse_Du
}
}
func (v *validator) HasProposerSettings() bool {
return v.ProposerSettings != nil
}
// PushProposerSettings calls the prepareBeaconProposer RPC to set the fee recipient and also the register validator API if using a custom builder.
func (v *validator) PushProposerSettings(ctx context.Context, km keymanager.IKeymanager) error {
// only used after Bellatrix
if v.ProposerSettings == nil {
e := params.BeaconConfig().BellatrixForkEpoch
if e != math.MaxUint64 && slots.ToEpoch(slots.CurrentSlot(v.genesisTime)) < e {
log.Warn("You will need to specify the Ethereum addresses which will receive transaction fee rewards from proposing blocks. " +
"This is known as a fee recipient configuration. You can read more about this feature in our documentation portal here (https://docs.prylabs.network/docs/execution-node/fee-recipient)")
} else {
log.Warn("In order to receive transaction fees from proposing blocks post merge, " +
"you must specify a configuration known as a fee recipient config. " +
"If it is not provided, transaction fees will be burnt. Please see our documentation for more information on this requirement (https://docs.prylabs.network/docs/execution-node/fee-recipient).")
}
return nil
}
if km == nil {
return errors.New("keymanager is nil when calling PrepareBeaconProposer")
}
@@ -999,7 +990,7 @@ func (v *validator) PushProposerSettings(ctx context.Context, km keymanager.IKey
log.WithFields(logrus.Fields{
"pubkeysCount": len(pubkeys),
"reqCount": len(proposerReqs),
}).Warnln("Prepare proposer request did not success with all pubkeys")
}).Debugln("Prepare proposer request did not success with all pubkeys")
}
if _, err := v.validatorClient.PrepareBeaconProposer(ctx, &ethpb.PrepareBeaconProposerRequest{
Recipients: proposerReqs,
@@ -1121,7 +1112,7 @@ func (v *validator) validatorIndex(ctx context.Context, pubkey [fieldparams.BLSP
resp, err := v.validatorClient.ValidatorIndex(ctx, &ethpb.ValidatorIndexRequest{PublicKey: pubkey[:]})
switch {
case status.Code(err) == codes.NotFound:
log.Warnf("Could not find validator index for public key %#x. "+
log.Debugf("Could not find validator index for public key %#x. "+
"Perhaps the validator is not yet active.", pubkey)
return 0, false, nil
case err != nil:

View File

@@ -1806,26 +1806,6 @@ func TestValidator_PushProposerSettings(t *testing.T) {
},
},
},
{
name: " Skip if no config",
validatorSetter: func(t *testing.T) *validator {
v := validator{
validatorClient: client,
db: db,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
useWeb: false,
interopKeysConfig: &local.InteropKeymanagerConfig{
NumValidatorKeys: 1,
Offset: 1,
},
}
err := v.WaitForKeymanagerInitialization(ctx)
require.NoError(t, err)
return &v
},
},
{
name: " proposer config not nil but fee recipient empty",
validatorSetter: func(t *testing.T) *validator {
@@ -1910,26 +1890,6 @@ func TestValidator_PushProposerSettings(t *testing.T) {
return &v
},
},
{
name: "Before Bellatrix returns nil",
validatorSetter: func(t *testing.T) *validator {
v := validator{
validatorClient: client,
db: db,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
useWeb: false,
interopKeysConfig: &local.InteropKeymanagerConfig{
NumValidatorKeys: 1,
Offset: 1,
},
}
err := v.WaitForKeymanagerInitialization(ctx)
require.NoError(t, err)
params.BeaconConfig().BellatrixForkEpoch = 123456789
return &v
},
},
{
name: "register validator batch failed",
validatorSetter: func(t *testing.T) *validator {

View File

@@ -497,26 +497,15 @@ func proposerSettings(cliCtx *cli.Context) (*validatorServiceConfig.ProposerSett
!cliCtx.IsSet(flags.ProposerSettingsFlag.Name) &&
!cliCtx.IsSet(flags.ProposerSettingsURLFlag.Name) {
suggestedFee := cliCtx.String(flags.SuggestedFeeRecipientFlag.Name)
var vr *validatorServiceConfig.BuilderConfig
if cliCtx.Bool(flags.EnableBuilderFlag.Name) {
sgl := cliCtx.String(flags.BuilderGasLimitFlag.Name)
vr = &validatorServiceConfig.BuilderConfig{
Enabled: true,
GasLimit: validatorServiceConfig.Uint64(params.BeaconConfig().DefaultBuilderGasLimit),
}
if sgl != "" {
gl, err := strconv.ParseUint(sgl, 10, 64)
if err != nil {
return nil, errors.New("Gas Limit is not a uint64")
}
vr.GasLimit = reviewGasLimit(validatorServiceConfig.Uint64(gl))
}
builderConfig, err := BuilderSettingsFromFlags(cliCtx)
if err != nil {
return nil, err
}
fileConfig = &validatorServiceConfig.ProposerSettingsPayload{
ProposerConfig: nil,
DefaultConfig: &validatorServiceConfig.ProposerOptionPayload{
FeeRecipient: suggestedFee,
BuilderConfig: vr,
BuilderConfig: builderConfig,
},
}
}
@@ -553,6 +542,14 @@ func proposerSettings(cliCtx *cli.Context) (*validatorServiceConfig.ProposerSett
FeeRecipient: common.HexToAddress(fileConfig.DefaultConfig.FeeRecipient),
BuilderConfig: fileConfig.DefaultConfig.BuilderConfig,
}
if vpSettings.DefaultConfig.BuilderConfig == nil {
builderConfig, err := BuilderSettingsFromFlags(cliCtx)
if err != nil {
return nil, err
}
vpSettings.DefaultConfig.BuilderConfig = builderConfig
}
if vpSettings.DefaultConfig.BuilderConfig != nil {
vpSettings.DefaultConfig.BuilderConfig.GasLimit = reviewGasLimit(vpSettings.DefaultConfig.BuilderConfig.GasLimit)
}
@@ -578,6 +575,12 @@ func proposerSettings(cliCtx *cli.Context) (*validatorServiceConfig.ProposerSett
}
if option.BuilderConfig != nil {
option.BuilderConfig.GasLimit = reviewGasLimit(option.BuilderConfig.GasLimit)
} else {
builderConfig, err := BuilderSettingsFromFlags(cliCtx)
if err != nil {
return nil, err
}
option.BuilderConfig = builderConfig
}
vpSettings.ProposeConfig[bytesutil.ToBytes48(decodedKey)] = &validatorServiceConfig.ProposerOption{
FeeRecipient: common.HexToAddress(option.FeeRecipient),
@@ -590,6 +593,26 @@ func proposerSettings(cliCtx *cli.Context) (*validatorServiceConfig.ProposerSett
return vpSettings, nil
}
func BuilderSettingsFromFlags(cliCtx *cli.Context) (*validatorServiceConfig.BuilderConfig, error) {
if cliCtx.Bool(flags.EnableBuilderFlag.Name) {
gasLimit := validatorServiceConfig.Uint64(params.BeaconConfig().DefaultBuilderGasLimit)
sgl := cliCtx.String(flags.BuilderGasLimitFlag.Name)
if sgl != "" {
gl, err := strconv.ParseUint(sgl, 10, 64)
if err != nil {
return nil, errors.New("Gas Limit is not a uint64")
}
gasLimit = reviewGasLimit(validatorServiceConfig.Uint64(gl))
}
return &validatorServiceConfig.BuilderConfig{
Enabled: true,
GasLimit: gasLimit,
}, nil
}
return nil, nil
}
func warnNonChecksummedAddress(feeRecipient string) error {
mixedcaseAddress, err := common.NewMixedcaseAddressFromString(feeRecipient)
if err != nil {

View File

@@ -462,16 +462,90 @@ func TestProposerSettings(t *testing.T) {
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validatorserviceconfig.ProposerOption{
bytesutil.ToBytes48(key1): {
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
BuilderConfig: &validatorserviceconfig.BuilderConfig{
Enabled: true,
GasLimit: validatorserviceconfig.Uint64(params.BeaconConfig().DefaultBuilderGasLimit),
},
},
},
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
BuilderConfig: &validatorserviceconfig.BuilderConfig{
Enabled: true,
GasLimit: validatorserviceconfig.Uint64(params.BeaconConfig().DefaultBuilderGasLimit),
},
},
}
},
wantErr: "",
validatorRegistrationEnabled: true,
},
{
name: "Enable Builder flag overrides empty config",
args: args{
proposerSettingsFlagValues: &proposerSettingsFlag{
dir: "./testdata/good-prepare-beacon-proposer-config.json",
url: "",
defaultfee: "",
},
},
want: func() *validatorserviceconfig.ProposerSettings {
key1, err := hexutil.Decode("0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a")
require.NoError(t, err)
return &validatorserviceconfig.ProposerSettings{
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validatorserviceconfig.ProposerOption{
bytesutil.ToBytes48(key1): {
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
BuilderConfig: &validatorserviceconfig.BuilderConfig{
Enabled: true,
GasLimit: validatorserviceconfig.Uint64(params.BeaconConfig().DefaultBuilderGasLimit),
},
},
},
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
BuilderConfig: &validatorserviceconfig.BuilderConfig{
Enabled: true,
GasLimit: validatorserviceconfig.Uint64(params.BeaconConfig().DefaultBuilderGasLimit),
},
},
}
},
validatorRegistrationEnabled: true,
},
{
name: "Enable Builder flag does not override completed builder config",
args: args{
proposerSettingsFlagValues: &proposerSettingsFlag{
dir: "./testdata/good-prepare-beacon-proposer-config.yaml",
url: "",
defaultfee: "",
},
},
want: func() *validatorserviceconfig.ProposerSettings {
key1, err := hexutil.Decode("0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a")
require.NoError(t, err)
return &validatorserviceconfig.ProposerSettings{
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validatorserviceconfig.ProposerOption{
bytesutil.ToBytes48(key1): {
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
BuilderConfig: &validatorserviceconfig.BuilderConfig{
Enabled: true,
GasLimit: validatorserviceconfig.Uint64(40000000),
},
},
},
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
BuilderConfig: &validatorserviceconfig.BuilderConfig{
Enabled: false,
GasLimit: validatorserviceconfig.Uint64(params.BeaconConfig().DefaultBuilderGasLimit),
},
},
}
},
validatorRegistrationEnabled: true,
},
{
name: "No flags set means empty config",
args: args{

View File

@@ -14,6 +14,7 @@ import (
validatorServiceConfig "github.com/prysmaticlabs/prysm/v3/config/validator/service"
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
ethpbservice "github.com/prysmaticlabs/prysm/v3/proto/eth/service"
eth "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v3/validator/keymanager"
"github.com/prysmaticlabs/prysm/v3/validator/keymanager/derived"
slashingprotection "github.com/prysmaticlabs/prysm/v3/validator/slashing-protection-history"
@@ -24,6 +25,8 @@ 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,
@@ -448,11 +451,26 @@ func (s *Server) SetGasLimit(ctx context.Context, req *ethpbservice.SetGasLimitR
pOption.BuilderConfig = pBuilderConfig
if s.validatorService.ProposerSettings == nil {
s.validatorService.ProposerSettings = &validatorServiceConfig.ProposerSettings{
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validatorServiceConfig.ProposerOption{
bytesutil.ToBytes48(validatorKey): &pOption,
},
DefaultConfig: &defaultOption,
// get the default fee recipient defined with an invalid public key from beacon node
resp, err := s.beaconNodeValidatorClient.GetFeeRecipientByPubKey(ctx, &eth.FeeRecipientByPubKeyRequest{
PublicKey: []byte(nonExistantPublicKey),
})
if resp == nil || len(resp.FeeRecipient) == 0 || err != nil {
s.validatorService.ProposerSettings = &validatorServiceConfig.ProposerSettings{
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validatorServiceConfig.ProposerOption{
bytesutil.ToBytes48(validatorKey): &pOption,
},
DefaultConfig: &defaultOption,
}
} else {
s.validatorService.ProposerSettings = &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 {
s.validatorService.ProposerSettings.ProposeConfig = make(map[[fieldparams.BLSPubkeyLength]byte]*validatorServiceConfig.ProposerOption)
@@ -510,7 +528,7 @@ func (s *Server) DeleteGasLimit(ctx context.Context, req *ethpbservice.DeleteGas
}
// ListFeeRecipientByPubkey returns the public key to eth address mapping object to the end user.
func (s *Server) ListFeeRecipientByPubkey(_ context.Context, req *ethpbservice.PubkeyRequest) (*ethpbservice.GetFeeRecipientByPubkeyResponse, error) {
func (s *Server) ListFeeRecipientByPubkey(ctx context.Context, req *ethpbservice.PubkeyRequest) (*ethpbservice.GetFeeRecipientByPubkeyResponse, error) {
if s.validatorService == nil {
return nil, status.Error(codes.FailedPrecondition, "Validator service not ready")
}
@@ -519,34 +537,34 @@ func (s *Server) ListFeeRecipientByPubkey(_ context.Context, req *ethpbservice.P
return nil, status.Error(codes.FailedPrecondition, err.Error())
}
defaultFeeRecipient := params.BeaconConfig().DefaultFeeRecipient.Bytes()
if s.validatorService.ProposerSettings == nil {
return &ethpbservice.GetFeeRecipientByPubkeyResponse{
Data: &ethpbservice.GetFeeRecipientByPubkeyResponse_FeeRecipient{
Pubkey: validatorKey,
Ethaddress: defaultFeeRecipient,
},
}, nil
}
if s.validatorService.ProposerSettings.ProposeConfig != nil {
proposerOption, found := s.validatorService.ProposerSettings.ProposeConfig[bytesutil.ToBytes48(validatorKey)]
if found {
return &ethpbservice.GetFeeRecipientByPubkeyResponse{
Data: &ethpbservice.GetFeeRecipientByPubkeyResponse_FeeRecipient{
Pubkey: validatorKey,
Ethaddress: proposerOption.FeeRecipient.Bytes(),
},
}, nil
}
}
if s.validatorService.ProposerSettings.DefaultConfig != nil {
defaultFeeRecipient = s.validatorService.ProposerSettings.DefaultConfig.FeeRecipient.Bytes()
}
return &ethpbservice.GetFeeRecipientByPubkeyResponse{
finalResp := &ethpbservice.GetFeeRecipientByPubkeyResponse{
Data: &ethpbservice.GetFeeRecipientByPubkeyResponse_FeeRecipient{
Pubkey: validatorKey,
Ethaddress: defaultFeeRecipient,
},
}, nil
}
if s.validatorService.ProposerSettings == nil {
resp, err := s.beaconNodeValidatorClient.GetFeeRecipientByPubKey(ctx, &eth.FeeRecipientByPubKeyRequest{
PublicKey: validatorKey,
})
if resp != nil && len(resp.FeeRecipient) != 0 && err == nil {
finalResp.Data.Ethaddress = resp.FeeRecipient
}
return finalResp, nil
}
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()
return finalResp, nil
}
}
if s.validatorService.ProposerSettings.DefaultConfig != nil {
finalResp.Data.Ethaddress = s.validatorService.ProposerSettings.DefaultConfig.FeeRecipient.Bytes()
}
return finalResp, nil
}
// SetFeeRecipientByPubkey updates the eth address mapped to the public key.
@@ -568,12 +586,23 @@ func (s *Server) SetFeeRecipientByPubkey(ctx context.Context, req *ethpbservice.
pOption.FeeRecipient = common.BytesToAddress(req.Ethaddress)
switch {
case s.validatorService.ProposerSettings == nil:
s.validatorService.ProposerSettings = &validatorServiceConfig.ProposerSettings{
// get the default fee recipient defined with an invalid public key from beacon node
resp, err := s.beaconNodeValidatorClient.GetFeeRecipientByPubKey(ctx, &eth.FeeRecipientByPubKeyRequest{
PublicKey: []byte(nonExistantPublicKey),
})
settings := &validatorServiceConfig.ProposerSettings{
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validatorServiceConfig.ProposerOption{
bytesutil.ToBytes48(validatorKey): &pOption,
},
DefaultConfig: &defaultOption,
}
if resp == nil || len(resp.FeeRecipient) == 0 || err != nil {
settings.DefaultConfig = &defaultOption
} else {
settings.DefaultConfig = &validatorServiceConfig.ProposerOption{
FeeRecipient: common.BytesToAddress(resp.FeeRecipient),
}
}
s.validatorService.ProposerSettings = settings
case s.validatorService.ProposerSettings.ProposeConfig == nil:
pOption.BuilderConfig = s.validatorService.ProposerSettings.DefaultConfig.BuilderConfig
s.validatorService.ProposerSettings.ProposeConfig = map[[fieldparams.BLSPubkeyLength]byte]*validatorServiceConfig.ProposerOption{

View File

@@ -9,6 +9,7 @@ import (
"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"
@@ -18,8 +19,10 @@ import (
"github.com/prysmaticlabs/prysm/v3/crypto/bls"
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
ethpbservice "github.com/prysmaticlabs/prysm/v3/proto/eth/service"
eth "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
validatorpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1/validator-client"
"github.com/prysmaticlabs/prysm/v3/testing/assert"
mock2 "github.com/prysmaticlabs/prysm/v3/testing/mock"
"github.com/prysmaticlabs/prysm/v3/testing/require"
"github.com/prysmaticlabs/prysm/v3/validator/accounts"
"github.com/prysmaticlabs/prysm/v3/validator/accounts/iface"
@@ -704,6 +707,7 @@ func TestServer_ListFeeRecipientByPubkey(t *testing.T) {
name string
args *validatorserviceconfig.ProposerSettings
want *want
cached *eth.FeeRecipientByPubKeyResponse
wantErr bool
}{
{
@@ -724,23 +728,60 @@ func TestServer_ListFeeRecipientByPubkey(t *testing.T) {
wantErr: false,
},
{
name: "empty settings",
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"),
},
},
want: &want{
EthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
},
cached: &eth.FeeRecipientByPubKeyResponse{
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9").Bytes(),
},
wantErr: false,
},
{
name: "empty settings non cached",
args: nil,
want: &want{
EthAddress: params.BeaconConfig().DefaultFeeRecipient.Hex(),
},
wantErr: false,
},
{
name: "empty settings cached",
args: nil,
want: &want{
EthAddress: common.HexToAddress("0x055Fb65722E7b2455012BFEBf6177F1D2e97387").Hex(),
},
wantErr: false,
cached: &eth.FeeRecipientByPubKeyResponse{
FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455012BFEBf6177F1D2e97387").Bytes(),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctrl := gomock.NewController(t)
mockValidatorClient := mock2.NewMockBeaconNodeValidatorClient(ctrl)
vs, err := client.NewValidatorService(ctx, &client.Config{
Validator: &mock.MockValidator{},
ProposerSettings: tt.args,
})
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,
validatorService: vs,
beaconNodeValidatorClient: mockValidatorClient,
}
got, err := s.ListFeeRecipientByPubkey(ctx, &ethpbservice.PubkeyRequest{Pubkey: byteval})
require.NoError(t, err)
@@ -749,11 +790,21 @@ func TestServer_ListFeeRecipientByPubkey(t *testing.T) {
}
}
func TestServer_SetFeeRecipientByPubkey(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
beaconClient := mock2.NewMockBeaconNodeValidatorClient(ctrl)
ctx := grpc.NewContextWithServerTransportStream(context.Background(), &runtime.ServerTransportStream{})
byteval, err := hexutil.Decode("0xaf2e7ba294e03438ea819bd4033c6c1bf6b04320ee2075b77273c08d02f8a61bcc303c2c06bd3713cb442072ae591493")
wantAddress := "0x055Fb65722e7b2455012Bfebf6177f1d2e9738d7"
cachedAddress := "0x055Fb65722E7b2455012BFEBf6177F1D2e97387"
require.NoError(t, err)
type want struct {
EthAddress string
valEthAddress string
defaultEthaddress string
}
type beaconResp struct {
resp *eth.FeeRecipientByPubKeyResponse
error error
}
tests := []struct {
name string
@@ -761,12 +812,66 @@ func TestServer_SetFeeRecipientByPubkey(t *testing.T) {
proposerSettings *validatorserviceconfig.ProposerSettings
want *want
wantErr bool
beaconReturn *beaconResp
}{
{
name: "Happy Path Test",
args: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
want: &want{
EthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
valEthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
defaultEthaddress: params.BeaconConfig().DefaultFeeRecipient.Hex(),
},
wantErr: false,
beaconReturn: &beaconResp{
resp: nil,
error: nil,
},
},
{
name: "Happy Path Test Beacon Cached",
args: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
want: &want{
valEthAddress: "0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9",
defaultEthaddress: common.HexToAddress(cachedAddress).Hex(),
},
wantErr: false,
beaconReturn: &beaconResp{
resp: &eth.FeeRecipientByPubKeyResponse{
FeeRecipient: common.HexToAddress(cachedAddress).Bytes(),
},
error: nil,
},
},
{
name: "Happy Path Test Beacon Cached preexisting proposer data",
args: wantAddress,
want: &want{
valEthAddress: wantAddress,
defaultEthaddress: common.HexToAddress(cachedAddress).Hex(),
},
proposerSettings: &validatorserviceconfig.ProposerSettings{
ProposeConfig: map[[48]byte]*validatorserviceconfig.ProposerOption{
bytesutil.ToBytes48(byteval): {
FeeRecipient: common.HexToAddress("0x055Fb65722e7b2455012Bfebf6177f1d2e9738d8"),
},
},
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress(cachedAddress),
},
},
wantErr: false,
},
{
name: "Happy Path Test Beacon Cached preexisting default data",
args: wantAddress,
want: &want{
valEthAddress: wantAddress,
defaultEthaddress: common.HexToAddress(cachedAddress).Hex(),
},
proposerSettings: &validatorserviceconfig.ProposerSettings{
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress(cachedAddress),
},
},
wantErr: false,
},
@@ -777,13 +882,21 @@ func TestServer_SetFeeRecipientByPubkey(t *testing.T) {
Validator: &mock.MockValidator{},
ProposerSettings: tt.proposerSettings,
})
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,
validatorService: vs,
beaconNodeValidatorClient: beaconClient,
}
_, err = s.SetFeeRecipientByPubkey(ctx, &ethpbservice.SetFeeRecipientByPubkeyRequest{Pubkey: byteval, Ethaddress: common.HexToAddress(tt.args).Bytes()})
require.NoError(t, err)
assert.Equal(t, tt.want.EthAddress, s.validatorService.ProposerSettings.ProposeConfig[bytesutil.ToBytes48(byteval)].FeeRecipient.Hex())
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())
})
}
}
@@ -905,12 +1018,18 @@ func TestServer_GetGasLimit(t *testing.T) {
}
func TestServer_SetGasLimit(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
beaconClient := mock2.NewMockBeaconNodeValidatorClient(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
@@ -922,6 +1041,7 @@ func TestServer_SetGasLimit(t *testing.T) {
newGasLimit uint64
proposerSettings *validatorserviceconfig.ProposerSettings
w []want
beaconReturn *beaconResp
}{
{
name: "update existing gas limit",
@@ -986,7 +1106,25 @@ func TestServer_SetGasLimit(t *testing.T) {
},
},
{
name: "create new gas limit value for nil proposerSettings",
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{
want{
pubkey: pubkey1,
gaslimit: 7777,
},
},
beaconReturn: &beaconResp{
resp: &eth.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
@@ -996,23 +1134,39 @@ func TestServer_SetGasLimit(t *testing.T) {
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()
vs, err := client.NewValidatorService(ctx, &client.Config{
Validator: &mock.MockValidator{},
ProposerSettings: tt.proposerSettings,
})
require.NoError(t, err)
s := &Server{
validatorService: vs,
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, &ethpbservice.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)
})
}
}