Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validator client builder support #10713

Closed
wants to merge 23 commits into from
Closed
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
253c773
suporting yaml
james-prysm May 17, 2022
fb8f064
fix yaml unmarshaling
kasey May 17, 2022
4935b1d
rolling back some changes from unmarshal from file
james-prysm May 17, 2022
288f338
adding yaml support
james-prysm May 18, 2022
e566041
Merge branch 'builder-1' into validator-client-builder-support
james-prysm May 18, 2022
5205f9b
adding register validator support
james-prysm May 18, 2022
9d29280
Merge branch 'develop' into validator-client-builder-support
james-prysm May 18, 2022
50b4244
added new validator requests into client/validator
james-prysm May 18, 2022
3cfa2de
Merge branch 'builder-1' into validator-client-builder-support
james-prysm May 18, 2022
449d12d
fixing gofmt
james-prysm May 18, 2022
1e7785e
updating flags and including gas limit, unit tests are still broken
james-prysm May 19, 2022
4961971
fixing bazel
james-prysm May 19, 2022
73cdbb4
more name changes and fixing unit tests
james-prysm May 20, 2022
fdd8848
fixing unit tests and renaming functions
james-prysm May 20, 2022
8c9b1f6
Merge branch 'builder-1' into validator-client-builder-support
james-prysm May 20, 2022
bdc2d81
fixing unit tests and renaming to match changes
james-prysm May 20, 2022
9e16452
adding new test for yaml
james-prysm May 20, 2022
8e59afc
fixing bazel linter
james-prysm May 20, 2022
14c412e
reverting change on validator service proto
james-prysm May 20, 2022
a4981f0
adding clarifying logs
james-prysm May 20, 2022
399f216
renaming function name to be more descriptive
james-prysm May 23, 2022
b47fd56
Merge branch 'builder-1' into validator-client-builder-support
james-prysm May 23, 2022
6da4f35
renaming variable
james-prysm May 24, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions cmd/validator/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,13 @@ var (
Usage: "Sets ALL validators' mapping to a suggested an eth address to receive gas fees when proposing a block. Overrides the --fee-recipient-config-file flag if set",
Value: field_params.EthBurnAddressHex,
}

// SuggestedBuilderFeeRecipientFlag defines the address of the fee recipient for custom builders.
SuggestedBuilderFeeRecipientFlag = &cli.StringFlag{
Name: "suggested-builder-fee-recipient",
Usage: "Sets ALL validators' mapping to a suggested an eth address to receive gas fees when proposing a block for custom builders.",
Value: field_params.EthBurnAddressHex,
}
james-prysm marked this conversation as resolved.
Show resolved Hide resolved
)

// DefaultValidatorDir returns OS-specific default validator directory.
Expand Down
1 change: 1 addition & 0 deletions config/fieldparams/mainnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ const (
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.
DefaultBuilderGasLimit = uint64(30000000) // BuilderGasLimit is the default gas limit sent to the Builder API if not provided by the user.
)
1 change: 1 addition & 0 deletions config/fieldparams/minimal.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ const (
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.
DefaultBuilderGasLimit = uint64(30000000) // BuilderGasLimit is the default gas limit sent to the Builder API if not provided by the user.
)
7 changes: 4 additions & 3 deletions config/validator/service/fee-recipient-config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ import (
// 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"`
ProposeConfig map[string]*FeeRecipientFileOptions `json:"proposer_config" yaml:"proposer_config"`
DefaultConfig *FeeRecipientFileOptions `json:"default_config" yaml:"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"`
FeeRecipient string `json:"fee_recipient" yaml:"fee_recipient"`
GasLimit uint64 `json:"builder_fee_recipient_override" yaml:"builder_fee_recipient_override"`
}

// FeeRecipientConfig is a Prysm internal representation of the fee recipient config on the validator client.
Expand Down
2 changes: 1 addition & 1 deletion proto/eth/service/validator_service.proto
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import "google/protobuf/descriptor.proto";
import "google/protobuf/empty.proto";

import "proto/eth/v1/validator.proto";
import "proto/eth/v2/ssz.proto";
//import "proto/eth/v2/ssz.proto";
import "proto/eth/v2/validator.proto";

option csharp_namespace = "Ethereum.Eth.Service";
Expand Down
1 change: 1 addition & 0 deletions validator/client/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ go_library(
"aggregate.go",
"attest.go",
"attest_protect.go",
"builder_registration.go",
"key_reload.go",
"log.go",
"metrics.go",
Expand Down
38 changes: 28 additions & 10 deletions validator/client/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -960,7 +960,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.buildValidatorRequests(ctx, pubkeys)
if err != nil {
return err
}
Expand All @@ -974,24 +974,32 @@ 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.")
for _, request := range registerValidatorRequests {
// calls beacon API but used for custom builders
if err := SubmitBuilderValidatorRegistration(ctx, v.validatorClient, v.node, km.Sign, request); err != nil {
return errors.Wrap(err, fmt.Sprintf("failed to register validator for custom builder for %s", hexutil.Encode(request.Pubkey)))
}
}
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) buildValidatorRequests(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 {
skipAppendToFeeRecipientArray := false
feeRecipient := common.HexToAddress(fieldparams.EthBurnAddressHex)
gasLimit := fieldparams.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
Expand All @@ -1009,12 +1017,22 @@ func (v *validator) feeRecipients(ctx context.Context, pubkeys [][fieldparams.BL
if hexutil.Encode(feeRecipient.Bytes()) == fieldparams.EthBurnAddressHex {
log.Warnln("Fee recipient is set to the burn address. You will not be rewarded transaction fees on this setting. Please set a different fee recipient.")
}
validatorToFeeRecipientArray = append(validatorToFeeRecipientArray, &ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
ValidatorIndex: validatorIndex,
FeeRecipient: feeRecipient[:],
if !skipAppendToFeeRecipientArray {
validatorToFeeRecipients = append(validatorToFeeRecipients, &ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
ValidatorIndex: validatorIndex,
FeeRecipient: feeRecipient[:],
})
}
//TODO: look at caching these values
registerValidatorRequests = append(registerValidatorRequests, &ethpb.ValidatorRegistrationV1{
FeeRecipient: feeRecipient[:],
GasLimit: gasLimit,
Timestamp: uint64(time.Now().UTC().Unix()),
Pubkey: key[:],
})

}
return validatorToFeeRecipientArray, nil
return validatorToFeeRecipients, registerValidatorRequests, nil
}

func (v *validator) cacheValidatorPubkeyHexToValidatorIndex(ctx context.Context, pubkey [fieldparams.BLSPubkeyLength]byte) (types.ValidatorIndex, bool, error) {
Expand Down
2 changes: 1 addition & 1 deletion validator/client/validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1718,7 +1718,7 @@ func TestValidator_UdpateFeeRecipient(t *testing.T) {
pubkeys, err := km.FetchValidatingPublicKeys(ctx)
require.NoError(t, err)
if tt.feeRecipientMap != nil {
feeRecipients, err := v.feeRecipients(ctx, pubkeys)
feeRecipients, err := v.buildValidatorRequests(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)))
Expand Down
1 change: 1 addition & 0 deletions validator/node/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,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",
],
)
29 changes: 11 additions & 18 deletions validator/node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -785,26 +786,18 @@ func unmarshalFromFile(ctx context.Context, from string, to interface{}) error {
}
cleanpath := filepath.Clean(from)
fileExtension := filepath.Ext(cleanpath)
if fileExtension != ".json" {
return errors.Errorf("unsupported file extension %s , (ex. '.json')", fileExtension)
if fileExtension != ".json" && fileExtension != ".yaml" {
return errors.Errorf("unsupported file extension %s , (ex. '.json','.yaml')", 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
}
56 changes: 37 additions & 19 deletions validator/node/node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ import (
"context"
"flag"
"fmt"
"io/fs"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"

"github.com/pkg/errors"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/prysmaticlabs/prysm/cmd/validator/flags"
Expand Down Expand Up @@ -191,70 +194,85 @@ func newWeb3SignerCli(t *testing.T, baseUrl string, publicKeysOrURL string) *cli
}

type test struct {
Foo string `json:"foo"`
Bar int `json:"bar"`
Foo string `json:"foo" yaml:"foo"`
Bar int `json:"bar" yaml:"bar"`
}

func TestUnmarshalFromFile(t *testing.T) {
ctx := context.Background()
type args struct {
File string
To interface{}
To *test
}
var pathError *fs.PathError
tests := []struct {
name string
args args
want interface{}
want *test
urlResponse string
wantErr bool
err error
}{
{
name: "Happy Path File",
args: args{
File: "./testdata/test-unmarshal-good.json",
File: "testdata/test-unmarshal-good.json",
To: &test{},
},
want: &test{
Foo: "foo",
Bar: 1,
},
},
{
name: "Happy Path File Yaml",
args: args{
File: "testdata/test-unmarshal-good.yaml",
To: &test{},
},
want: &test{
Foo: "foo",
Bar: 1,
},
wantErr: false,
},
{
name: "Bad File Path, not json",
args: args{
File: "./jsontools.go",
File: "jsontools.go",
To: &test{},
},
want: &test{},
wantErr: true,
want: &test{},
err: pathError,
},
{
name: "Bad File Path",
args: args{
File: "./testdata/test-unmarshal-bad.json",
File: "testdata/test-unmarshal-bad.json",
To: &test{},
},
want: &test{},
wantErr: true,
want: &test{},
err: pathError,
},
{
name: "Bad File Path, not found",
args: args{
File: "./test-notfound.json",
File: "test-notfound.json",
To: &test{},
},
want: &test{},
wantErr: true,
want: &test{},
err: pathError,
},
}
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)
to := tt.args.To
err := unmarshalFromFile(ctx, tt.args.File, to)
if tt.err != nil {
require.Equal(t, true, errors.As(err, &tt.err))
return
} else {
require.NoError(t, err)
}
require.DeepEqual(t, tt.want, tt.args.To)
require.DeepEqual(t, tt.want, to)
})
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
proposer_config:
'0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a':
fee_recipient: '0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3'
default_config:
fee_recipient: '0x6e35733c5af9B61374A128e6F85f553aF09ff89A'
3 changes: 3 additions & 0 deletions validator/node/testdata/test-unmarshal-good.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
foo: foo
bar: 1