diff --git a/api/client/builder/BUILD.bazel b/api/client/builder/BUILD.bazel index 08495fee16a0..5830310adaf5 100644 --- a/api/client/builder/BUILD.bazel +++ b/api/client/builder/BUILD.bazel @@ -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", diff --git a/api/client/builder/client_test.go b/api/client/builder/client_test.go index 767878793337..feec09760947 100644 --- a/api/client/builder/client_test.go +++ b/api/client/builder/client_test.go @@ -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 := ð.SignedValidatorRegistrationV1{ Message: ð.ValidatorRegistrationV1{ - FeeRecipient: ezDecode(t, fieldparams.EthBurnAddressHex), + FeeRecipient: ezDecode(t, params.BeaconConfig().EthBurnAddressHex), GasLimit: 23, Timestamp: 42, Pubkey: ezDecode(t, "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a"), diff --git a/beacon-chain/blockchain/execution_engine.go b/beacon-chain/blockchain/execution_engine.go index 61dbefa0e3a2..6487dddc625a 100644 --- a/beacon-chain/blockchain/execution_engine.go +++ b/beacon-chain/blockchain/execution_engine.go @@ -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. " + diff --git a/beacon-chain/blockchain/execution_engine_test.go b/beacon-chain/blockchain/execution_engine_test.go index 9a03c755cc7c..49fa7e4425f5 100644 --- a/beacon-chain/blockchain/execution_engine_test.go +++ b/beacon-chain/blockchain/execution_engine_test.go @@ -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 diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_execution_payload.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_execution_payload.go index d9ae6d83d4f3..733bec351cdf 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_execution_payload.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_execution_payload.go @@ -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. " + diff --git a/cmd/beacon-chain/flags/BUILD.bazel b/cmd/beacon-chain/flags/BUILD.bazel index e19df4a5e883..82cbee64f538 100644 --- a/cmd/beacon-chain/flags/BUILD.bazel +++ b/cmd/beacon-chain/flags/BUILD.bazel @@ -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", diff --git a/cmd/beacon-chain/flags/base.go b/cmd/beacon-chain/flags/base.go index eb24033f5541..88f51be26509 100644 --- a/cmd/beacon-chain/flags/base.go +++ b/cmd/beacon-chain/flags/base.go @@ -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{ diff --git a/cmd/validator/flags/BUILD.bazel b/cmd/validator/flags/BUILD.bazel index 71d06450489f..0e6632903a75 100644 --- a/cmd/validator/flags/BUILD.bazel +++ b/cmd/validator/flags/BUILD.bazel @@ -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", ], diff --git a/cmd/validator/flags/flags.go b/cmd/validator/flags/flags.go index 1f2959dc5cc8..6b133e545816 100644 --- a/cmd/validator/flags/flags.go +++ b/cmd/validator/flags/flags.go @@ -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, } ) diff --git a/cmd/validator/main.go b/cmd/validator/main.go index b1967c07b5a7..d203d0c1ef16 100644 --- a/cmd/validator/main.go +++ b/cmd/validator/main.go @@ -78,6 +78,8 @@ var appFlags = []cli.Flag{ flags.FeeRecipientConfigFileFlag, flags.FeeRecipientConfigURLFlag, flags.SuggestedFeeRecipientFlag, + flags.ProposerSettingsURLFlag, + flags.ProposerSettingsFlag, //////////////////// cmd.DisableMonitoringFlag, cmd.MonitoringHostFlag, diff --git a/cmd/validator/usage.go b/cmd/validator/usage.go index c263d3891128..a79905093bfc 100644 --- a/cmd/validator/usage.go +++ b/cmd/validator/usage.go @@ -111,6 +111,8 @@ var appHelpFlagGroups = []flagGroup{ flags.Web3SignerPublicValidatorKeysFlag, flags.FeeRecipientConfigFileFlag, flags.FeeRecipientConfigURLFlag, + flags.ProposerSettingsFlag, + flags.ProposerSettingsURLFlag, flags.SuggestedFeeRecipientFlag, }, }, diff --git a/config/fieldparams/mainnet.go b/config/fieldparams/mainnet.go index d9601add9e96..b66f65ee2967 100644 --- a/config/fieldparams/mainnet.go +++ b/config/fieldparams/mainnet.go @@ -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. ) diff --git a/config/fieldparams/minimal.go b/config/fieldparams/minimal.go index 50d27f3151b4..620a64e14cd4 100644 --- a/config/fieldparams/minimal.go +++ b/config/fieldparams/minimal.go @@ -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. ) diff --git a/config/params/config.go b/config/params/config.go index 502ce4297142..a47c93d66b24 100644 --- a/config/params/config.go +++ b/config/params/config.go @@ -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. diff --git a/config/params/mainnet_config.go b/config/params/mainnet_config.go index 771b6ac094a5..91e5636e963c 100644 --- a/config/params/mainnet_config.go +++ b/config/params/mainnet_config.go @@ -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 diff --git a/config/validator/service/BUILD.bazel b/config/validator/service/BUILD.bazel index ef68c36947e4..aa7dc2f17cb7 100644 --- a/config/validator/service/BUILD.bazel +++ b/config/validator/service/BUILD.bazel @@ -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 = [ diff --git a/config/validator/service/fee-recipient-config.go b/config/validator/service/fee-recipient-config.go deleted file mode 100644 index a5d937078534..000000000000 --- a/config/validator/service/fee-recipient-config.go +++ /dev/null @@ -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 -} diff --git a/config/validator/service/proposer-settings.go b/config/validator/service/proposer-settings.go new file mode 100644 index 000000000000..67500e1e92ee --- /dev/null +++ b/config/validator/service/proposer-settings.go @@ -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 +} diff --git a/proto/prysm/v1alpha1/generated.ssz.go b/proto/prysm/v1alpha1/generated.ssz.go index 8dac1a8561b9..86ddac2304d1 100644 --- a/proto/prysm/v1alpha1/generated.ssz.go +++ b/proto/prysm/v1alpha1/generated.ssz.go @@ -1,5 +1,7 @@ // Code generated by fastssz. DO NOT EDIT. + // Hash: 926169eadd07a1db289a5e8416388f330cfb302b03fb1a1590af7fc8f52db228 + package eth import ( diff --git a/testing/mock/beacon_validator_client_mock.go b/testing/mock/beacon_validator_client_mock.go index f208295651b5..0e1cf6a26260 100644 --- a/testing/mock/beacon_validator_client_mock.go +++ b/testing/mock/beacon_validator_client_mock.go @@ -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...) diff --git a/validator/accounts/testing/mock.go b/validator/accounts/testing/mock.go index 5a7705c66e87..7a9f190858c6 100644 --- a/validator/accounts/testing/mock.go +++ b/validator/accounts/testing/mock.go @@ -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") } diff --git a/validator/client/BUILD.bazel b/validator/client/BUILD.bazel index dd79403d4529..8cccaf02ebe5 100644 --- a/validator/client/BUILD.bazel +++ b/validator/client/BUILD.bazel @@ -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", diff --git a/validator/client/iface/validator.go b/validator/client/iface/validator.go index d2150ba5be5c..08338bfd6340 100644 --- a/validator/client/iface/validator.go +++ b/validator/client/iface/validator.go @@ -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 } diff --git a/validator/client/runner.go b/validator/client/runner.go index 7c7e78cd9c0b..b37ab93572d8 100644 --- a/validator/client/runner.go +++ b/validator/client/runner.go @@ -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) } diff --git a/validator/client/runner_test.go b/validator/client/runner_test.go index 79d8699f4a6e..43ccaee8c728 100644 --- a/validator/client/runner_test.go +++ b/validator/client/runner_test.go @@ -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") +} diff --git a/validator/client/service.go b/validator/client/service.go index 6d2af7ad79b1..d15fe705dece 100644 --- a/validator/client/service.go +++ b/validator/client/service.go @@ -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 diff --git a/validator/client/testutil/BUILD.bazel b/validator/client/testutil/BUILD.bazel index 03d8b22856da..3b0901528276 100644 --- a/validator/client/testutil/BUILD.bazel +++ b/validator/client/testutil/BUILD.bazel @@ -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", ], ) diff --git a/validator/client/testutil/mock_validator.go b/validator/client/testutil/mock_validator.go index 643e67d5b5ca..424ac941debe 100644 --- a/validator/client/testutil/mock_validator.go +++ b/validator/client/testutil/mock_validator.go @@ -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 } diff --git a/validator/client/validator.go b/validator/client/validator.go index d3f133ed7974..0304a4f18ebf 100644 --- a/validator/client/validator.go +++ b/validator/client/validator.go @@ -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, ðpb.PrepareBeaconProposerRequest_FeeRecipientContainer{ - ValidatorIndex: validatorIndex, - FeeRecipient: feeRecipient[:], + if !skipAppendToFeeRecipientArray { + validatorToFeeRecipients = append(validatorToFeeRecipients, ðpb.PrepareBeaconProposerRequest_FeeRecipientContainer{ + ValidatorIndex: validatorIndex, + FeeRecipient: feeRecipient[:], + }) + } + registerValidatorRequests = append(registerValidatorRequests, ðpb.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) { diff --git a/validator/client/validator_test.go b/validator/client/validator_test.go index 90ed9ed36940..39e336290ade 100644 --- a/validator/client/validator_test.go +++ b/validator/client/validator_test.go @@ -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( + ðpb.Genesis{GenesisTime: timestamppb.Now()}, nil) + + client.EXPECT().DomainData( + gomock.Any(), + gomock.Any(), + ).Return( + ðpb.DomainResponse{ + SignatureDomain: make([]byte, 32), + }, + nil) + client.EXPECT().SubmitValidatorRegistration( + gomock.Any(), + gomock.Any(), + ).Return(&empty.Empty{}, nil) + client.EXPECT().PrepareBeaconProposer(gomock.Any(), ðpb.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( + ðpb.Genesis{GenesisTime: timestamppb.Now()}, nil) + + client.EXPECT().DomainData( + gomock.Any(), + gomock.Any(), + ).Return( + ðpb.DomainResponse{ + SignatureDomain: make([]byte, 32), + }, + nil) + client.EXPECT().SubmitValidatorRegistration( + gomock.Any(), + gomock.Any(), + ).Return(&empty.Empty{}, nil) client.EXPECT().PrepareBeaconProposer(gomock.Any(), ðpb.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(ðpb.ValidatorIndexResponse{ Index: 1, }, nil) + nodeClient.EXPECT().GetGenesis( + gomock.Any(), + &emptypb.Empty{}, + ).Return( + ðpb.Genesis{GenesisTime: timestamppb.Now()}, nil) + + client.EXPECT().DomainData( + gomock.Any(), + gomock.Any(), + ).Return( + ðpb.DomainResponse{ + SignatureDomain: make([]byte, 32), + }, + nil) + client.EXPECT().SubmitValidatorRegistration( + gomock.Any(), + gomock.Any(), + ).Return(&empty.Empty{}, nil) client.EXPECT().PrepareBeaconProposer(gomock.Any(), ðpb.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(ðpb.ValidatorIndexResponse{ Index: 1, }, nil) + nodeClient.EXPECT().GetGenesis( + gomock.Any(), + &emptypb.Empty{}, + ).Return( + ðpb.Genesis{GenesisTime: timestamppb.Now()}, nil) + + client.EXPECT().DomainData( + gomock.Any(), + gomock.Any(), + ).Return( + ðpb.DomainResponse{ + SignatureDomain: make([]byte, 32), + }, + nil) + client.EXPECT().SubmitValidatorRegistration( + gomock.Any(), + gomock.Any(), + ).Return(&empty.Empty{}, nil) client.EXPECT().PrepareBeaconProposer(gomock.Any(), ðpb.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( + ðpb.Genesis{GenesisTime: timestamppb.Now()}, nil) + + client.EXPECT().DomainData( + gomock.Any(), + gomock.Any(), + ).Times(2).Return( + ðpb.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 ðpb.ValidatorIndexRequest{PublicKey: keys[0][:]}, ).Return(ðpb.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( + ðpb.Genesis{GenesisTime: timestamppb.Now()}, nil) + + client.EXPECT().DomainData( + gomock.Any(), + gomock.Any(), + ).Return( + ðpb.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 ðpb.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 + ðpb.ValidatorIndexRequest{PublicKey: keys[0][:]}, + ).Return(ðpb.ValidatorIndexResponse{ + Index: 1, + }, nil) + client.EXPECT().PrepareBeaconProposer(gomock.Any(), ðpb.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( + ðpb.Genesis{GenesisTime: timestamppb.Now()}, nil) + + client.EXPECT().DomainData( + gomock.Any(), + gomock.Any(), + ).Return( + ðpb.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) } - }) } } diff --git a/validator/client/wait_for_activation.go b/validator/client/wait_for_activation.go index 6b069af93de1..0f1c5a94d4b4 100644 --- a/validator/client/wait_for_activation.go +++ b/validator/client/wait_for_activation.go @@ -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 { diff --git a/validator/client/wait_for_activation_test.go b/validator/client/wait_for_activation_test.go index cb4f2ff6e62e..4c6ea23520ee 100644 --- a/validator/client/wait_for_activation_test.go +++ b/validator/client/wait_for_activation_test.go @@ -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( + ðpb.Genesis{GenesisTime: timestamppb.Now()}, nil) + + client.EXPECT().DomainData( + gomock.Any(), + gomock.Any(), + ).Return( + ðpb.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( + ðpb.Genesis{GenesisTime: timestamppb.Now()}, nil) + + client.EXPECT().DomainData( + gomock.Any(), + gomock.Any(), + ).Return( + ðpb.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( + ðpb.Genesis{GenesisTime: timestamppb.Now()}, nil) + + client.EXPECT().DomainData( + gomock.Any(), + gomock.Any(), + ).Return( + ðpb.DomainResponse{ + SignatureDomain: make([]byte, 32), + }, + nil) + client.EXPECT().SubmitValidatorRegistration( + gomock.Any(), + gomock.Any(), + ).Return(&empty.Empty{}, nil) client.EXPECT().PrepareBeaconProposer(gomock.Any(), ðpb.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( + ðpb.Genesis{GenesisTime: timestamppb.Now()}, nil) + + client.EXPECT().DomainData( + gomock.Any(), + gomock.Any(), + ).Return( + ðpb.DomainResponse{ + SignatureDomain: make([]byte, 32), + }, + nil) + client.EXPECT().SubmitValidatorRegistration( + gomock.Any(), + gomock.Any(), + ).Return(&empty.Empty{}, nil) client.EXPECT().PrepareBeaconProposer(gomock.Any(), ðpb.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( + ðpb.Genesis{GenesisTime: timestamppb.Now()}, nil) + + client.EXPECT().DomainData( + gomock.Any(), + gomock.Any(), + ).Return( + ðpb.DomainResponse{ + SignatureDomain: make([]byte, 32), + }, + nil) + client.EXPECT().SubmitValidatorRegistration( + gomock.Any(), + gomock.Any(), + ).Return(&empty.Empty{}, nil) client.EXPECT().PrepareBeaconProposer(gomock.Any(), ðpb.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( + ðpb.Genesis{GenesisTime: timestamppb.Now()}, nil) + + client.EXPECT().DomainData( + gomock.Any(), + gomock.Any(), + ).Times(2).Return( + ðpb.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( + ðpb.Genesis{GenesisTime: timestamppb.Now()}, nil) + + client.EXPECT().DomainData( + gomock.Any(), + gomock.Any(), + ).Times(2).Return( + ðpb.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( + ðpb.Genesis{GenesisTime: timestamppb.Now()}, nil) + client.EXPECT().DomainData( + gomock.Any(), + gomock.Any(), + ).Times(2).Return( + ðpb.DomainResponse{ + SignatureDomain: make([]byte, 32), + }, + nil) + client.EXPECT().SubmitValidatorRegistration( + gomock.Any(), + gomock.Any(), + ).Times(2).Return(&empty.Empty{}, nil) client.EXPECT().PrepareBeaconProposer(gomock.Any(), ðpb.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( + ðpb.Genesis{GenesisTime: timestamppb.Now()}, nil) + + client.EXPECT().DomainData( + gomock.Any(), + gomock.Any(), + ).Times(2).Return( + ðpb.DomainResponse{ + SignatureDomain: make([]byte, 32), + }, + nil) + client.EXPECT().SubmitValidatorRegistration( + gomock.Any(), + gomock.Any(), + ).Times(2).Return(&empty.Empty{}, nil) client.EXPECT().PrepareBeaconProposer(gomock.Any(), ðpb.PrepareBeaconProposerRequest{ Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{ {FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 2}, diff --git a/validator/keymanager/remote/mock/BUILD.bazel b/validator/keymanager/remote/mock/BUILD.bazel index 1d4c044cccce..c4ba19d471db 100644 --- a/validator/keymanager/remote/mock/BUILD.bazel +++ b/validator/keymanager/remote/mock/BUILD.bazel @@ -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", ], ) diff --git a/validator/keymanager/remote/mock/mock_keymanager.go b/validator/keymanager/remote/mock/mock_keymanager.go index e3d695869348..9be8d383e661 100644 --- a/validator/keymanager/remote/mock/mock_keymanager.go +++ b/validator/keymanager/remote/mock/mock_keymanager.go @@ -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 -- diff --git a/validator/node/BUILD.bazel b/validator/node/BUILD.bazel index 04c67d689a24..bd68d9a740e6 100644 --- a/validator/node/BUILD.bazel +++ b/validator/node/BUILD.bazel @@ -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", ], ) diff --git a/validator/node/node.go b/validator/node/node.go index 0786477ee3b4..83f254dd7549 100644 --- a/validator/node/node.go +++ b/validator/node/node.go @@ -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) - } - 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") + b, err := os.ReadFile(cleanpath) + if err != nil { + return errors.Wrap(err, "failed to open 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 } diff --git a/validator/node/node_test.go b/validator/node/node_test.go index f8c814d03e92..a480db9c1b57 100644 --- a/validator/node/node_test.go +++ b/validator/node/node_test.go @@ -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 diff --git a/validator/node/testdata/bad-prepare-beacon-proposer-config.json b/validator/node/testdata/bad-prepare-beacon-proposer-config.json new file mode 100644 index 000000000000..61ca52437f51 --- /dev/null +++ b/validator/node/testdata/bad-prepare-beacon-proposer-config.json @@ -0,0 +1,4 @@ +{ + "proposer_config": { + "0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a": { + diff --git a/validator/node/testdata/good-prepare-beacon-proposer-config.yaml b/validator/node/testdata/good-prepare-beacon-proposer-config.yaml new file mode 100644 index 000000000000..0d0e0cf41c20 --- /dev/null +++ b/validator/node/testdata/good-prepare-beacon-proposer-config.yaml @@ -0,0 +1,8 @@ +--- +proposer_config: + '0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a': + fee_recipient: '0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3' + gas_limit: 40000000 +default_config: + fee_recipient: '0x6e35733c5af9B61374A128e6F85f553aF09ff89A' + gas_limit: 45000000 \ No newline at end of file diff --git a/validator/node/testdata/test-unmarshal-bad.json b/validator/node/testdata/test-unmarshal-bad.json deleted file mode 100644 index 0b2fffd4fd9d..000000000000 --- a/validator/node/testdata/test-unmarshal-bad.json +++ /dev/null @@ -1,2 +0,0 @@ -{ - "foo": "bar" diff --git a/validator/node/testdata/test-unmarshal-good.json b/validator/node/testdata/test-unmarshal-good.json deleted file mode 100644 index c1150b906a41..000000000000 --- a/validator/node/testdata/test-unmarshal-good.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "foo": "foo", - "bar": 1 -} \ No newline at end of file