Skip to content

Commit

Permalink
Main: v3-proposals & Custom Graffiti (#1569)
Browse files Browse the repository at this point in the history
* add option to supply custom graffiti through env file
* v3 proposals
Co-authored-by: rehs0y <[email protected]>

---------

Co-authored-by: guy muroch <[email protected]>
Co-authored-by: moshe-blox <[email protected]>
Co-authored-by: moshe-blox <[email protected]>
Co-authored-by: rehs0y <[email protected]>
  • Loading branch information
5 people authored Aug 7, 2024
1 parent 791cf27 commit 65a9ced
Show file tree
Hide file tree
Showing 29 changed files with 160 additions and 280 deletions.
5 changes: 2 additions & 3 deletions beacon/goclient/goclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const (

// Client timeouts.
DefaultCommonTimeout = time.Second * 5 // For dialing and most requests.
DefaultLongTimeout = time.Second * 10 // For long requests.
DefaultLongTimeout = time.Second * 60 // For long requests.
)

type beaconNodeStatus int32
Expand Down Expand Up @@ -119,8 +119,6 @@ type Client interface {
eth2client.NodeSyncingProvider
eth2client.ProposalProvider
eth2client.ProposalSubmitter
eth2client.BlindedProposalProvider
eth2client.V3ProposalProvider
eth2client.BlindedProposalSubmitter
eth2client.DomainProvider
eth2client.SyncCommitteeMessagesSubmitter
Expand Down Expand Up @@ -182,6 +180,7 @@ func New(
// LogLevel supplies the level of logging to carry out.
eth2clienthttp.WithLogLevel(zerolog.DebugLevel),
eth2clienthttp.WithTimeout(commonTimeout),
eth2clienthttp.WithReducedMemoryUsage(true),
)
if err != nil {
return nil, fmt.Errorf("failed to create http client: %w", err)
Expand Down
12 changes: 10 additions & 2 deletions beacon/goclient/goclient_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"testing"
"time"

"github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/bloxapp/ssv-spec/types"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
Expand All @@ -34,7 +35,7 @@ func TestTimeouts(t *testing.T) {
BaseDelay: commonTimeout * 2,
})
_, err := mockClient(t, ctx, undialableServer.URL, commonTimeout, longTimeout)
require.ErrorContains(t, err, "context deadline exceeded")
require.ErrorContains(t, err, "client is not active")
}

// Too slow to respond to the Validators request.
Expand All @@ -46,8 +47,15 @@ func TestTimeouts(t *testing.T) {
client, err := mockClient(t, ctx, unresponsiveServer.URL, commonTimeout, longTimeout)
require.NoError(t, err)

validators, err := client.(*goClient).GetValidatorData(nil) // Should call BeaconState internally.
require.NoError(t, err)
_, err = client.(*goClient).GetValidatorData(nil) // Shouldn't call BeaconState internally.

var validatorKeys []phase0.BLSPubKey
for _, v := range validators {
validatorKeys = append(validatorKeys, v.Validator.PublicKey)
}

_, err = client.(*goClient).GetValidatorData(validatorKeys) // Shouldn't call BeaconState internally.
require.ErrorContains(t, err, "context deadline exceeded")

duties, err := client.(*goClient).ProposerDuties(ctx, mockServerEpoch, nil)
Expand Down
157 changes: 22 additions & 135 deletions beacon/goclient/proposer.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,153 +70,30 @@ func (gc *goClient) GetBeaconBlock(slot phase0.Slot, graffitiBytes, randao []byt
metricsProposerDataRequest.Observe(time.Since(reqStart).Seconds())
beaconBlock := proposalResp.Data

switch beaconBlock.Version {
case spec.DataVersionCapella:
if beaconBlock.Capella == nil {
return nil, DataVersionNil, fmt.Errorf("capella block is nil")
}
if beaconBlock.Capella.Body == nil {
return nil, DataVersionNil, fmt.Errorf("capella block body is nil")
}
if beaconBlock.Capella.Body.ExecutionPayload == nil {
return nil, DataVersionNil, fmt.Errorf("capella block execution payload is nil")
}
return beaconBlock.Capella, beaconBlock.Version, nil
case spec.DataVersionDeneb:
if beaconBlock.Deneb == nil {
return nil, DataVersionNil, fmt.Errorf("deneb block contents is nil")
}
if beaconBlock.Deneb.Block == nil {
return nil, DataVersionNil, fmt.Errorf("deneb block is nil")
}
if beaconBlock.Deneb.Block.Body == nil {
return nil, DataVersionNil, fmt.Errorf("deneb block body is nil")
}
if beaconBlock.Deneb.Block.Body.ExecutionPayload == nil {
return nil, DataVersionNil, fmt.Errorf("deneb block execution payload is nil")
}
return beaconBlock.Deneb, beaconBlock.Version, nil

default:
return nil, DataVersionNil, fmt.Errorf("beacon block version %s not supported", beaconBlock.Version)
}
}

func (gc *goClient) GetBlindedBeaconBlock(slot phase0.Slot, graffitiBytes, randao []byte) (ssz.Marshaler, spec.DataVersion, error) {
if gc.nodeClient == NodePrysm {
gc.log.Debug("using V3 endpoint for prysm blinded block")
return gc.V3Proposal(slot, graffitiBytes, randao)
}
return gc.DefaultGetBlindedBeaconBlock(slot, graffitiBytes, randao)
}

// GetBlindedBeaconBlock returns blinded beacon block by the given slot, graffiti, and randao.
func (gc *goClient) DefaultGetBlindedBeaconBlock(slot phase0.Slot, graffitiBytes, randao []byte) (ssz.Marshaler, spec.DataVersion, error) {
sig := phase0.BLSSignature{}
copy(sig[:], randao[:])

graffiti := [32]byte{}
copy(graffiti[:], graffitiBytes[:])

reqStart := time.Now()
blindedProposalResp, err := gc.client.BlindedProposal(gc.ctx, &api.BlindedProposalOpts{
Slot: slot,
RandaoReveal: sig,
Graffiti: graffiti,
SkipRandaoVerification: false,
})
if err != nil {
return nil, DataVersionNil, fmt.Errorf("failed to get blinded proposal: %w", err)
}
if blindedProposalResp == nil {
return nil, DataVersionNil, fmt.Errorf("blinded proposal response is nil")
}
if blindedProposalResp.Data == nil {
return nil, DataVersionNil, fmt.Errorf("blinded proposal data is nil")
}

metricsProposerDataRequest.Observe(time.Since(reqStart).Seconds())
beaconBlock := blindedProposalResp.Data

switch beaconBlock.Version {
case spec.DataVersionCapella:
if beaconBlock.Capella == nil {
return nil, DataVersionNil, fmt.Errorf("capella block is nil")
}
if beaconBlock.Capella.Body == nil {
return nil, DataVersionNil, fmt.Errorf("capella block body is nil")
}
if beaconBlock.Capella.Body.ExecutionPayloadHeader == nil {
return nil, DataVersionNil, fmt.Errorf("capella block execution payload header is nil")
}
return beaconBlock.Capella, beaconBlock.Version, nil
case spec.DataVersionDeneb:
if beaconBlock.Deneb == nil {
return nil, DataVersionNil, fmt.Errorf("deneb block contents is nil")
}
if beaconBlock.Deneb.Body == nil {
return nil, DataVersionNil, fmt.Errorf("deneb block body is nil")
}
if beaconBlock.Deneb.Body.ExecutionPayloadHeader == nil {
return nil, DataVersionNil, fmt.Errorf("deneb block execution payload header is nil")
}
return beaconBlock.Deneb, beaconBlock.Version, nil
default:
return nil, DataVersionNil, fmt.Errorf("beacon block version %s not supported", beaconBlock.Version)
}
}

func (gc *goClient) V3Proposal(slot phase0.Slot, graffitiBytes, randao []byte) (ssz.Marshaler, spec.DataVersion, error) {
sig := phase0.BLSSignature{}
copy(sig[:], randao[:])

graffiti := [32]byte{}
copy(graffiti[:], graffitiBytes[:])

reqStart := time.Now()
v3ProposalResp, err := gc.client.V3Proposal(gc.ctx, &api.V3ProposalOpts{
Slot: slot,
RandaoReveal: sig,
Graffiti: graffiti,
SkipRandaoVerification: false,
})
if err != nil {
return nil, DataVersionNil, fmt.Errorf("failed to get v3 proposal: %w", err)
}
if v3ProposalResp == nil {
return nil, DataVersionNil, fmt.Errorf("v3 proposal response is nil")
}
if v3ProposalResp.Data == nil {
return nil, DataVersionNil, fmt.Errorf("v3 proposal data is nil")
}

metricsProposerDataRequest.Observe(time.Since(reqStart).Seconds())
beaconBlock := v3ProposalResp.Data

if beaconBlock.ExecutionPayloadBlinded {
if beaconBlock.Blinded {
switch beaconBlock.Version {
case spec.DataVersionCapella:
if beaconBlock.BlindedCapella == nil {
if beaconBlock.CapellaBlinded == nil {
return nil, DataVersionNil, fmt.Errorf("capella blinded block is nil")
}
if beaconBlock.BlindedCapella.Body == nil {
if beaconBlock.CapellaBlinded.Body == nil {
return nil, DataVersionNil, fmt.Errorf("capella blinded block body is nil")
}
if beaconBlock.BlindedCapella.Body.ExecutionPayloadHeader == nil {
if beaconBlock.CapellaBlinded.Body.ExecutionPayloadHeader == nil {
return nil, DataVersionNil, fmt.Errorf("capella blinded block execution payload header is nil")
}
return beaconBlock.BlindedCapella, beaconBlock.Version, nil
return beaconBlock.CapellaBlinded, beaconBlock.Version, nil
case spec.DataVersionDeneb:
if beaconBlock.BlindedDeneb == nil {
if beaconBlock.DenebBlinded == nil {
return nil, DataVersionNil, fmt.Errorf("deneb blinded block contents is nil")
}
if beaconBlock.BlindedDeneb.Body == nil {
if beaconBlock.DenebBlinded.Body == nil {
return nil, DataVersionNil, fmt.Errorf("deneb blinded block body is nil")
}
if beaconBlock.BlindedDeneb.Body.ExecutionPayloadHeader == nil {
if beaconBlock.DenebBlinded.Body.ExecutionPayloadHeader == nil {
return nil, DataVersionNil, fmt.Errorf("deneb blinded block execution payload header is nil")
}
return beaconBlock.BlindedDeneb, beaconBlock.Version, nil
return beaconBlock.DenebBlinded, beaconBlock.Version, nil
default:
return nil, DataVersionNil, fmt.Errorf("beacon blinded block version %s not supported", beaconBlock.Version)
}
Expand Down Expand Up @@ -248,11 +125,13 @@ func (gc *goClient) V3Proposal(slot phase0.Slot, graffitiBytes, randao []byte) (
return nil, DataVersionNil, fmt.Errorf("deneb block execution payload is nil")
}
return beaconBlock.Deneb, beaconBlock.Version, nil

default:
return nil, DataVersionNil, fmt.Errorf("beacon block version %s not supported", beaconBlock.Version)
}
}

func (gc *goClient) GetBlindedBeaconBlock(slot phase0.Slot, graffiti, randao []byte) (ssz.Marshaler, spec.DataVersion, error) {
return gc.GetBeaconBlock(slot, graffiti, randao)
}

func (gc *goClient) SubmitBlindedBeaconBlock(block *api.VersionedBlindedProposal, sig phase0.BLSSignature) error {
Expand Down Expand Up @@ -286,7 +165,11 @@ func (gc *goClient) SubmitBlindedBeaconBlock(block *api.VersionedBlindedProposal
return fmt.Errorf("unknown block version")
}

return gc.client.SubmitBlindedProposal(gc.ctx, signedBlock)
opts := &api.SubmitBlindedProposalOpts{
Proposal: signedBlock,
}

return gc.client.SubmitBlindedProposal(gc.ctx, opts)
}

// SubmitBeaconBlock submit the block to the node
Expand Down Expand Up @@ -328,7 +211,11 @@ func (gc *goClient) SubmitBeaconBlock(block *api.VersionedProposal, sig phase0.B
return fmt.Errorf("unknown block version")
}

return gc.client.SubmitProposal(gc.ctx, signedBlock)
opts := &api.SubmitProposalOpts{
Proposal: signedBlock,
}

return gc.client.SubmitProposal(gc.ctx, opts)
}

func (gc *goClient) SubmitValidatorRegistration(pubkey []byte, feeRecipient bellatrix.ExecutionAddress, sig phase0.BLSSignature) error {
Expand Down
9 changes: 9 additions & 0 deletions beacon/goclient/testdata/mock-beacon-responses.json
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,15 @@
}
]
},
"/eth/v1/node/syncing": {
"data": {
"head_slot": "4239945",
"sync_distance": "1",
"is_syncing": false,
"is_optimistic": false,
"el_offline": false
}
},
"/eth/v1/node/version": {
"data": {
"version": "Lighthouse/v4.5.0-441fc16/x86_64-linux"
Expand Down
7 changes: 3 additions & 4 deletions beacon/goclient/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ import (
// GetValidatorData returns metadata (balance, index, status, more) for each pubkey from the node
func (gc *goClient) GetValidatorData(validatorPubKeys []phase0.BLSPubKey) (map[phase0.ValidatorIndex]*eth2apiv1.Validator, error) {
resp, err := gc.client.Validators(gc.ctx, &api.ValidatorsOpts{
State: "head", // TODO maybe need to get the chainId (head) as var
PubKeys: validatorPubKeys,
WithoutBeaconState: true,
Common: api.CommonOpts{Timeout: gc.longTimeout},
State: "head", // TODO maybe need to get the chainId (head) as var
PubKeys: validatorPubKeys,
Common: api.CommonOpts{Timeout: gc.longTimeout},
})
if err != nil {
return nil, fmt.Errorf("failed to obtain validators: %w", err)
Expand Down
11 changes: 4 additions & 7 deletions cli/operator/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ type config struct {
P2pNetworkConfig p2pv1.Config `yaml:"p2p"`
KeyStore KeyStore `yaml:"KeyStore"`
OperatorPrivateKey string `yaml:"OperatorPrivateKey" env:"OPERATOR_KEY" env-description:"Operator private key, used to decrypt contract events"`
Graffiti string `yaml:"Graffiti" env:"GRAFFITI" env-description:"Custom graffiti for block proposals." env-default:"SSV.Network" `
MetricsAPIPort int `yaml:"MetricsAPIPort" env:"METRICS_API_PORT" env-description:"Port to listen on for the metrics API."`
EnableProfile bool `yaml:"EnableProfile" env:"ENABLE_PROFILE" env-description:"flag that indicates whether go profiling tools are enabled"`
NetworkPrivateKey string `yaml:"NetworkPrivateKey" env:"NETWORK_PRIVATE_KEY" env-description:"private key for network identity"`
Expand Down Expand Up @@ -169,7 +170,7 @@ var StartNodeCmd = &cobra.Command{
logger.Fatal("could not get operator private key hash", zap.Error(err))
}

keyManager, err := ekm.NewETHKeyManagerSigner(logger, db, networkConfig, cfg.SSVOptions.ValidatorOptions.BuilderProposals, ekmHashedKey)
keyManager, err := ekm.NewETHKeyManagerSigner(logger, db, networkConfig, ekmHashedKey)
if err != nil {
logger.Fatal("could not create new eth-key-manager signer", zap.Error(err))
}
Expand All @@ -189,7 +190,7 @@ var StartNodeCmd = &cobra.Command{
}

cfg.ConsensusClient.Context = cmd.Context()
cfg.ConsensusClient.Graffiti = []byte("SSV.Network")
cfg.ConsensusClient.Graffiti = []byte(cfg.Graffiti)
cfg.ConsensusClient.GasLimit = spectypes.DefaultGasLimit
cfg.ConsensusClient.Network = networkConfig.Beacon.GetNetwork()

Expand Down Expand Up @@ -282,6 +283,7 @@ var StartNodeCmd = &cobra.Command{

cfg.SSVOptions.ValidatorOptions.StorageMap = storageMap
cfg.SSVOptions.ValidatorOptions.Metrics = metricsReporter
cfg.SSVOptions.ValidatorOptions.Graffiti = []byte(cfg.Graffiti)
cfg.SSVOptions.Metrics = metricsReporter

validatorCtrl := validator.NewController(logger, cfg.SSVOptions.ValidatorOptions)
Expand Down Expand Up @@ -564,16 +566,11 @@ func setupSSVNetwork(logger *zap.Logger) (networkconfig.NetworkConfig, error) {
if cfg.SSVOptions.ValidatorOptions.FullNode {
nodeType = "full"
}
builderProposals := "disabled"
if cfg.SSVOptions.ValidatorOptions.BuilderProposals {
builderProposals = "enabled"
}

logger.Info("setting ssv network",
fields.Network(networkConfig.Name),
fields.Domain(networkConfig.Domain),
zap.String("nodeType", nodeType),
zap.String("builderProposals(MEV)", builderProposals),
zap.Any("beaconNetwork", networkConfig.Beacon.GetNetwork().BeaconNetwork),
zap.Uint64("genesisEpoch", uint64(networkConfig.GenesisEpoch)),
zap.String("registryContract", networkConfig.RegistryContractAddr),
Expand Down
4 changes: 0 additions & 4 deletions config/config.example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@ eth2:
# HTTP URL of the Beacon node to connect to.
BeaconNodeAddr: http://example.url:5052

ValidatorOptions:
# Whether to enable MEV block production. Requires the connected Beacon node to be MEV-enabled.
BuilderProposals: false

eth1:
# WebSocket URL of the Eth1 node to connect to.
ETH1Addr: ws://example.url:8546/ws
Expand Down
3 changes: 0 additions & 3 deletions docs/EXTERNAL_BUILDERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
1. Configure your beacon node to use an external builder
- Lighthouse: https://lighthouse-book.sigmaprime.io/builders.html
- Prysm: https://docs.prylabs.network/docs/prysm-usage/parameters
2. Enable builder proposals for SSV node by setting an according variable to `true`:
- YAML config: `BuilderProposals`
- environment variable: `BUILDER_PROPOSALS`

## How it works

Expand Down
2 changes: 0 additions & 2 deletions e2e/config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ p2p:

ssv:
Network: holesky-e2e
ValidatorOptions:
BuilderProposals: false

#bootnode:
# PrivateKey: 3b503a31004ae799fe93e547a96a27630d74f2555892e6c5190135e50d8f3bf1
Expand Down
Loading

0 comments on commit 65a9ced

Please sign in to comment.