diff --git a/.k8/yamls-stage-v1/ssv-node-v2-1-deployment.yml b/.k8/yamls-stage-v1/ssv-node-v2-1-deployment.yml index d2e5ad60b0..f318be1f75 100644 --- a/.k8/yamls-stage-v1/ssv-node-v2-1-deployment.yml +++ b/.k8/yamls-stage-v1/ssv-node-v2-1-deployment.yml @@ -136,6 +136,8 @@ spec: value: "152834" - name: CLEAN_REGISTRY_DATA value: "false" + - name: BUILDER_PROPOSALS + value: "true" volumeMounts: - mountPath: /data name: ssv-node-v2-1 diff --git a/.k8/yamls-stage/ssv-node-10-deployment.yml b/.k8/yamls-stage/ssv-node-10-deployment.yml index dd2931f934..6b87c567c1 100644 --- a/.k8/yamls-stage/ssv-node-10-deployment.yml +++ b/.k8/yamls-stage/ssv-node-10-deployment.yml @@ -132,6 +132,8 @@ spec: value: 'false' - name: GENESIS_EPOCH value: "152834" + - name: BUILDER_PROPOSALS + value: "true" volumeMounts: - mountPath: /data name: ssv-node-10 diff --git a/.k8/yamls-stage/ssv-node-11-deployment.yml b/.k8/yamls-stage/ssv-node-11-deployment.yml index 6c6ba0c530..ceaef50353 100644 --- a/.k8/yamls-stage/ssv-node-11-deployment.yml +++ b/.k8/yamls-stage/ssv-node-11-deployment.yml @@ -132,6 +132,8 @@ spec: value: 'false' - name: GENESIS_EPOCH value: "152834" + - name: BUILDER_PROPOSALS + value: "true" volumeMounts: - mountPath: /data name: ssv-node-11 diff --git a/.k8/yamls-stage/ssv-node-12-deployment.yml b/.k8/yamls-stage/ssv-node-12-deployment.yml index e8d81d21ec..2ebfd36dc8 100644 --- a/.k8/yamls-stage/ssv-node-12-deployment.yml +++ b/.k8/yamls-stage/ssv-node-12-deployment.yml @@ -132,6 +132,8 @@ spec: value: 'false' - name: GENESIS_EPOCH value: "152834" + - name: BUILDER_PROPOSALS + value: "true" volumeMounts: - mountPath: /data name: ssv-node-12 diff --git a/.k8/yamls-stage/ssv-node-9-deployment.yml b/.k8/yamls-stage/ssv-node-9-deployment.yml index 811bfa2f0c..22006cb2cf 100644 --- a/.k8/yamls-stage/ssv-node-9-deployment.yml +++ b/.k8/yamls-stage/ssv-node-9-deployment.yml @@ -132,6 +132,8 @@ spec: value: 'false' - name: GENESIS_EPOCH value: "152834" + - name: BUILDER_PROPOSALS + value: "true" volumeMounts: - mountPath: /data name: ssv-node-9 diff --git a/.k8/yamls-stage/ssv-node-v2-2-deployment.yml b/.k8/yamls-stage/ssv-node-v2-2-deployment.yml index 58924c43f5..71ec000ad4 100644 --- a/.k8/yamls-stage/ssv-node-v2-2-deployment.yml +++ b/.k8/yamls-stage/ssv-node-v2-2-deployment.yml @@ -138,6 +138,8 @@ spec: value: 'false' - name: GENESIS_EPOCH value: "152834" + - name: BUILDER_PROPOSALS + value: "true" volumeMounts: - mountPath: /data name: ssv-node-v2-2 diff --git a/.k8/yamls-stage/ssv-node-v2-3-deployment.yml b/.k8/yamls-stage/ssv-node-v2-3-deployment.yml index 450a8d37a5..60a4ef732b 100644 --- a/.k8/yamls-stage/ssv-node-v2-3-deployment.yml +++ b/.k8/yamls-stage/ssv-node-v2-3-deployment.yml @@ -140,6 +140,8 @@ spec: value: "152834" - name: CLEAN_REGISTRY_DATA value: "false" + - name: BUILDER_PROPOSALS + value: "true" volumeMounts: - mountPath: /data name: ssv-node-v2-3 diff --git a/.k8/yamls-stage/ssv-node-v2-4-deployment.yml b/.k8/yamls-stage/ssv-node-v2-4-deployment.yml index 5b5ea6b3d2..beb5e1b73f 100644 --- a/.k8/yamls-stage/ssv-node-v2-4-deployment.yml +++ b/.k8/yamls-stage/ssv-node-v2-4-deployment.yml @@ -140,6 +140,8 @@ spec: value: "152834" - name: CLEAN_REGISTRY_DATA value: "false" + - name: BUILDER_PROPOSALS + value: "true" volumeMounts: - mountPath: /data name: ssv-node-v2-4 diff --git a/.k8/yamls-stage/ssv-node-v2-5-deployment.yml b/.k8/yamls-stage/ssv-node-v2-5-deployment.yml index 0b319afe68..f4134a3224 100644 --- a/.k8/yamls-stage/ssv-node-v2-5-deployment.yml +++ b/.k8/yamls-stage/ssv-node-v2-5-deployment.yml @@ -132,6 +132,8 @@ spec: value: "152834" - name: CLEAN_REGISTRY_DATA value: "false" + - name: BUILDER_PROPOSALS + value: "true" volumeMounts: - mountPath: /data name: ssv-node-v2-5 diff --git a/.k8/yamls-stage/ssv-node-v2-6-deployment.yml b/.k8/yamls-stage/ssv-node-v2-6-deployment.yml index fc5935bf49..9d106d9142 100644 --- a/.k8/yamls-stage/ssv-node-v2-6-deployment.yml +++ b/.k8/yamls-stage/ssv-node-v2-6-deployment.yml @@ -134,6 +134,8 @@ spec: value: "152834" - name: CLEAN_REGISTRY_DATA value: "false" + - name: BUILDER_PROPOSALS + value: "true" volumeMounts: - mountPath: /data name: ssv-node-v2-6 diff --git a/.k8/yamls-stage/ssv-node-v2-7-deployment.yml b/.k8/yamls-stage/ssv-node-v2-7-deployment.yml index e46afd3101..2d09aff4a3 100644 --- a/.k8/yamls-stage/ssv-node-v2-7-deployment.yml +++ b/.k8/yamls-stage/ssv-node-v2-7-deployment.yml @@ -132,6 +132,8 @@ spec: value: 'false' - name: GENESIS_EPOCH value: "152834" + - name: BUILDER_PROPOSALS + value: "true" volumeMounts: - mountPath: /data name: ssv-node-v2-7 diff --git a/.k8/yamls-stage/ssv-node-v2-8-deployment.yml b/.k8/yamls-stage/ssv-node-v2-8-deployment.yml index bc97612cfc..590c7b30c9 100644 --- a/.k8/yamls-stage/ssv-node-v2-8-deployment.yml +++ b/.k8/yamls-stage/ssv-node-v2-8-deployment.yml @@ -132,6 +132,8 @@ spec: value: 'false' - name: GENESIS_EPOCH value: "152834" + - name: BUILDER_PROPOSALS + value: "true" volumeMounts: - mountPath: /data name: ssv-node-v2-8 diff --git a/.k8/yamls-v3-stage/ssv-node-v3-1-deployment.yml b/.k8/yamls-v3-stage/ssv-node-v3-1-deployment.yml index 6529fa7925..7d3173ecb0 100644 --- a/.k8/yamls-v3-stage/ssv-node-v3-1-deployment.yml +++ b/.k8/yamls-v3-stage/ssv-node-v3-1-deployment.yml @@ -138,6 +138,8 @@ spec: value: "16301" - name: FULLNODE value: "true" + - name: BUILDER_PROPOSALS + value: "true" volumeMounts: - mountPath: /data name: ssv-node-v3-1 diff --git a/.k8/yamls-v3-stage/ssv-node-v3-2-deployment.yml b/.k8/yamls-v3-stage/ssv-node-v3-2-deployment.yml index 7b22028f61..7f6572ceb6 100644 --- a/.k8/yamls-v3-stage/ssv-node-v3-2-deployment.yml +++ b/.k8/yamls-v3-stage/ssv-node-v3-2-deployment.yml @@ -130,6 +130,8 @@ spec: value: "true" - name: GENESIS_EPOCH value: "152834" + - name: BUILDER_PROPOSALS + value: "true" volumeMounts: - mountPath: /data name: ssv-node-v3-2 diff --git a/.k8/yamls-v3-stage/ssv-node-v3-3-deployment.yml b/.k8/yamls-v3-stage/ssv-node-v3-3-deployment.yml index 86fcc25016..d029a334e7 100644 --- a/.k8/yamls-v3-stage/ssv-node-v3-3-deployment.yml +++ b/.k8/yamls-v3-stage/ssv-node-v3-3-deployment.yml @@ -138,6 +138,8 @@ spec: value: "16303" - name: FULLNODE value: "true" + - name: BUILDER_PROPOSALS + value: "true" volumeMounts: - mountPath: /data name: ssv-node-v3-3 diff --git a/.k8/yamls-v3-stage/ssv-node-v3-4-deployment.yml b/.k8/yamls-v3-stage/ssv-node-v3-4-deployment.yml index 6378fefe6e..20a94ed3a2 100644 --- a/.k8/yamls-v3-stage/ssv-node-v3-4-deployment.yml +++ b/.k8/yamls-v3-stage/ssv-node-v3-4-deployment.yml @@ -130,6 +130,8 @@ spec: value: "true" - name: GENESIS_EPOCH value: "152834" + - name: BUILDER_PROPOSALS + value: "true" volumeMounts: - mountPath: /data name: ssv-node-v3-4 diff --git a/beacon/goclient/goclient.go b/beacon/goclient/goclient.go index ef59c21d92..ded30bac4d 100644 --- a/beacon/goclient/goclient.go +++ b/beacon/goclient/goclient.go @@ -9,19 +9,21 @@ import ( "time" eth2client "github.com/attestantio/go-eth2-client" + "github.com/attestantio/go-eth2-client/api" "github.com/attestantio/go-eth2-client/http" "github.com/attestantio/go-eth2-client/spec" "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/bloxapp/eth2-key-manager/core" spectypes "github.com/bloxapp/ssv-spec/types" - "github.com/bloxapp/ssv/logging/fields" "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "github.com/rs/zerolog" "go.uber.org/zap" + "github.com/bloxapp/ssv/logging/fields" "github.com/bloxapp/ssv/monitoring/metrics" + "github.com/bloxapp/ssv/operator/slot_ticker" beaconprotocol "github.com/bloxapp/ssv/protocol/v2/blockchain/beacon" ) @@ -99,22 +101,30 @@ type Client interface { eth2client.ValidatorsProvider eth2client.ProposalPreparationsSubmitter eth2client.EventsProvider + eth2client.BlindedBeaconBlockProposalProvider + eth2client.BlindedBeaconBlockSubmitter + eth2client.ValidatorRegistrationsSubmitter } // goClient implementing Beacon struct type goClient struct { - ctx context.Context - network beaconprotocol.Network - client Client - indicesMapLock sync.Mutex - graffiti []byte + log *zap.Logger + ctx context.Context + network beaconprotocol.Network + client Client + graffiti []byte + gasLimit uint64 + operatorID spectypes.OperatorID + registrationMu sync.Mutex + registrationLastSlot phase0.Slot + registrationCache map[phase0.BLSPubKey]*api.VersionedSignedValidatorRegistration } // verifies that the client implements HealthCheckAgent var _ metrics.HealthCheckAgent = &goClient{} // New init new client and go-client instance -func New(logger *zap.Logger, opt beaconprotocol.Options) (beaconprotocol.Beacon, error) { +func New(logger *zap.Logger, opt beaconprotocol.Options, operatorID spectypes.OperatorID, slotTicker slot_ticker.Ticker) (beaconprotocol.Beacon, error) { logger.Info("consensus client: connecting", fields.Address(opt.BeaconNodeAddr), fields.Network(opt.Network)) httpClient, err := http.New(opt.Context, @@ -131,15 +141,24 @@ func New(logger *zap.Logger, opt beaconprotocol.Options) (beaconprotocol.Beacon, logger.Info("consensus client: connected", fields.Name(httpClient.Name()), fields.Address(httpClient.Address())) network := beaconprotocol.NewNetwork(core.NetworkFromString(opt.Network), opt.MinGenesisTime) - _client := &goClient{ - ctx: opt.Context, - network: network, - client: httpClient.(*http.Service), - indicesMapLock: sync.Mutex{}, - graffiti: opt.Graffiti, + + tickerChan := make(chan phase0.Slot, 32) + slotTicker.Subscribe(tickerChan) + + client := &goClient{ + log: logger, + ctx: opt.Context, + network: network, + client: httpClient.(*http.Service), + graffiti: opt.Graffiti, + gasLimit: opt.GasLimit, + operatorID: operatorID, + registrationCache: map[phase0.BLSPubKey]*api.VersionedSignedValidatorRegistration{}, } - return _client, nil + go client.registrationSubmitter(tickerChan) + + return client, nil } // HealthCheck provides health status of beacon node diff --git a/beacon/goclient/proposer.go b/beacon/goclient/proposer.go index f0bbcdad0d..1f7bc3232f 100644 --- a/beacon/goclient/proposer.go +++ b/beacon/goclient/proposer.go @@ -6,6 +6,8 @@ import ( "github.com/attestantio/go-eth2-client/api" eth2apiv1 "github.com/attestantio/go-eth2-client/api/v1" + apiv1bellatrix "github.com/attestantio/go-eth2-client/api/v1/bellatrix" + apiv1capella "github.com/attestantio/go-eth2-client/api/v1/capella" "github.com/attestantio/go-eth2-client/spec" "github.com/attestantio/go-eth2-client/spec/altair" "github.com/attestantio/go-eth2-client/spec/bellatrix" @@ -13,15 +15,17 @@ import ( "github.com/attestantio/go-eth2-client/spec/phase0" ssz "github.com/ferranbt/fastssz" "github.com/pkg/errors" + "go.uber.org/zap" - apiv1bellatrix "github.com/attestantio/go-eth2-client/api/v1/bellatrix" - apiv1capella "github.com/attestantio/go-eth2-client/api/v1/capella" + "github.com/bloxapp/ssv/logging/fields" +) + +const ( + batchSize = 500 ) // GetBeaconBlock returns beacon block by the given slot and committee index -func (gc *goClient) GetBeaconBlock(slot phase0.Slot, committeeIndex phase0.CommitteeIndex, graffiti, randao []byte) (ssz.Marshaler, spec.DataVersion, error) { - // TODO need to support blinded? - // TODO what with fee recipient? +func (gc *goClient) GetBeaconBlock(slot phase0.Slot, graffiti, randao []byte) (ssz.Marshaler, spec.DataVersion, error) { sig := phase0.BLSSignature{} copy(sig[:], randao[:]) @@ -32,22 +36,87 @@ func (gc *goClient) GetBeaconBlock(slot phase0.Slot, committeeIndex phase0.Commi } metricsProposerDataRequest.Observe(time.Since(reqStart).Seconds()) + if beaconBlock == nil { + return nil, 0, fmt.Errorf("block is nil") + } + switch beaconBlock.Version { case spec.DataVersionPhase0: return beaconBlock.Phase0, beaconBlock.Version, nil case spec.DataVersionAltair: return beaconBlock.Altair, beaconBlock.Version, nil case spec.DataVersionBellatrix: + if beaconBlock.Bellatrix.Body == nil { + return nil, DataVersionNil, fmt.Errorf("bellatrix block body is nil") + } + if beaconBlock.Bellatrix.Body.ExecutionPayload == nil { + return nil, DataVersionNil, fmt.Errorf("bellatrix block execution payload is nil") + } + gc.log.Info("got beacon block", + fields.BlockHash(beaconBlock.Bellatrix.Body.ExecutionPayload.BlockHash), + fields.BlockVersion(beaconBlock.Version), + fields.Slot(beaconBlock.Bellatrix.Slot)) return beaconBlock.Bellatrix, beaconBlock.Version, nil case spec.DataVersionCapella: + 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") + } + gc.log.Info("got beacon block", + fields.BlockHash(beaconBlock.Capella.Body.ExecutionPayload.BlockHash), + fields.BlockVersion(beaconBlock.Version), + fields.Slot(beaconBlock.Capella.Slot)) return beaconBlock.Capella, beaconBlock.Version, nil default: - return nil, DataVersionNil, errors.New(fmt.Sprintf("beacon block version %s not supported", beaconBlock.Version)) + return nil, DataVersionNil, fmt.Errorf("beacon block version %s not supported", beaconBlock.Version) } } -func (gc *goClient) GetBlindedBeaconBlock(slot phase0.Slot, committeeIndex phase0.CommitteeIndex, graffiti, randao []byte) (ssz.Marshaler, spec.DataVersion, error) { - return nil, DataVersionNil, nil +func (gc *goClient) GetBlindedBeaconBlock(slot phase0.Slot, graffiti, randao []byte) (ssz.Marshaler, spec.DataVersion, error) { + sig := phase0.BLSSignature{} + copy(sig[:], randao[:]) + + reqStart := time.Now() + beaconBlock, err := gc.client.BlindedBeaconBlockProposal(gc.ctx, slot, sig, graffiti) + if err != nil { + return nil, 0, err + } + metricsProposerDataRequest.Observe(time.Since(reqStart).Seconds()) + + if beaconBlock == nil { + return nil, 0, fmt.Errorf("blinded block is nil") + } + + switch beaconBlock.Version { + case spec.DataVersionBellatrix: + if beaconBlock.Bellatrix.Body == nil { + return nil, DataVersionNil, fmt.Errorf("bellatrix block body is nil") + } + if beaconBlock.Bellatrix.Body.ExecutionPayloadHeader == nil { + return nil, DataVersionNil, fmt.Errorf("bellatrix block execution payload header is nil") + } + gc.log.Info("got blinded beacon block", + fields.BlockHash(beaconBlock.Bellatrix.Body.ExecutionPayloadHeader.BlockHash), + fields.BlockVersion(beaconBlock.Version), + fields.Slot(beaconBlock.Bellatrix.Slot)) + return beaconBlock.Bellatrix, beaconBlock.Version, nil + case spec.DataVersionCapella: + 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") + } + gc.log.Info("got blinded beacon block", + fields.BlockHash(beaconBlock.Capella.Body.ExecutionPayloadHeader.BlockHash), + fields.BlockVersion(beaconBlock.Version), + fields.Slot(beaconBlock.Capella.Slot)) + return beaconBlock.Capella, beaconBlock.Version, nil + default: + return nil, DataVersionNil, fmt.Errorf("beacon block version %s not supported", beaconBlock.Version) + } } func (gc *goClient) SubmitBlindedBeaconBlock(block *api.VersionedBlindedBeaconBlock, sig phase0.BLSSignature) error { @@ -63,6 +132,10 @@ func (gc *goClient) SubmitBlindedBeaconBlock(block *api.VersionedBlindedBeaconBl Message: block.Bellatrix, } copy(signedBlock.Bellatrix.Signature[:], sig[:]) + gc.log.Info("submitting blinded beacon block", + fields.BlockHash(block.Bellatrix.Body.ExecutionPayloadHeader.BlockHash), + fields.BlockVersion(block.Version), + fields.Slot(block.Bellatrix.Slot)) case spec.DataVersionCapella: if block.Capella == nil { return errors.New("capella blinded block is nil") @@ -71,6 +144,10 @@ func (gc *goClient) SubmitBlindedBeaconBlock(block *api.VersionedBlindedBeaconBl Message: block.Capella, } copy(signedBlock.Capella.Signature[:], sig[:]) + gc.log.Info("submitting blinded beacon block", + fields.BlockHash(block.Capella.Body.ExecutionPayloadHeader.BlockHash), + fields.BlockVersion(block.Version), + fields.Slot(block.Capella.Slot)) default: return errors.New("unknown block version") } @@ -108,6 +185,10 @@ func (gc *goClient) SubmitBeaconBlock(block *spec.VersionedBeaconBlock, sig phas Message: block.Bellatrix, } copy(signedBlock.Bellatrix.Signature[:], sig[:]) + gc.log.Info("submitting block", + fields.BlockHash(block.Bellatrix.Body.ExecutionPayload.BlockHash), + fields.BlockVersion(block.Version), + fields.Slot(block.Bellatrix.Slot)) case spec.DataVersionCapella: if block.Capella == nil { return errors.New("capella block is nil") @@ -116,6 +197,10 @@ func (gc *goClient) SubmitBeaconBlock(block *spec.VersionedBeaconBlock, sig phas Message: block.Capella, } copy(signedBlock.Capella.Signature[:], sig[:]) + gc.log.Info("submitting block", + fields.BlockHash(block.Capella.Body.ExecutionPayload.BlockHash), + fields.BlockVersion(block.Version), + fields.Slot(block.Capella.Slot)) default: return errors.New("unknown block version") } @@ -123,6 +208,10 @@ func (gc *goClient) SubmitBeaconBlock(block *spec.VersionedBeaconBlock, sig phas return gc.client.SubmitBeaconBlock(gc.ctx, signedBlock) } +func (gc *goClient) SubmitValidatorRegistration(pubkey []byte, feeRecipient bellatrix.ExecutionAddress, sig phase0.BLSSignature) error { + return gc.updateBatchRegistrationCache(gc.createValidatorRegistration(pubkey, feeRecipient, sig)) +} + func (gc *goClient) SubmitProposalPreparation(feeRecipients map[phase0.ValidatorIndex]bellatrix.ExecutionAddress) error { var preparations []*eth2apiv1.ProposalPreparation for index, recipient := range feeRecipients { @@ -133,3 +222,112 @@ func (gc *goClient) SubmitProposalPreparation(feeRecipients map[phase0.Validator } return gc.client.SubmitProposalPreparations(gc.ctx, preparations) } + +func (gc *goClient) updateBatchRegistrationCache(registration *api.VersionedSignedValidatorRegistration) error { + pk, err := registration.PubKey() + if err != nil { + return err + } + + gc.registrationMu.Lock() + defer gc.registrationMu.Unlock() + + gc.registrationCache[pk] = registration + return nil +} + +func (gc *goClient) createValidatorRegistration(pubkey []byte, feeRecipient bellatrix.ExecutionAddress, sig phase0.BLSSignature) *api.VersionedSignedValidatorRegistration { + pk := phase0.BLSPubKey{} + copy(pk[:], pubkey) + + signedReg := &api.VersionedSignedValidatorRegistration{ + Version: spec.BuilderVersionV1, + V1: ð2apiv1.SignedValidatorRegistration{ + Message: ð2apiv1.ValidatorRegistration{ + FeeRecipient: feeRecipient, + GasLimit: gc.gasLimit, + Timestamp: gc.network.GetSlotStartTime(gc.network.GetEpochFirstSlot(gc.network.EstimatedCurrentEpoch())), + Pubkey: pk, + }, + Signature: sig, + }, + } + return signedReg +} + +func (gc *goClient) registrationSubmitter(slots <-chan phase0.Slot) { + for currentSlot := range slots { + gc.submitRegistrationsFromCache(currentSlot) + } +} + +func (gc *goClient) submitRegistrationsFromCache(currentSlot phase0.Slot) { + slotsPerEpoch := gc.network.SlotsPerEpoch() + + // Lock: + // - getting and updating last slot to avoid multiple submission (both should be an atomic action but cannot be done with CAS) + // - getting and updating registration cache + gc.registrationMu.Lock() + + slotsSinceLastRegistration := currentSlot - gc.registrationLastSlot + operatorSubmissionSlotModulo := gc.operatorID % slotsPerEpoch + + hasRegistrations := len(gc.registrationCache) != 0 + operatorSubmissionSlot := uint64(currentSlot)%slotsPerEpoch == operatorSubmissionSlotModulo + oneEpochPassed := slotsSinceLastRegistration >= phase0.Slot(slotsPerEpoch) + twoEpochsAndOperatorDelayPassed := uint64(slotsSinceLastRegistration) >= slotsPerEpoch*2+operatorSubmissionSlotModulo + + if hasRegistrations && (oneEpochPassed && operatorSubmissionSlot || twoEpochsAndOperatorDelayPassed) { + gc.registrationLastSlot = currentSlot + registrations := gc.registrationList() + + // Release lock after building a registrations list for submission. + gc.registrationMu.Unlock() + + if err := gc.submitBatchedRegistrations(currentSlot, registrations); err != nil { + gc.log.Error("Failed to submit validator registrations", + zap.Error(err), + fields.Slot(currentSlot)) + } + + return + } + + gc.registrationMu.Unlock() +} + +// registrationList is not thread-safe +func (gc *goClient) registrationList() []*api.VersionedSignedValidatorRegistration { + result := make([]*api.VersionedSignedValidatorRegistration, 0) + + for _, registration := range gc.registrationCache { + result = append(result, registration) + } + + return result +} + +func (gc *goClient) submitBatchedRegistrations(slot phase0.Slot, registrations []*api.VersionedSignedValidatorRegistration) error { + gc.log.Info("going to submit batch validator registrations", + fields.Slot(slot), + fields.Count(len(registrations))) + + for len(registrations) != 0 { + bs := batchSize + if bs > len(registrations) { + bs = len(registrations) + } + + if err := gc.client.SubmitValidatorRegistrations(gc.ctx, registrations[0:bs]); err != nil { + return err + } + + registrations = registrations[bs:] + + gc.log.Info("submitted batched validator registrations", + fields.Slot(slot), + fields.Count(bs)) + } + + return nil +} diff --git a/beacon/goclient/signing.go b/beacon/goclient/signing.go index 10b0517347..f40bd00e7b 100644 --- a/beacon/goclient/signing.go +++ b/beacon/goclient/signing.go @@ -6,11 +6,27 @@ import ( "sync" "github.com/attestantio/go-eth2-client/spec/phase0" + spectypes "github.com/bloxapp/ssv-spec/types" ssz "github.com/ferranbt/fastssz" "github.com/pkg/errors" ) func (gc *goClient) DomainData(epoch phase0.Epoch, domain phase0.DomainType) (phase0.Domain, error) { + if domain == spectypes.DomainApplicationBuilder { // no domain for DomainApplicationBuilder. need to create. https://github.com/bloxapp/ethereum2-validator/blob/v2-main/signing/keyvault/signer.go#L62 + var appDomain phase0.Domain + forkData := phase0.ForkData{ + CurrentVersion: GenesisForkVersion, + GenesisValidatorsRoot: phase0.Root{}, + } + root, err := forkData.HashTreeRoot() + if err != nil { + return phase0.Domain{}, errors.Wrap(err, "failed to get fork data root") + } + copy(appDomain[:], domain[:]) + copy(appDomain[4:], root[:]) + return appDomain, nil + } + data, err := gc.client.Domain(gc.ctx, domain, epoch) if err != nil { return phase0.Domain{}, err diff --git a/beacon/goclient/types.go b/beacon/goclient/types.go index 2818f0f8dd..d9da524cf8 100644 --- a/beacon/goclient/types.go +++ b/beacon/goclient/types.go @@ -9,4 +9,5 @@ var ( EpochsPerSyncCommitteePeriod uint64 = 256 TargetAggregatorsPerCommittee uint64 = 16 FarFutureEpoch phase0.Epoch = 1<<64 - 1 + GenesisForkVersion = phase0.Version{0x0, 0x0, 0x10, 0x20} // prater ) diff --git a/cli/operator/node.go b/cli/operator/node.go index bb95d2765c..ecda3ff7d1 100644 --- a/cli/operator/node.go +++ b/cli/operator/node.go @@ -8,9 +8,7 @@ import ( "net/http" "time" - "github.com/bloxapp/ssv/logging" - "github.com/bloxapp/ssv/logging/fields" - + "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/bloxapp/eth2-key-manager/core" spectypes "github.com/bloxapp/ssv-spec/types" "github.com/ilyakaznacheev/cleanenv" @@ -26,6 +24,8 @@ import ( "github.com/bloxapp/ssv/exporter/api" "github.com/bloxapp/ssv/exporter/api/decided" ssv_identity "github.com/bloxapp/ssv/identity" + "github.com/bloxapp/ssv/logging" + "github.com/bloxapp/ssv/logging/fields" "github.com/bloxapp/ssv/migrations" "github.com/bloxapp/ssv/monitoring/metrics" "github.com/bloxapp/ssv/network" @@ -33,10 +33,12 @@ import ( p2pv1 "github.com/bloxapp/ssv/network/p2p" "github.com/bloxapp/ssv/network/records" "github.com/bloxapp/ssv/operator" + "github.com/bloxapp/ssv/operator/slot_ticker" operatorstorage "github.com/bloxapp/ssv/operator/storage" "github.com/bloxapp/ssv/operator/validator" forksprotocol "github.com/bloxapp/ssv/protocol/forks" beaconprotocol "github.com/bloxapp/ssv/protocol/v2/blockchain/beacon" + validatorprotocol "github.com/bloxapp/ssv/protocol/v2/ssv/validator" "github.com/bloxapp/ssv/protocol/v2/types" registrystorage "github.com/bloxapp/ssv/registry/storage" "github.com/bloxapp/ssv/storage" @@ -90,7 +92,7 @@ var StartNodeCmd = &cobra.Command{ } nodeStorage, operatorData := setupOperatorStorage(logger, db) - keyManager, err := ekm.NewETHKeyManagerSigner(db, eth2Network, types.GetDefaultDomain(), logger) + keyManager, err := ekm.NewETHKeyManagerSigner(logger, db, eth2Network, types.GetDefaultDomain(), cfg.SSVOptions.ValidatorOptions.BuilderProposals) if err != nil { logger.Fatal("could not create new eth-key-manager signer", zap.Error(err)) } @@ -98,10 +100,12 @@ var StartNodeCmd = &cobra.Command{ cfg.P2pNetworkConfig.Ctx = cmd.Context() p2pNetwork := setupP2P(forkVersion, operatorData, db, logger) + ctx := cmd.Context() + slotTicker := slot_ticker.NewTicker(ctx, eth2Network, phase0.Epoch(cfg.SSVOptions.GenesisEpoch)) + cfg.ETH2Options.Context = cmd.Context() - el, cl := setupNodes(logger) + el, cl := setupNodes(logger, operatorData.ID, slotTicker) - ctx := cmd.Context() cfg.SSVOptions.ForkVersion = forkVersion cfg.SSVOptions.Context = ctx cfg.SSVOptions.DB = db @@ -120,6 +124,7 @@ var StartNodeCmd = &cobra.Command{ cfg.SSVOptions.ValidatorOptions.ShareEncryptionKeyProvider = nodeStorage.GetPrivateKey cfg.SSVOptions.ValidatorOptions.OperatorData = operatorData cfg.SSVOptions.ValidatorOptions.RegistryStorage = nodeStorage + cfg.SSVOptions.ValidatorOptions.GasLimit = cfg.ETH2Options.GasLimit cfg.SSVOptions.Eth1Client = cl @@ -134,7 +139,7 @@ var StartNodeCmd = &cobra.Command{ validatorCtrl := validator.NewController(logger, cfg.SSVOptions.ValidatorOptions) cfg.SSVOptions.ValidatorController = validatorCtrl - operatorNode = operator.New(logger, cfg.SSVOptions) + operatorNode = operator.New(logger, cfg.SSVOptions, slotTicker) if cfg.MetricsAPIPort > 0 { go startMetricsHandler(cmd.Context(), logger, db, cfg.MetricsAPIPort, cfg.EnableProfile) @@ -320,10 +325,11 @@ func setupP2P(forkVersion forksprotocol.ForkVersion, operatorData *registrystora return p2pv1.New(logger, &cfg.P2pNetworkConfig) } -func setupNodes(logger *zap.Logger) (beaconprotocol.Beacon, eth1.Client) { +func setupNodes(logger *zap.Logger, operatorID spectypes.OperatorID, slotTicker slot_ticker.Ticker) (beaconprotocol.Beacon, eth1.Client) { // consensus client cfg.ETH2Options.Graffiti = []byte("SSV.Network") - cl, err := goclient.New(logger, cfg.ETH2Options) + cfg.ETH2Options.GasLimit = validatorprotocol.DefaultGasLimit + cl, err := goclient.New(logger, cfg.ETH2Options, operatorID, slotTicker) if err != nil { logger.Fatal("failed to create beacon go-client", zap.Error(err), fields.Address(cfg.ETH2Options.BeaconNodeAddr)) diff --git a/docs/EXTERNAL_BUILDERS.md b/docs/EXTERNAL_BUILDERS.md new file mode 100644 index 0000000000..b329e1be14 --- /dev/null +++ b/docs/EXTERNAL_BUILDERS.md @@ -0,0 +1,81 @@ +# Builder proposals + +## How to use + +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 + +### Blinded beacon block proposals + +If builder proposals are enabled, +the SSV node attempts to get/submit blinded beacon block proposals (`/eth/v1/beacon/blinded_blocks`) to beacon node +instead of regular ones (`/eth/v1/beacon/blocks`). + +### Validator registrations + +If builder proposals are enabled, the SSV node regularly submits validator registrations according to the following logic: + +- Registration for each validator is submitted to registrations collector every 10 epochs. To reduce beacon node load, slot for submission is chosen according to the validator index. +- The first registration after the SSV node start is an exception to the rule above to avoid waiting up to 10 epochs: All validator registrations are submitted within 32 slots after the node start according to the validator index. +- Registration collector submits queued validator registrations to beacon node once per epoch. The slot index within an epoch is different for each operator and is calculated based on operator ID to reduce beacon node load. The maximal amount of registrations in one request is 500. If the queue contains more than that, all queued registrations are submitted by chunks of 500 registrations without a delay. + +## Known issues + +1. Builder proposals don't work with Prysm as it returns `400 Unsupported block type` when requesting a blinded block. +2. [The mev-boost-relay external builder](https://github.com/flashbots/mev-boost-relay) allows to get a blinded block payload (beacon node requests a blinded block payload from an external builder to un-blind block and submit it to the network) only once, therefore only the first blinded block submitter can submit it successfully, others' submissions fail. This may increase chances of a missing proposal if the first submitter's beacon node has bad connectivity + - Issue link: https://github.com/flashbots/mev-boost-relay/issues/397 + +## Edge cases outcomes + +### Scenario 1. 4 operators, 4 BNs, 4 MEVs, MEV1&2 use relay-1, MEV3&4 use relay-2 + +- Blinded block header received from MEV, local block is more profitable + +Local block is successfully submitted and shown on beaconchain as regular, non-MEV block + +- Blinded block header received from MEV more than once in the same slot + +Successful receipt, no error, block hashes may be same, may be different + +- Nodes receive same MEV block hashes, round leader proposes its received block hash for consensus, any node submits it + +The first submitter using the same relay, whether or not it's the round reader, successfully submits the block, it's shown as MEV on beaconchain. Others fail to submit the block due to "no successful relay response" + +- Nodes receive different MEV block hashes, round leader proposes its received block hash for consensus, any node submits it + +The first submitter using the same relay, whether or not it's the round reader, successfully submits the block, it's shown as MEV on beaconchain. Others fail to submit the block due to "no successful relay response" + + +### Scenario 2. 4 operators, 4 BNs, 4 MEVs using 3 shared relays + +- Blinded block header received from MEV, local block is more profitable + +Local block is successfully submitted and shown on beaconchain as regular, non-MEV block + +- Blinded block header received from MEV more than once in the same slot + +Successful receipt, no error, block hashes may be same, may be different + +- Nodes receive same MEV block hashes, round leader proposes its received block hash for consensus, any node submits it + +The first submitter, whether or not it's the round reader, successfully submits the block, it's shown as MEV on beaconchain, others fail to submit the block due to "no successful relay response" + +- Nodes receive different MEV block hashes, round leader proposes its received block hash for consensus, any node submits it + +The first submitter, whether or not it's the round reader, successfully submits the block, it's shown as MEV on beaconchain, others fail to submit the block due to "no successful relay response" + +### Scenario 3. 4 operators, 2 have MEV on, 2 have MEV off + +- Round leader has MEV on + +Nodes having MEV off fail to validate the proposed MEV block as input data and return "blinded blocks are not supported", so the consensus in the round is not met. Nodes proceed to the next round and choose the next round leader + +- Round leader has MEV off + +Nodes run consensus on a regular non-MEV block and submit it diff --git a/ekm/eth_key_manager_signer.go b/ekm/eth_key_manager_signer.go index 3efabdaae2..3500a389df 100644 --- a/ekm/eth_key_manager_signer.go +++ b/ekm/eth_key_manager_signer.go @@ -2,6 +2,7 @@ package ekm import ( "encoding/hex" + "fmt" "sync" "github.com/attestantio/go-eth2-client/api" @@ -39,11 +40,11 @@ type ethKeyManagerSigner struct { storage Storage domain spectypes.DomainType slashingProtector core.SlashingProtector - isBlinded bool + builderProposals bool } // NewETHKeyManagerSigner returns a new instance of ethKeyManagerSigner -func NewETHKeyManagerSigner(db basedb.IDb, network beaconprotocol.Network, domain spectypes.DomainType, logger *zap.Logger) (spectypes.KeyManager, error) { +func NewETHKeyManagerSigner(logger *zap.Logger, db basedb.IDb, network beaconprotocol.Network, domain spectypes.DomainType, builderProposals bool) (spectypes.KeyManager, error) { signerStore := NewSignerStorage(db, network, logger) options := ð2keymanager.KeyVaultOptions{} options.SetStorage(signerStore) @@ -74,6 +75,7 @@ func NewETHKeyManagerSigner(db basedb.IDb, network beaconprotocol.Network, domai storage: signerStore, domain: domain, slashingProtector: slashingProtector, + builderProposals: builderProposals, }, nil } @@ -99,7 +101,7 @@ func (km *ethKeyManagerSigner) signBeaconObject(obj ssz.HashRoot, domain phase0. } return km.signer.SignBeaconAttestation(data, domain, pk) case spectypes.DomainProposer: - if km.isBlinded { + if km.builderProposals { var vBlindedBlock *api.VersionedBlindedBeaconBlock switch v := obj.(type) { case *apiv1bellatrix.BlindedBeaconBlock: @@ -107,15 +109,14 @@ func (km *ethKeyManagerSigner) signBeaconObject(obj ssz.HashRoot, domain phase0. Version: spec.DataVersionBellatrix, Bellatrix: v, } + return km.signer.SignBlindedBeaconBlock(vBlindedBlock, domain, pk) case *apiv1capella.BlindedBeaconBlock: vBlindedBlock = &api.VersionedBlindedBeaconBlock{ Version: spec.DataVersionCapella, Capella: v, } - default: - return nil, nil, errors.New("obj type is unknown") + return km.signer.SignBlindedBeaconBlock(vBlindedBlock, domain, pk) } - return km.signer.SignBlindedBeaconBlock(vBlindedBlock, domain, pk) } var vBlock *spec.VersionedBeaconBlock @@ -141,7 +142,7 @@ func (km *ethKeyManagerSigner) signBeaconObject(obj ssz.HashRoot, domain phase0. Capella: v, } default: - return nil, nil, errors.New("obj type is unknown") + return nil, nil, fmt.Errorf("obj type is unknown: %T", obj) } return km.signer.SignBeaconBlock(vBlock, domain, pk) @@ -192,7 +193,7 @@ func (km *ethKeyManagerSigner) signBeaconObject(obj ssz.HashRoot, domain phase0. V1: v, } default: - return nil, nil, errors.New("obj type is unknown") + return nil, nil, fmt.Errorf("obj type is unknown: %T", obj) } return km.signer.SignRegistration(data, domain, pk) default: diff --git a/ekm/signer_key_manager_test.go b/ekm/signer_key_manager_test.go index 6f26f8f0df..c9c8392917 100644 --- a/ekm/signer_key_manager_test.go +++ b/ekm/signer_key_manager_test.go @@ -5,17 +5,16 @@ import ( "github.com/attestantio/go-eth2-client/spec/altair" "github.com/attestantio/go-eth2-client/spec/bellatrix" - "github.com/bloxapp/ssv/logging" - "github.com/prysmaticlabs/go-bitfield" - "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/bloxapp/eth2-key-manager/core" specqbft "github.com/bloxapp/ssv-spec/qbft" spectypes "github.com/bloxapp/ssv-spec/types" "github.com/herumi/bls-eth-go-binary/bls" + "github.com/prysmaticlabs/go-bitfield" "github.com/stretchr/testify/require" - beacon2 "github.com/bloxapp/ssv/protocol/v2/blockchain/beacon" + "github.com/bloxapp/ssv/logging" + "github.com/bloxapp/ssv/protocol/v2/blockchain/beacon" "github.com/bloxapp/ssv/protocol/v2/types" "github.com/bloxapp/ssv/utils/threshold" ) @@ -35,7 +34,7 @@ func testKeyManager(t *testing.T) spectypes.KeyManager { db, err := getBaseStorage(logger) require.NoError(t, err) - km, err := NewETHKeyManagerSigner(db, beacon2.NewNetwork(core.PraterNetwork, 0), types.GetDefaultDomain(), logger) + km, err := NewETHKeyManagerSigner(logger, db, beacon.NewNetwork(core.PraterNetwork, 0), types.GetDefaultDomain(), true) require.NoError(t, err) sk1 := &bls.SecretKey{} diff --git a/eth1/mock_client.go b/eth1/mock_client.go index a9bd622b60..0472a44f62 100644 --- a/eth1/mock_client.go +++ b/eth1/mock_client.go @@ -5,38 +5,37 @@ package eth1 import ( - big "math/big" - reflect "reflect" - gomock "github.com/golang/mock/gomock" event "github.com/prysmaticlabs/prysm/async/event" zap "go.uber.org/zap" + big "math/big" + reflect "reflect" ) -// MockClient is a mock of Client interface. +// MockClient is a mock of Client interface type MockClient struct { ctrl *gomock.Controller recorder *MockClientMockRecorder } -// MockClientMockRecorder is the mock recorder for MockClient. +// MockClientMockRecorder is the mock recorder for MockClient type MockClientMockRecorder struct { mock *MockClient } -// NewMockClient creates a new mock instance. +// NewMockClient creates a new mock instance func NewMockClient(ctrl *gomock.Controller) *MockClient { mock := &MockClient{ctrl: ctrl} mock.recorder = &MockClientMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use. +// EXPECT returns an object that allows the caller to indicate expected use func (m *MockClient) EXPECT() *MockClientMockRecorder { return m.recorder } -// EventsFeed mocks base method. +// EventsFeed mocks base method func (m *MockClient) EventsFeed() *event.Feed { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "EventsFeed") @@ -44,13 +43,13 @@ func (m *MockClient) EventsFeed() *event.Feed { return ret0 } -// EventsFeed indicates an expected call of EventsFeed. +// EventsFeed indicates an expected call of EventsFeed func (mr *MockClientMockRecorder) EventsFeed() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EventsFeed", reflect.TypeOf((*MockClient)(nil).EventsFeed)) } -// Start mocks base method. +// Start mocks base method func (m *MockClient) Start(logger *zap.Logger) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Start", logger) @@ -58,13 +57,13 @@ func (m *MockClient) Start(logger *zap.Logger) error { return ret0 } -// Start indicates an expected call of Start. +// Start indicates an expected call of Start func (mr *MockClientMockRecorder) Start(logger interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockClient)(nil).Start), logger) } -// Sync mocks base method. +// Sync mocks base method func (m *MockClient) Sync(logger *zap.Logger, fromBlock *big.Int) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Sync", logger, fromBlock) @@ -72,7 +71,7 @@ func (m *MockClient) Sync(logger *zap.Logger, fromBlock *big.Int) error { return ret0 } -// Sync indicates an expected call of Sync. +// Sync indicates an expected call of Sync func (mr *MockClientMockRecorder) Sync(logger, fromBlock interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Sync", reflect.TypeOf((*MockClient)(nil).Sync), logger, fromBlock) diff --git a/eth1/mock_sync.go b/eth1/mock_sync.go index 74538ce250..fe75e49f24 100644 --- a/eth1/mock_sync.go +++ b/eth1/mock_sync.go @@ -5,35 +5,48 @@ package eth1 import ( - reflect "reflect" - gomock "github.com/golang/mock/gomock" + reflect "reflect" ) -// MockSyncOffsetStorage is a mock of SyncOffsetStorage interface. +// MockSyncOffsetStorage is a mock of SyncOffsetStorage interface type MockSyncOffsetStorage struct { ctrl *gomock.Controller recorder *MockSyncOffsetStorageMockRecorder } -// MockSyncOffsetStorageMockRecorder is the mock recorder for MockSyncOffsetStorage. +// MockSyncOffsetStorageMockRecorder is the mock recorder for MockSyncOffsetStorage type MockSyncOffsetStorageMockRecorder struct { mock *MockSyncOffsetStorage } -// NewMockSyncOffsetStorage creates a new mock instance. +// NewMockSyncOffsetStorage creates a new mock instance func NewMockSyncOffsetStorage(ctrl *gomock.Controller) *MockSyncOffsetStorage { mock := &MockSyncOffsetStorage{ctrl: ctrl} mock.recorder = &MockSyncOffsetStorageMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use. +// EXPECT returns an object that allows the caller to indicate expected use func (m *MockSyncOffsetStorage) EXPECT() *MockSyncOffsetStorageMockRecorder { return m.recorder } -// GetSyncOffset mocks base method. +// SaveSyncOffset mocks base method +func (m *MockSyncOffsetStorage) SaveSyncOffset(offset *SyncOffset) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SaveSyncOffset", offset) + ret0, _ := ret[0].(error) + return ret0 +} + +// SaveSyncOffset indicates an expected call of SaveSyncOffset +func (mr *MockSyncOffsetStorageMockRecorder) SaveSyncOffset(offset interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveSyncOffset", reflect.TypeOf((*MockSyncOffsetStorage)(nil).SaveSyncOffset), offset) +} + +// GetSyncOffset mocks base method func (m *MockSyncOffsetStorage) GetSyncOffset() (*SyncOffset, bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetSyncOffset") @@ -43,22 +56,8 @@ func (m *MockSyncOffsetStorage) GetSyncOffset() (*SyncOffset, bool, error) { return ret0, ret1, ret2 } -// GetSyncOffset indicates an expected call of GetSyncOffset. +// GetSyncOffset indicates an expected call of GetSyncOffset func (mr *MockSyncOffsetStorageMockRecorder) GetSyncOffset() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSyncOffset", reflect.TypeOf((*MockSyncOffsetStorage)(nil).GetSyncOffset)) } - -// SaveSyncOffset mocks base method. -func (m *MockSyncOffsetStorage) SaveSyncOffset(offset *SyncOffset) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SaveSyncOffset", offset) - ret0, _ := ret[0].(error) - return ret0 -} - -// SaveSyncOffset indicates an expected call of SaveSyncOffset. -func (mr *MockSyncOffsetStorageMockRecorder) SaveSyncOffset(offset interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveSyncOffset", reflect.TypeOf((*MockSyncOffsetStorage)(nil).SaveSyncOffset), offset) -} diff --git a/go.mod b/go.mod index 4bc9899b6c..537b7d3dc3 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,6 @@ require ( github.com/gorilla/websocket v1.5.0 github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 - github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/golang-lru/v2 v2.0.2 github.com/herumi/bls-eth-go-binary v1.28.1 github.com/ilyakaznacheev/cleanenv v1.2.5 @@ -80,6 +79,7 @@ require ( github.com/google/gopacket v1.1.19 // indirect github.com/google/pprof v0.0.0-20221219190121-3cb0bae90811 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect github.com/huin/goupnp v1.0.3 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect @@ -183,3 +183,5 @@ replace github.com/prysmaticlabs/prysm => github.com/prysmaticlabs/prysm v1.4.2- replace github.com/google/flatbuffers => github.com/google/flatbuffers v1.11.0 replace github.com/dgraph-io/ristretto => github.com/dgraph-io/ristretto v0.1.1-0.20211108053508-297c39e6640f + +replace github.com/bloxapp/ssv-spec => github.com/nkryuchkov/ssv-spec v0.0.0-20230510164359-d0730a175f45 // mev-support-on-21e97ea2ad3d diff --git a/go.sum b/go.sum index d65be47c51..bf4c1ca425 100644 --- a/go.sum +++ b/go.sum @@ -139,8 +139,6 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bloxapp/eth2-key-manager v1.3.0-rc.0 h1:Hfq7/mdjUHmqI5Tv0Pc1TdGgXfHrgrY1tOTIz290pAg= github.com/bloxapp/eth2-key-manager v1.3.0-rc.0/go.mod h1:cT+qAJfnAzNz9StFoHQ8xAkyU2eyEukd6xfxvcBWuZA= -github.com/bloxapp/ssv-spec v0.3.1-0.20230322125847-21e97ea2ad3d h1:53fq26+ulTdIB35ea5ji64pmb9hE2sAuK5FRYXP2liA= -github.com/bloxapp/ssv-spec v0.3.1-0.20230322125847-21e97ea2ad3d/go.mod h1:VDG27SnHcC2coL/eCXFvROuzvlOrdlSTzgCWcoUvh7s= github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= @@ -1138,6 +1136,8 @@ github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxzi github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/nkryuchkov/ssv-spec v0.0.0-20230510164359-d0730a175f45 h1:hjFZ9T1B6dSCSTsCXRYgHVunnaEpQHoovKZZdHnV05A= +github.com/nkryuchkov/ssv-spec v0.0.0-20230510164359-d0730a175f45/go.mod h1:VDG27SnHcC2coL/eCXFvROuzvlOrdlSTzgCWcoUvh7s= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= diff --git a/integration/qbft/tests/scenario_test.go b/integration/qbft/tests/scenario_test.go index 85e49ebcb0..39760f236c 100644 --- a/integration/qbft/tests/scenario_test.go +++ b/integration/qbft/tests/scenario_test.go @@ -11,6 +11,8 @@ import ( "github.com/bloxapp/ssv-spec/types/testingutils" spectestingutils "github.com/bloxapp/ssv-spec/types/testingutils" "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + "go.uber.org/zap" qbftstorage "github.com/bloxapp/ssv/ibft/storage" "github.com/bloxapp/ssv/logging" @@ -28,8 +30,6 @@ import ( "github.com/bloxapp/ssv/protocol/v2/types" "github.com/bloxapp/ssv/storage" "github.com/bloxapp/ssv/storage/basedb" - "github.com/stretchr/testify/require" - "go.uber.org/zap" ) var ( @@ -183,6 +183,7 @@ func newStores(logger *zap.Logger) *qbftstorage.QBFTStores { spectypes.BNRoleAggregator, spectypes.BNRoleSyncCommittee, spectypes.BNRoleSyncCommitteeContribution, + spectypes.BNRoleValidatorRegistration, } for _, role := range roles { storageMap.Add(role, qbftstorage.New(db, role.String(), protocolforks.GenesisForkVersion)) diff --git a/logging/fields/fields.go b/logging/fields/fields.go index b412c1d6dd..7919cfef2d 100644 --- a/logging/fields/fields.go +++ b/logging/fields/fields.go @@ -9,23 +9,23 @@ import ( "strconv" "time" - forksprotocol "github.com/bloxapp/ssv/protocol/forks" - "github.com/bloxapp/ssv/protocol/v2/message" - "github.com/bloxapp/ssv/utils/format" + "github.com/attestantio/go-eth2-client/spec" + "github.com/attestantio/go-eth2-client/spec/phase0" + specqbft "github.com/bloxapp/ssv-spec/qbft" + spectypes "github.com/bloxapp/ssv-spec/types" "github.com/dgraph-io/ristretto" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/libp2p/go-libp2p/core/peer" "go.uber.org/zap" "go.uber.org/zap/zapcore" - "github.com/attestantio/go-eth2-client/spec/phase0" - spec "github.com/attestantio/go-eth2-client/spec/phase0" - specqbft "github.com/bloxapp/ssv-spec/qbft" - spectypes "github.com/bloxapp/ssv-spec/types" "github.com/bloxapp/ssv/logging/fields/stringer" "github.com/bloxapp/ssv/network/records" + forksprotocol "github.com/bloxapp/ssv/protocol/forks" "github.com/bloxapp/ssv/protocol/v2/blockchain/beacon" + "github.com/bloxapp/ssv/protocol/v2/message" protocolp2p "github.com/bloxapp/ssv/protocol/v2/p2p" + "github.com/bloxapp/ssv/utils/format" ) const ( @@ -34,7 +34,10 @@ const ( FieldAddress = "address" FieldBindIP = "bind_ip" FieldBlock = "block" + FieldBlockHash = "block_hash" + FieldBlockVersion = "block_version" FieldBlockCacheMetrics = "block_cache_metrics_field" + FieldBuilderProposals = "builder_proposals" FieldConnectionID = "connection_id" FieldConsensusTime = "consensus_time" FieldCount = "count" @@ -46,6 +49,7 @@ const ( FieldErrors = "errors" FieldEvent = "event" FieldEventID = "event_id" + FieldFeeRecipient = "fee_recipient" FieldFork = "fork" FieldFromBlock = "from_block" FieldHeight = "height" @@ -151,7 +155,7 @@ func CurrentSlot(network beacon.Network) zapcore.Field { return zap.Stringer(FieldCurrentSlot, stringer.Uint64Stringer{Val: uint64(network.EstimatedCurrentSlot())}) } -func StartTimeUnixMilli(network beacon.Network, slot spec.Slot) zapcore.Field { +func StartTimeUnixMilli(network beacon.Network, slot phase0.Slot) zapcore.Field { return zap.Stringer(FieldStartTimeUnixMilli, stringer.FuncStringer{ Fn: func() string { return strconv.Itoa(int(network.GetSlotStartTime(slot).UnixMilli())) @@ -211,6 +215,14 @@ func BlockNumber(val uint64) zap.Field { return zap.Stringer(FieldBlock, stringer.Uint64Stringer{Val: val}) } +func BlockHash(val phase0.Hash32) zap.Field { + return zap.Stringer(FieldBlockHash, val) +} + +func BlockVersion(val spec.DataVersion) zap.Field { + return zap.Stringer(FieldBlockVersion, val) +} + func Name(val string) zap.Field { return zap.String(FieldName, val) } @@ -271,6 +283,14 @@ func ToBlock(val *big.Int) zap.Field { return zap.Int64(FieldToBlock, val.Int64()) } +func FeeRecipient(pubKey []byte) zap.Field { + return zap.Stringer(FieldFeeRecipient, stringer.HexStringer{Val: pubKey}) +} + +func BuilderProposals(v bool) zap.Field { + return zap.Bool(FieldBuilderProposals, v) +} + func FormatDutyID(epoch phase0.Epoch, duty *spectypes.Duty) string { return fmt.Sprintf("%v-e%v-s%v-v%v", duty.Type.String(), epoch, duty.Slot, duty.ValidatorIndex) } diff --git a/operator/duties/controller.go b/operator/duties/controller.go index 9c07d4296a..0f9a6df6bb 100644 --- a/operator/duties/controller.go +++ b/operator/duties/controller.go @@ -6,8 +6,6 @@ import ( "encoding/json" "fmt" - "github.com/bloxapp/ssv/logging/fields" - eth2apiv1 "github.com/attestantio/go-eth2-client/api/v1" "github.com/attestantio/go-eth2-client/spec/phase0" spectypes "github.com/bloxapp/ssv-spec/types" @@ -18,6 +16,7 @@ import ( "github.com/bloxapp/ssv/beacon/goclient" "github.com/bloxapp/ssv/logging" + "github.com/bloxapp/ssv/logging/fields" "github.com/bloxapp/ssv/operator/slot_ticker" "github.com/bloxapp/ssv/operator/validator" forksprotocol "github.com/bloxapp/ssv/protocol/forks" @@ -33,6 +32,8 @@ import ( // period change at which to prepare the relevant duties. var syncCommitteePreparationEpochs = uint64(2) +const validatorRegistrationEpochInterval = uint64(10) + // DutyExecutor represents the component that executes duties type DutyExecutor interface { ExecuteDuty(logger *zap.Logger, duty *spectypes.Duty) error @@ -53,6 +54,7 @@ type ControllerOptions struct { DutyLimit uint64 ForkVersion forksprotocol.ForkVersion Ticker slot_ticker.Ticker + BuilderProposals bool } // dutyController internal implementation of DutyController @@ -65,11 +67,13 @@ type dutyController struct { validatorController validator.Controller dutyLimit uint64 ticker slot_ticker.Ticker + builderProposals bool // sync committee duties map [period, map[index, duty]] - syncCommitteeDutiesMap *hashmap.Map[uint64, *hashmap.Map[phase0.ValidatorIndex, *eth2apiv1.SyncCommitteeDuty]] - lastBlockEpoch phase0.Epoch - currentDutyDependentRoot phase0.Root + syncCommitteeDutiesMap *hashmap.Map[uint64, *hashmap.Map[phase0.ValidatorIndex, *eth2apiv1.SyncCommitteeDuty]] + lastBlockEpoch phase0.Epoch + currentDutyDependentRoot phase0.Root + validatorsPassedFirstRegistration map[string]struct{} } var secPerSlot int64 = 12 @@ -85,8 +89,10 @@ func NewDutyController(logger *zap.Logger, opts *ControllerOptions) DutyControll dutyLimit: opts.DutyLimit, executor: opts.Executor, ticker: opts.Ticker, + builderProposals: opts.BuilderProposals, - syncCommitteeDutiesMap: hashmap.New[uint64, *hashmap.Map[phase0.ValidatorIndex, *eth2apiv1.SyncCommitteeDuty]](), + syncCommitteeDutiesMap: hashmap.New[uint64, *hashmap.Map[phase0.ValidatorIndex, *eth2apiv1.SyncCommitteeDuty]](), + validatorsPassedFirstRegistration: map[string]struct{}{}, } return &dc } @@ -241,6 +247,57 @@ func (dc *dutyController) handleSlot(logger *zap.Logger, slot phase0.Slot) { go dc.onDuty(logger, &duties[i]) } + dc.handleSyncCommittee(logger, slot, syncPeriod) + if dc.builderProposals { + dc.handleValidatorRegistration(logger, slot) + } +} + +func (dc *dutyController) handleValidatorRegistration(logger *zap.Logger, slot phase0.Slot) { + shares, err := dc.validatorController.GetOperatorShares(logger) + if err != nil { + logger.Warn("failed to get all validators share", zap.Error(err)) + return + } + + sent := 0 + for _, share := range shares { + if !share.HasBeaconMetadata() { + continue + } + + // if not passed first registration, should be registered within one epoch time in a corresponding slot + // if passed first registration, should be registered within validatorRegistrationEpochInterval epochs time in a corresponding slot + registrationSlotInterval := dc.ethNetwork.SlotsPerEpoch() + if _, ok := dc.validatorsPassedFirstRegistration[string(share.ValidatorPubKey)]; ok { + registrationSlotInterval *= validatorRegistrationEpochInterval + } + + if uint64(share.BeaconMetadata.Index)%registrationSlotInterval != uint64(slot)%registrationSlotInterval { + continue + } + + pk := phase0.BLSPubKey{} + copy(pk[:], share.ValidatorPubKey) + + go dc.onDuty(logger, &spectypes.Duty{ + Type: spectypes.BNRoleValidatorRegistration, + PubKey: pk, + Slot: slot, + // no need for other params + }) + + sent++ + dc.validatorsPassedFirstRegistration[string(share.ValidatorPubKey)] = struct{}{} + } + logger.Debug("validator registration duties sent", zap.Uint64("slot", uint64(slot)), fields.Count(sent)) +} + +// handleSyncCommittee preform the following processes - +// 1. execute sync committee duties +// 2. Get next period's sync committee duties, but wait until half-way through the epoch +// This allows us to set them up at a time when the beacon node should be less busy. +func (dc *dutyController) handleSyncCommittee(logger *zap.Logger, slot phase0.Slot, syncPeriod uint64) { // execute sync committee duties if syncCommitteeDuties, found := dc.syncCommitteeDutiesMap.Get(syncPeriod); found { toSpecDuty := func(duty *eth2apiv1.SyncCommitteeDuty, slot phase0.Slot, role spectypes.BeaconRole) *spectypes.Duty { diff --git a/operator/fee_recipient/controller.go b/operator/fee_recipient/controller.go index 6d831cc510..8a9fa8686a 100644 --- a/operator/fee_recipient/controller.go +++ b/operator/fee_recipient/controller.go @@ -2,7 +2,6 @@ package fee_recipient import ( "context" - "sync/atomic" "github.com/attestantio/go-eth2-client/spec/bellatrix" "github.com/attestantio/go-eth2-client/spec/phase0" @@ -14,8 +13,6 @@ import ( beaconprotocol "github.com/bloxapp/ssv/protocol/v2/blockchain/beacon" "github.com/bloxapp/ssv/protocol/v2/types" "github.com/bloxapp/ssv/registry/storage" - - "github.com/hashicorp/go-multierror" ) //go:generate mockgen -package=mocks -destination=./mocks/controller.go -source=./controller.go @@ -74,44 +71,61 @@ func (rc *recipientController) listenToTicker(logger *zap.Logger, slots chan pha firstTimeSubmitted := false for currentSlot := range slots { // submit if first time or if first slot in epoch - slotsPerEpoch := rc.ethNetwork.SlotsPerEpoch() - if firstTimeSubmitted && uint64(currentSlot)%slotsPerEpoch != 0 { + if firstTimeSubmitted && uint64(currentSlot)%rc.ethNetwork.SlotsPerEpoch() != 0 { continue } - firstTimeSubmitted = true - // submit fee recipient - shares, err := rc.shareStorage.GetFilteredShares(logger, storage.ByOperatorIDAndActive(rc.operatorData.ID)) + + err := rc.prepareAndSubmit(logger, currentSlot) if err != nil { - logger.Warn("could not get validators shares", zap.Error(err)) - continue + logger.Warn("could not submit proposal preparations", zap.Error(err)) } + } +} - var g multierror.Group - const batchSize = 500 - var counter int32 - for start, end := 0, 0; start <= len(shares)-1; start = end { - end = start + batchSize - if end > len(shares) { - end = len(shares) - } - batch := shares[start:end] - - g.Go(func() error { - m, err := rc.toProposalPreparation(logger, batch) - if err != nil { - return errors.Wrap(err, "could not build proposal preparation") - } - atomic.AddInt32(&counter, int32(len(batch))) - return rc.beaconClient.SubmitProposalPreparation(m) - }) +func (rc *recipientController) prepareAndSubmit(logger *zap.Logger, slot phase0.Slot) error { + shares, err := rc.shareStorage.GetFilteredShares(logger, storage.ByOperatorIDAndActive(rc.operatorData.ID)) + if err != nil { + return errors.Wrap(err, "could not get shares") + } + + const batchSize = 500 + var submitted int + for start := 0; start < len(shares); start += batchSize { + end := start + batchSize + if end > len(shares) { + end = len(shares) } - if err := g.Wait().ErrorOrNil(); err != nil { - logger.Warn("failed to submit proposal preparation", zap.Error(err)) - } else { - logger.Debug("proposal preparation submitted", zap.Int32("count", atomic.LoadInt32(&counter))) + batch := shares[start:end] + + count, err := rc.submit(logger, batch) + if err != nil { + logger.Warn("could not submit proposal preparation batch", + zap.Int("start_index", start), + zap.Error(err), + ) + continue } + submitted += count + } + + logger.Debug("✅ successfully submitted proposal preparations", + zap.Int("submitted", submitted), + zap.Int("total", len(shares)), + ) + return nil +} + +func (rc *recipientController) submit(logger *zap.Logger, shares []*types.SSVShare) (int, error) { + m, err := rc.toProposalPreparation(logger, shares) + if err != nil { + return 0, errors.Wrap(err, "could not build proposal preparation batch") + } + err = rc.beaconClient.SubmitProposalPreparation(m) + if err != nil { + return 0, errors.Wrap(err, "could not submit proposal preparation batch") } + return len(m), nil } func (rc *recipientController) toProposalPreparation(logger *zap.Logger, shares []*types.SSVShare) (map[phase0.ValidatorIndex]bellatrix.ExecutionAddress, error) { diff --git a/operator/fee_recipient/mocks/controller.go b/operator/fee_recipient/mocks/controller.go index bb7f7fd32c..d501acea58 100644 --- a/operator/fee_recipient/mocks/controller.go +++ b/operator/fee_recipient/mocks/controller.go @@ -5,42 +5,41 @@ package mocks import ( - reflect "reflect" - gomock "github.com/golang/mock/gomock" zap "go.uber.org/zap" + reflect "reflect" ) -// MockRecipientController is a mock of RecipientController interface. +// MockRecipientController is a mock of RecipientController interface type MockRecipientController struct { ctrl *gomock.Controller recorder *MockRecipientControllerMockRecorder } -// MockRecipientControllerMockRecorder is the mock recorder for MockRecipientController. +// MockRecipientControllerMockRecorder is the mock recorder for MockRecipientController type MockRecipientControllerMockRecorder struct { mock *MockRecipientController } -// NewMockRecipientController creates a new mock instance. +// NewMockRecipientController creates a new mock instance func NewMockRecipientController(ctrl *gomock.Controller) *MockRecipientController { mock := &MockRecipientController{ctrl: ctrl} mock.recorder = &MockRecipientControllerMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use. +// EXPECT returns an object that allows the caller to indicate expected use func (m *MockRecipientController) EXPECT() *MockRecipientControllerMockRecorder { return m.recorder } -// Start mocks base method. +// Start mocks base method func (m *MockRecipientController) Start(logger *zap.Logger) { m.ctrl.T.Helper() m.ctrl.Call(m, "Start", logger) } -// Start indicates an expected call of Start. +// Start indicates an expected call of Start func (mr *MockRecipientControllerMockRecorder) Start(logger interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockRecipientController)(nil).Start), logger) diff --git a/operator/node.go b/operator/node.go index f159ce240c..d8c44e70ca 100644 --- a/operator/node.go +++ b/operator/node.go @@ -77,7 +77,7 @@ type operatorNode struct { } // New is the constructor of operatorNode -func New(logger *zap.Logger, opts Options) Node { +func New(logger *zap.Logger, opts Options, slotTicker slot_ticker.Ticker) Node { storageMap := qbftstorage.NewStores() roles := []spectypes.BeaconRole{ @@ -86,16 +86,15 @@ func New(logger *zap.Logger, opts Options) Node { spectypes.BNRoleAggregator, spectypes.BNRoleSyncCommittee, spectypes.BNRoleSyncCommitteeContribution, + spectypes.BNRoleValidatorRegistration, } for _, role := range roles { storageMap.Add(role, qbftstorage.New(opts.DB, role.String(), opts.ForkVersion)) } - ticker := slot_ticker.NewTicker(opts.Context, opts.ETHNetwork, phase0.Epoch(opts.GenesisEpoch)) - node := &operatorNode{ context: opts.Context, - ticker: ticker, + ticker: slotTicker, validatorsCtrl: opts.ValidatorController, ethNetwork: opts.ETHNetwork, beacon: opts.Beacon, @@ -111,7 +110,8 @@ func New(logger *zap.Logger, opts Options) Node { DutyLimit: opts.DutyLimit, Executor: opts.DutyExec, ForkVersion: opts.ForkVersion, - Ticker: ticker, + Ticker: slotTicker, + BuilderProposals: opts.ValidatorOptions.BuilderProposals, }), feeRecipientCtrl: fee_recipient.NewController(&fee_recipient.ControllerOptions{ Ctx: opts.Context, @@ -119,7 +119,7 @@ func New(logger *zap.Logger, opts Options) Node { EthNetwork: opts.ETHNetwork, ShareStorage: opts.ValidatorOptions.RegistryStorage, RecipientStorage: opts.ValidatorOptions.RegistryStorage, - Ticker: ticker, + Ticker: slotTicker, OperatorData: opts.ValidatorOptions.OperatorData, }), forkVersion: opts.ForkVersion, diff --git a/operator/slot_ticker/mocks/ticker.go b/operator/slot_ticker/mocks/ticker.go index d86695a5e8..2899205e5c 100644 --- a/operator/slot_ticker/mocks/ticker.go +++ b/operator/slot_ticker/mocks/ticker.go @@ -5,50 +5,49 @@ package mocks import ( - reflect "reflect" - phase0 "github.com/attestantio/go-eth2-client/spec/phase0" gomock "github.com/golang/mock/gomock" event "github.com/prysmaticlabs/prysm/async/event" zap "go.uber.org/zap" + reflect "reflect" ) -// MockTicker is a mock of Ticker interface. +// MockTicker is a mock of Ticker interface type MockTicker struct { ctrl *gomock.Controller recorder *MockTickerMockRecorder } -// MockTickerMockRecorder is the mock recorder for MockTicker. +// MockTickerMockRecorder is the mock recorder for MockTicker type MockTickerMockRecorder struct { mock *MockTicker } -// NewMockTicker creates a new mock instance. +// NewMockTicker creates a new mock instance func NewMockTicker(ctrl *gomock.Controller) *MockTicker { mock := &MockTicker{ctrl: ctrl} mock.recorder = &MockTickerMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use. +// EXPECT returns an object that allows the caller to indicate expected use func (m *MockTicker) EXPECT() *MockTickerMockRecorder { return m.recorder } -// Start mocks base method. +// Start mocks base method func (m *MockTicker) Start(logger *zap.Logger) { m.ctrl.T.Helper() m.ctrl.Call(m, "Start", logger) } -// Start indicates an expected call of Start. +// Start indicates an expected call of Start func (mr *MockTickerMockRecorder) Start(logger interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockTicker)(nil).Start), logger) } -// Subscribe mocks base method. +// Subscribe mocks base method func (m *MockTicker) Subscribe(subscription chan phase0.Slot) event.Subscription { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Subscribe", subscription) @@ -56,7 +55,7 @@ func (m *MockTicker) Subscribe(subscription chan phase0.Slot) event.Subscription return ret0 } -// Subscribe indicates an expected call of Subscribe. +// Subscribe indicates an expected call of Subscribe func (mr *MockTickerMockRecorder) Subscribe(subscription interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Subscribe", reflect.TypeOf((*MockTicker)(nil).Subscribe), subscription) diff --git a/operator/validator/controller.go b/operator/validator/controller.go index 799aa2a225..87448dc597 100644 --- a/operator/validator/controller.go +++ b/operator/validator/controller.go @@ -73,6 +73,7 @@ type ControllerOptions struct { CleanRegistryData bool FullNode bool `yaml:"FullNode" env:"FULLNODE" env-default:"false" env-description:"Save decided history rather than just highest messages"` Exporter bool `yaml:"Exporter" env:"EXPORTER" env-default:"false" env-description:""` + BuilderProposals bool `yaml:"BuilderProposals" env:"BUILDER_PROPOSALS" env-default:"false" env-description:"Use external builders to produce blocks"` KeyManager spectypes.KeyManager OperatorData *registrystorage.OperatorData RegistryStorage nodestorage.Storage @@ -83,6 +84,7 @@ type ControllerOptions struct { // worker flags WorkersCount int `yaml:"MsgWorkersCount" env:"MSG_WORKERS_COUNT" env-default:"256" env-description:"Number of goroutines to use for message workers"` QueueBufferSize int `yaml:"MsgWorkerBufferSize" env:"MSG_WORKER_BUFFER_SIZE" env-default:"1024" env-description:"Buffer size for message workers"` + GasLimit uint64 } // Controller represent the validators controller, @@ -95,6 +97,7 @@ type Controller interface { UpdateValidatorMetaDataLoop(logger *zap.Logger) StartNetworkHandlers(logger *zap.Logger) Eth1EventHandler(logger *zap.Logger, ongoingSync bool) eth1.SyncEventHandler + GetOperatorShares(logger *zap.Logger) ([]*types.SSVShare, error) // GetValidatorStats returns stats of validators, including the following: // - the amount of validators in the network // - the amount of active validators (i.e. not slashed or existed) @@ -140,13 +143,14 @@ type controller struct { // NewController creates a new validator controller instance func NewController(logger *zap.Logger, options ControllerOptions) Controller { - logger.Debug("CreatingController", zap.Bool("full_node", options.FullNode)) + logger.Debug("CreatingController", zap.Bool("full_node", options.FullNode), fields.BuilderProposals(options.BuilderProposals)) storageMap := storage.NewStores() storageMap.Add(spectypes.BNRoleAttester, storage.New(options.DB, spectypes.BNRoleAttester.String(), options.ForkVersion)) storageMap.Add(spectypes.BNRoleProposer, storage.New(options.DB, spectypes.BNRoleProposer.String(), options.ForkVersion)) storageMap.Add(spectypes.BNRoleAggregator, storage.New(options.DB, spectypes.BNRoleAggregator.String(), options.ForkVersion)) storageMap.Add(spectypes.BNRoleSyncCommittee, storage.New(options.DB, spectypes.BNRoleSyncCommittee.String(), options.ForkVersion)) storageMap.Add(spectypes.BNRoleSyncCommitteeContribution, storage.New(options.DB, spectypes.BNRoleSyncCommitteeContribution.String(), options.ForkVersion)) + storageMap.Add(spectypes.BNRoleValidatorRegistration, storage.New(options.DB, spectypes.BNRoleValidatorRegistration.String(), options.ForkVersion)) // lookup in a map that holds all relevant operators operatorsIDs := &sync.Map{} @@ -170,6 +174,8 @@ func NewController(logger *zap.Logger, options ControllerOptions) Controller { NewDecidedHandler: options.NewDecidedHandler, FullNode: options.FullNode, Exporter: options.Exporter, + BuilderProposals: options.BuilderProposals, + GasLimit: options.GasLimit, } // If full node, increase queue size to make enough room @@ -243,6 +249,10 @@ func (c *controller) setupNetworkHandlers(logger *zap.Logger) error { return nil } +func (c *controller) GetOperatorShares(logger *zap.Logger) ([]*types.SSVShare, error) { + return c.sharesStorage.GetFilteredShares(logger, registrystorage.ByOperatorIDAndActive(c.operatorData.ID)) +} + func (c *controller) GetOperatorData() *registrystorage.OperatorData { return c.operatorData } @@ -627,7 +637,7 @@ func (c *controller) onShareStart(logger *zap.Logger, share *types.SSVShare) (bo return false, nil } - if err := SetShareFeeRecipient(share, c.recipientsStorage.GetRecipientData); err != nil { + if err := SetShareFeeRecipient(logger, share, c.recipientsStorage.GetRecipientData); err != nil { return false, errors.Wrap(err, "could not set share fee recipient") } @@ -689,6 +699,7 @@ func SetupRunners(ctx context.Context, logger *zap.Logger, options validator.Opt spectypes.BNRoleAggregator, spectypes.BNRoleSyncCommittee, spectypes.BNRoleSyncCommitteeContribution, + spectypes.BNRoleValidatorRegistration, } domainType := types.GetDefaultDomain() @@ -723,9 +734,10 @@ func SetupRunners(ctx context.Context, logger *zap.Logger, options validator.Opt qbftCtrl := buildController(spectypes.BNRoleAttester, valCheck) runners[role] = runner.NewAttesterRunnner(spectypes.PraterNetwork, &options.SSVShare.Share, qbftCtrl, options.Beacon, options.Network, options.Signer, valCheck) case spectypes.BNRoleProposer: - proposedValueCheck := specssv.ProposerValueCheckF(options.Signer, spectypes.PraterNetwork, options.SSVShare.Share.ValidatorPubKey, options.SSVShare.BeaconMetadata.Index, options.SSVShare.SharePubKey) + proposedValueCheck := specssv.ProposerValueCheckF(options.Signer, spectypes.PraterNetwork, options.SSVShare.Share.ValidatorPubKey, options.SSVShare.BeaconMetadata.Index, options.SSVShare.SharePubKey, options.BuilderProposals) qbftCtrl := buildController(spectypes.BNRoleProposer, proposedValueCheck) runners[role] = runner.NewProposerRunner(spectypes.PraterNetwork, &options.SSVShare.Share, qbftCtrl, options.Beacon, options.Network, options.Signer, proposedValueCheck) + runners[role].(*runner.ProposerRunner).ProducesBlindedBlocks = options.BuilderProposals // apply blinded block flag case spectypes.BNRoleAggregator: aggregatorValueCheckF := specssv.AggregatorValueCheckF(options.Signer, spectypes.PraterNetwork, options.SSVShare.Share.ValidatorPubKey, options.SSVShare.BeaconMetadata.Index) qbftCtrl := buildController(spectypes.BNRoleAggregator, aggregatorValueCheckF) @@ -738,6 +750,10 @@ func SetupRunners(ctx context.Context, logger *zap.Logger, options validator.Opt syncCommitteeContributionValueCheckF := specssv.SyncCommitteeContributionValueCheckF(options.Signer, spectypes.PraterNetwork, options.SSVShare.Share.ValidatorPubKey, options.SSVShare.BeaconMetadata.Index) qbftCtrl := buildController(spectypes.BNRoleSyncCommitteeContribution, syncCommitteeContributionValueCheckF) runners[role] = runner.NewSyncCommitteeAggregatorRunner(spectypes.PraterNetwork, &options.SSVShare.Share, qbftCtrl, options.Beacon, options.Network, options.Signer, syncCommitteeContributionValueCheckF) + case spectypes.BNRoleValidatorRegistration: + qbftCtrl := buildController(spectypes.BNRoleValidatorRegistration, nil) + runners[role] = runner.NewValidatorRegistrationRunner(spectypes.PraterNetwork, &options.SSVShare.Share, qbftCtrl, options.Beacon, options.Network, options.Signer) + runners[role].(*runner.ValidatorRegistrationRunner).GasLimit = options.GasLimit // apply gas limit } } return runners diff --git a/operator/validator/event_handler.go b/operator/validator/event_handler.go index 6e52df6179..bd6bf0e87c 100644 --- a/operator/validator/event_handler.go +++ b/operator/validator/event_handler.go @@ -340,7 +340,7 @@ func (c *controller) handleFeeRecipientAddressUpdatedEvent( if ongoingSync && r != nil { _ = c.validatorsMap.ForEach(func(v *validator.Validator) error { if bytes.Equal(v.Share.OwnerAddress.Bytes(), r.Owner.Bytes()) { - v.Share.FeeRecipient = r.FeeRecipient + v.Share.FeeRecipientAddress = r.FeeRecipient } return nil }) diff --git a/operator/validator/mocks/controller.go b/operator/validator/mocks/controller.go index f5e27de3e8..a6582e5c54 100644 --- a/operator/validator/mocks/controller.go +++ b/operator/validator/mocks/controller.go @@ -5,69 +5,79 @@ package mocks import ( - reflect "reflect" - phase0 "github.com/attestantio/go-eth2-client/spec/phase0" eth1 "github.com/bloxapp/ssv/eth1" validator "github.com/bloxapp/ssv/protocol/v2/ssv/validator" + types "github.com/bloxapp/ssv/protocol/v2/types" storage "github.com/bloxapp/ssv/registry/storage" gomock "github.com/golang/mock/gomock" event "github.com/prysmaticlabs/prysm/async/event" zap "go.uber.org/zap" + reflect "reflect" ) -// MockController is a mock of Controller interface. +// MockController is a mock of Controller interface type MockController struct { ctrl *gomock.Controller recorder *MockControllerMockRecorder } -// MockControllerMockRecorder is the mock recorder for MockController. +// MockControllerMockRecorder is the mock recorder for MockController type MockControllerMockRecorder struct { mock *MockController } -// NewMockController creates a new mock instance. +// NewMockController creates a new mock instance func NewMockController(ctrl *gomock.Controller) *MockController { mock := &MockController{ctrl: ctrl} mock.recorder = &MockControllerMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use. +// EXPECT returns an object that allows the caller to indicate expected use func (m *MockController) EXPECT() *MockControllerMockRecorder { return m.recorder } -// Eth1EventHandler mocks base method. -func (m *MockController) Eth1EventHandler(logger *zap.Logger, ongoingSync bool) eth1.SyncEventHandler { +// ListenToEth1Events mocks base method +func (m *MockController) ListenToEth1Events(logger *zap.Logger, feed *event.Feed) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Eth1EventHandler", logger, ongoingSync) - ret0, _ := ret[0].(eth1.SyncEventHandler) - return ret0 + m.ctrl.Call(m, "ListenToEth1Events", logger, feed) } -// Eth1EventHandler indicates an expected call of Eth1EventHandler. -func (mr *MockControllerMockRecorder) Eth1EventHandler(logger, ongoingSync interface{}) *gomock.Call { +// ListenToEth1Events indicates an expected call of ListenToEth1Events +func (mr *MockControllerMockRecorder) ListenToEth1Events(logger, feed interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Eth1EventHandler", reflect.TypeOf((*MockController)(nil).Eth1EventHandler), logger, ongoingSync) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListenToEth1Events", reflect.TypeOf((*MockController)(nil).ListenToEth1Events), logger, feed) } -// GetOperatorData mocks base method. -func (m *MockController) GetOperatorData() *storage.OperatorData { +// StartValidators mocks base method +func (m *MockController) StartValidators(logger *zap.Logger) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetOperatorData") - ret0, _ := ret[0].(*storage.OperatorData) + m.ctrl.Call(m, "StartValidators", logger) +} + +// StartValidators indicates an expected call of StartValidators +func (mr *MockControllerMockRecorder) StartValidators(logger interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartValidators", reflect.TypeOf((*MockController)(nil).StartValidators), logger) +} + +// GetValidatorsIndices mocks base method +func (m *MockController) GetValidatorsIndices(logger *zap.Logger) []phase0.ValidatorIndex { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetValidatorsIndices", logger) + ret0, _ := ret[0].([]phase0.ValidatorIndex) return ret0 } -// GetOperatorData indicates an expected call of GetOperatorData. -func (mr *MockControllerMockRecorder) GetOperatorData() *gomock.Call { +// GetValidatorsIndices indicates an expected call of GetValidatorsIndices +func (mr *MockControllerMockRecorder) GetValidatorsIndices(logger interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOperatorData", reflect.TypeOf((*MockController)(nil).GetOperatorData)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidatorsIndices", reflect.TypeOf((*MockController)(nil).GetValidatorsIndices), logger) } -// GetValidator mocks base method. +// GetValidator mocks base method func (m *MockController) GetValidator(pubKey string) (*validator.Validator, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetValidator", pubKey) @@ -76,87 +86,92 @@ func (m *MockController) GetValidator(pubKey string) (*validator.Validator, bool return ret0, ret1 } -// GetValidator indicates an expected call of GetValidator. +// GetValidator indicates an expected call of GetValidator func (mr *MockControllerMockRecorder) GetValidator(pubKey interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidator", reflect.TypeOf((*MockController)(nil).GetValidator), pubKey) } -// GetValidatorStats mocks base method. -func (m *MockController) GetValidatorStats(logger *zap.Logger) (uint64, uint64, uint64, error) { +// UpdateValidatorMetaDataLoop mocks base method +func (m *MockController) UpdateValidatorMetaDataLoop(logger *zap.Logger) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetValidatorStats", logger) - ret0, _ := ret[0].(uint64) - ret1, _ := ret[1].(uint64) - ret2, _ := ret[2].(uint64) - ret3, _ := ret[3].(error) - return ret0, ret1, ret2, ret3 + m.ctrl.Call(m, "UpdateValidatorMetaDataLoop", logger) } -// GetValidatorStats indicates an expected call of GetValidatorStats. -func (mr *MockControllerMockRecorder) GetValidatorStats(logger interface{}) *gomock.Call { +// UpdateValidatorMetaDataLoop indicates an expected call of UpdateValidatorMetaDataLoop +func (mr *MockControllerMockRecorder) UpdateValidatorMetaDataLoop(logger interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidatorStats", reflect.TypeOf((*MockController)(nil).GetValidatorStats), logger) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateValidatorMetaDataLoop", reflect.TypeOf((*MockController)(nil).UpdateValidatorMetaDataLoop), logger) } -// GetValidatorsIndices mocks base method. -func (m *MockController) GetValidatorsIndices(logger *zap.Logger) []phase0.ValidatorIndex { +// StartNetworkHandlers mocks base method +func (m *MockController) StartNetworkHandlers(logger *zap.Logger) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetValidatorsIndices", logger) - ret0, _ := ret[0].([]phase0.ValidatorIndex) - return ret0 + m.ctrl.Call(m, "StartNetworkHandlers", logger) } -// GetValidatorsIndices indicates an expected call of GetValidatorsIndices. -func (mr *MockControllerMockRecorder) GetValidatorsIndices(logger interface{}) *gomock.Call { +// StartNetworkHandlers indicates an expected call of StartNetworkHandlers +func (mr *MockControllerMockRecorder) StartNetworkHandlers(logger interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidatorsIndices", reflect.TypeOf((*MockController)(nil).GetValidatorsIndices), logger) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartNetworkHandlers", reflect.TypeOf((*MockController)(nil).StartNetworkHandlers), logger) } -// ListenToEth1Events mocks base method. -func (m *MockController) ListenToEth1Events(logger *zap.Logger, feed *event.Feed) { +// Eth1EventHandler mocks base method +func (m *MockController) Eth1EventHandler(logger *zap.Logger, ongoingSync bool) eth1.SyncEventHandler { m.ctrl.T.Helper() - m.ctrl.Call(m, "ListenToEth1Events", logger, feed) + ret := m.ctrl.Call(m, "Eth1EventHandler", logger, ongoingSync) + ret0, _ := ret[0].(eth1.SyncEventHandler) + return ret0 } -// ListenToEth1Events indicates an expected call of ListenToEth1Events. -func (mr *MockControllerMockRecorder) ListenToEth1Events(logger, feed interface{}) *gomock.Call { +// Eth1EventHandler indicates an expected call of Eth1EventHandler +func (mr *MockControllerMockRecorder) Eth1EventHandler(logger, ongoingSync interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListenToEth1Events", reflect.TypeOf((*MockController)(nil).ListenToEth1Events), logger, feed) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Eth1EventHandler", reflect.TypeOf((*MockController)(nil).Eth1EventHandler), logger, ongoingSync) } -// StartNetworkHandlers mocks base method. -func (m *MockController) StartNetworkHandlers(logger *zap.Logger) { +// GetOperatorShares mocks base method +func (m *MockController) GetOperatorShares(logger *zap.Logger) ([]*types.SSVShare, error) { m.ctrl.T.Helper() - m.ctrl.Call(m, "StartNetworkHandlers", logger) + ret := m.ctrl.Call(m, "GetOperatorShares", logger) + ret0, _ := ret[0].([]*types.SSVShare) + ret1, _ := ret[1].(error) + return ret0, ret1 } -// StartNetworkHandlers indicates an expected call of StartNetworkHandlers. -func (mr *MockControllerMockRecorder) StartNetworkHandlers(logger interface{}) *gomock.Call { +// GetOperatorShares indicates an expected call of GetOperatorShares +func (mr *MockControllerMockRecorder) GetOperatorShares(logger interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartNetworkHandlers", reflect.TypeOf((*MockController)(nil).StartNetworkHandlers), logger) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOperatorShares", reflect.TypeOf((*MockController)(nil).GetOperatorShares), logger) } -// StartValidators mocks base method. -func (m *MockController) StartValidators(logger *zap.Logger) { +// GetValidatorStats mocks base method +func (m *MockController) GetValidatorStats(logger *zap.Logger) (uint64, uint64, uint64, error) { m.ctrl.T.Helper() - m.ctrl.Call(m, "StartValidators", logger) + ret := m.ctrl.Call(m, "GetValidatorStats", logger) + ret0, _ := ret[0].(uint64) + ret1, _ := ret[1].(uint64) + ret2, _ := ret[2].(uint64) + ret3, _ := ret[3].(error) + return ret0, ret1, ret2, ret3 } -// StartValidators indicates an expected call of StartValidators. -func (mr *MockControllerMockRecorder) StartValidators(logger interface{}) *gomock.Call { +// GetValidatorStats indicates an expected call of GetValidatorStats +func (mr *MockControllerMockRecorder) GetValidatorStats(logger interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartValidators", reflect.TypeOf((*MockController)(nil).StartValidators), logger) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidatorStats", reflect.TypeOf((*MockController)(nil).GetValidatorStats), logger) } -// UpdateValidatorMetaDataLoop mocks base method. -func (m *MockController) UpdateValidatorMetaDataLoop(logger *zap.Logger) { +// GetOperatorData mocks base method +func (m *MockController) GetOperatorData() *storage.OperatorData { m.ctrl.T.Helper() - m.ctrl.Call(m, "UpdateValidatorMetaDataLoop", logger) + ret := m.ctrl.Call(m, "GetOperatorData") + ret0, _ := ret[0].(*storage.OperatorData) + return ret0 } -// UpdateValidatorMetaDataLoop indicates an expected call of UpdateValidatorMetaDataLoop. -func (mr *MockControllerMockRecorder) UpdateValidatorMetaDataLoop(logger interface{}) *gomock.Call { +// GetOperatorData indicates an expected call of GetOperatorData +func (mr *MockControllerMockRecorder) GetOperatorData() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateValidatorMetaDataLoop", reflect.TypeOf((*MockController)(nil).UpdateValidatorMetaDataLoop), logger) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOperatorData", reflect.TypeOf((*MockController)(nil).GetOperatorData)) } diff --git a/operator/validator/utils.go b/operator/validator/utils.go index f95dd23c28..a7d084b9a7 100644 --- a/operator/validator/utils.go +++ b/operator/validator/utils.go @@ -15,6 +15,7 @@ import ( "github.com/bloxapp/ssv/eth1" "github.com/bloxapp/ssv/eth1/abiparser" + "github.com/bloxapp/ssv/logging/fields" beaconprotocol "github.com/bloxapp/ssv/protocol/v2/blockchain/beacon" "github.com/bloxapp/ssv/protocol/v2/types" registrystorage "github.com/bloxapp/ssv/registry/storage" @@ -103,15 +104,19 @@ func ShareFromValidatorEvent( return &validatorShare, shareSecret, nil } -func SetShareFeeRecipient(share *types.SSVShare, getRecipientData GetRecipientDataFunc) error { +func SetShareFeeRecipient(logger *zap.Logger, share *types.SSVShare, getRecipientData GetRecipientDataFunc) error { var feeRecipient bellatrix.ExecutionAddress data, found, err := getRecipientData(share.OwnerAddress) if err != nil { return errors.Wrap(err, "could not get recipient data") } if !found { + logger.Debug("setting fee recipient to owner address", + fields.Validator(share.ValidatorPubKey), fields.FeeRecipient(share.OwnerAddress.Bytes())) copy(feeRecipient[:], share.OwnerAddress.Bytes()) } else { + logger.Debug("setting fee recipient to storage data", + fields.Validator(share.ValidatorPubKey), fields.FeeRecipient(data.FeeRecipient[:])) feeRecipient = data.FeeRecipient } share.SetFeeRecipient(feeRecipient) diff --git a/operator/validator/validators_map.go b/operator/validator/validators_map.go index 4d7a6d8efa..02d351f39c 100644 --- a/operator/validator/validators_map.go +++ b/operator/validator/validators_map.go @@ -120,5 +120,7 @@ func printShare(s *types.SSVShare, logger *zap.Logger, msg string) { logger.Debug(msg, fields.PubKey(s.ValidatorPubKey), zap.Uint64("node_id", s.OperatorID), - zap.Strings("committee", committee)) + zap.Strings("committee", committee), + fields.FeeRecipient(s.FeeRecipientAddress[:]), + ) } diff --git a/protocol/v2/blockchain/beacon/client.go b/protocol/v2/blockchain/beacon/client.go index d0926e50c6..346196432e 100644 --- a/protocol/v2/blockchain/beacon/client.go +++ b/protocol/v2/blockchain/beacon/client.go @@ -64,4 +64,5 @@ type Options struct { MinGenesisTime uint64 `yaml:"MinGenesisTime" env:"MinGenesisTime"` BeaconNodeAddr string `yaml:"BeaconNodeAddr" env:"BEACON_NODE_ADDR" env-required:"true"` Graffiti []byte + GasLimit uint64 `yaml:"GasLimit" env:"GAS_LIMIT" env-default:"30000000" env-description:"Gas limit used for beacon node actions"` } diff --git a/protocol/v2/blockchain/beacon/mock_client.go b/protocol/v2/blockchain/beacon/mock_client.go index e8a5bcb458..ed0e8acfdf 100644 --- a/protocol/v2/blockchain/beacon/mock_client.go +++ b/protocol/v2/blockchain/beacon/mock_client.go @@ -21,44 +21,30 @@ import ( zap "go.uber.org/zap" ) -// MockbeaconDuties is a mock of beaconDuties interface. +// MockbeaconDuties is a mock of beaconDuties interface type MockbeaconDuties struct { ctrl *gomock.Controller recorder *MockbeaconDutiesMockRecorder } -// MockbeaconDutiesMockRecorder is the mock recorder for MockbeaconDuties. +// MockbeaconDutiesMockRecorder is the mock recorder for MockbeaconDuties type MockbeaconDutiesMockRecorder struct { mock *MockbeaconDuties } -// NewMockbeaconDuties creates a new mock instance. +// NewMockbeaconDuties creates a new mock instance func NewMockbeaconDuties(ctrl *gomock.Controller) *MockbeaconDuties { mock := &MockbeaconDuties{ctrl: ctrl} mock.recorder = &MockbeaconDutiesMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use. +// EXPECT returns an object that allows the caller to indicate expected use func (m *MockbeaconDuties) EXPECT() *MockbeaconDutiesMockRecorder { return m.recorder } -// Events mocks base method. -func (m *MockbeaconDuties) Events(ctx context.Context, topics []string, handler client.EventHandlerFunc) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Events", ctx, topics, handler) - ret0, _ := ret[0].(error) - return ret0 -} - -// Events indicates an expected call of Events. -func (mr *MockbeaconDutiesMockRecorder) Events(ctx, topics, handler interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Events", reflect.TypeOf((*MockbeaconDuties)(nil).Events), ctx, topics, handler) -} - -// GetDuties mocks base method. +// GetDuties mocks base method func (m *MockbeaconDuties) GetDuties(logger *zap.Logger, epoch phase0.Epoch, validatorIndices []phase0.ValidatorIndex) ([]*types.Duty, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDuties", logger, epoch, validatorIndices) @@ -67,13 +53,13 @@ func (m *MockbeaconDuties) GetDuties(logger *zap.Logger, epoch phase0.Epoch, val return ret0, ret1 } -// GetDuties indicates an expected call of GetDuties. +// GetDuties indicates an expected call of GetDuties func (mr *MockbeaconDutiesMockRecorder) GetDuties(logger, epoch, validatorIndices interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDuties", reflect.TypeOf((*MockbeaconDuties)(nil).GetDuties), logger, epoch, validatorIndices) } -// SyncCommitteeDuties mocks base method. +// SyncCommitteeDuties mocks base method func (m *MockbeaconDuties) SyncCommitteeDuties(epoch phase0.Epoch, indices []phase0.ValidatorIndex) ([]*v1.SyncCommitteeDuty, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SyncCommitteeDuties", epoch, indices) @@ -82,87 +68,101 @@ func (m *MockbeaconDuties) SyncCommitteeDuties(epoch phase0.Epoch, indices []pha return ret0, ret1 } -// SyncCommitteeDuties indicates an expected call of SyncCommitteeDuties. +// SyncCommitteeDuties indicates an expected call of SyncCommitteeDuties func (mr *MockbeaconDutiesMockRecorder) SyncCommitteeDuties(epoch, indices interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SyncCommitteeDuties", reflect.TypeOf((*MockbeaconDuties)(nil).SyncCommitteeDuties), epoch, indices) } -// MockbeaconSubscriber is a mock of beaconSubscriber interface. +// Events mocks base method +func (m *MockbeaconDuties) Events(ctx context.Context, topics []string, handler client.EventHandlerFunc) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Events", ctx, topics, handler) + ret0, _ := ret[0].(error) + return ret0 +} + +// Events indicates an expected call of Events +func (mr *MockbeaconDutiesMockRecorder) Events(ctx, topics, handler interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Events", reflect.TypeOf((*MockbeaconDuties)(nil).Events), ctx, topics, handler) +} + +// MockbeaconSubscriber is a mock of beaconSubscriber interface type MockbeaconSubscriber struct { ctrl *gomock.Controller recorder *MockbeaconSubscriberMockRecorder } -// MockbeaconSubscriberMockRecorder is the mock recorder for MockbeaconSubscriber. +// MockbeaconSubscriberMockRecorder is the mock recorder for MockbeaconSubscriber type MockbeaconSubscriberMockRecorder struct { mock *MockbeaconSubscriber } -// NewMockbeaconSubscriber creates a new mock instance. +// NewMockbeaconSubscriber creates a new mock instance func NewMockbeaconSubscriber(ctrl *gomock.Controller) *MockbeaconSubscriber { mock := &MockbeaconSubscriber{ctrl: ctrl} mock.recorder = &MockbeaconSubscriberMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use. +// EXPECT returns an object that allows the caller to indicate expected use func (m *MockbeaconSubscriber) EXPECT() *MockbeaconSubscriberMockRecorder { return m.recorder } -// SubmitSyncCommitteeSubscriptions mocks base method. -func (m *MockbeaconSubscriber) SubmitSyncCommitteeSubscriptions(subscription []*v1.SyncCommitteeSubscription) error { +// SubscribeToCommitteeSubnet mocks base method +func (m *MockbeaconSubscriber) SubscribeToCommitteeSubnet(subscription []*v1.BeaconCommitteeSubscription) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubmitSyncCommitteeSubscriptions", subscription) + ret := m.ctrl.Call(m, "SubscribeToCommitteeSubnet", subscription) ret0, _ := ret[0].(error) return ret0 } -// SubmitSyncCommitteeSubscriptions indicates an expected call of SubmitSyncCommitteeSubscriptions. -func (mr *MockbeaconSubscriberMockRecorder) SubmitSyncCommitteeSubscriptions(subscription interface{}) *gomock.Call { +// SubscribeToCommitteeSubnet indicates an expected call of SubscribeToCommitteeSubnet +func (mr *MockbeaconSubscriberMockRecorder) SubscribeToCommitteeSubnet(subscription interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitSyncCommitteeSubscriptions", reflect.TypeOf((*MockbeaconSubscriber)(nil).SubmitSyncCommitteeSubscriptions), subscription) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubscribeToCommitteeSubnet", reflect.TypeOf((*MockbeaconSubscriber)(nil).SubscribeToCommitteeSubnet), subscription) } -// SubscribeToCommitteeSubnet mocks base method. -func (m *MockbeaconSubscriber) SubscribeToCommitteeSubnet(subscription []*v1.BeaconCommitteeSubscription) error { +// SubmitSyncCommitteeSubscriptions mocks base method +func (m *MockbeaconSubscriber) SubmitSyncCommitteeSubscriptions(subscription []*v1.SyncCommitteeSubscription) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubscribeToCommitteeSubnet", subscription) + ret := m.ctrl.Call(m, "SubmitSyncCommitteeSubscriptions", subscription) ret0, _ := ret[0].(error) return ret0 } -// SubscribeToCommitteeSubnet indicates an expected call of SubscribeToCommitteeSubnet. -func (mr *MockbeaconSubscriberMockRecorder) SubscribeToCommitteeSubnet(subscription interface{}) *gomock.Call { +// SubmitSyncCommitteeSubscriptions indicates an expected call of SubmitSyncCommitteeSubscriptions +func (mr *MockbeaconSubscriberMockRecorder) SubmitSyncCommitteeSubscriptions(subscription interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubscribeToCommitteeSubnet", reflect.TypeOf((*MockbeaconSubscriber)(nil).SubscribeToCommitteeSubnet), subscription) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitSyncCommitteeSubscriptions", reflect.TypeOf((*MockbeaconSubscriber)(nil).SubmitSyncCommitteeSubscriptions), subscription) } -// MockbeaconValidator is a mock of beaconValidator interface. +// MockbeaconValidator is a mock of beaconValidator interface type MockbeaconValidator struct { ctrl *gomock.Controller recorder *MockbeaconValidatorMockRecorder } -// MockbeaconValidatorMockRecorder is the mock recorder for MockbeaconValidator. +// MockbeaconValidatorMockRecorder is the mock recorder for MockbeaconValidator type MockbeaconValidatorMockRecorder struct { mock *MockbeaconValidator } -// NewMockbeaconValidator creates a new mock instance. +// NewMockbeaconValidator creates a new mock instance func NewMockbeaconValidator(ctrl *gomock.Controller) *MockbeaconValidator { mock := &MockbeaconValidator{ctrl: ctrl} mock.recorder = &MockbeaconValidatorMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use. +// EXPECT returns an object that allows the caller to indicate expected use func (m *MockbeaconValidator) EXPECT() *MockbeaconValidatorMockRecorder { return m.recorder } -// GetValidatorData mocks base method. +// GetValidatorData mocks base method func (m *MockbeaconValidator) GetValidatorData(validatorPubKeys []phase0.BLSPubKey) (map[phase0.ValidatorIndex]*v1.Validator, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetValidatorData", validatorPubKeys) @@ -171,36 +171,36 @@ func (m *MockbeaconValidator) GetValidatorData(validatorPubKeys []phase0.BLSPubK return ret0, ret1 } -// GetValidatorData indicates an expected call of GetValidatorData. +// GetValidatorData indicates an expected call of GetValidatorData func (mr *MockbeaconValidatorMockRecorder) GetValidatorData(validatorPubKeys interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidatorData", reflect.TypeOf((*MockbeaconValidator)(nil).GetValidatorData), validatorPubKeys) } -// Mockproposer is a mock of proposer interface. +// Mockproposer is a mock of proposer interface type Mockproposer struct { ctrl *gomock.Controller recorder *MockproposerMockRecorder } -// MockproposerMockRecorder is the mock recorder for Mockproposer. +// MockproposerMockRecorder is the mock recorder for Mockproposer type MockproposerMockRecorder struct { mock *Mockproposer } -// NewMockproposer creates a new mock instance. +// NewMockproposer creates a new mock instance func NewMockproposer(ctrl *gomock.Controller) *Mockproposer { mock := &Mockproposer{ctrl: ctrl} mock.recorder = &MockproposerMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use. +// EXPECT returns an object that allows the caller to indicate expected use func (m *Mockproposer) EXPECT() *MockproposerMockRecorder { return m.recorder } -// SubmitProposalPreparation mocks base method. +// SubmitProposalPreparation mocks base method func (m *Mockproposer) SubmitProposalPreparation(feeRecipients map[phase0.ValidatorIndex]bellatrix.ExecutionAddress) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SubmitProposalPreparation", feeRecipients) @@ -208,36 +208,36 @@ func (m *Mockproposer) SubmitProposalPreparation(feeRecipients map[phase0.Valida return ret0 } -// SubmitProposalPreparation indicates an expected call of SubmitProposalPreparation. +// SubmitProposalPreparation indicates an expected call of SubmitProposalPreparation func (mr *MockproposerMockRecorder) SubmitProposalPreparation(feeRecipients interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitProposalPreparation", reflect.TypeOf((*Mockproposer)(nil).SubmitProposalPreparation), feeRecipients) } -// Mocksigner is a mock of signer interface. +// Mocksigner is a mock of signer interface type Mocksigner struct { ctrl *gomock.Controller recorder *MocksignerMockRecorder } -// MocksignerMockRecorder is the mock recorder for Mocksigner. +// MocksignerMockRecorder is the mock recorder for Mocksigner type MocksignerMockRecorder struct { mock *Mocksigner } -// NewMocksigner creates a new mock instance. +// NewMocksigner creates a new mock instance func NewMocksigner(ctrl *gomock.Controller) *Mocksigner { mock := &Mocksigner{ctrl: ctrl} mock.recorder = &MocksignerMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use. +// EXPECT returns an object that allows the caller to indicate expected use func (m *Mocksigner) EXPECT() *MocksignerMockRecorder { return m.recorder } -// ComputeSigningRoot mocks base method. +// ComputeSigningRoot mocks base method func (m *Mocksigner) ComputeSigningRoot(object interface{}, domain phase0.Domain) ([32]byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ComputeSigningRoot", object, domain) @@ -246,173 +246,184 @@ func (m *Mocksigner) ComputeSigningRoot(object interface{}, domain phase0.Domain return ret0, ret1 } -// ComputeSigningRoot indicates an expected call of ComputeSigningRoot. +// ComputeSigningRoot indicates an expected call of ComputeSigningRoot func (mr *MocksignerMockRecorder) ComputeSigningRoot(object, domain interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ComputeSigningRoot", reflect.TypeOf((*Mocksigner)(nil).ComputeSigningRoot), object, domain) } -// MockBeacon is a mock of Beacon interface. +// MockBeacon is a mock of Beacon interface type MockBeacon struct { ctrl *gomock.Controller recorder *MockBeaconMockRecorder } -// MockBeaconMockRecorder is the mock recorder for MockBeacon. +// MockBeaconMockRecorder is the mock recorder for MockBeacon type MockBeaconMockRecorder struct { mock *MockBeacon } -// NewMockBeacon creates a new mock instance. +// NewMockBeacon creates a new mock instance func NewMockBeacon(ctrl *gomock.Controller) *MockBeacon { mock := &MockBeacon{ctrl: ctrl} mock.recorder = &MockBeaconMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use. +// EXPECT returns an object that allows the caller to indicate expected use func (m *MockBeacon) EXPECT() *MockBeaconMockRecorder { return m.recorder } -// ComputeSigningRoot mocks base method. -func (m *MockBeacon) ComputeSigningRoot(object interface{}, domain phase0.Domain) ([32]byte, error) { +// GetBeaconNetwork mocks base method +func (m *MockBeacon) GetBeaconNetwork() types.BeaconNetwork { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ComputeSigningRoot", object, domain) - ret0, _ := ret[0].([32]byte) - ret1, _ := ret[1].(error) - return ret0, ret1 + ret := m.ctrl.Call(m, "GetBeaconNetwork") + ret0, _ := ret[0].(types.BeaconNetwork) + return ret0 } -// ComputeSigningRoot indicates an expected call of ComputeSigningRoot. -func (mr *MockBeaconMockRecorder) ComputeSigningRoot(object, domain interface{}) *gomock.Call { +// GetBeaconNetwork indicates an expected call of GetBeaconNetwork +func (mr *MockBeaconMockRecorder) GetBeaconNetwork() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ComputeSigningRoot", reflect.TypeOf((*MockBeacon)(nil).ComputeSigningRoot), object, domain) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBeaconNetwork", reflect.TypeOf((*MockBeacon)(nil).GetBeaconNetwork)) } -// DomainData mocks base method. -func (m *MockBeacon) DomainData(epoch phase0.Epoch, domain phase0.DomainType) (phase0.Domain, error) { +// GetAttestationData mocks base method +func (m *MockBeacon) GetAttestationData(slot phase0.Slot, committeeIndex phase0.CommitteeIndex) (ssz.Marshaler, spec.DataVersion, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DomainData", epoch, domain) - ret0, _ := ret[0].(phase0.Domain) - ret1, _ := ret[1].(error) - return ret0, ret1 + ret := m.ctrl.Call(m, "GetAttestationData", slot, committeeIndex) + ret0, _ := ret[0].(ssz.Marshaler) + ret1, _ := ret[1].(spec.DataVersion) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 } -// DomainData indicates an expected call of DomainData. -func (mr *MockBeaconMockRecorder) DomainData(epoch, domain interface{}) *gomock.Call { +// GetAttestationData indicates an expected call of GetAttestationData +func (mr *MockBeaconMockRecorder) GetAttestationData(slot, committeeIndex interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DomainData", reflect.TypeOf((*MockBeacon)(nil).DomainData), epoch, domain) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAttestationData", reflect.TypeOf((*MockBeacon)(nil).GetAttestationData), slot, committeeIndex) } -// Events mocks base method. -func (m *MockBeacon) Events(ctx context.Context, topics []string, handler client.EventHandlerFunc) error { +// SubmitAttestation mocks base method +func (m *MockBeacon) SubmitAttestation(attestation *phase0.Attestation) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Events", ctx, topics, handler) + ret := m.ctrl.Call(m, "SubmitAttestation", attestation) ret0, _ := ret[0].(error) return ret0 } -// Events indicates an expected call of Events. -func (mr *MockBeaconMockRecorder) Events(ctx, topics, handler interface{}) *gomock.Call { +// SubmitAttestation indicates an expected call of SubmitAttestation +func (mr *MockBeaconMockRecorder) SubmitAttestation(attestation interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Events", reflect.TypeOf((*MockBeacon)(nil).Events), ctx, topics, handler) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitAttestation", reflect.TypeOf((*MockBeacon)(nil).SubmitAttestation), attestation) } -// GetAttestationData mocks base method. -func (m *MockBeacon) GetAttestationData(slot phase0.Slot, committeeIndex phase0.CommitteeIndex) (ssz.Marshaler, spec.DataVersion, error) { +// SubmitValidatorRegistration mocks base method +func (m *MockBeacon) SubmitValidatorRegistration(pubkey []byte, feeRecipient bellatrix.ExecutionAddress, sig phase0.BLSSignature) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAttestationData", slot, committeeIndex) + ret := m.ctrl.Call(m, "SubmitValidatorRegistration", pubkey, feeRecipient, sig) + ret0, _ := ret[0].(error) + return ret0 +} + +// SubmitValidatorRegistration indicates an expected call of SubmitValidatorRegistration +func (mr *MockBeaconMockRecorder) SubmitValidatorRegistration(pubkey, feeRecipient, sig interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitValidatorRegistration", reflect.TypeOf((*MockBeacon)(nil).SubmitValidatorRegistration), pubkey, feeRecipient, sig) +} + +// GetBeaconBlock mocks base method +func (m *MockBeacon) GetBeaconBlock(slot phase0.Slot, graffiti, randao []byte) (ssz.Marshaler, spec.DataVersion, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetBeaconBlock", slot, graffiti, randao) ret0, _ := ret[0].(ssz.Marshaler) ret1, _ := ret[1].(spec.DataVersion) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } -// GetAttestationData indicates an expected call of GetAttestationData. -func (mr *MockBeaconMockRecorder) GetAttestationData(slot, committeeIndex interface{}) *gomock.Call { +// GetBeaconBlock indicates an expected call of GetBeaconBlock +func (mr *MockBeaconMockRecorder) GetBeaconBlock(slot, graffiti, randao interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAttestationData", reflect.TypeOf((*MockBeacon)(nil).GetAttestationData), slot, committeeIndex) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBeaconBlock", reflect.TypeOf((*MockBeacon)(nil).GetBeaconBlock), slot, graffiti, randao) } -// GetBeaconBlock mocks base method. -func (m *MockBeacon) GetBeaconBlock(slot phase0.Slot, committeeIndex phase0.CommitteeIndex, graffiti, randao []byte) (ssz.Marshaler, spec.DataVersion, error) { +// GetBlindedBeaconBlock mocks base method +func (m *MockBeacon) GetBlindedBeaconBlock(slot phase0.Slot, graffiti, randao []byte) (ssz.Marshaler, spec.DataVersion, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetBeaconBlock", slot, committeeIndex, graffiti, randao) + ret := m.ctrl.Call(m, "GetBlindedBeaconBlock", slot, graffiti, randao) ret0, _ := ret[0].(ssz.Marshaler) ret1, _ := ret[1].(spec.DataVersion) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } -// GetBeaconBlock indicates an expected call of GetBeaconBlock. -func (mr *MockBeaconMockRecorder) GetBeaconBlock(slot, committeeIndex, graffiti, randao interface{}) *gomock.Call { +// GetBlindedBeaconBlock indicates an expected call of GetBlindedBeaconBlock +func (mr *MockBeaconMockRecorder) GetBlindedBeaconBlock(slot, graffiti, randao interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBeaconBlock", reflect.TypeOf((*MockBeacon)(nil).GetBeaconBlock), slot, committeeIndex, graffiti, randao) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlindedBeaconBlock", reflect.TypeOf((*MockBeacon)(nil).GetBlindedBeaconBlock), slot, graffiti, randao) } -// GetBeaconNetwork mocks base method. -func (m *MockBeacon) GetBeaconNetwork() types.BeaconNetwork { +// SubmitBeaconBlock mocks base method +func (m *MockBeacon) SubmitBeaconBlock(block *spec.VersionedBeaconBlock, sig phase0.BLSSignature) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetBeaconNetwork") - ret0, _ := ret[0].(types.BeaconNetwork) + ret := m.ctrl.Call(m, "SubmitBeaconBlock", block, sig) + ret0, _ := ret[0].(error) return ret0 } -// GetBeaconNetwork indicates an expected call of GetBeaconNetwork. -func (mr *MockBeaconMockRecorder) GetBeaconNetwork() *gomock.Call { +// SubmitBeaconBlock indicates an expected call of SubmitBeaconBlock +func (mr *MockBeaconMockRecorder) SubmitBeaconBlock(block, sig interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBeaconNetwork", reflect.TypeOf((*MockBeacon)(nil).GetBeaconNetwork)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitBeaconBlock", reflect.TypeOf((*MockBeacon)(nil).SubmitBeaconBlock), block, sig) } -// GetBlindedBeaconBlock mocks base method. -func (m *MockBeacon) GetBlindedBeaconBlock(slot phase0.Slot, committeeIndex phase0.CommitteeIndex, graffiti, randao []byte) (ssz.Marshaler, spec.DataVersion, error) { +// SubmitBlindedBeaconBlock mocks base method +func (m *MockBeacon) SubmitBlindedBeaconBlock(block *api.VersionedBlindedBeaconBlock, sig phase0.BLSSignature) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetBlindedBeaconBlock", slot, committeeIndex, graffiti, randao) - ret0, _ := ret[0].(ssz.Marshaler) - ret1, _ := ret[1].(spec.DataVersion) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 + ret := m.ctrl.Call(m, "SubmitBlindedBeaconBlock", block, sig) + ret0, _ := ret[0].(error) + return ret0 } -// GetBlindedBeaconBlock indicates an expected call of GetBlindedBeaconBlock. -func (mr *MockBeaconMockRecorder) GetBlindedBeaconBlock(slot, committeeIndex, graffiti, randao interface{}) *gomock.Call { +// SubmitBlindedBeaconBlock indicates an expected call of SubmitBlindedBeaconBlock +func (mr *MockBeaconMockRecorder) SubmitBlindedBeaconBlock(block, sig interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlindedBeaconBlock", reflect.TypeOf((*MockBeacon)(nil).GetBlindedBeaconBlock), slot, committeeIndex, graffiti, randao) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitBlindedBeaconBlock", reflect.TypeOf((*MockBeacon)(nil).SubmitBlindedBeaconBlock), block, sig) } -// GetDuties mocks base method. -func (m *MockBeacon) GetDuties(logger *zap.Logger, epoch phase0.Epoch, validatorIndices []phase0.ValidatorIndex) ([]*types.Duty, error) { +// SubmitAggregateSelectionProof mocks base method +func (m *MockBeacon) SubmitAggregateSelectionProof(slot phase0.Slot, committeeIndex phase0.CommitteeIndex, committeeLength uint64, index phase0.ValidatorIndex, slotSig []byte) (ssz.Marshaler, spec.DataVersion, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetDuties", logger, epoch, validatorIndices) - ret0, _ := ret[0].([]*types.Duty) - ret1, _ := ret[1].(error) - return ret0, ret1 + ret := m.ctrl.Call(m, "SubmitAggregateSelectionProof", slot, committeeIndex, committeeLength, index, slotSig) + ret0, _ := ret[0].(ssz.Marshaler) + ret1, _ := ret[1].(spec.DataVersion) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 } -// GetDuties indicates an expected call of GetDuties. -func (mr *MockBeaconMockRecorder) GetDuties(logger, epoch, validatorIndices interface{}) *gomock.Call { +// SubmitAggregateSelectionProof indicates an expected call of SubmitAggregateSelectionProof +func (mr *MockBeaconMockRecorder) SubmitAggregateSelectionProof(slot, committeeIndex, committeeLength, index, slotSig interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDuties", reflect.TypeOf((*MockBeacon)(nil).GetDuties), logger, epoch, validatorIndices) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitAggregateSelectionProof", reflect.TypeOf((*MockBeacon)(nil).SubmitAggregateSelectionProof), slot, committeeIndex, committeeLength, index, slotSig) } -// GetSyncCommitteeContribution mocks base method. -func (m *MockBeacon) GetSyncCommitteeContribution(slot phase0.Slot, selectionProofs []phase0.BLSSignature, subnetIDs []uint64) (ssz.Marshaler, spec.DataVersion, error) { +// SubmitSignedAggregateSelectionProof mocks base method +func (m *MockBeacon) SubmitSignedAggregateSelectionProof(msg *phase0.SignedAggregateAndProof) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSyncCommitteeContribution", slot, selectionProofs, subnetIDs) - ret0, _ := ret[0].(ssz.Marshaler) - ret1, _ := ret[1].(spec.DataVersion) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 + ret := m.ctrl.Call(m, "SubmitSignedAggregateSelectionProof", msg) + ret0, _ := ret[0].(error) + return ret0 } -// GetSyncCommitteeContribution indicates an expected call of GetSyncCommitteeContribution. -func (mr *MockBeaconMockRecorder) GetSyncCommitteeContribution(slot, selectionProofs, subnetIDs interface{}) *gomock.Call { +// SubmitSignedAggregateSelectionProof indicates an expected call of SubmitSignedAggregateSelectionProof +func (mr *MockBeaconMockRecorder) SubmitSignedAggregateSelectionProof(msg interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSyncCommitteeContribution", reflect.TypeOf((*MockBeacon)(nil).GetSyncCommitteeContribution), slot, selectionProofs, subnetIDs) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitSignedAggregateSelectionProof", reflect.TypeOf((*MockBeacon)(nil).SubmitSignedAggregateSelectionProof), msg) } -// GetSyncMessageBlockRoot mocks base method. +// GetSyncMessageBlockRoot mocks base method func (m *MockBeacon) GetSyncMessageBlockRoot(slot phase0.Slot) (phase0.Root, spec.DataVersion, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetSyncMessageBlockRoot", slot) @@ -422,28 +433,27 @@ func (m *MockBeacon) GetSyncMessageBlockRoot(slot phase0.Slot) (phase0.Root, spe return ret0, ret1, ret2 } -// GetSyncMessageBlockRoot indicates an expected call of GetSyncMessageBlockRoot. +// GetSyncMessageBlockRoot indicates an expected call of GetSyncMessageBlockRoot func (mr *MockBeaconMockRecorder) GetSyncMessageBlockRoot(slot interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSyncMessageBlockRoot", reflect.TypeOf((*MockBeacon)(nil).GetSyncMessageBlockRoot), slot) } -// GetValidatorData mocks base method. -func (m *MockBeacon) GetValidatorData(validatorPubKeys []phase0.BLSPubKey) (map[phase0.ValidatorIndex]*v1.Validator, error) { +// SubmitSyncMessage mocks base method +func (m *MockBeacon) SubmitSyncMessage(msg *altair.SyncCommitteeMessage) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetValidatorData", validatorPubKeys) - ret0, _ := ret[0].(map[phase0.ValidatorIndex]*v1.Validator) - ret1, _ := ret[1].(error) - return ret0, ret1 + ret := m.ctrl.Call(m, "SubmitSyncMessage", msg) + ret0, _ := ret[0].(error) + return ret0 } -// GetValidatorData indicates an expected call of GetValidatorData. -func (mr *MockBeaconMockRecorder) GetValidatorData(validatorPubKeys interface{}) *gomock.Call { +// SubmitSyncMessage indicates an expected call of SubmitSyncMessage +func (mr *MockBeaconMockRecorder) SubmitSyncMessage(msg interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidatorData", reflect.TypeOf((*MockBeacon)(nil).GetValidatorData), validatorPubKeys) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitSyncMessage", reflect.TypeOf((*MockBeacon)(nil).SubmitSyncMessage), msg) } -// IsSyncCommitteeAggregator mocks base method. +// IsSyncCommitteeAggregator mocks base method func (m *MockBeacon) IsSyncCommitteeAggregator(proof []byte) (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsSyncCommitteeAggregator", proof) @@ -452,113 +462,131 @@ func (m *MockBeacon) IsSyncCommitteeAggregator(proof []byte) (bool, error) { return ret0, ret1 } -// IsSyncCommitteeAggregator indicates an expected call of IsSyncCommitteeAggregator. +// IsSyncCommitteeAggregator indicates an expected call of IsSyncCommitteeAggregator func (mr *MockBeaconMockRecorder) IsSyncCommitteeAggregator(proof interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsSyncCommitteeAggregator", reflect.TypeOf((*MockBeacon)(nil).IsSyncCommitteeAggregator), proof) } -// SubmitAggregateSelectionProof mocks base method. -func (m *MockBeacon) SubmitAggregateSelectionProof(slot phase0.Slot, committeeIndex phase0.CommitteeIndex, committeeLength uint64, index phase0.ValidatorIndex, slotSig []byte) (ssz.Marshaler, spec.DataVersion, error) { +// SyncCommitteeSubnetID mocks base method +func (m *MockBeacon) SyncCommitteeSubnetID(index phase0.CommitteeIndex) (uint64, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubmitAggregateSelectionProof", slot, committeeIndex, committeeLength, index, slotSig) + ret := m.ctrl.Call(m, "SyncCommitteeSubnetID", index) + ret0, _ := ret[0].(uint64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SyncCommitteeSubnetID indicates an expected call of SyncCommitteeSubnetID +func (mr *MockBeaconMockRecorder) SyncCommitteeSubnetID(index interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SyncCommitteeSubnetID", reflect.TypeOf((*MockBeacon)(nil).SyncCommitteeSubnetID), index) +} + +// GetSyncCommitteeContribution mocks base method +func (m *MockBeacon) GetSyncCommitteeContribution(slot phase0.Slot, selectionProofs []phase0.BLSSignature, subnetIDs []uint64) (ssz.Marshaler, spec.DataVersion, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSyncCommitteeContribution", slot, selectionProofs, subnetIDs) ret0, _ := ret[0].(ssz.Marshaler) ret1, _ := ret[1].(spec.DataVersion) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } -// SubmitAggregateSelectionProof indicates an expected call of SubmitAggregateSelectionProof. -func (mr *MockBeaconMockRecorder) SubmitAggregateSelectionProof(slot, committeeIndex, committeeLength, index, slotSig interface{}) *gomock.Call { +// GetSyncCommitteeContribution indicates an expected call of GetSyncCommitteeContribution +func (mr *MockBeaconMockRecorder) GetSyncCommitteeContribution(slot, selectionProofs, subnetIDs interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitAggregateSelectionProof", reflect.TypeOf((*MockBeacon)(nil).SubmitAggregateSelectionProof), slot, committeeIndex, committeeLength, index, slotSig) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSyncCommitteeContribution", reflect.TypeOf((*MockBeacon)(nil).GetSyncCommitteeContribution), slot, selectionProofs, subnetIDs) } -// SubmitAttestation mocks base method. -func (m *MockBeacon) SubmitAttestation(attestation *phase0.Attestation) error { +// SubmitSignedContributionAndProof mocks base method +func (m *MockBeacon) SubmitSignedContributionAndProof(contribution *altair.SignedContributionAndProof) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubmitAttestation", attestation) + ret := m.ctrl.Call(m, "SubmitSignedContributionAndProof", contribution) ret0, _ := ret[0].(error) return ret0 } -// SubmitAttestation indicates an expected call of SubmitAttestation. -func (mr *MockBeaconMockRecorder) SubmitAttestation(attestation interface{}) *gomock.Call { +// SubmitSignedContributionAndProof indicates an expected call of SubmitSignedContributionAndProof +func (mr *MockBeaconMockRecorder) SubmitSignedContributionAndProof(contribution interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitAttestation", reflect.TypeOf((*MockBeacon)(nil).SubmitAttestation), attestation) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitSignedContributionAndProof", reflect.TypeOf((*MockBeacon)(nil).SubmitSignedContributionAndProof), contribution) } -// SubmitBeaconBlock mocks base method. -func (m *MockBeacon) SubmitBeaconBlock(block *spec.VersionedBeaconBlock, sig phase0.BLSSignature) error { +// DomainData mocks base method +func (m *MockBeacon) DomainData(epoch phase0.Epoch, domain phase0.DomainType) (phase0.Domain, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubmitBeaconBlock", block, sig) - ret0, _ := ret[0].(error) - return ret0 + ret := m.ctrl.Call(m, "DomainData", epoch, domain) + ret0, _ := ret[0].(phase0.Domain) + ret1, _ := ret[1].(error) + return ret0, ret1 } -// SubmitBeaconBlock indicates an expected call of SubmitBeaconBlock. -func (mr *MockBeaconMockRecorder) SubmitBeaconBlock(block, sig interface{}) *gomock.Call { +// DomainData indicates an expected call of DomainData +func (mr *MockBeaconMockRecorder) DomainData(epoch, domain interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitBeaconBlock", reflect.TypeOf((*MockBeacon)(nil).SubmitBeaconBlock), block, sig) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DomainData", reflect.TypeOf((*MockBeacon)(nil).DomainData), epoch, domain) } -// SubmitBlindedBeaconBlock mocks base method. -func (m *MockBeacon) SubmitBlindedBeaconBlock(block *api.VersionedBlindedBeaconBlock, sig phase0.BLSSignature) error { +// GetDuties mocks base method +func (m *MockBeacon) GetDuties(logger *zap.Logger, epoch phase0.Epoch, validatorIndices []phase0.ValidatorIndex) ([]*types.Duty, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubmitBlindedBeaconBlock", block, sig) - ret0, _ := ret[0].(error) - return ret0 + ret := m.ctrl.Call(m, "GetDuties", logger, epoch, validatorIndices) + ret0, _ := ret[0].([]*types.Duty) + ret1, _ := ret[1].(error) + return ret0, ret1 } -// SubmitBlindedBeaconBlock indicates an expected call of SubmitBlindedBeaconBlock. -func (mr *MockBeaconMockRecorder) SubmitBlindedBeaconBlock(block, sig interface{}) *gomock.Call { +// GetDuties indicates an expected call of GetDuties +func (mr *MockBeaconMockRecorder) GetDuties(logger, epoch, validatorIndices interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitBlindedBeaconBlock", reflect.TypeOf((*MockBeacon)(nil).SubmitBlindedBeaconBlock), block, sig) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDuties", reflect.TypeOf((*MockBeacon)(nil).GetDuties), logger, epoch, validatorIndices) } -// SubmitProposalPreparation mocks base method. -func (m *MockBeacon) SubmitProposalPreparation(feeRecipients map[phase0.ValidatorIndex]bellatrix.ExecutionAddress) error { +// SyncCommitteeDuties mocks base method +func (m *MockBeacon) SyncCommitteeDuties(epoch phase0.Epoch, indices []phase0.ValidatorIndex) ([]*v1.SyncCommitteeDuty, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubmitProposalPreparation", feeRecipients) - ret0, _ := ret[0].(error) - return ret0 + ret := m.ctrl.Call(m, "SyncCommitteeDuties", epoch, indices) + ret0, _ := ret[0].([]*v1.SyncCommitteeDuty) + ret1, _ := ret[1].(error) + return ret0, ret1 } -// SubmitProposalPreparation indicates an expected call of SubmitProposalPreparation. -func (mr *MockBeaconMockRecorder) SubmitProposalPreparation(feeRecipients interface{}) *gomock.Call { +// SyncCommitteeDuties indicates an expected call of SyncCommitteeDuties +func (mr *MockBeaconMockRecorder) SyncCommitteeDuties(epoch, indices interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitProposalPreparation", reflect.TypeOf((*MockBeacon)(nil).SubmitProposalPreparation), feeRecipients) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SyncCommitteeDuties", reflect.TypeOf((*MockBeacon)(nil).SyncCommitteeDuties), epoch, indices) } -// SubmitSignedAggregateSelectionProof mocks base method. -func (m *MockBeacon) SubmitSignedAggregateSelectionProof(msg *phase0.SignedAggregateAndProof) error { +// Events mocks base method +func (m *MockBeacon) Events(ctx context.Context, topics []string, handler client.EventHandlerFunc) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubmitSignedAggregateSelectionProof", msg) + ret := m.ctrl.Call(m, "Events", ctx, topics, handler) ret0, _ := ret[0].(error) return ret0 } -// SubmitSignedAggregateSelectionProof indicates an expected call of SubmitSignedAggregateSelectionProof. -func (mr *MockBeaconMockRecorder) SubmitSignedAggregateSelectionProof(msg interface{}) *gomock.Call { +// Events indicates an expected call of Events +func (mr *MockBeaconMockRecorder) Events(ctx, topics, handler interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitSignedAggregateSelectionProof", reflect.TypeOf((*MockBeacon)(nil).SubmitSignedAggregateSelectionProof), msg) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Events", reflect.TypeOf((*MockBeacon)(nil).Events), ctx, topics, handler) } -// SubmitSignedContributionAndProof mocks base method. -func (m *MockBeacon) SubmitSignedContributionAndProof(contribution *altair.SignedContributionAndProof) error { +// SubscribeToCommitteeSubnet mocks base method +func (m *MockBeacon) SubscribeToCommitteeSubnet(subscription []*v1.BeaconCommitteeSubscription) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubmitSignedContributionAndProof", contribution) + ret := m.ctrl.Call(m, "SubscribeToCommitteeSubnet", subscription) ret0, _ := ret[0].(error) return ret0 } -// SubmitSignedContributionAndProof indicates an expected call of SubmitSignedContributionAndProof. -func (mr *MockBeaconMockRecorder) SubmitSignedContributionAndProof(contribution interface{}) *gomock.Call { +// SubscribeToCommitteeSubnet indicates an expected call of SubscribeToCommitteeSubnet +func (mr *MockBeaconMockRecorder) SubscribeToCommitteeSubnet(subscription interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitSignedContributionAndProof", reflect.TypeOf((*MockBeacon)(nil).SubmitSignedContributionAndProof), contribution) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubscribeToCommitteeSubnet", reflect.TypeOf((*MockBeacon)(nil).SubscribeToCommitteeSubnet), subscription) } -// SubmitSyncCommitteeSubscriptions mocks base method. +// SubmitSyncCommitteeSubscriptions mocks base method func (m *MockBeacon) SubmitSyncCommitteeSubscriptions(subscription []*v1.SyncCommitteeSubscription) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SubmitSyncCommitteeSubscriptions", subscription) @@ -566,66 +594,52 @@ func (m *MockBeacon) SubmitSyncCommitteeSubscriptions(subscription []*v1.SyncCom return ret0 } -// SubmitSyncCommitteeSubscriptions indicates an expected call of SubmitSyncCommitteeSubscriptions. +// SubmitSyncCommitteeSubscriptions indicates an expected call of SubmitSyncCommitteeSubscriptions func (mr *MockBeaconMockRecorder) SubmitSyncCommitteeSubscriptions(subscription interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitSyncCommitteeSubscriptions", reflect.TypeOf((*MockBeacon)(nil).SubmitSyncCommitteeSubscriptions), subscription) } -// SubmitSyncMessage mocks base method. -func (m *MockBeacon) SubmitSyncMessage(msg *altair.SyncCommitteeMessage) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubmitSyncMessage", msg) - ret0, _ := ret[0].(error) - return ret0 -} - -// SubmitSyncMessage indicates an expected call of SubmitSyncMessage. -func (mr *MockBeaconMockRecorder) SubmitSyncMessage(msg interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitSyncMessage", reflect.TypeOf((*MockBeacon)(nil).SubmitSyncMessage), msg) -} - -// SubscribeToCommitteeSubnet mocks base method. -func (m *MockBeacon) SubscribeToCommitteeSubnet(subscription []*v1.BeaconCommitteeSubscription) error { +// GetValidatorData mocks base method +func (m *MockBeacon) GetValidatorData(validatorPubKeys []phase0.BLSPubKey) (map[phase0.ValidatorIndex]*v1.Validator, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubscribeToCommitteeSubnet", subscription) - ret0, _ := ret[0].(error) - return ret0 + ret := m.ctrl.Call(m, "GetValidatorData", validatorPubKeys) + ret0, _ := ret[0].(map[phase0.ValidatorIndex]*v1.Validator) + ret1, _ := ret[1].(error) + return ret0, ret1 } -// SubscribeToCommitteeSubnet indicates an expected call of SubscribeToCommitteeSubnet. -func (mr *MockBeaconMockRecorder) SubscribeToCommitteeSubnet(subscription interface{}) *gomock.Call { +// GetValidatorData indicates an expected call of GetValidatorData +func (mr *MockBeaconMockRecorder) GetValidatorData(validatorPubKeys interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubscribeToCommitteeSubnet", reflect.TypeOf((*MockBeacon)(nil).SubscribeToCommitteeSubnet), subscription) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetValidatorData", reflect.TypeOf((*MockBeacon)(nil).GetValidatorData), validatorPubKeys) } -// SyncCommitteeDuties mocks base method. -func (m *MockBeacon) SyncCommitteeDuties(epoch phase0.Epoch, indices []phase0.ValidatorIndex) ([]*v1.SyncCommitteeDuty, error) { +// ComputeSigningRoot mocks base method +func (m *MockBeacon) ComputeSigningRoot(object interface{}, domain phase0.Domain) ([32]byte, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SyncCommitteeDuties", epoch, indices) - ret0, _ := ret[0].([]*v1.SyncCommitteeDuty) + ret := m.ctrl.Call(m, "ComputeSigningRoot", object, domain) + ret0, _ := ret[0].([32]byte) ret1, _ := ret[1].(error) return ret0, ret1 } -// SyncCommitteeDuties indicates an expected call of SyncCommitteeDuties. -func (mr *MockBeaconMockRecorder) SyncCommitteeDuties(epoch, indices interface{}) *gomock.Call { +// ComputeSigningRoot indicates an expected call of ComputeSigningRoot +func (mr *MockBeaconMockRecorder) ComputeSigningRoot(object, domain interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SyncCommitteeDuties", reflect.TypeOf((*MockBeacon)(nil).SyncCommitteeDuties), epoch, indices) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ComputeSigningRoot", reflect.TypeOf((*MockBeacon)(nil).ComputeSigningRoot), object, domain) } -// SyncCommitteeSubnetID mocks base method. -func (m *MockBeacon) SyncCommitteeSubnetID(index phase0.CommitteeIndex) (uint64, error) { +// SubmitProposalPreparation mocks base method +func (m *MockBeacon) SubmitProposalPreparation(feeRecipients map[phase0.ValidatorIndex]bellatrix.ExecutionAddress) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SyncCommitteeSubnetID", index) - ret0, _ := ret[0].(uint64) - ret1, _ := ret[1].(error) - return ret0, ret1 + ret := m.ctrl.Call(m, "SubmitProposalPreparation", feeRecipients) + ret0, _ := ret[0].(error) + return ret0 } -// SyncCommitteeSubnetID indicates an expected call of SyncCommitteeSubnetID. -func (mr *MockBeaconMockRecorder) SyncCommitteeSubnetID(index interface{}) *gomock.Call { +// SubmitProposalPreparation indicates an expected call of SubmitProposalPreparation +func (mr *MockBeaconMockRecorder) SubmitProposalPreparation(feeRecipients interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SyncCommitteeSubnetID", reflect.TypeOf((*MockBeacon)(nil).SyncCommitteeSubnetID), index) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitProposalPreparation", reflect.TypeOf((*MockBeacon)(nil).SubmitProposalPreparation), feeRecipients) } diff --git a/protocol/v2/blockchain/beacon/mock_validator_metadata.go b/protocol/v2/blockchain/beacon/mock_validator_metadata.go index a0f5d10315..590f769743 100644 --- a/protocol/v2/blockchain/beacon/mock_validator_metadata.go +++ b/protocol/v2/blockchain/beacon/mock_validator_metadata.go @@ -5,36 +5,35 @@ package beacon import ( - reflect "reflect" - gomock "github.com/golang/mock/gomock" zap "go.uber.org/zap" + reflect "reflect" ) -// MockValidatorMetadataStorage is a mock of ValidatorMetadataStorage interface. +// MockValidatorMetadataStorage is a mock of ValidatorMetadataStorage interface type MockValidatorMetadataStorage struct { ctrl *gomock.Controller recorder *MockValidatorMetadataStorageMockRecorder } -// MockValidatorMetadataStorageMockRecorder is the mock recorder for MockValidatorMetadataStorage. +// MockValidatorMetadataStorageMockRecorder is the mock recorder for MockValidatorMetadataStorage type MockValidatorMetadataStorageMockRecorder struct { mock *MockValidatorMetadataStorage } -// NewMockValidatorMetadataStorage creates a new mock instance. +// NewMockValidatorMetadataStorage creates a new mock instance func NewMockValidatorMetadataStorage(ctrl *gomock.Controller) *MockValidatorMetadataStorage { mock := &MockValidatorMetadataStorage{ctrl: ctrl} mock.recorder = &MockValidatorMetadataStorageMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use. +// EXPECT returns an object that allows the caller to indicate expected use func (m *MockValidatorMetadataStorage) EXPECT() *MockValidatorMetadataStorageMockRecorder { return m.recorder } -// UpdateValidatorMetadata mocks base method. +// UpdateValidatorMetadata mocks base method func (m *MockValidatorMetadataStorage) UpdateValidatorMetadata(logger *zap.Logger, pk string, metadata *ValidatorMetadata) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateValidatorMetadata", logger, pk, metadata) @@ -42,7 +41,7 @@ func (m *MockValidatorMetadataStorage) UpdateValidatorMetadata(logger *zap.Logge return ret0 } -// UpdateValidatorMetadata indicates an expected call of UpdateValidatorMetadata. +// UpdateValidatorMetadata indicates an expected call of UpdateValidatorMetadata func (mr *MockValidatorMetadataStorageMockRecorder) UpdateValidatorMetadata(logger, pk, metadata interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateValidatorMetadata", reflect.TypeOf((*MockValidatorMetadataStorage)(nil).UpdateValidatorMetadata), logger, pk, metadata) diff --git a/protocol/v2/message/msg.go b/protocol/v2/message/msg.go index 58b4606e10..898023f7d2 100644 --- a/protocol/v2/message/msg.go +++ b/protocol/v2/message/msg.go @@ -44,6 +44,8 @@ func BeaconRoleFromString(s string) (spectypes.BeaconRole, error) { return spectypes.BNRoleSyncCommittee, nil case "SYNC_COMMITTEE_CONTRIBUTION": return spectypes.BNRoleSyncCommitteeContribution, nil + case "VALIDATOR_REGISTRATION": + return spectypes.BNRoleValidatorRegistration, nil default: return 0, fmt.Errorf("unknown role: %s", s) } diff --git a/protocol/v2/qbft/testing/storage.go b/protocol/v2/qbft/testing/storage.go index 77e12b10f5..e36bea6a33 100644 --- a/protocol/v2/qbft/testing/storage.go +++ b/protocol/v2/qbft/testing/storage.go @@ -37,6 +37,7 @@ var allRoles = []spectypes.BeaconRole{ spectypes.BNRoleAggregator, spectypes.BNRoleSyncCommittee, spectypes.BNRoleSyncCommitteeContribution, + spectypes.BNRoleValidatorRegistration, } func TestingStores(logger *zap.Logger) *qbftstorage.QBFTStores { diff --git a/protocol/v2/ssv/queue/message_prioritizer_test.go b/protocol/v2/ssv/queue/message_prioritizer_test.go index bd3098e5c9..74e11efb90 100644 --- a/protocol/v2/ssv/queue/message_prioritizer_test.go +++ b/protocol/v2/ssv/queue/message_prioritizer_test.go @@ -320,6 +320,8 @@ func ssvMessageFactory(role types.BeaconRole) func(*qbft.SignedMessage, *spectyp return testingutils.SSVMsgSyncCommittee case types.BNRoleSyncCommitteeContribution: return testingutils.SSVMsgSyncCommitteeContribution + case types.BNRoleValidatorRegistration: + return testingutils.SSVMsgValidatorRegistration default: panic("invalid role") } diff --git a/protocol/v2/ssv/runner/proposer.go b/protocol/v2/ssv/runner/proposer.go index 840b357360..a7b0dfa6f3 100644 --- a/protocol/v2/ssv/runner/proposer.go +++ b/protocol/v2/ssv/runner/proposer.go @@ -93,15 +93,17 @@ func (r *ProposerRunner) ProcessPreConsensus(logger *zap.Logger, signedMsg *spec var obj ssz.Marshaler if r.ProducesBlindedBlocks { // get block data - obj, ver, err = r.GetBeaconNode().GetBlindedBeaconBlock(duty.Slot, duty.CommitteeIndex, r.GetShare().Graffiti, fullSig) + obj, ver, err = r.GetBeaconNode().GetBlindedBeaconBlock(duty.Slot, r.GetShare().Graffiti, fullSig) if err != nil { - return errors.Wrap(err, "failed to get Beacon block") + // Prysm currently doesn’t support MEV. + // TODO: Check Prysm MEV support after https://github.com/prysmaticlabs/prysm/issues/12103 is resolved. + return errors.Wrap(err, "failed to get blinded beacon block") } } else { // get block data - obj, ver, err = r.GetBeaconNode().GetBeaconBlock(duty.Slot, duty.CommitteeIndex, r.GetShare().Graffiti, fullSig) + obj, ver, err = r.GetBeaconNode().GetBeaconBlock(duty.Slot, r.GetShare().Graffiti, fullSig) if err != nil { - return errors.Wrap(err, "failed to get Beacon block") + return errors.Wrap(err, "failed to get beacon block") } } @@ -206,7 +208,8 @@ func (r *ProposerRunner) ProcessPostConsensus(logger *zap.Logger, signedMsg *spe blockSubmissionEnd := r.metrics.StartBeaconSubmission() - if r.decidedBlindedBlock() { + decidedBlockIsBlinded := r.decidedBlindedBlock() + if decidedBlockIsBlinded { vBlindedBlk, _, err := r.GetState().DecidedValue.GetBlindedBlockData() if err != nil { return errors.Wrap(err, "could not get blinded block") @@ -237,7 +240,8 @@ func (r *ProposerRunner) ProcessPostConsensus(logger *zap.Logger, signedMsg *spe logger.Info("✅ successfully submitted block proposal", fields.Slot(signedMsg.Message.Slot), fields.Height(r.BaseRunner.QBFTController.Height), - fields.Round(r.GetState().RunningInstance.State.Round)) + fields.Round(r.GetState().RunningInstance.State.Round), + fields.BuilderProposals(decidedBlockIsBlinded)) } r.GetState().Finished = true return nil diff --git a/protocol/v2/ssv/runner/runner_signatures.go b/protocol/v2/ssv/runner/runner_signatures.go index 6e9541e713..96b2a723f5 100644 --- a/protocol/v2/ssv/runner/runner_signatures.go +++ b/protocol/v2/ssv/runner/runner_signatures.go @@ -20,7 +20,6 @@ func (b *BaseRunner) signBeaconObject( if err != nil { return nil, errors.Wrap(err, "could not get beacon domain") } - sig, r, err := runner.GetSigner().SignBeaconObject(obj, domain, runner.GetBaseRunner().Share.SharePubKey, domainType) if err != nil { return nil, errors.Wrap(err, "could not sign beacon object") diff --git a/protocol/v2/ssv/runner/validator_registration.go b/protocol/v2/ssv/runner/validator_registration.go index 077772fac7..21815c8bbf 100644 --- a/protocol/v2/ssv/runner/validator_registration.go +++ b/protocol/v2/ssv/runner/validator_registration.go @@ -3,30 +3,39 @@ package runner import ( "crypto/sha256" "encoding/json" + "log" eth2apiv1 "github.com/attestantio/go-eth2-client/api/v1" "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/bloxapp/ssv-spec/qbft" specssv "github.com/bloxapp/ssv-spec/ssv" spectypes "github.com/bloxapp/ssv-spec/types" - "github.com/bloxapp/ssv/protocol/v2/types" ssz "github.com/ferranbt/fastssz" "github.com/pkg/errors" "go.uber.org/zap" + + "github.com/bloxapp/ssv/logging/fields" + "github.com/bloxapp/ssv/protocol/v2/qbft/controller" + "github.com/bloxapp/ssv/protocol/v2/ssv/runner/metrics" + "github.com/bloxapp/ssv/protocol/v2/types" ) type ValidatorRegistrationRunner struct { BaseRunner *BaseRunner + GasLimit uint64 beacon specssv.BeaconNode network specssv.Network signer spectypes.KeyManager valCheck qbft.ProposedValueCheckF + + metrics metrics.ConsensusMetrics } func NewValidatorRegistrationRunner( beaconNetwork spectypes.BeaconNetwork, share *spectypes.Share, + qbftController *controller.Controller, beacon specssv.BeaconNode, network specssv.Network, signer spectypes.KeyManager, @@ -36,11 +45,13 @@ func NewValidatorRegistrationRunner( BeaconRoleType: spectypes.BNRoleValidatorRegistration, BeaconNetwork: beaconNetwork, Share: share, + QBFTController: qbftController, }, beacon: beacon, network: network, signer: signer, + metrics: metrics.NewConsensusMetrics(spectypes.BNRoleValidatorRegistration), } } @@ -54,7 +65,7 @@ func (r *ValidatorRegistrationRunner) HasRunningDuty() bool { } func (r *ValidatorRegistrationRunner) ProcessPreConsensus(logger *zap.Logger, signedMsg *spectypes.SignedPartialSignatureMessage) error { - quorum, _, err := r.BaseRunner.basePreConsensusMsgProcessing(r, signedMsg) + quorum, roots, err := r.BaseRunner.basePreConsensusMsgProcessing(r, signedMsg) if err != nil { return errors.Wrap(err, "failed processing validator registration message") } @@ -64,6 +75,22 @@ func (r *ValidatorRegistrationRunner) ProcessPreConsensus(logger *zap.Logger, si return nil } + // only 1 root, verified in basePreConsensusMsgProcessing + root := roots[0] + // randao is relevant only for block proposals, no need to check type + fullSig, err := r.GetState().ReconstructBeaconSig(r.GetState().PreConsensusContainer, root, r.GetShare().ValidatorPubKey) + if err != nil { + return errors.Wrap(err, "could not reconstruct randao sig") + } + specSig := phase0.BLSSignature{} + copy(specSig[:], fullSig) + + if err := r.beacon.SubmitValidatorRegistration(r.BaseRunner.Share.ValidatorPubKey, r.BaseRunner.Share.FeeRecipientAddress, specSig); err != nil { + return errors.Wrap(err, "could not submit validator registration") + } + + logger.Debug("validator registration submitted successfully", fields.FeeRecipient(r.BaseRunner.Share.FeeRecipientAddress[:])) + r.GetState().Finished = true return nil } @@ -141,7 +168,7 @@ func (r *ValidatorRegistrationRunner) calculateValidatorRegistration() (*eth2api return ð2apiv1.ValidatorRegistration{ FeeRecipient: r.BaseRunner.Share.FeeRecipientAddress, - GasLimit: 1, + GasLimit: r.GasLimit, Timestamp: r.BaseRunner.BeaconNetwork.EpochStartTime(epoch), Pubkey: pk, }, nil @@ -191,6 +218,7 @@ func (r *ValidatorRegistrationRunner) GetRoot() ([32]byte, error) { if err != nil { return [32]byte{}, errors.Wrap(err, "could not encode DutyRunnerState") } + log.Printf("runner root: %v", string(marshaledRoot)) ret := sha256.Sum256(marshaledRoot) return ret, nil } diff --git a/protocol/v2/ssv/spectest/ssv_mapping_test.go b/protocol/v2/ssv/spectest/ssv_mapping_test.go index 9f1b4201c0..446c82dc09 100644 --- a/protocol/v2/ssv/spectest/ssv_mapping_test.go +++ b/protocol/v2/ssv/spectest/ssv_mapping_test.go @@ -15,10 +15,11 @@ import ( "github.com/bloxapp/ssv-spec/ssv/spectest/tests/valcheck" spectypes "github.com/bloxapp/ssv-spec/types" "github.com/bloxapp/ssv-spec/types/testingutils" - "github.com/bloxapp/ssv/logging" "github.com/stretchr/testify/require" "go.uber.org/zap" + "github.com/bloxapp/ssv/logging" + "github.com/bloxapp/ssv/protocol/v2/qbft/controller" "github.com/bloxapp/ssv/protocol/v2/qbft/instance" qbfttesting "github.com/bloxapp/ssv/protocol/v2/qbft/testing" @@ -238,6 +239,10 @@ func fixRunnerForRun(t *testing.T, runnerMap map[string]interface{}, ks *testing ret.(*runner.ProposerRunner).ProducesBlindedBlocks = blindedBlocks.(bool) } + if gasLimit, ok := runnerMap["GasLimit"]; ok { + ret.(*runner.ValidatorRegistrationRunner).GasLimit = uint64(gasLimit.(float64)) + } + if ret.GetBaseRunner().QBFTController != nil { ret.GetBaseRunner().QBFTController = fixControllerForRun(t, logger, ret, ret.GetBaseRunner().QBFTController, ks) if ret.GetBaseRunner().State != nil { diff --git a/protocol/v2/ssv/testing/runner.go b/protocol/v2/ssv/testing/runner.go index 0dcfc4141d..933122a3b0 100644 --- a/protocol/v2/ssv/testing/runner.go +++ b/protocol/v2/ssv/testing/runner.go @@ -5,9 +5,10 @@ import ( specssv "github.com/bloxapp/ssv-spec/ssv" spectypes "github.com/bloxapp/ssv-spec/types" spectestingutils "github.com/bloxapp/ssv-spec/types/testingutils" + "go.uber.org/zap" + "github.com/bloxapp/ssv/protocol/v2/qbft/testing" "github.com/bloxapp/ssv/protocol/v2/ssv/runner" - "go.uber.org/zap" ) var AttesterRunner = func(logger *zap.Logger, keySet *spectestingutils.TestKeySet) runner.Runner { @@ -19,14 +20,14 @@ var AttesterRunner = func(logger *zap.Logger, keySet *spectestingutils.TestKeySe //} var ProposerRunner = func(logger *zap.Logger, keySet *spectestingutils.TestKeySet) runner.Runner { - return baseRunner(logger, spectypes.BNRoleProposer, specssv.ProposerValueCheckF(spectestingutils.NewTestingKeyManager(), spectypes.BeaconTestNetwork, spectestingutils.TestingValidatorPubKey[:], spectestingutils.TestingValidatorIndex, nil), keySet) + return baseRunner(logger, spectypes.BNRoleProposer, specssv.ProposerValueCheckF(spectestingutils.NewTestingKeyManager(), spectypes.BeaconTestNetwork, spectestingutils.TestingValidatorPubKey[:], spectestingutils.TestingValidatorIndex, nil, true), keySet) } var ProposerBlindedBlockRunner = func(logger *zap.Logger, keySet *spectestingutils.TestKeySet) runner.Runner { ret := baseRunner( logger, spectypes.BNRoleProposer, - specssv.ProposerValueCheckF(spectestingutils.NewTestingKeyManager(), spectypes.BeaconTestNetwork, spectestingutils.TestingValidatorPubKey[:], spectestingutils.TestingValidatorIndex, nil), + specssv.ProposerValueCheckF(spectestingutils.NewTestingKeyManager(), spectypes.BeaconTestNetwork, spectestingutils.TestingValidatorPubKey[:], spectestingutils.TestingValidatorIndex, nil, true), keySet, ) ret.(*runner.ProposerRunner).ProducesBlindedBlocks = true @@ -46,7 +47,9 @@ var SyncCommitteeContributionRunner = func(logger *zap.Logger, keySet *spectesti } var ValidatorRegistrationRunner = func(logger *zap.Logger, keySet *spectestingutils.TestKeySet) runner.Runner { - return baseRunner(logger, spectypes.BNRoleValidatorRegistration, nil, keySet) + ret := baseRunner(logger, spectypes.BNRoleValidatorRegistration, nil, keySet) + ret.(*runner.ValidatorRegistrationRunner).GasLimit = 1 + return ret } var UnknownDutyTypeRunner = func(logger *zap.Logger, keySet *spectestingutils.TestKeySet) runner.Runner { @@ -129,6 +132,7 @@ var baseRunner = func(logger *zap.Logger, role spectypes.BeaconRole, valCheck sp return runner.NewValidatorRegistrationRunner( spectypes.PraterNetwork, share, + contr, spectestingutils.NewTestingBeaconNode(), net, km, diff --git a/protocol/v2/ssv/testing/validator.go b/protocol/v2/ssv/testing/validator.go index 3e80f0bdff..a4e5f3d7c8 100644 --- a/protocol/v2/ssv/testing/validator.go +++ b/protocol/v2/ssv/testing/validator.go @@ -3,12 +3,11 @@ package testing import ( "context" - "github.com/bloxapp/ssv/protocol/v2/qbft/testing" - "go.uber.org/zap" - spectypes "github.com/bloxapp/ssv-spec/types" spectestingutils "github.com/bloxapp/ssv-spec/types/testingutils" + "go.uber.org/zap" + "github.com/bloxapp/ssv/protocol/v2/qbft/testing" "github.com/bloxapp/ssv/protocol/v2/ssv/runner" "github.com/bloxapp/ssv/protocol/v2/ssv/validator" "github.com/bloxapp/ssv/protocol/v2/types" @@ -33,6 +32,7 @@ var BaseValidator = func(logger *zap.Logger, keySet *spectestingutils.TestKeySet spectypes.BNRoleAggregator: AggregatorRunner(logger, keySet), spectypes.BNRoleSyncCommittee: SyncCommitteeRunner(logger, keySet), spectypes.BNRoleSyncCommitteeContribution: SyncCommitteeContributionRunner(logger, keySet), + spectypes.BNRoleValidatorRegistration: ValidatorRegistrationRunner(logger, keySet), }, }, ) diff --git a/protocol/v2/ssv/validator/msgqueue_consumer.go b/protocol/v2/ssv/validator/msgqueue_consumer.go index a37b15ccea..032e6630d5 100644 --- a/protocol/v2/ssv/validator/msgqueue_consumer.go +++ b/protocol/v2/ssv/validator/msgqueue_consumer.go @@ -149,7 +149,10 @@ func (v *Validator) GetLastHeight(identifier spectypes.MessageID) specqbft.Heigh if r == nil { return specqbft.Height(0) } - return r.GetBaseRunner().QBFTController.Height + if ctrl := r.GetBaseRunner().QBFTController; ctrl != nil { + return ctrl.Height + } + return specqbft.Height(0) } // GetLastRound returns the last height for the given identifier diff --git a/protocol/v2/ssv/validator/opts.go b/protocol/v2/ssv/validator/opts.go index b30edae145..e7699743d3 100644 --- a/protocol/v2/ssv/validator/opts.go +++ b/protocol/v2/ssv/validator/opts.go @@ -4,6 +4,7 @@ import ( specqbft "github.com/bloxapp/ssv-spec/qbft" specssv "github.com/bloxapp/ssv-spec/ssv" spectypes "github.com/bloxapp/ssv-spec/types" + "github.com/bloxapp/ssv/ibft/storage" qbftctrl "github.com/bloxapp/ssv/protocol/v2/qbft/controller" "github.com/bloxapp/ssv/protocol/v2/ssv/runner" @@ -12,6 +13,10 @@ import ( const ( DefaultQueueSize = 32 + // DefaultGasLimit sets validator registration gas limit. + // TODO: This is a reasonable default, but we should probably make this configurable. + // Discussion here: https://github.com/ethereum/builder-specs/issues/17 + DefaultGasLimit = 30_000_000 ) // Options represents options that should be passed to a new instance of Validator. @@ -25,13 +30,18 @@ type Options struct { NewDecidedHandler qbftctrl.NewDecidedHandler FullNode bool Exporter bool + BuilderProposals bool QueueSize int + GasLimit uint64 } func (o *Options) defaults() { if o.QueueSize == 0 { o.QueueSize = DefaultQueueSize } + if o.GasLimit == 0 { + o.GasLimit = DefaultGasLimit + } } // State of the validator diff --git a/protocol/v2/ssv/validator/startup.go b/protocol/v2/ssv/validator/startup.go index 29c3935c8e..29e7fbf31f 100644 --- a/protocol/v2/ssv/validator/startup.go +++ b/protocol/v2/ssv/validator/startup.go @@ -32,9 +32,14 @@ func (v *Validator) Start(logger *zap.Logger) error { continue } identifier := spectypes.NewMsgID(types.GetDefaultDomain(), r.GetBaseRunner().Share.ValidatorPubKey, role) - if err := r.GetBaseRunner().QBFTController.LoadHighestInstance(identifier[:]); err != nil { - logger.Warn("❗ failed to load highest instance", fields.PubKey(identifier.GetPubKey()), zap.Error(err)) + if ctrl := r.GetBaseRunner().QBFTController; ctrl != nil { + if err := ctrl.LoadHighestInstance(identifier[:]); err != nil { + logger.Warn("❗failed to load highest instance", + fields.PubKey(identifier.GetPubKey()), + zap.Error(err)) + } } + if err := n.Subscribe(identifier.GetPubKey()); err != nil { return err } diff --git a/protocol/v2/types/ssvshare.go b/protocol/v2/types/ssvshare.go index f6e16540c5..08bd541ef3 100644 --- a/protocol/v2/types/ssvshare.go +++ b/protocol/v2/types/ssvshare.go @@ -53,7 +53,7 @@ func (s *SSVShare) HasBeaconMetadata() bool { } func (s *SSVShare) SetFeeRecipient(feeRecipient bellatrix.ExecutionAddress) { - s.FeeRecipient = feeRecipient + s.FeeRecipientAddress = feeRecipient } // ComputeClusterIDHash will compute cluster ID hash with given owner address and operator ids @@ -89,5 +89,4 @@ type Metadata struct { BeaconMetadata *beaconprotocol.ValidatorMetadata OwnerAddress common.Address Liquidated bool - FeeRecipient bellatrix.ExecutionAddress } diff --git a/storage/kv/badger.go b/storage/kv/badger.go index e108fd7a76..0b672b3768 100644 --- a/storage/kv/badger.go +++ b/storage/kv/badger.go @@ -49,6 +49,8 @@ func New(logger *zap.Logger, options basedb.Options) (basedb.IDb, error) { opt.Logger = newLogger(zap.NewNop()) if logger != nil && options.Reporting { opt.Logger = newLogger(logger) + } else { + opt.Logger = newLogger(zap.NewNop()) // TODO: we should allow only errors to be logged } opt.ValueLogFileSize = 1024 * 1024 * 100 // TODO:need to set the vlog proper (max) size