Validator client builder support (#10749)

* Startinb builder service and interface

* Get header from builder

* Add get builder block

* Single validator registration

* Add mev-builder http cli flag

* Add method to verify registration signature

* Add builder registration

* Add submit validator registration

* suporting yaml

* fix yaml unmarshaling

* rolling back some changes from unmarshal from file

* adding yaml support

* adding register validator support

* added new validator requests into client/validator

* fixing gofmt

* updating flags and including gas limit, unit tests are still broken

* fixing bazel

* more name changes and fixing unit tests

* fixing unit tests and renaming functions

* fixing unit tests and renaming to match changes

* adding new test for yaml

* fixing bazel linter

* reverting change on validator service proto

* adding clarifying logs

* renaming function name to be more descriptive

* renaming variable

* rolling back some files that will be added from the builder-1 branch

* reverting more

* more reverting

* need placeholder

* need placeholder

* fixing unit test

* fixing unit test

* fixing unit test

* fixing unit test

* fixing more unit tests

* fixing more unit tests

* rolling back mockgen

* fixing bazel

* rolling back changes

* removing duplicate function

* fixing client mock

* removing unused type

* fixing missing brace

* fixing bad field name

* fixing bazel

* updating naming

* fixing bazel

* fixing unit test

* fixing bazel linting

* unhandled err

* fixing gofmt

* simplifying name based on feedback

* using corrected function

* moving default fee recipient and gaslimit to beaconconfig

* missing a few constant changes

* fixing bazel

* fixing more missed default renames

* fixing more constants in tests

* fixing bazel

* adding update proposer setting per epoch

* refactoring to reduce complexity

* adding unit test for proposer settings

* Update validator/client/validator.go

Co-authored-by: terencechain <terence@prysmaticlabs.com>

* trying out renaming based on feedback

* adjusting based on review comments

* making tests more appropriate

* fixing bazel

* updating flag description based on review feedback

* addressing review feedback

* switching to pushing at start of epoch for more time

* adding new unit test and properly throwing error

* switching keys in error to count

* fixing log variable

* resolving conflict

* resolving more conflicts

* adjusting error message

Co-authored-by: terence tsao <terence@prysmaticlabs.com>
Co-authored-by: Kasey Kirkham <kasey@users.noreply.github.com>
This commit is contained in:
james-prysm
2022-06-06 15:32:41 -04:00
committed by GitHub
parent f0403afb25
commit db687bf56d
41 changed files with 858 additions and 398 deletions

View File

@@ -29,7 +29,7 @@ go_test(
data = glob(["testdata/**"]),
embed = [":go_default_library"],
deps = [
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types/primitives:go_default_library",
"//encoding/bytesutil:go_default_library",
"//proto/engine/v1:go_default_library",

View File

@@ -11,7 +11,7 @@ import (
"testing"
"github.com/prysmaticlabs/go-bitfield"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
eth "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
@@ -98,7 +98,7 @@ func TestClient_RegisterValidator(t *testing.T) {
}
reg := &eth.SignedValidatorRegistrationV1{
Message: &eth.ValidatorRegistrationV1{
FeeRecipient: ezDecode(t, fieldparams.EthBurnAddressHex),
FeeRecipient: ezDecode(t, params.BeaconConfig().EthBurnAddressHex),
GasLimit: 23,
Timestamp: 42,
Pubkey: ezDecode(t, "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a"),

View File

@@ -12,7 +12,6 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/db/kv"
"github.com/prysmaticlabs/prysm/beacon-chain/powchain"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/consensus-types/interfaces"
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
@@ -270,10 +269,10 @@ func (s *Service) getPayloadAttribute(ctx context.Context, st state.BeaconState,
recipient, err := s.cfg.BeaconDB.FeeRecipientByValidatorID(ctx, proposerID)
switch {
case errors.Is(err, kv.ErrNotFoundFeeRecipient):
if feeRecipient.String() == fieldparams.EthBurnAddressHex {
if feeRecipient.String() == params.BeaconConfig().EthBurnAddressHex {
logrus.WithFields(logrus.Fields{
"validatorIndex": proposerID,
"burnAddress": fieldparams.EthBurnAddressHex,
"burnAddress": params.BeaconConfig().EthBurnAddressHex,
}).Warn("Fee recipient is currently using the burn address, " +
"you will not be rewarded transaction fees on this setting. " +
"Please set a different eth address as the fee recipient. " +

View File

@@ -806,7 +806,7 @@ func Test_GetPayloadAttribute(t *testing.T) {
require.NoError(t, err)
require.Equal(t, true, hasPayload)
require.Equal(t, suggestedVid, vId)
require.Equal(t, fieldparams.EthBurnAddressHex, common.BytesToAddress(attr.SuggestedFeeRecipient).String())
require.Equal(t, params.BeaconConfig().EthBurnAddressHex, common.BytesToAddress(attr.SuggestedFeeRecipient).String())
require.LogsContain(t, hook, "Fee recipient is currently using the burn address")
// Cache hit, advance state, has fee recipient

View File

@@ -127,10 +127,10 @@ func (vs *Server) getExecutionPayload(ctx context.Context, slot types.Slot, vIdx
case errors.As(err, kv.ErrNotFoundFeeRecipient):
// If fee recipient is not found in DB and not set from beacon node CLI,
// use the burn address.
if feeRecipient.String() == fieldparams.EthBurnAddressHex {
if feeRecipient.String() == params.BeaconConfig().EthBurnAddressHex {
logrus.WithFields(logrus.Fields{
"validatorIndex": vIdx,
"burnAddress": fieldparams.EthBurnAddressHex,
"burnAddress": params.BeaconConfig().EthBurnAddressHex,
}).Warn("Fee recipient is currently using the burn address, " +
"you will not be rewarded transaction fees on this setting. " +
"Please set a different eth address as the fee recipient. " +

View File

@@ -18,7 +18,6 @@ go_library(
],
deps = [
"//cmd:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_urfave_cli_v2//:go_default_library",

View File

@@ -5,7 +5,6 @@ package flags
import (
"strings"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/urfave/cli/v2"
)
@@ -215,7 +214,7 @@ var (
SuggestedFeeRecipient = &cli.StringFlag{
Name: "suggested-fee-recipient",
Usage: "Post bellatrix, this address will receive the transaction fees produced by any blocks from this node. Default to junk whilst bellatrix is in development state. Validator client can override this value through the preparebeaconproposer api.",
Value: fieldparams.EthBurnAddressHex,
Value: params.BeaconConfig().EthBurnAddressHex,
}
// TerminalTotalDifficultyOverride specifies the total difficulty to manual overrides the `TERMINAL_TOTAL_DIFFICULTY` parameter.
TerminalTotalDifficultyOverride = &cli.StringFlag{

View File

@@ -13,7 +13,7 @@ go_library(
"//validator:__subpackages__",
],
deps = [
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//io/file:go_default_library",
"@com_github_urfave_cli_v2//:go_default_library",
],

View File

@@ -7,7 +7,7 @@ import (
"runtime"
"time"
field_params "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/io/file"
"github.com/urfave/cli/v2"
)
@@ -327,21 +327,36 @@ var (
// FeeRecipientConfigFileFlag defines the path or URL to a file with proposer config.
FeeRecipientConfigFileFlag = &cli.StringFlag{
Name: "fee-recipient-config-file",
Usage: "Set path to a JSON file containing validator mappings to ETH addresses for receiving transaction fees when proposing blocks (i.e. --fee-recipient-config-file=/path/to/proposer.json). File format found in docs",
Usage: "DEPRECATED, please use proposer-settings-file",
Value: "",
}
// FeeRecipientConfigURLFlag defines the path or URL to a file with proposer config.
FeeRecipientConfigURLFlag = &cli.StringFlag{
Name: "fee-recipient-config-url",
Usage: "Set URL to a REST endpoint containing validator mappings to ETH addresses for receiving transaction fees when proposing blocks (i.e. --fee-recipient-config-url=https://example.com/api/getConfig). File format found in docs",
Usage: "DEPRECATED, please use proposer-settings-url",
Value: "",
}
// ProposerSettingsFlag defines the path or URL to a file with proposer config.
ProposerSettingsFlag = &cli.StringFlag{
Name: "proposer-settings-file",
Usage: "Set path to a YAML or JSON file containing validator settings used when proposing blocks such as (fee recipient and gas limit) (i.e. --proposer-settings-file=/path/to/proposer.json). File format found in docs",
Value: "",
}
// ProposerSettingsURLFlag defines the path or URL to a file with proposer config.
ProposerSettingsURLFlag = &cli.StringFlag{
Name: "proposer-settings-url",
Usage: "Set URL to a REST endpoint containing validator settings used when proposing blocks such as (fee recipient) (i.e. --proposer-settings-url=https://example.com/api/getConfig). File format found in docs",
Value: "",
}
// SuggestedFeeRecipientFlag defines the address of the fee recipient.
SuggestedFeeRecipientFlag = &cli.StringFlag{
Name: "suggested-fee-recipient",
Usage: "Sets ALL validators' mapping to a suggested an eth address to receive gas fees when proposing a block. This is a fall back setting and will be overridden by other flags that update the proposer settings. ",
Value: field_params.EthBurnAddressHex,
Name: "suggested-fee-recipient",
Usage: "Sets ALL validators' mapping to a suggested an eth address to receive gas fees when proposing a block." +
" note that this is only a suggestion when integrating with a Builder API, which may choose to specify a different fee recipient as payment for the blocks it builds." +
" For additional setting overrides use the --" + ProposerSettingsFlag.Name + " or --" + ProposerSettingsURLFlag.Name + " Flags. ",
Value: params.BeaconConfig().EthBurnAddressHex,
}
)

View File

@@ -78,6 +78,8 @@ var appFlags = []cli.Flag{
flags.FeeRecipientConfigFileFlag,
flags.FeeRecipientConfigURLFlag,
flags.SuggestedFeeRecipientFlag,
flags.ProposerSettingsURLFlag,
flags.ProposerSettingsFlag,
////////////////////
cmd.DisableMonitoringFlag,
cmd.MonitoringHostFlag,

View File

@@ -111,6 +111,8 @@ var appHelpFlagGroups = []flagGroup{
flags.Web3SignerPublicValidatorKeysFlag,
flags.FeeRecipientConfigFileFlag,
flags.FeeRecipientConfigURLFlag,
flags.ProposerSettingsFlag,
flags.ProposerSettingsURLFlag,
flags.SuggestedFeeRecipientFlag,
},
},

View File

@@ -5,23 +5,22 @@ package field_params
const (
Preset = "mainnet"
BlockRootsLength = 8192 // SLOTS_PER_HISTORICAL_ROOT
StateRootsLength = 8192 // SLOTS_PER_HISTORICAL_ROOT
RandaoMixesLength = 65536 // EPOCHS_PER_HISTORICAL_VECTOR
HistoricalRootsLength = 16777216 // HISTORICAL_ROOTS_LIMIT
ValidatorRegistryLimit = 1099511627776 // VALIDATOR_REGISTRY_LIMIT
Eth1DataVotesLength = 2048 // SLOTS_PER_ETH1_VOTING_PERIOD
PreviousEpochAttestationsLength = 4096 // MAX_ATTESTATIONS * SLOTS_PER_EPOCH
CurrentEpochAttestationsLength = 4096 // MAX_ATTESTATIONS * SLOTS_PER_EPOCH
SlashingsLength = 8192 // EPOCHS_PER_SLASHINGS_VECTOR
SyncCommitteeLength = 512 // SYNC_COMMITTEE_SIZE
RootLength = 32 // RootLength defines the byte length of a Merkle root.
BLSSignatureLength = 96 // BLSSignatureLength defines the byte length of a BLSSignature.
BLSPubkeyLength = 48 // BLSPubkeyLength defines the byte length of a BLSSignature.
MaxTxsPerPayloadLength = 1048576 // MaxTxsPerPayloadLength defines the maximum number of transactions that can be included in a payload.
MaxBytesPerTxLength = 1073741824 // MaxBytesPerTxLength defines the maximum number of bytes that can be included in a transaction.
FeeRecipientLength = 20 // FeeRecipientLength defines the byte length of a fee recipient.
LogsBloomLength = 256 // LogsBloomLength defines the byte length of a logs bloom.
VersionLength = 4 // VersionLength defines the byte length of a fork version number.
EthBurnAddressHex = "0x0000000000000000000000000000000000000000" // EthBurnAddressHex defines the hex encoded address of the eth1.0 burn contract.
BlockRootsLength = 8192 // SLOTS_PER_HISTORICAL_ROOT
StateRootsLength = 8192 // SLOTS_PER_HISTORICAL_ROOT
RandaoMixesLength = 65536 // EPOCHS_PER_HISTORICAL_VECTOR
HistoricalRootsLength = 16777216 // HISTORICAL_ROOTS_LIMIT
ValidatorRegistryLimit = 1099511627776 // VALIDATOR_REGISTRY_LIMIT
Eth1DataVotesLength = 2048 // SLOTS_PER_ETH1_VOTING_PERIOD
PreviousEpochAttestationsLength = 4096 // MAX_ATTESTATIONS * SLOTS_PER_EPOCH
CurrentEpochAttestationsLength = 4096 // MAX_ATTESTATIONS * SLOTS_PER_EPOCH
SlashingsLength = 8192 // EPOCHS_PER_SLASHINGS_VECTOR
SyncCommitteeLength = 512 // SYNC_COMMITTEE_SIZE
RootLength = 32 // RootLength defines the byte length of a Merkle root.
BLSSignatureLength = 96 // BLSSignatureLength defines the byte length of a BLSSignature.
BLSPubkeyLength = 48 // BLSPubkeyLength defines the byte length of a BLSSignature.
MaxTxsPerPayloadLength = 1048576 // MaxTxsPerPayloadLength defines the maximum number of transactions that can be included in a payload.
MaxBytesPerTxLength = 1073741824 // MaxBytesPerTxLength defines the maximum number of bytes that can be included in a transaction.
FeeRecipientLength = 20 // FeeRecipientLength defines the byte length of a fee recipient.
LogsBloomLength = 256 // LogsBloomLength defines the byte length of a logs bloom.
VersionLength = 4 // VersionLength defines the byte length of a fork version number.
)

View File

@@ -5,23 +5,22 @@ package field_params
const (
Preset = "minimal"
BlockRootsLength = 64 // SLOTS_PER_HISTORICAL_ROOT
StateRootsLength = 64 // SLOTS_PER_HISTORICAL_ROOT
RandaoMixesLength = 64 // EPOCHS_PER_HISTORICAL_VECTOR
HistoricalRootsLength = 16777216 // HISTORICAL_ROOTS_LIMIT
ValidatorRegistryLimit = 1099511627776 // VALIDATOR_REGISTRY_LIMIT
Eth1DataVotesLength = 32 // SLOTS_PER_ETH1_VOTING_PERIOD
PreviousEpochAttestationsLength = 1024 // MAX_ATTESTATIONS * SLOTS_PER_EPOCH
CurrentEpochAttestationsLength = 1024 // MAX_ATTESTATIONS * SLOTS_PER_EPOCH
SlashingsLength = 64 // EPOCHS_PER_SLASHINGS_VECTOR
SyncCommitteeLength = 32 // SYNC_COMMITTEE_SIZE
RootLength = 32 // RootLength defines the byte length of a Merkle root.
BLSSignatureLength = 96 // BLSSignatureLength defines the byte length of a BLSSignature.
BLSPubkeyLength = 48 // BLSPubkeyLength defines the byte length of a BLSSignature.
MaxTxsPerPayloadLength = 1048576 // MaxTxsPerPayloadLength defines the maximum number of transactions that can be included in a payload.
MaxBytesPerTxLength = 1073741824 // MaxBytesPerTxLength defines the maximum number of bytes that can be included in a transaction.
FeeRecipientLength = 20 // FeeRecipientLength defines the byte length of a fee recipient.
LogsBloomLength = 256 // LogsBloomLength defines the byte length of a logs bloom.
VersionLength = 4 // VersionLength defines the byte length of a fork version number.
EthBurnAddressHex = "0x0000000000000000000000000000000000000000" // EthBurnAddressHex defines the hex encoded address of the eth1.0 burn contract.
BlockRootsLength = 64 // SLOTS_PER_HISTORICAL_ROOT
StateRootsLength = 64 // SLOTS_PER_HISTORICAL_ROOT
RandaoMixesLength = 64 // EPOCHS_PER_HISTORICAL_VECTOR
HistoricalRootsLength = 16777216 // HISTORICAL_ROOTS_LIMIT
ValidatorRegistryLimit = 1099511627776 // VALIDATOR_REGISTRY_LIMIT
Eth1DataVotesLength = 32 // SLOTS_PER_ETH1_VOTING_PERIOD
PreviousEpochAttestationsLength = 1024 // MAX_ATTESTATIONS * SLOTS_PER_EPOCH
CurrentEpochAttestationsLength = 1024 // MAX_ATTESTATIONS * SLOTS_PER_EPOCH
SlashingsLength = 64 // EPOCHS_PER_SLASHINGS_VECTOR
SyncCommitteeLength = 32 // SYNC_COMMITTEE_SIZE
RootLength = 32 // RootLength defines the byte length of a Merkle root.
BLSSignatureLength = 96 // BLSSignatureLength defines the byte length of a BLSSignature.
BLSPubkeyLength = 48 // BLSPubkeyLength defines the byte length of a BLSSignature.
MaxTxsPerPayloadLength = 1048576 // MaxTxsPerPayloadLength defines the maximum number of transactions that can be included in a payload.
MaxBytesPerTxLength = 1073741824 // MaxBytesPerTxLength defines the maximum number of bytes that can be included in a transaction.
FeeRecipientLength = 20 // FeeRecipientLength defines the byte length of a fee recipient.
LogsBloomLength = 256 // LogsBloomLength defines the byte length of a logs bloom.
VersionLength = 4 // VersionLength defines the byte length of a fork version number.
)

View File

@@ -192,6 +192,8 @@ type BeaconChainConfig struct {
TerminalBlockHashActivationEpoch types.Epoch `yaml:"TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH" spec:"true"` // TerminalBlockHashActivationEpoch of beacon chain.
TerminalTotalDifficulty string `yaml:"TERMINAL_TOTAL_DIFFICULTY" spec:"true"` // TerminalTotalDifficulty is part of the experimental Bellatrix spec. This value is type is currently TBD.
DefaultFeeRecipient common.Address // DefaultFeeRecipient where the transaction fee goes to.
EthBurnAddressHex string // EthBurnAddressHex is the constant eth address written in hex format to burn fees in that network. the default is 0x0
DefaultBuilderGasLimit uint64 // DefaultBuilderGasLimit is the default used to set the gaslimit for the Builder APIs, typically at around 30M wei.
}
// InitializeForkSchedule initializes the schedules forks baked into the config.

View File

@@ -244,6 +244,8 @@ var mainnetBeaconConfig = &BeaconChainConfig{
TerminalBlockHashActivationEpoch: 18446744073709551615,
TerminalBlockHash: [32]byte{},
TerminalTotalDifficulty: "115792089237316195423570985008687907853269984665640564039457584007913129638912",
EthBurnAddressHex: "0x0000000000000000000000000000000000000000",
DefaultBuilderGasLimit: uint64(30000000),
}
// MainnetTestConfig provides a version of the mainnet config that has a different name

View File

@@ -2,7 +2,7 @@ load("@prysm//tools/go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["fee-recipient-config.go"],
srcs = ["proposer-settings.go"],
importpath = "github.com/prysmaticlabs/prysm/config/validator/service",
visibility = ["//visibility:public"],
deps = [

View File

@@ -1,32 +0,0 @@
package validator_service_config
import (
"github.com/ethereum/go-ethereum/common"
field_params "github.com/prysmaticlabs/prysm/config/fieldparams"
)
// FeeRecipientFileConfig is the struct representation of the JSON config file set in the validator through the CLI.
// ProposeConfig is the map of validator address to fee recipient options all in hex format.
// DefaultConfig is the default fee recipient address for all validators unless otherwise specified in the propose config.required.
type FeeRecipientFileConfig struct {
ProposeConfig map[string]*FeeRecipientFileOptions `json:"proposer_config"`
DefaultConfig *FeeRecipientFileOptions `json:"default_config"`
}
// FeeRecipientFileOptions is the struct representation of the JSON config file set in the validator through the CLI.
// FeeRecipient is set to an eth address in hex string format with 0x prefix.
type FeeRecipientFileOptions struct {
FeeRecipient string `json:"fee_recipient"`
}
// FeeRecipientConfig is a Prysm internal representation of the fee recipient config on the validator client.
// FeeRecipientFileConfig maps to FeeRecipientConfig on import through the CLI.
type FeeRecipientConfig struct {
ProposeConfig map[[field_params.BLSPubkeyLength]byte]*FeeRecipientOptions
DefaultConfig *FeeRecipientOptions
}
// FeeRecipientOptions is a Prysm internal representation of the FeeRecipientFileOptions on the validator client in bytes format instead of hex.
type FeeRecipientOptions struct {
FeeRecipient common.Address
}

View File

@@ -0,0 +1,35 @@
package validator_service_config
import (
"github.com/ethereum/go-ethereum/common"
field_params "github.com/prysmaticlabs/prysm/config/fieldparams"
)
// ProposerSettingsPayload is the struct representation of the JSON or YAML payload set in the validator through the CLI.
// ProposeConfig is the map of validator address to fee recipient options all in hex format.
// DefaultConfig is the default fee recipient address for all validators unless otherwise specified in the propose config.required.
type ProposerSettingsPayload struct {
ProposeConfig map[string]*ProposerOptionPayload `json:"proposer_config" yaml:"proposer_config"`
DefaultConfig *ProposerOptionPayload `json:"default_config" yaml:"default_config"`
}
// ProposerOptionPayload is the struct representation of the JSON config file set in the validator through the CLI.
// FeeRecipient is set to an eth address in hex string format with 0x prefix.
// GasLimit is a number set to help the network decide on the maximum gas in each block.
type ProposerOptionPayload struct {
FeeRecipient string `json:"fee_recipient" yaml:"fee_recipient"`
GasLimit uint64 `json:"gas_limit,omitempty" yaml:"gas_limit,omitempty"`
}
// ProposerSettings is a Prysm internal representation of the fee recipient config on the validator client.
// ProposerSettingsPayload maps to ProposerSettings on import through the CLI.
type ProposerSettings struct {
ProposeConfig map[[field_params.BLSPubkeyLength]byte]*ProposerOption
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
GasLimit uint64
}

View File

@@ -1,5 +1,7 @@
// Code generated by fastssz. DO NOT EDIT.
// Hash: 926169eadd07a1db289a5e8416388f330cfb302b03fb1a1590af7fc8f52db228
package eth
import (

View File

@@ -111,7 +111,7 @@ func (m *MockBeaconNodeValidatorClient) GetBeaconBlock(arg0 context.Context, arg
return ret0, ret1
}
// GetBeaconBlock indicates an expected call of GetBeaconBlock.
// GetBeaconBlock indicates an expected call of GetBeaconBlock
func (mr *MockBeaconNodeValidatorClientMockRecorder) GetBeaconBlock(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{arg0, arg1}, arg2...)

View File

@@ -179,8 +179,8 @@ func (_ MockValidator) CheckDoppelGanger(_ context.Context) error {
panic("implement me")
}
// UpdateFeeRecipient for mocking
func (_ MockValidator) UpdateFeeRecipient(_ context.Context, _ keymanager.IKeymanager) error {
// PushProposerSettings for mocking
func (_ MockValidator) PushProposerSettings(_ context.Context, _ keymanager.IKeymanager) error {
panic("implement me")
}

View File

@@ -159,6 +159,7 @@ go_test(
"@com_github_wealdtech_go_eth2_util//:go_default_library",
"@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//:go_default_library",
"@org_golang_google_grpc//metadata:go_default_library",
"@org_golang_google_protobuf//types/known/emptypb:go_default_library",

View File

@@ -57,5 +57,5 @@ 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
UpdateFeeRecipient(ctx context.Context, km keymanager.IKeymanager) error
PushProposerSettings(ctx context.Context, km keymanager.IKeymanager) error
}

View File

@@ -13,6 +13,7 @@ import (
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/time/slots"
"github.com/prysmaticlabs/prysm/validator/client/iface"
"github.com/prysmaticlabs/prysm/validator/keymanager"
"github.com/prysmaticlabs/prysm/validator/keymanager/remote"
"go.opencensus.io/trace"
"google.golang.org/grpc/codes"
@@ -115,10 +116,9 @@ func run(ctx context.Context, v iface.Validator) {
log.Fatalf("Could not get keymanager: %v", err)
}
sub := km.SubscribeAccountChanges(accountsChangedChan)
// Set properties on the beacon node like the fee recipient for validators that are being used & active.
if err := v.UpdateFeeRecipient(ctx, km); err != nil {
log.Fatalf("PreparedBeaconProposer Failed: %v", err) // allow fatal. skipcq
if err := v.PushProposerSettings(ctx, km); err != nil {
log.Fatalf("Failed to update proposer settings: %v", err) // allow fatal. skipcq
}
for {
slotCtx, cancel := context.WithCancel(ctx)
@@ -152,15 +152,7 @@ func run(ctx context.Context, v iface.Validator) {
}
case slot := <-v.NextSlot():
span.AddAttributes(trace.Int64Attribute("slot", int64(slot))) // lint:ignore uintcast -- This conversion is OK for tracing.
remoteKm, ok := km.(remote.RemoteKeymanager)
if ok {
_, err := remoteKm.ReloadPublicKeys(ctx)
if err != nil {
log.WithError(err).Error(msgCouldNotFetchKeys)
}
}
reloadRemoteKeys(ctx, km)
allExited, err := v.AllValidatorsAreExited(ctx)
if err != nil {
log.WithError(err).Error("Could not check if validators are exited")
@@ -184,6 +176,15 @@ func run(ctx context.Context, v iface.Validator) {
continue
}
if slots.IsEpochStart(slot) {
go func() {
//deadline set for next epoch rounded up
if err := v.PushProposerSettings(ctx, km); err != nil {
log.Warnf("Failed to update proposer settings: %v", err)
}
}()
}
// Start fetching domain data for the next epoch.
if slots.IsEpochEnd(slot) {
go v.UpdateDomainDataCaches(ctx, slot+1)
@@ -246,6 +247,16 @@ func run(ctx context.Context, v iface.Validator) {
}
}
func reloadRemoteKeys(ctx context.Context, km keymanager.IKeymanager) {
remoteKm, ok := km.(remote.RemoteKeymanager)
if ok {
_, err := remoteKm.ReloadPublicKeys(ctx)
if err != nil {
log.WithError(err).Error(msgCouldNotFetchKeys)
}
}
}
func isConnectionError(err error) bool {
return err != nil && errors.Is(err, iface.ErrConnectionIssue)
}

View File

@@ -8,6 +8,7 @@ import (
"github.com/prysmaticlabs/prysm/async/event"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
@@ -245,3 +246,20 @@ func TestKeyReload_RemoteKeymanager(t *testing.T) {
run(ctx, v)
assert.Equal(t, true, km.ReloadPublicKeysCalled)
}
func TestUpdateProposerSettingsAt_EpochStart(t *testing.T) {
v := &testutil.FakeValidator{Km: &mockKeymanager{accountsChangedFeed: &event.Feed{}}}
ctx, cancel := context.WithCancel(context.Background())
hook := logTest.NewGlobal()
slot := params.BeaconConfig().SlotsPerEpoch
ticker := make(chan types.Slot)
v.NextSlotRet = ticker
go func() {
ticker <- slot
cancel()
}()
run(ctx, v)
assert.LogsContain(t, hook, "updated proposer settings")
}

View File

@@ -70,7 +70,7 @@ type ValidatorService struct {
grpcHeaders []string
graffiti []byte
Web3SignerConfig *remote_web3signer.SetupConfig
feeRecipientConfig *validator_service_config.FeeRecipientConfig
proposerSettings *validator_service_config.ProposerSettings
}
// Config for the validator service.
@@ -94,7 +94,7 @@ type Config struct {
GraffitiFlag string
Endpoint string
Web3SignerConfig *remote_web3signer.SetupConfig
FeeRecipientConfig *validator_service_config.FeeRecipientConfig
ProposerSettings *validator_service_config.ProposerSettings
}
// NewValidatorService creates a new validator service for the service
@@ -123,7 +123,7 @@ func NewValidatorService(ctx context.Context, cfg *Config) (*ValidatorService, e
graffitiStruct: cfg.GraffitiStruct,
logDutyCountDown: cfg.LogDutyCountDown,
Web3SignerConfig: cfg.Web3SignerConfig,
feeRecipientConfig: cfg.FeeRecipientConfig,
proposerSettings: cfg.ProposerSettings,
}
dialOpts := ConstructDialOptions(
@@ -206,7 +206,7 @@ func (v *ValidatorService) Start() {
eipImportBlacklistedPublicKeys: slashablePublicKeys,
logDutyCountDown: v.logDutyCountDown,
Web3SignerConfig: v.Web3SignerConfig,
feeRecipientConfig: v.feeRecipientConfig,
ProposerSettings: v.proposerSettings,
walletIntializedChannel: make(chan *wallet.Wallet, 1),
}
// To resolve a race condition at startup due to the interface

View File

@@ -17,5 +17,6 @@ go_library(
"//time:go_default_library",
"//validator/client/iface:go_default_library",
"//validator/keymanager:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
],
)

View File

@@ -11,6 +11,7 @@ import (
prysmTime "github.com/prysmaticlabs/prysm/time"
"github.com/prysmaticlabs/prysm/validator/client/iface"
"github.com/prysmaticlabs/prysm/validator/keymanager"
log "github.com/sirupsen/logrus"
)
var _ iface.Validator = (*FakeValidator)(nil)
@@ -247,8 +248,9 @@ func (fv *FakeValidator) HandleKeyReload(_ context.Context, newKeys [][fieldpara
func (_ *FakeValidator) SubmitSignedContributionAndProof(_ context.Context, _ types.Slot, _ [fieldparams.BLSPubkeyLength]byte) {
}
// UpdateFeeRecipient for mocking
func (_ *FakeValidator) UpdateFeeRecipient(_ context.Context, _ keymanager.IKeymanager) error {
// PushProposerSettings for mocking
func (_ *FakeValidator) PushProposerSettings(_ context.Context, _ keymanager.IKeymanager) error {
log.Infoln("Mock updated proposer settings")
return nil
}

View File

@@ -96,7 +96,7 @@ type validator struct {
graffiti []byte
voteStats voteStats
Web3SignerConfig *remote_web3signer.SetupConfig
feeRecipientConfig *validator_service_config.FeeRecipientConfig
ProposerSettings *validator_service_config.ProposerSettings
walletIntializedChannel chan *wallet.Wallet
}
@@ -941,10 +941,10 @@ func (v *validator) logDuties(slot types.Slot, duties []*ethpb.DutiesResponse_Du
}
}
// UpdateFeeRecipient calls the prepareBeaconProposer RPC to set the fee recipient.
func (v *validator) UpdateFeeRecipient(ctx context.Context, km keymanager.IKeymanager) error {
// 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.feeRecipientConfig == nil {
if v.ProposerSettings == nil {
e := params.BeaconConfig().BellatrixForkEpoch
if e != math.MaxUint64 && slots.ToEpoch(slots.CurrentSlot(v.genesisTime)) < e {
log.Warn("After the Ethereum merge, you will need to specify the Ethereum addresses which will receive transaction fee rewards from proposing blocks. " +
@@ -956,6 +956,9 @@ func (v *validator) UpdateFeeRecipient(ctx context.Context, km keymanager.IKeyma
}
return nil
}
deadline := v.SlotDeadline(slots.RoundUpToNearestEpoch(slots.CurrentSlot(v.genesisTime)))
ctx, cancel := context.WithDeadline(ctx, deadline)
defer cancel()
if km == nil {
return errors.New("keymanager is nil when calling PrepareBeaconProposer")
}
@@ -963,7 +966,7 @@ func (v *validator) UpdateFeeRecipient(ctx context.Context, km keymanager.IKeyma
if err != nil {
return err
}
feeRecipients, err := v.feeRecipients(ctx, pubkeys)
feeRecipients, registerValidatorRequests, err := v.buildProposerSettingsRequests(ctx, pubkeys)
if err != nil {
return err
}
@@ -977,47 +980,73 @@ func (v *validator) UpdateFeeRecipient(ctx context.Context, km keymanager.IKeyma
return err
}
log.Infoln("Successfully prepared beacon proposer with fee recipient to validator index mapping.")
failedRegistrationCount := 0
for _, request := range registerValidatorRequests {
// calls beacon API but used for custom builders
if err := SubmitValidatorRegistration(ctx, v.validatorClient, v.node, km.Sign, request); err != nil {
// log the error and keep going
failedRegistrationCount++
log.Warnf("failed to register validator for custom builder for %s, error: %v ", hexutil.Encode(request.Pubkey), err)
}
}
if failedRegistrationCount != 0 {
return errors.Errorf("Register validator requests failed %d times out of %d requests", failedRegistrationCount, len(registerValidatorRequests))
}
log.Infoln("Successfully submitted builder validator registration settings for custom builders.")
return nil
}
func (v *validator) feeRecipients(ctx context.Context, pubkeys [][fieldparams.BLSPubkeyLength]byte) ([]*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer, error) {
var validatorToFeeRecipientArray []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer
func (v *validator) buildProposerSettingsRequests(ctx context.Context, pubkeys [][fieldparams.BLSPubkeyLength]byte) ([]*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer, []*ethpb.ValidatorRegistrationV1, error) {
var validatorToFeeRecipients []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer
var registerValidatorRequests []*ethpb.ValidatorRegistrationV1
// need to check for pubkey to validator index mappings
for _, key := range pubkeys {
feeRecipient := common.HexToAddress(fieldparams.EthBurnAddressHex)
skipAppendToFeeRecipientArray := false
feeRecipient := common.HexToAddress(params.BeaconConfig().EthBurnAddressHex)
gasLimit := params.BeaconConfig().DefaultBuilderGasLimit
validatorIndex, found := v.pubkeyToValidatorIndex[key]
// ignore updating fee recipient if validator index is not found
if !found {
ind, foundIndex, err := v.cacheValidatorPubkeyHexToValidatorIndex(ctx, key)
if err != nil {
return nil, err
return nil, nil, err
}
if !foundIndex {
//if still not found, skip this validator
continue
skipAppendToFeeRecipientArray = true
}
validatorIndex = ind
v.pubkeyToValidatorIndex[key] = validatorIndex
}
if v.feeRecipientConfig.DefaultConfig != nil {
feeRecipient = v.feeRecipientConfig.DefaultConfig.FeeRecipient
if v.ProposerSettings.DefaultConfig != nil {
feeRecipient = v.ProposerSettings.DefaultConfig.FeeRecipient
gasLimit = v.ProposerSettings.DefaultConfig.GasLimit
}
if v.feeRecipientConfig.ProposeConfig != nil {
option, ok := v.feeRecipientConfig.ProposeConfig[key]
if v.ProposerSettings.ProposeConfig != nil {
option, ok := v.ProposerSettings.ProposeConfig[key]
if ok && option != nil {
// override the default if a proposeconfig is set
feeRecipient = option.FeeRecipient
gasLimit = option.GasLimit
}
}
if hexutil.Encode(feeRecipient.Bytes()) == fieldparams.EthBurnAddressHex {
if hexutil.Encode(feeRecipient.Bytes()) == params.BeaconConfig().EthBurnAddressHex {
log.Warnln("Fee recipient is set to the burn address. You will not be rewarded transaction fees on this setting. Please set a different fee recipient.")
}
validatorToFeeRecipientArray = append(validatorToFeeRecipientArray, &ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
ValidatorIndex: validatorIndex,
FeeRecipient: feeRecipient[:],
if !skipAppendToFeeRecipientArray {
validatorToFeeRecipients = append(validatorToFeeRecipients, &ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
ValidatorIndex: validatorIndex,
FeeRecipient: feeRecipient[:],
})
}
registerValidatorRequests = append(registerValidatorRequests, &ethpb.ValidatorRegistrationV1{
FeeRecipient: feeRecipient[:],
GasLimit: gasLimit,
Timestamp: uint64(time.Now().UTC().Unix()),
Pubkey: key[:],
})
}
return validatorToFeeRecipientArray, nil
return validatorToFeeRecipients, registerValidatorRequests, nil
}
func (v *validator) cacheValidatorPubkeyHexToValidatorIndex(ctx context.Context, pubkey [fieldparams.BLSPubkeyLength]byte) (types.ValidatorIndex, bool, error) {

View File

@@ -14,6 +14,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/prysmaticlabs/prysm/async/event"
"github.com/prysmaticlabs/prysm/config/features"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
@@ -39,6 +40,7 @@ import (
logTest "github.com/sirupsen/logrus/hooks/test"
"google.golang.org/grpc"
"google.golang.org/protobuf/types/known/emptypb"
"google.golang.org/protobuf/types/known/timestamppb"
)
func init() {
@@ -357,6 +359,7 @@ func TestWaitMultipleActivation_LogsActivationEpochOK(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
client := mock2.NewMockBeaconNodeValidatorClient(ctrl)
nodeClient := mock2.NewMockNodeClient(ctrl)
privKey, err := bls.RandKey()
require.NoError(t, err)
pubKey := [fieldparams.BLSPubkeyLength]byte{}
@@ -369,11 +372,12 @@ func TestWaitMultipleActivation_LogsActivationEpochOK(t *testing.T) {
v := validator{
validatorClient: client,
keyManager: km,
node: nodeClient,
genesisTime: 1,
pubkeyToValidatorIndex: map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex{pubKey: 1},
feeRecipientConfig: &validator_service_config.FeeRecipientConfig{
ProposerSettings: &validator_service_config.ProposerSettings{
ProposeConfig: nil,
DefaultConfig: &validator_service_config.FeeRecipientOptions{
DefaultConfig: &validator_service_config.ProposerOption{
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
},
},
@@ -392,11 +396,32 @@ func TestWaitMultipleActivation_LogsActivationEpochOK(t *testing.T) {
resp,
nil,
)
nodeClient.EXPECT().GetGenesis(
gomock.Any(),
&emptypb.Empty{},
).Return(
&ethpb.Genesis{GenesisTime: timestamppb.Now()}, nil)
client.EXPECT().DomainData(
gomock.Any(),
gomock.Any(),
).Return(
&ethpb.DomainResponse{
SignatureDomain: make([]byte, 32),
},
nil)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Return(&empty.Empty{}, nil)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A").Bytes(), ValidatorIndex: 1},
},
}).Return(nil, nil)
require.NoError(t, v.WaitForActivation(ctx, nil), "Could not wait for activation")
require.LogsContain(t, hook, "Validator activated")
}
@@ -405,7 +430,7 @@ func TestWaitActivation_NotAllValidatorsActivatedOK(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
client := mock2.NewMockBeaconNodeValidatorClient(ctrl)
nodeClient := mock2.NewMockNodeClient(ctrl)
privKey, err := bls.RandKey()
require.NoError(t, err)
pubKey := [fieldparams.BLSPubkeyLength]byte{}
@@ -417,12 +442,13 @@ func TestWaitActivation_NotAllValidatorsActivatedOK(t *testing.T) {
}
v := validator{
validatorClient: client,
node: nodeClient,
keyManager: km,
genesisTime: 1,
pubkeyToValidatorIndex: map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex{pubKey: 1},
feeRecipientConfig: &validator_service_config.FeeRecipientConfig{
ProposerSettings: &validator_service_config.ProposerSettings{
ProposeConfig: nil,
DefaultConfig: &validator_service_config.FeeRecipientOptions{
DefaultConfig: &validator_service_config.ProposerOption{
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
},
},
@@ -442,6 +468,24 @@ func TestWaitActivation_NotAllValidatorsActivatedOK(t *testing.T) {
resp,
nil,
)
nodeClient.EXPECT().GetGenesis(
gomock.Any(),
&emptypb.Empty{},
).Return(
&ethpb.Genesis{GenesisTime: timestamppb.Now()}, nil)
client.EXPECT().DomainData(
gomock.Any(),
gomock.Any(),
).Return(
&ethpb.DomainResponse{
SignatureDomain: make([]byte, 32),
},
nil)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Return(&empty.Empty{}, nil)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A").Bytes(), ValidatorIndex: 1},
@@ -1464,17 +1508,29 @@ func TestValidator_WaitForKeymanagerInitialization_Interop(t *testing.T) {
require.NotNil(t, km)
}
func TestValidator_UpdateFeeRecipient(t *testing.T) {
func TestValidator_PushProposerSettings(t *testing.T) {
ctrl := gomock.NewController(t)
ctx := context.Background()
db := dbTest.SetupDB(t, [][fieldparams.BLSPubkeyLength]byte{})
client := mock2.NewMockBeaconNodeValidatorClient(ctrl)
nodeClient := mock2.NewMockNodeClient(ctrl)
defaultFeeHex := "0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9"
byteValueAddress, err := hexutil.Decode("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9")
require.NoError(t, err)
type ExpectedValidatorRegistration struct {
FeeRecipient []byte
GasLimit uint64
Timestamp uint64
Pubkey []byte
}
tests := []struct {
name string
validatorSetter func(t *testing.T) *validator
feeRecipientMap map[types.ValidatorIndex]string
err string
name string
validatorSetter func(t *testing.T) *validator
feeRecipientMap map[types.ValidatorIndex]string
mockExpectedRequests []ExpectedValidatorRegistration
err string
}{
{
name: " Happy Path",
@@ -1482,6 +1538,7 @@ func TestValidator_UpdateFeeRecipient(t *testing.T) {
v := validator{
validatorClient: client,
node: nodeClient,
db: db,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
useWeb: false,
@@ -1499,10 +1556,11 @@ func TestValidator_UpdateFeeRecipient(t *testing.T) {
require.NoError(t, err)
keys, err := km.FetchValidatingPublicKeys(ctx)
require.NoError(t, err)
v.feeRecipientConfig = &validator_service_config.FeeRecipientConfig{
v.ProposerSettings = &validator_service_config.ProposerSettings{
ProposeConfig: nil,
DefaultConfig: &validator_service_config.FeeRecipientOptions{
DefaultConfig: &validator_service_config.ProposerOption{
FeeRecipient: common.HexToAddress(defaultFeeHex),
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
}
client.EXPECT().ValidatorIndex(
@@ -1511,6 +1569,24 @@ func TestValidator_UpdateFeeRecipient(t *testing.T) {
).Return(&ethpb.ValidatorIndexResponse{
Index: 1,
}, nil)
nodeClient.EXPECT().GetGenesis(
gomock.Any(),
&emptypb.Empty{},
).Return(
&ethpb.Genesis{GenesisTime: timestamppb.Now()}, nil)
client.EXPECT().DomainData(
gomock.Any(),
gomock.Any(),
).Return(
&ethpb.DomainResponse{
SignatureDomain: make([]byte, 32),
},
nil)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Return(&empty.Empty{}, nil)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress(defaultFeeHex).Bytes(), ValidatorIndex: 1},
@@ -1521,6 +1597,12 @@ func TestValidator_UpdateFeeRecipient(t *testing.T) {
feeRecipientMap: map[types.ValidatorIndex]string{
1: defaultFeeHex,
},
mockExpectedRequests: []ExpectedValidatorRegistration{
{
FeeRecipient: byteValueAddress,
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
},
},
{
name: " Skip if no config",
@@ -1547,6 +1629,7 @@ func TestValidator_UpdateFeeRecipient(t *testing.T) {
v := validator{
validatorClient: client,
node: nodeClient,
db: db,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
useWeb: false,
@@ -1557,9 +1640,9 @@ func TestValidator_UpdateFeeRecipient(t *testing.T) {
}
err := v.WaitForKeymanagerInitialization(ctx)
require.NoError(t, err)
v.feeRecipientConfig = &validator_service_config.FeeRecipientConfig{
v.ProposerSettings = &validator_service_config.ProposerSettings{
ProposeConfig: nil,
DefaultConfig: &validator_service_config.FeeRecipientOptions{
DefaultConfig: &validator_service_config.ProposerOption{
FeeRecipient: common.HexToAddress(defaultFeeHex),
},
}
@@ -1573,6 +1656,24 @@ func TestValidator_UpdateFeeRecipient(t *testing.T) {
).Return(&ethpb.ValidatorIndexResponse{
Index: 1,
}, nil)
nodeClient.EXPECT().GetGenesis(
gomock.Any(),
&emptypb.Empty{},
).Return(
&ethpb.Genesis{GenesisTime: timestamppb.Now()}, nil)
client.EXPECT().DomainData(
gomock.Any(),
gomock.Any(),
).Return(
&ethpb.DomainResponse{
SignatureDomain: make([]byte, 32),
},
nil)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Return(&empty.Empty{}, nil)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress(defaultFeeHex).Bytes(), ValidatorIndex: 1},
@@ -1583,6 +1684,12 @@ func TestValidator_UpdateFeeRecipient(t *testing.T) {
feeRecipientMap: map[types.ValidatorIndex]string{
1: defaultFeeHex,
},
mockExpectedRequests: []ExpectedValidatorRegistration{
{
FeeRecipient: byteValueAddress,
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
},
},
{
name: " Happy Path proposer config not nil",
@@ -1590,6 +1697,7 @@ func TestValidator_UpdateFeeRecipient(t *testing.T) {
v := validator{
validatorClient: client,
node: nodeClient,
db: db,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
useWeb: false,
@@ -1600,7 +1708,7 @@ func TestValidator_UpdateFeeRecipient(t *testing.T) {
}
err := v.WaitForKeymanagerInitialization(ctx)
require.NoError(t, err)
config := make(map[[fieldparams.BLSPubkeyLength]byte]*validator_service_config.FeeRecipientOptions)
config := make(map[[fieldparams.BLSPubkeyLength]byte]*validator_service_config.ProposerOption)
km, err := v.Keymanager()
require.NoError(t, err)
keys, err := km.FetchValidatingPublicKeys(ctx)
@@ -1623,21 +1731,52 @@ func TestValidator_UpdateFeeRecipient(t *testing.T) {
{FeeRecipient: common.HexToAddress(defaultFeeHex).Bytes(), ValidatorIndex: 2},
},
}).Return(nil, nil)
config[keys[0]] = &validator_service_config.FeeRecipientOptions{
config[keys[0]] = &validator_service_config.ProposerOption{
FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9"),
GasLimit: uint64(40000000),
}
v.feeRecipientConfig = &validator_service_config.FeeRecipientConfig{
v.ProposerSettings = &validator_service_config.ProposerSettings{
ProposeConfig: config,
DefaultConfig: &validator_service_config.FeeRecipientOptions{
DefaultConfig: &validator_service_config.ProposerOption{
FeeRecipient: common.HexToAddress(defaultFeeHex),
GasLimit: uint64(35000000),
},
}
nodeClient.EXPECT().GetGenesis(
gomock.Any(),
&emptypb.Empty{},
).Times(2).Return(
&ethpb.Genesis{GenesisTime: timestamppb.Now()}, nil)
client.EXPECT().DomainData(
gomock.Any(),
gomock.Any(),
).Times(2).Return(
&ethpb.DomainResponse{
SignatureDomain: make([]byte, 32),
},
nil)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Times(2).Return(&empty.Empty{}, nil)
return &v
},
feeRecipientMap: map[types.ValidatorIndex]string{
1: "0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9",
2: defaultFeeHex,
},
mockExpectedRequests: []ExpectedValidatorRegistration{
{
FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(),
GasLimit: uint64(40000000),
},
{
FeeRecipient: byteValueAddress,
GasLimit: uint64(35000000),
},
},
},
{
name: " proposer config not nil but fee recipient empty ",
@@ -1645,6 +1784,7 @@ func TestValidator_UpdateFeeRecipient(t *testing.T) {
v := validator{
validatorClient: client,
node: nodeClient,
db: db,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
useWeb: false,
@@ -1655,13 +1795,13 @@ func TestValidator_UpdateFeeRecipient(t *testing.T) {
}
err := v.WaitForKeymanagerInitialization(ctx)
require.NoError(t, err)
config := make(map[[fieldparams.BLSPubkeyLength]byte]*validator_service_config.FeeRecipientOptions)
config := make(map[[fieldparams.BLSPubkeyLength]byte]*validator_service_config.ProposerOption)
km, err := v.Keymanager()
require.NoError(t, err)
keys, err := km.FetchValidatingPublicKeys(ctx)
require.NoError(t, err)
client.EXPECT().ValidatorIndex(
ctx, // ctx
gomock.Any(), // ctx
&ethpb.ValidatorIndexRequest{PublicKey: keys[0][:]},
).Return(&ethpb.ValidatorIndexResponse{
Index: 1,
@@ -1671,16 +1811,33 @@ func TestValidator_UpdateFeeRecipient(t *testing.T) {
{FeeRecipient: common.HexToAddress("0x0").Bytes(), ValidatorIndex: 1},
},
}).Return(nil, nil)
config[keys[0]] = &validator_service_config.FeeRecipientOptions{
config[keys[0]] = &validator_service_config.ProposerOption{
FeeRecipient: common.Address{},
}
v.feeRecipientConfig = &validator_service_config.FeeRecipientConfig{
v.ProposerSettings = &validator_service_config.ProposerSettings{
ProposeConfig: config,
DefaultConfig: &validator_service_config.FeeRecipientOptions{
DefaultConfig: &validator_service_config.ProposerOption{
FeeRecipient: common.HexToAddress(defaultFeeHex),
},
}
nodeClient.EXPECT().GetGenesis(
gomock.Any(),
&emptypb.Empty{},
).Return(
&ethpb.Genesis{GenesisTime: timestamppb.Now()}, nil)
client.EXPECT().DomainData(
gomock.Any(),
gomock.Any(),
).Return(
&ethpb.DomainResponse{
SignatureDomain: make([]byte, 32),
},
nil)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Return(&empty.Empty{}, nil)
return &v
},
},
@@ -1700,21 +1857,21 @@ func TestValidator_UpdateFeeRecipient(t *testing.T) {
}
err := v.WaitForKeymanagerInitialization(ctx)
require.NoError(t, err)
config := make(map[[fieldparams.BLSPubkeyLength]byte]*validator_service_config.FeeRecipientOptions)
config := make(map[[fieldparams.BLSPubkeyLength]byte]*validator_service_config.ProposerOption)
km, err := v.Keymanager()
require.NoError(t, err)
keys, err := km.FetchValidatingPublicKeys(ctx)
require.NoError(t, err)
client.EXPECT().ValidatorIndex(
ctx, // ctx
gomock.Any(), // ctx
&ethpb.ValidatorIndexRequest{PublicKey: keys[0][:]},
).Return(nil, errors.New("Could not find validator index for public key"))
config[keys[0]] = &validator_service_config.FeeRecipientOptions{
config[keys[0]] = &validator_service_config.ProposerOption{
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9"),
}
v.feeRecipientConfig = &validator_service_config.FeeRecipientConfig{
v.ProposerSettings = &validator_service_config.ProposerSettings{
ProposeConfig: config,
DefaultConfig: &validator_service_config.FeeRecipientOptions{
DefaultConfig: &validator_service_config.ProposerOption{
FeeRecipient: common.HexToAddress(defaultFeeHex),
},
}
@@ -1740,6 +1897,70 @@ func TestValidator_UpdateFeeRecipient(t *testing.T) {
return &v
},
},
{
name: "register validator batch failed ",
validatorSetter: func(t *testing.T) *validator {
v := validator{
validatorClient: client,
node: nodeClient,
db: db,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
useWeb: false,
interopKeysConfig: &local.InteropKeymanagerConfig{
NumValidatorKeys: 1,
Offset: 1,
},
}
err := v.WaitForKeymanagerInitialization(ctx)
require.NoError(t, err)
config := make(map[[fieldparams.BLSPubkeyLength]byte]*validator_service_config.ProposerOption)
km, err := v.Keymanager()
require.NoError(t, err)
keys, err := km.FetchValidatingPublicKeys(ctx)
require.NoError(t, err)
client.EXPECT().ValidatorIndex(
gomock.Any(), // ctx
&ethpb.ValidatorIndexRequest{PublicKey: keys[0][:]},
).Return(&ethpb.ValidatorIndexResponse{
Index: 1,
}, nil)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("0x0").Bytes(), ValidatorIndex: 1},
},
}).Return(nil, nil)
config[keys[0]] = &validator_service_config.ProposerOption{
FeeRecipient: common.Address{},
}
v.ProposerSettings = &validator_service_config.ProposerSettings{
ProposeConfig: config,
DefaultConfig: &validator_service_config.ProposerOption{
FeeRecipient: common.HexToAddress(defaultFeeHex),
},
}
nodeClient.EXPECT().GetGenesis(
gomock.Any(),
&emptypb.Empty{},
).Return(
&ethpb.Genesis{GenesisTime: timestamppb.Now()}, nil)
client.EXPECT().DomainData(
gomock.Any(),
gomock.Any(),
).Return(
&ethpb.DomainResponse{
SignatureDomain: make([]byte, 32),
},
nil)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Return(&empty.Empty{}, errors.New("request failed"))
return &v
},
err: "Register validator requests failed",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -1749,18 +1970,21 @@ func TestValidator_UpdateFeeRecipient(t *testing.T) {
pubkeys, err := km.FetchValidatingPublicKeys(ctx)
require.NoError(t, err)
if tt.feeRecipientMap != nil {
feeRecipients, err := v.feeRecipients(ctx, pubkeys)
feeRecipients, registerValidatorRequests, err := v.buildProposerSettingsRequests(ctx, pubkeys)
require.NoError(t, err)
for _, recipient := range feeRecipients {
require.Equal(t, strings.ToLower(tt.feeRecipientMap[recipient.ValidatorIndex]), strings.ToLower(hexutil.Encode(recipient.FeeRecipient)))
}
require.Equal(t, len(tt.feeRecipientMap), len(feeRecipients))
for i, request := range tt.mockExpectedRequests {
require.Equal(t, tt.mockExpectedRequests[i].GasLimit, request.GasLimit)
require.Equal(t, hexutil.Encode(tt.mockExpectedRequests[i].FeeRecipient), hexutil.Encode(request.FeeRecipient))
}
require.Equal(t, len(tt.mockExpectedRequests), len(registerValidatorRequests))
}
if err := v.UpdateFeeRecipient(ctx, km); tt.err != "" {
if err := v.PushProposerSettings(ctx, km); tt.err != "" {
assert.ErrorContains(t, tt.err, err)
}
})
}
}

View File

@@ -105,7 +105,6 @@ func (v *validator) waitForActivation(ctx context.Context, accountsChangedChan <
if ctx.Err() == context.Canceled {
return errors.Wrap(ctx.Err(), "context canceled, not waiting for activation anymore")
}
validatingKeys, err = remoteKm.ReloadPublicKeys(ctx)
if err != nil {
return errors.Wrap(err, msgCouldNotFetchKeys)
@@ -133,7 +132,7 @@ func (v *validator) waitForActivation(ctx context.Context, accountsChangedChan <
if valActivated {
logActiveValidatorStatus(statuses)
// Set properties on the beacon node like the fee recipient for validators that are being used & active.
if err := v.UpdateFeeRecipient(ctx, remoteKm); err != nil {
if err := v.PushProposerSettings(ctx, remoteKm); err != nil {
return err
}
} else {
@@ -180,8 +179,9 @@ func (v *validator) waitForActivation(ctx context.Context, accountsChangedChan <
valActivated := v.checkAndLogValidatorStatus(statuses)
if valActivated {
logActiveValidatorStatus(statuses)
// Set properties on the beacon node like the fee recipient for validators that are being used & active.
if err := v.UpdateFeeRecipient(ctx, v.keyManager); err != nil {
if err := v.PushProposerSettings(ctx, v.keyManager); err != nil {
return err
}
} else {

View File

@@ -8,6 +8,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/golang/mock/gomock"
"github.com/golang/protobuf/ptypes/empty"
"github.com/pkg/errors"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
validator_service_config "github.com/prysmaticlabs/prysm/config/validator/service"
@@ -27,6 +28,8 @@ import (
logTest "github.com/sirupsen/logrus/hooks/test"
"github.com/tyler-smith/go-bip39"
util "github.com/wealdtech/go-eth2-util"
"google.golang.org/protobuf/types/known/emptypb"
"google.golang.org/protobuf/types/known/timestamppb"
)
func TestWaitActivation_ContextCanceled(t *testing.T) {
@@ -67,6 +70,7 @@ func TestWaitActivation_StreamSetupFails_AttemptsToReconnect(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
client := mock.NewMockBeaconNodeValidatorClient(ctrl)
nodeClient := mock.NewMockNodeClient(ctrl)
privKey, err := bls.RandKey()
require.NoError(t, err)
pubKey := [fieldparams.BLSPubkeyLength]byte{}
@@ -78,11 +82,12 @@ func TestWaitActivation_StreamSetupFails_AttemptsToReconnect(t *testing.T) {
}
v := validator{
validatorClient: client,
node: nodeClient,
keyManager: km,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
feeRecipientConfig: &validator_service_config.FeeRecipientConfig{
ProposerSettings: &validator_service_config.ProposerSettings{
ProposeConfig: nil,
DefaultConfig: &validator_service_config.FeeRecipientOptions{
DefaultConfig: &validator_service_config.ProposerOption{
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9"),
},
},
@@ -103,6 +108,24 @@ func TestWaitActivation_StreamSetupFails_AttemptsToReconnect(t *testing.T) {
resp := generateMockStatusResponse([][]byte{pubKey[:]})
resp.Statuses[0].Status.Status = ethpb.ValidatorStatus_ACTIVE
clientStream.EXPECT().Recv().Return(resp, nil)
nodeClient.EXPECT().GetGenesis(
gomock.Any(),
&emptypb.Empty{},
).Return(
&ethpb.Genesis{GenesisTime: timestamppb.Now()}, nil)
client.EXPECT().DomainData(
gomock.Any(),
gomock.Any(),
).Return(
&ethpb.DomainResponse{
SignatureDomain: make([]byte, 32),
},
nil)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Return(&empty.Empty{}, nil)
assert.NoError(t, v.WaitForActivation(context.Background(), nil))
}
@@ -110,7 +133,7 @@ func TestWaitForActivation_ReceiveErrorFromStream_AttemptsReconnection(t *testin
ctrl := gomock.NewController(t)
defer ctrl.Finish()
client := mock.NewMockBeaconNodeValidatorClient(ctrl)
nodeClient := mock.NewMockNodeClient(ctrl)
privKey, err := bls.RandKey()
require.NoError(t, err)
pubKey := [fieldparams.BLSPubkeyLength]byte{}
@@ -122,11 +145,12 @@ func TestWaitForActivation_ReceiveErrorFromStream_AttemptsReconnection(t *testin
}
v := validator{
validatorClient: client,
node: nodeClient,
keyManager: km,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
feeRecipientConfig: &validator_service_config.FeeRecipientConfig{
ProposerSettings: &validator_service_config.ProposerSettings{
ProposeConfig: nil,
DefaultConfig: &validator_service_config.FeeRecipientOptions{
DefaultConfig: &validator_service_config.ProposerOption{
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9"),
},
},
@@ -151,6 +175,24 @@ func TestWaitForActivation_ReceiveErrorFromStream_AttemptsReconnection(t *testin
nil,
errors.New("fails"),
).Return(resp, nil)
nodeClient.EXPECT().GetGenesis(
gomock.Any(),
&emptypb.Empty{},
).Return(
&ethpb.Genesis{GenesisTime: timestamppb.Now()}, nil)
client.EXPECT().DomainData(
gomock.Any(),
gomock.Any(),
).Return(
&ethpb.DomainResponse{
SignatureDomain: make([]byte, 32),
},
nil)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Return(&empty.Empty{}, nil)
assert.NoError(t, v.WaitForActivation(context.Background(), nil))
}
@@ -159,6 +201,7 @@ func TestWaitActivation_LogsActivationEpochOK(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
client := mock.NewMockBeaconNodeValidatorClient(ctrl)
nodeClient := mock.NewMockNodeClient(ctrl)
privKey, err := bls.RandKey()
require.NoError(t, err)
pubKey := [fieldparams.BLSPubkeyLength]byte{}
@@ -170,12 +213,13 @@ func TestWaitActivation_LogsActivationEpochOK(t *testing.T) {
}
v := validator{
validatorClient: client,
node: nodeClient,
keyManager: km,
genesisTime: 1,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
feeRecipientConfig: &validator_service_config.FeeRecipientConfig{
ProposerSettings: &validator_service_config.ProposerSettings{
ProposeConfig: nil,
DefaultConfig: &validator_service_config.FeeRecipientOptions{
DefaultConfig: &validator_service_config.ProposerOption{
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9"),
},
},
@@ -194,6 +238,24 @@ func TestWaitActivation_LogsActivationEpochOK(t *testing.T) {
resp,
nil,
)
nodeClient.EXPECT().GetGenesis(
gomock.Any(),
&emptypb.Empty{},
).Return(
&ethpb.Genesis{GenesisTime: timestamppb.Now()}, nil)
client.EXPECT().DomainData(
gomock.Any(),
gomock.Any(),
).Return(
&ethpb.DomainResponse{
SignatureDomain: make([]byte, 32),
},
nil)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Return(&empty.Empty{}, nil)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 1},
@@ -207,6 +269,7 @@ func TestWaitForActivation_Exiting(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
client := mock.NewMockBeaconNodeValidatorClient(ctrl)
nodeClient := mock.NewMockNodeClient(ctrl)
privKey, err := bls.RandKey()
require.NoError(t, err)
pubKey := [fieldparams.BLSPubkeyLength]byte{}
@@ -218,12 +281,13 @@ func TestWaitForActivation_Exiting(t *testing.T) {
}
v := validator{
validatorClient: client,
node: nodeClient,
keyManager: km,
genesisTime: 1,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
feeRecipientConfig: &validator_service_config.FeeRecipientConfig{
ProposerSettings: &validator_service_config.ProposerSettings{
ProposeConfig: nil,
DefaultConfig: &validator_service_config.FeeRecipientOptions{
DefaultConfig: &validator_service_config.ProposerOption{
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9"),
},
},
@@ -242,6 +306,24 @@ func TestWaitForActivation_Exiting(t *testing.T) {
resp,
nil,
)
nodeClient.EXPECT().GetGenesis(
gomock.Any(),
&emptypb.Empty{},
).Return(
&ethpb.Genesis{GenesisTime: timestamppb.Now()}, nil)
client.EXPECT().DomainData(
gomock.Any(),
gomock.Any(),
).Return(
&ethpb.DomainResponse{
SignatureDomain: make([]byte, 32),
},
nil)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Return(&empty.Empty{}, nil)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 1},
@@ -261,6 +343,7 @@ func TestWaitForActivation_RefetchKeys(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
client := mock.NewMockBeaconNodeValidatorClient(ctrl)
nodeClient := mock.NewMockNodeClient(ctrl)
privKey, err := bls.RandKey()
require.NoError(t, err)
pubKey := [fieldparams.BLSPubkeyLength]byte{}
@@ -273,12 +356,13 @@ func TestWaitForActivation_RefetchKeys(t *testing.T) {
}
v := validator{
validatorClient: client,
node: nodeClient,
keyManager: km,
genesisTime: 1,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
feeRecipientConfig: &validator_service_config.FeeRecipientConfig{
ProposerSettings: &validator_service_config.ProposerSettings{
ProposeConfig: nil,
DefaultConfig: &validator_service_config.FeeRecipientOptions{
DefaultConfig: &validator_service_config.ProposerOption{
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9"),
},
},
@@ -295,8 +379,25 @@ func TestWaitForActivation_RefetchKeys(t *testing.T) {
).Return(clientStream, nil)
clientStream.EXPECT().Recv().Return(
resp,
nil,
)
nil)
nodeClient.EXPECT().GetGenesis(
gomock.Any(),
&emptypb.Empty{},
).Return(
&ethpb.Genesis{GenesisTime: timestamppb.Now()}, nil)
client.EXPECT().DomainData(
gomock.Any(),
gomock.Any(),
).Return(
&ethpb.DomainResponse{
SignatureDomain: make([]byte, 32),
},
nil)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Return(&empty.Empty{}, nil)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 1},
@@ -328,14 +429,16 @@ func TestWaitForActivation_AccountsChanged(t *testing.T) {
},
}
client := mock.NewMockBeaconNodeValidatorClient(ctrl)
nodeClient := mock.NewMockNodeClient(ctrl)
v := validator{
validatorClient: client,
node: nodeClient,
keyManager: km,
genesisTime: 1,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
feeRecipientConfig: &validator_service_config.FeeRecipientConfig{
ProposerSettings: &validator_service_config.ProposerSettings{
ProposeConfig: nil,
DefaultConfig: &validator_service_config.FeeRecipientOptions{
DefaultConfig: &validator_service_config.ProposerOption{
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9"),
},
},
@@ -375,6 +478,24 @@ func TestWaitForActivation_AccountsChanged(t *testing.T) {
activeResp,
nil,
)
nodeClient.EXPECT().GetGenesis(
gomock.Any(),
&emptypb.Empty{},
).Times(2).Return(
&ethpb.Genesis{GenesisTime: timestamppb.Now()}, nil)
client.EXPECT().DomainData(
gomock.Any(),
gomock.Any(),
).Times(2).Return(
&ethpb.DomainResponse{
SignatureDomain: make([]byte, 32),
},
nil)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Times(2).Return(&empty.Empty{}, nil)
go func() {
// We add the active key into the keymanager and simulate a key refresh.
@@ -415,14 +536,16 @@ func TestWaitForActivation_AccountsChanged(t *testing.T) {
err = km.RecoverAccountsFromMnemonic(ctx, constant.TestMnemonic, "", 1)
require.NoError(t, err)
client := mock.NewMockBeaconNodeValidatorClient(ctrl)
nodeClient := mock.NewMockNodeClient(ctrl)
v := validator{
validatorClient: client,
node: nodeClient,
keyManager: km,
genesisTime: 1,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
feeRecipientConfig: &validator_service_config.FeeRecipientConfig{
ProposerSettings: &validator_service_config.ProposerSettings{
ProposeConfig: nil,
DefaultConfig: &validator_service_config.FeeRecipientOptions{
DefaultConfig: &validator_service_config.ProposerOption{
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9"),
},
},
@@ -463,6 +586,24 @@ func TestWaitForActivation_AccountsChanged(t *testing.T) {
activeResp,
nil,
)
nodeClient.EXPECT().GetGenesis(
gomock.Any(),
&emptypb.Empty{},
).Times(2).Return(
&ethpb.Genesis{GenesisTime: timestamppb.Now()}, nil)
client.EXPECT().DomainData(
gomock.Any(),
gomock.Any(),
).Times(2).Return(
&ethpb.DomainResponse{
SignatureDomain: make([]byte, 32),
},
nil)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Times(2).Return(&empty.Empty{}, nil)
channel := make(chan [][fieldparams.BLSPubkeyLength]byte)
go func() {
@@ -504,19 +645,20 @@ func TestWaitForActivation_RemoteKeymanager(t *testing.T) {
t.Run("activated", func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
hook := logTest.NewGlobal()
nodeClient := mock.NewMockNodeClient(ctrl)
tickerChan := make(chan types.Slot)
ticker := &slotutilmock.MockTicker{
Channel: tickerChan,
}
v := validator{
validatorClient: client,
node: nodeClient,
keyManager: &km,
ticker: ticker,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
feeRecipientConfig: &validator_service_config.FeeRecipientConfig{
ProposerSettings: &validator_service_config.ProposerSettings{
ProposeConfig: nil,
DefaultConfig: &validator_service_config.FeeRecipientOptions{
DefaultConfig: &validator_service_config.ProposerOption{
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9"),
},
},
@@ -539,6 +681,23 @@ func TestWaitForActivation_RemoteKeymanager(t *testing.T) {
PublicKeys: [][]byte{inactiveKey[:], activeKey[:]},
},
).Return(resp, nil /* err */)
nodeClient.EXPECT().GetGenesis(
gomock.Any(),
&emptypb.Empty{},
).Times(2).Return(
&ethpb.Genesis{GenesisTime: timestamppb.Now()}, nil)
client.EXPECT().DomainData(
gomock.Any(),
gomock.Any(),
).Times(2).Return(
&ethpb.DomainResponse{
SignatureDomain: make([]byte, 32),
},
nil)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Times(2).Return(&empty.Empty{}, nil)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 2},
@@ -577,19 +736,20 @@ func TestWaitForActivation_RemoteKeymanager(t *testing.T) {
hook := logTest.NewGlobal()
remoteKm := remotekeymanagermock.NewMock()
remoteKm.PublicKeys = [][fieldparams.BLSPubkeyLength]byte{inactiveKey}
nodeClient := mock.NewMockNodeClient(ctrl)
tickerChan := make(chan types.Slot)
ticker := &slotutilmock.MockTicker{
Channel: tickerChan,
}
v := validator{
validatorClient: client,
node: nodeClient,
keyManager: &remoteKm,
ticker: ticker,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
feeRecipientConfig: &validator_service_config.FeeRecipientConfig{
ProposerSettings: &validator_service_config.ProposerSettings{
ProposeConfig: nil,
DefaultConfig: &validator_service_config.FeeRecipientOptions{
DefaultConfig: &validator_service_config.ProposerOption{
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9"),
},
},
@@ -623,6 +783,24 @@ func TestWaitForActivation_RemoteKeymanager(t *testing.T) {
PublicKeys: [][]byte{inactiveKey[:], activeKey[:]},
},
).Return(resp2, nil /* err */)
nodeClient.EXPECT().GetGenesis(
gomock.Any(),
&emptypb.Empty{},
).Times(2).Return(
&ethpb.Genesis{GenesisTime: timestamppb.Now()}, nil)
client.EXPECT().DomainData(
gomock.Any(),
gomock.Any(),
).Times(2).Return(
&ethpb.DomainResponse{
SignatureDomain: make([]byte, 32),
},
nil)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Times(2).Return(&empty.Empty{}, nil)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 2},

View File

@@ -8,10 +8,14 @@ go_library(
visibility = ["//visibility:public"],
deps = [
"//async/event:go_default_library",
"//beacon-chain/core/signing:go_default_library",
"//config/fieldparams:go_default_library",
"//crypto/bls:go_default_library",
"//encoding/bytesutil:go_default_library",
"//proto/eth/service:go_default_library",
"//proto/prysm/v1alpha1/validator-client:go_default_library",
"//testing/util:go_default_library",
"//time/slots:go_default_library",
"//validator/keymanager:go_default_library",
],
)

View File

@@ -5,10 +5,14 @@ import (
"errors"
"github.com/prysmaticlabs/prysm/async/event"
"github.com/prysmaticlabs/prysm/beacon-chain/core/signing"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/crypto/bls"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpbservice "github.com/prysmaticlabs/prysm/proto/eth/service"
validatorpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/validator-client"
"github.com/prysmaticlabs/prysm/testing/util"
"github.com/prysmaticlabs/prysm/time/slots"
"github.com/prysmaticlabs/prysm/validator/keymanager"
)
@@ -33,8 +37,18 @@ func (m *MockKeymanager) FetchValidatingPublicKeys(context.Context) ([][fieldpar
}
// Sign --
func (*MockKeymanager) Sign(context.Context, *validatorpb.SignRequest) (bls.Signature, error) {
panic("implement me")
func (*MockKeymanager) Sign(_ context.Context, s *validatorpb.SignRequest) (bls.Signature, error) {
key, err := bls.RandKey()
if err != nil {
return nil, err
}
st, _ := util.DeterministicGenesisState(nil, 1)
e := slots.ToEpoch(st.Slot())
byteValue, err := signing.ComputeDomainAndSign(st, e, s.SigningSlot, bytesutil.ToBytes4(s.SignatureDomain), key)
if err != nil {
return nil, err
}
return bls.SignatureFromBytes(byteValue)
}
// SubscribeAccountChanges --

View File

@@ -9,6 +9,7 @@ go_test(
deps = [
"//cmd/validator/flags:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//config/validator/service:go_default_library",
"//encoding/bytesutil:go_default_library",
"//testing/assert:go_default_library",
@@ -72,6 +73,7 @@ go_library(
"@com_github_pkg_errors//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_urfave_cli_v2//:go_default_library",
"@in_gopkg_yaml_v2//:go_default_library",
"@org_golang_google_protobuf//encoding/protojson:go_default_library",
],
)

View File

@@ -54,6 +54,7 @@ import (
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
"google.golang.org/protobuf/encoding/protojson"
"gopkg.in/yaml.v2"
)
// ValidatorClient defines an instance of an Ethereum validator that manages
@@ -401,7 +402,7 @@ func (c *ValidatorClient) registerValidatorService(cliCtx *cli.Context) error {
return err
}
bpc, err := feeRecipientConfig(c.cliCtx)
bpc, err := proposerSettings(c.cliCtx)
if err != nil {
return err
}
@@ -425,7 +426,7 @@ func (c *ValidatorClient) registerValidatorService(cliCtx *cli.Context) error {
GraffitiStruct: gStruct,
LogDutyCountDown: c.cliCtx.Bool(flags.EnableDutyCountDown.Name),
Web3SignerConfig: wsc,
FeeRecipientConfig: bpc,
ProposerSettings: bpc,
})
if err != nil {
return errors.Wrap(err, "could not initialize validator service")
@@ -470,29 +471,44 @@ func web3SignerConfig(cliCtx *cli.Context) (*remote_web3signer.SetupConfig, erro
return web3signerConfig, nil
}
func feeRecipientConfig(cliCtx *cli.Context) (*validatorServiceConfig.FeeRecipientConfig, error) {
var fileConfig *validatorServiceConfig.FeeRecipientFileConfig
func proposerSettings(cliCtx *cli.Context) (*validatorServiceConfig.ProposerSettings, error) {
var fileConfig *validatorServiceConfig.ProposerSettingsPayload
//TODO(10809): remove when fully deprecated
if cliCtx.IsSet(flags.FeeRecipientConfigFileFlag.Name) && cliCtx.IsSet(flags.FeeRecipientConfigURLFlag.Name) {
return nil, fmt.Errorf("cannot specify both --%s and --%s", flags.FeeRecipientConfigFileFlag.Name, flags.FeeRecipientConfigURLFlag.Name)
}
if cliCtx.IsSet(flags.ProposerSettingsFlag.Name) && cliCtx.IsSet(flags.ProposerSettingsURLFlag.Name) {
return nil, errors.New("cannot specify both " + flags.ProposerSettingsFlag.Name + " and " + flags.ProposerSettingsURLFlag.Name)
}
// is overridden by file and URL flags
if cliCtx.IsSet(flags.SuggestedFeeRecipientFlag.Name) {
suggestedFee := cliCtx.String(flags.SuggestedFeeRecipientFlag.Name)
fileConfig = &validatorServiceConfig.FeeRecipientFileConfig{
fileConfig = &validatorServiceConfig.ProposerSettingsPayload{
ProposeConfig: nil,
DefaultConfig: &validatorServiceConfig.FeeRecipientFileOptions{
DefaultConfig: &validatorServiceConfig.ProposerOptionPayload{
FeeRecipient: suggestedFee,
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
}
}
if cliCtx.IsSet(flags.FeeRecipientConfigFileFlag.Name) {
if err := unmarshalFromFile(cliCtx.Context, cliCtx.String(flags.FeeRecipientConfigFileFlag.Name), &fileConfig); err != nil {
return nil, errors.New(flags.FeeRecipientConfigFileFlag.Usage)
}
if cliCtx.IsSet(flags.FeeRecipientConfigURLFlag.Name) {
return nil, errors.New(flags.FeeRecipientConfigURLFlag.Usage)
}
if cliCtx.IsSet(flags.ProposerSettingsFlag.Name) {
if err := unmarshalFromFile(cliCtx.Context, cliCtx.String(flags.ProposerSettingsFlag.Name), &fileConfig); err != nil {
return nil, err
}
}
if cliCtx.IsSet(flags.FeeRecipientConfigURLFlag.Name) {
if err := unmarshalFromURL(cliCtx.Context, cliCtx.String(flags.FeeRecipientConfigURLFlag.Name), &fileConfig); err != nil {
if cliCtx.IsSet(flags.ProposerSettingsURLFlag.Name) {
if err := unmarshalFromURL(cliCtx.Context, cliCtx.String(flags.ProposerSettingsURLFlag.Name), &fileConfig); err != nil {
return nil, err
}
}
@@ -502,7 +518,7 @@ func feeRecipientConfig(cliCtx *cli.Context) (*validatorServiceConfig.FeeRecipie
return nil, nil
}
//convert file config to proposer config for internal use
frConfig := &validatorServiceConfig.FeeRecipientConfig{}
vpSettings := &validatorServiceConfig.ProposerSettings{}
// default fileConfig is mandatory
if fileConfig.DefaultConfig == nil {
@@ -515,12 +531,14 @@ func feeRecipientConfig(cliCtx *cli.Context) (*validatorServiceConfig.FeeRecipie
if !common.IsHexAddress(fileConfig.DefaultConfig.FeeRecipient) {
return nil, errors.New("default fileConfig fee recipient is not a valid eth1 address")
}
frConfig.DefaultConfig = &validatorServiceConfig.FeeRecipientOptions{
vpSettings.DefaultConfig = &validatorServiceConfig.ProposerOption{
FeeRecipient: common.BytesToAddress(bytes),
GasLimit: reviewGasLimit(fileConfig.DefaultConfig.GasLimit),
}
if fileConfig.ProposeConfig != nil {
frConfig.ProposeConfig = make(map[[fieldparams.BLSPubkeyLength]byte]*validatorServiceConfig.FeeRecipientOptions)
vpSettings.ProposeConfig = make(map[[fieldparams.BLSPubkeyLength]byte]*validatorServiceConfig.ProposerOption)
for key, option := range fileConfig.ProposeConfig {
decodedKey, err := hexutil.Decode(key)
if err != nil {
@@ -550,13 +568,23 @@ func feeRecipientConfig(cliCtx *cli.Context) (*validatorServiceConfig.FeeRecipie
"We recommend using a mixed-case address (checksum) "+
"to prevent spelling mistakes in your fee recipient Ethereum address", option.FeeRecipient, checksumAddress.Hex())
}
frConfig.ProposeConfig[bytesutil.ToBytes48(decodedKey)] = &validatorServiceConfig.FeeRecipientOptions{
vpSettings.ProposeConfig[bytesutil.ToBytes48(decodedKey)] = &validatorServiceConfig.ProposerOption{
FeeRecipient: checksumAddress,
GasLimit: reviewGasLimit(option.GasLimit),
}
}
}
return frConfig, nil
return vpSettings, nil
}
func reviewGasLimit(gasLimit uint64) uint64 {
// sets gas limit to default if not defined or set to 0
if gasLimit == 0 {
return params.BeaconConfig().DefaultBuilderGasLimit
}
//TODO(10810): add in warning for ranges
return gasLimit
}
func (c *ValidatorClient) registerRPCService(cliCtx *cli.Context) error {
@@ -784,27 +812,14 @@ func unmarshalFromFile(ctx context.Context, from string, to interface{}) error {
return errors.New("node: nil context passed to unmarshalFromFile")
}
cleanpath := filepath.Clean(from)
fileExtension := filepath.Ext(cleanpath)
if fileExtension != ".json" {
return errors.Errorf("unsupported file extension %s , (ex. '.json')", fileExtension)
b, err := os.ReadFile(cleanpath)
if err != nil {
return errors.Wrap(err, "failed to open file")
}
jsonFile, jsonerr := os.Open(cleanpath)
if jsonerr != nil {
return errors.Wrap(jsonerr, "failed to open json file")
}
// defer the closing of our jsonFile so that we can parse it later on
defer func(jsonFile *os.File) {
err := jsonFile.Close()
if err != nil {
log.WithError(err).Error("failed to close json file")
}
}(jsonFile)
byteValue, readerror := io.ReadAll(jsonFile)
if readerror != nil {
return errors.Wrap(readerror, "failed to read json file")
}
if unmarshalerr := json.Unmarshal(byteValue, &to); unmarshalerr != nil {
return errors.Wrap(unmarshalerr, "failed to unmarshal json file")
if err := yaml.Unmarshal(b, to); err != nil {
return errors.Wrap(err, "failed to unmarshal yaml file")
}
return nil
}

View File

@@ -10,6 +10,8 @@ import (
"path/filepath"
"testing"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/prysmaticlabs/prysm/cmd/validator/flags"
@@ -190,143 +192,22 @@ func newWeb3SignerCli(t *testing.T, baseUrl string, publicKeysOrURL string) *cli
return cli.NewContext(&app, set, nil)
}
type test struct {
Foo string `json:"foo"`
Bar int `json:"bar"`
}
func TestUnmarshalFromFile(t *testing.T) {
ctx := context.Background()
type args struct {
File string
To interface{}
}
tests := []struct {
name string
args args
want interface{}
urlResponse string
wantErr bool
}{
{
name: "Happy Path File",
args: args{
File: "./testdata/test-unmarshal-good.json",
To: &test{},
},
want: &test{
Foo: "foo",
Bar: 1,
},
wantErr: false,
},
{
name: "Bad File Path, not json",
args: args{
File: "./jsontools.go",
To: &test{},
},
want: &test{},
wantErr: true,
},
{
name: "Bad File Path",
args: args{
File: "./testdata/test-unmarshal-bad.json",
To: &test{},
},
want: &test{},
wantErr: true,
},
{
name: "Bad File Path, not found",
args: args{
File: "./test-notfound.json",
To: &test{},
},
want: &test{},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := unmarshalFromFile(ctx, tt.args.File, tt.args.To); (err != nil) != tt.wantErr {
t.Errorf(" error = %v, wantErr %v", err, tt.wantErr)
return
}
require.DeepEqual(t, tt.want, tt.args.To)
})
}
}
func TestUnmarshalFromURL(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
w.Header().Set("Content-Type", "application/json")
_, err := fmt.Fprintf(w, `{ "foo": "foo", "bar": 1}`)
require.NoError(t, err)
}))
defer srv.Close()
ctx := context.Background()
type args struct {
URL string
To interface{}
}
tests := []struct {
name string
args args
want interface{}
urlResponse string
wantErr bool
}{
{
name: "Happy Path URL",
args: args{
URL: srv.URL,
To: &test{},
},
want: &test{
Foo: "foo",
Bar: 1,
},
wantErr: false,
},
{
name: "Bad URL",
args: args{
URL: "sadjflksdjflksadjflkdj",
To: &test{},
},
want: &test{},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := unmarshalFromURL(ctx, tt.args.URL, tt.args.To); (err != nil) != tt.wantErr {
t.Errorf(" error = %v, wantErr %v", err, tt.wantErr)
return
}
require.DeepEqual(t, tt.want, tt.args.To)
})
}
}
func TestFeeRecipientConfig(t *testing.T) {
func TestProposerSettings(t *testing.T) {
hook := logTest.NewGlobal()
type feeRecipientFlag struct {
type proposerSettingsFlag struct {
dir string
url string
defaultfee string
}
type args struct {
feeRecipientFlagValues *feeRecipientFlag
proposerSettingsFlagValues *proposerSettingsFlag
}
tests := []struct {
name string
args args
want func() *validator_service_config.FeeRecipientConfig
want func() *validator_service_config.ProposerSettings
urlResponse string
wantErr string
wantLog string
@@ -334,23 +215,25 @@ func TestFeeRecipientConfig(t *testing.T) {
{
name: "Happy Path Config file File, bad checksum",
args: args{
feeRecipientFlagValues: &feeRecipientFlag{
proposerSettingsFlagValues: &proposerSettingsFlag{
dir: "./testdata/good-prepare-beacon-proposer-config-badchecksum.json",
url: "",
defaultfee: "",
},
},
want: func() *validator_service_config.FeeRecipientConfig {
want: func() *validator_service_config.ProposerSettings {
key1, err := hexutil.Decode("0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a")
require.NoError(t, err)
return &validator_service_config.FeeRecipientConfig{
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validator_service_config.FeeRecipientOptions{
return &validator_service_config.ProposerSettings{
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validator_service_config.ProposerOption{
bytesutil.ToBytes48(key1): {
FeeRecipient: common.HexToAddress("0xae967917c465db8578ca9024c205720b1a3651A9"),
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
},
DefaultConfig: &validator_service_config.FeeRecipientOptions{
DefaultConfig: &validator_service_config.ProposerOption{
FeeRecipient: common.HexToAddress("0xae967917c465db8578ca9024c205720b1a3651A9"),
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
}
},
@@ -360,28 +243,31 @@ func TestFeeRecipientConfig(t *testing.T) {
{
name: "Happy Path Config file File multiple fee recipients",
args: args{
feeRecipientFlagValues: &feeRecipientFlag{
proposerSettingsFlagValues: &proposerSettingsFlag{
dir: "./testdata/good-prepare-beacon-proposer-config-multiple.json",
url: "",
defaultfee: "",
},
},
want: func() *validator_service_config.FeeRecipientConfig {
want: func() *validator_service_config.ProposerSettings {
key1, err := hexutil.Decode("0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a")
require.NoError(t, err)
key2, err := hexutil.Decode("0xb057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7b")
require.NoError(t, err)
return &validator_service_config.FeeRecipientConfig{
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validator_service_config.FeeRecipientOptions{
return &validator_service_config.ProposerSettings{
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validator_service_config.ProposerOption{
bytesutil.ToBytes48(key1): {
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
bytesutil.ToBytes48(key2): {
FeeRecipient: common.HexToAddress("0x60155530FCE8a85ec7055A5F8b2bE214B3DaeFd4"),
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
},
DefaultConfig: &validator_service_config.FeeRecipientOptions{
DefaultConfig: &validator_service_config.ProposerOption{
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
}
},
@@ -390,23 +276,52 @@ func TestFeeRecipientConfig(t *testing.T) {
{
name: "Happy Path Config URL File",
args: args{
feeRecipientFlagValues: &feeRecipientFlag{
proposerSettingsFlagValues: &proposerSettingsFlag{
dir: "",
url: "./testdata/good-prepare-beacon-proposer-config.json",
defaultfee: "",
},
},
want: func() *validator_service_config.FeeRecipientConfig {
want: func() *validator_service_config.ProposerSettings {
key1, err := hexutil.Decode("0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a")
require.NoError(t, err)
return &validator_service_config.FeeRecipientConfig{
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validator_service_config.FeeRecipientOptions{
return &validator_service_config.ProposerSettings{
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validator_service_config.ProposerOption{
bytesutil.ToBytes48(key1): {
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
},
DefaultConfig: &validator_service_config.FeeRecipientOptions{
DefaultConfig: &validator_service_config.ProposerOption{
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
}
},
wantErr: "",
},
{
name: "Happy Path Config YAML file with custom Gas Limit",
args: args{
proposerSettingsFlagValues: &proposerSettingsFlag{
dir: "./testdata/good-prepare-beacon-proposer-config.yaml",
url: "",
defaultfee: "",
},
},
want: func() *validator_service_config.ProposerSettings {
key1, err := hexutil.Decode("0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a")
require.NoError(t, err)
return &validator_service_config.ProposerSettings{
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validator_service_config.ProposerOption{
bytesutil.ToBytes48(key1): {
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
GasLimit: uint64(40000000),
},
},
DefaultConfig: &validator_service_config.ProposerOption{
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
GasLimit: uint64(45000000),
},
}
},
@@ -415,17 +330,18 @@ func TestFeeRecipientConfig(t *testing.T) {
{
name: "Happy Path Suggested Fee File",
args: args{
feeRecipientFlagValues: &feeRecipientFlag{
proposerSettingsFlagValues: &proposerSettingsFlag{
dir: "",
url: "",
defaultfee: "0x6e35733c5af9B61374A128e6F85f553aF09ff89A",
},
},
want: func() *validator_service_config.FeeRecipientConfig {
return &validator_service_config.FeeRecipientConfig{
want: func() *validator_service_config.ProposerSettings {
return &validator_service_config.ProposerSettings{
ProposeConfig: nil,
DefaultConfig: &validator_service_config.FeeRecipientOptions{
DefaultConfig: &validator_service_config.ProposerOption{
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
}
},
@@ -434,23 +350,25 @@ func TestFeeRecipientConfig(t *testing.T) {
{
name: "Suggested Fee does not Override Config",
args: args{
feeRecipientFlagValues: &feeRecipientFlag{
proposerSettingsFlagValues: &proposerSettingsFlag{
dir: "./testdata/good-prepare-beacon-proposer-config.json",
url: "",
defaultfee: "0x6e35733c5af9B61374A128e6F85f553aF09ff89B",
},
},
want: func() *validator_service_config.FeeRecipientConfig {
want: func() *validator_service_config.ProposerSettings {
key1, err := hexutil.Decode("0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a")
require.NoError(t, err)
return &validator_service_config.FeeRecipientConfig{
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validator_service_config.FeeRecipientOptions{
return &validator_service_config.ProposerSettings{
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validator_service_config.ProposerOption{
bytesutil.ToBytes48(key1): {
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
},
DefaultConfig: &validator_service_config.FeeRecipientOptions{
DefaultConfig: &validator_service_config.ProposerOption{
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
}
},
@@ -459,28 +377,42 @@ func TestFeeRecipientConfig(t *testing.T) {
{
name: "No flags set means empty config",
args: args{
feeRecipientFlagValues: &feeRecipientFlag{
proposerSettingsFlagValues: &proposerSettingsFlag{
dir: "",
url: "",
defaultfee: "",
},
},
want: func() *validator_service_config.FeeRecipientConfig {
want: func() *validator_service_config.ProposerSettings {
return nil
},
wantErr: "",
},
{
name: "Bad File Path",
args: args{
proposerSettingsFlagValues: &proposerSettingsFlag{
dir: "./testdata/bad-prepare-beacon-proposer-config.json",
url: "",
defaultfee: "",
},
},
want: func() *validator_service_config.ProposerSettings {
return nil
},
wantErr: "failed to unmarshal yaml file",
},
{
name: "Both URL and Dir flags used resulting in error",
args: args{
feeRecipientFlagValues: &feeRecipientFlag{
proposerSettingsFlagValues: &proposerSettingsFlag{
dir: "./testdata/good-prepare-beacon-proposer-config.json",
url: "./testdata/good-prepare-beacon-proposer-config.json",
defaultfee: "",
},
},
want: func() *validator_service_config.FeeRecipientConfig {
return &validator_service_config.FeeRecipientConfig{}
want: func() *validator_service_config.ProposerSettings {
return &validator_service_config.ProposerSettings{}
},
wantErr: "cannot specify both",
},
@@ -489,12 +421,12 @@ func TestFeeRecipientConfig(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
app := cli.App{}
set := flag.NewFlagSet("test", 0)
if tt.args.feeRecipientFlagValues.dir != "" {
set.String(flags.FeeRecipientConfigFileFlag.Name, tt.args.feeRecipientFlagValues.dir, "")
require.NoError(t, set.Set(flags.FeeRecipientConfigFileFlag.Name, tt.args.feeRecipientFlagValues.dir))
if tt.args.proposerSettingsFlagValues.dir != "" {
set.String(flags.ProposerSettingsFlag.Name, tt.args.proposerSettingsFlagValues.dir, "")
require.NoError(t, set.Set(flags.ProposerSettingsFlag.Name, tt.args.proposerSettingsFlagValues.dir))
}
if tt.args.feeRecipientFlagValues.url != "" {
content, err := os.ReadFile(tt.args.feeRecipientFlagValues.url)
if tt.args.proposerSettingsFlagValues.url != "" {
content, err := os.ReadFile(tt.args.proposerSettingsFlagValues.url)
require.NoError(t, err)
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
@@ -504,15 +436,15 @@ func TestFeeRecipientConfig(t *testing.T) {
}))
defer srv.Close()
set.String(flags.FeeRecipientConfigURLFlag.Name, tt.args.feeRecipientFlagValues.url, "")
require.NoError(t, set.Set(flags.FeeRecipientConfigURLFlag.Name, srv.URL))
set.String(flags.ProposerSettingsURLFlag.Name, tt.args.proposerSettingsFlagValues.url, "")
require.NoError(t, set.Set(flags.ProposerSettingsURLFlag.Name, srv.URL))
}
if tt.args.feeRecipientFlagValues.defaultfee != "" {
set.String(flags.SuggestedFeeRecipientFlag.Name, tt.args.feeRecipientFlagValues.defaultfee, "")
require.NoError(t, set.Set(flags.SuggestedFeeRecipientFlag.Name, tt.args.feeRecipientFlagValues.defaultfee))
if tt.args.proposerSettingsFlagValues.defaultfee != "" {
set.String(flags.SuggestedFeeRecipientFlag.Name, tt.args.proposerSettingsFlagValues.defaultfee, "")
require.NoError(t, set.Set(flags.SuggestedFeeRecipientFlag.Name, tt.args.proposerSettingsFlagValues.defaultfee))
}
cliCtx := cli.NewContext(&app, set, nil)
got, err := feeRecipientConfig(cliCtx)
got, err := proposerSettings(cliCtx)
if tt.wantErr != "" {
require.ErrorContains(t, tt.wantErr, err)
return

View File

@@ -0,0 +1,4 @@
{
"proposer_config": {
"0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a": {

View File

@@ -0,0 +1,8 @@
---
proposer_config:
'0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a':
fee_recipient: '0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3'
gas_limit: 40000000
default_config:
fee_recipient: '0x6e35733c5af9B61374A128e6F85f553aF09ff89A'
gas_limit: 45000000

View File

@@ -1,2 +0,0 @@
{
"foo": "bar"

View File

@@ -1,4 +0,0 @@
{
"foo": "foo",
"bar": 1
}