From 7801ae56361bffa7fa87050dea4aeab48f5373c4 Mon Sep 17 00:00:00 2001 From: Dhruv Bodani Date: Tue, 23 Jan 2024 17:12:12 +0530 Subject: [PATCH 01/19] implement beacon committee selections --- beacon-chain/rpc/eth/shared/structs.go | 58 ++++++++ cmd/validator/flags/flags.go | 7 + cmd/validator/main.go | 1 + cmd/validator/usage.go | 1 + crypto/bls/common/mock/interface_mock.go | 138 +++++++++--------- .../validator-mock/validator_client_mock.go | 16 ++ validator/client/BUILD.bazel | 1 + validator/client/aggregate.go | 24 ++- validator/client/aggregate_test.go | 59 ++++++++ validator/client/beacon-api/BUILD.bazel | 1 + .../client/beacon-api/activation_test.go | 32 ++-- .../beacon-api/beacon_api_validator_client.go | 6 + .../beacon-api/beacon_committee_selections.go | 40 +++++ .../beacon_committee_selections_test.go | 124 ++++++++++++++++ validator/client/beacon-api/index_test.go | 56 ++++--- .../mock/beacon_block_converter_mock.go | 18 +-- .../client/beacon-api/mock/genesis_mock.go | 10 +- .../beacon-api/mock/json_rest_handler_mock.go | 18 +-- .../client/beacon-api/state_validators.go | 18 ++- .../beacon-api/state_validators_test.go | 64 +++++--- validator/client/grpc-api/BUILD.bazel | 1 + .../client/grpc-api/grpc_validator_client.go | 6 + validator/client/iface/BUILD.bazel | 1 + validator/client/iface/validator_client.go | 3 + validator/client/service.go | 7 + validator/client/validator.go | 125 +++++++++++++++- validator/client/validator_test.go | 88 +++++++++++ validator/node/node.go | 1 + 28 files changed, 745 insertions(+), 179 deletions(-) create mode 100644 validator/client/beacon-api/beacon_committee_selections.go create mode 100644 validator/client/beacon-api/beacon_committee_selections_test.go diff --git a/beacon-chain/rpc/eth/shared/structs.go b/beacon-chain/rpc/eth/shared/structs.go index 93fb926ecf5a..d61e433f50fa 100644 --- a/beacon-chain/rpc/eth/shared/structs.go +++ b/beacon-chain/rpc/eth/shared/structs.go @@ -1,5 +1,14 @@ package shared +import ( + "encoding/json" + "strconv" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/pkg/errors" + "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives" +) + type Validator struct { PublicKey string `json:"pubkey"` WithdrawalCredentials string `json:"withdrawal_credentials"` @@ -207,3 +216,52 @@ type Withdrawal struct { ExecutionAddress string `json:"address"` Amount string `json:"amount"` } + +type BeaconCommitteeSelection struct { + SelectionProof []byte + Slot primitives.Slot + ValidatorIndex primitives.ValidatorIndex +} + +type beaconCommitteeSelectionJson struct { + SelectionProof string `json:"selection_proof"` + Slot string `json:"slot"` + ValidatorIndex string `json:"validator_index"` +} + +func (b BeaconCommitteeSelection) MarshalJSON() ([]byte, error) { + return json.Marshal(beaconCommitteeSelectionJson{ + SelectionProof: hexutil.Encode(b.SelectionProof), + Slot: strconv.FormatUint(uint64(b.Slot), 10), + ValidatorIndex: strconv.FormatUint(uint64(b.ValidatorIndex), 10), + }) +} + +func (b *BeaconCommitteeSelection) UnmarshalJSON(input []byte) error { + var bjson beaconCommitteeSelectionJson + err := json.Unmarshal(input, &bjson) + if err != nil { + return errors.Wrap(err, "unmarshal beacon committee selection") + } + + slot, err := strconv.ParseUint(bjson.Slot, 10, 64) + if err != nil { + return errors.Wrap(err, "failed to parse slot") + } + + vIdx, err := strconv.ParseUint(bjson.ValidatorIndex, 10, 64) + if err != nil { + return errors.Wrap(err, "failed to parse validator index") + } + + selectionProof, err := hexutil.Decode(bjson.SelectionProof) + if err != nil { + return errors.Wrap(err, "failed to parse selection proof") + } + + b.Slot = primitives.Slot(slot) + b.SelectionProof = selectionProof + b.ValidatorIndex = primitives.ValidatorIndex(vIdx) + + return nil +} diff --git a/cmd/validator/flags/flags.go b/cmd/validator/flags/flags.go index d9eb8d66cf1e..e24c06490383 100644 --- a/cmd/validator/flags/flags.go +++ b/cmd/validator/flags/flags.go @@ -377,6 +377,13 @@ var ( Usage: "Sets the maximum size for one batch of validator registrations. Use a non-positive value to disable batching.", Value: 0, } + + // EnableDistributed enables the usage of prysm validator client in a Distributed Validator Cluster. + EnableDistributed = &cli.BoolFlag{ + Name: "distributed", + Usage: "To enable the use of prysm validator client in Distributed Validator Cluster", + Value: false, + } ) // DefaultValidatorDir returns OS-specific default validator directory. diff --git a/cmd/validator/main.go b/cmd/validator/main.go index 150425dd117d..099ced70644a 100644 --- a/cmd/validator/main.go +++ b/cmd/validator/main.go @@ -74,6 +74,7 @@ var appFlags = []cli.Flag{ flags.WalletDirFlag, flags.EnableWebFlag, flags.GraffitiFileFlag, + flags.EnableDistributed, // Consensys' Web3Signer flags flags.Web3SignerURLFlag, flags.Web3SignerPublicValidatorKeysFlag, diff --git a/cmd/validator/usage.go b/cmd/validator/usage.go index 8369551336f4..0746f4b7d7d8 100644 --- a/cmd/validator/usage.go +++ b/cmd/validator/usage.go @@ -122,6 +122,7 @@ var appHelpFlagGroups = []flagGroup{ flags.EnableBuilderFlag, flags.BuilderGasLimitFlag, flags.ValidatorsRegistrationBatchSizeFlag, + flags.EnableDistributed, }, }, { diff --git a/crypto/bls/common/mock/interface_mock.go b/crypto/bls/common/mock/interface_mock.go index 3400c9d80dee..939ccf9b3784 100644 --- a/crypto/bls/common/mock/interface_mock.go +++ b/crypto/bls/common/mock/interface_mock.go @@ -12,30 +12,30 @@ import ( ) // MockSecretKey is a mock of SecretKey interface. -type SecretKey struct { +type MockSecretKey struct { ctrl *gomock.Controller - recorder *SecretKeyMockRecorder + recorder *MockSecretKeyMockRecorder } // MockSecretKeyMockRecorder is the mock recorder for MockSecretKey. -type SecretKeyMockRecorder struct { - mock *SecretKey +type MockSecretKeyMockRecorder struct { + mock *MockSecretKey } -// NewSecretKey creates a new mock instance. -func NewSecretKey(ctrl *gomock.Controller) *SecretKey { - mock := &SecretKey{ctrl: ctrl} - mock.recorder = &SecretKeyMockRecorder{mock} +// NewMockSecretKey creates a new mock instance. +func NewMockSecretKey(ctrl *gomock.Controller) *MockSecretKey { + mock := &MockSecretKey{ctrl: ctrl} + mock.recorder = &MockSecretKeyMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. -func (m *SecretKey) EXPECT() *SecretKeyMockRecorder { +func (m *MockSecretKey) EXPECT() *MockSecretKeyMockRecorder { return m.recorder } // Marshal mocks base method. -func (m *SecretKey) Marshal() []byte { +func (m *MockSecretKey) Marshal() []byte { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Marshal") ret0, _ := ret[0].([]byte) @@ -43,13 +43,13 @@ func (m *SecretKey) Marshal() []byte { } // Marshal indicates an expected call of Marshal. -func (mr *SecretKeyMockRecorder) Marshal() *gomock.Call { +func (mr *MockSecretKeyMockRecorder) Marshal() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Marshal", reflect.TypeOf((*SecretKey)(nil).Marshal)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Marshal", reflect.TypeOf((*MockSecretKey)(nil).Marshal)) } // PublicKey mocks base method. -func (m *SecretKey) PublicKey() common.PublicKey { +func (m *MockSecretKey) PublicKey() common.PublicKey { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PublicKey") ret0, _ := ret[0].(common.PublicKey) @@ -57,13 +57,13 @@ func (m *SecretKey) PublicKey() common.PublicKey { } // PublicKey indicates an expected call of PublicKey. -func (mr *SecretKeyMockRecorder) PublicKey() *gomock.Call { +func (mr *MockSecretKeyMockRecorder) PublicKey() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PublicKey", reflect.TypeOf((*SecretKey)(nil).PublicKey)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PublicKey", reflect.TypeOf((*MockSecretKey)(nil).PublicKey)) } // Sign mocks base method. -func (m *SecretKey) Sign(msg []byte) common.Signature { +func (m *MockSecretKey) Sign(msg []byte) common.Signature { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Sign", msg) ret0, _ := ret[0].(common.Signature) @@ -71,36 +71,36 @@ func (m *SecretKey) Sign(msg []byte) common.Signature { } // Sign indicates an expected call of Sign. -func (mr *SecretKeyMockRecorder) Sign(msg interface{}) *gomock.Call { +func (mr *MockSecretKeyMockRecorder) Sign(msg interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Sign", reflect.TypeOf((*SecretKey)(nil).Sign), msg) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Sign", reflect.TypeOf((*MockSecretKey)(nil).Sign), msg) } // MockPublicKey is a mock of PublicKey interface. -type PublicKey struct { +type MockPublicKey struct { ctrl *gomock.Controller - recorder *PublicKeyMockRecorder + recorder *MockPublicKeyMockRecorder } // MockPublicKeyMockRecorder is the mock recorder for MockPublicKey. -type PublicKeyMockRecorder struct { - mock *PublicKey +type MockPublicKeyMockRecorder struct { + mock *MockPublicKey } -// NewPublicKey creates a new mock instance. -func NewPublicKey(ctrl *gomock.Controller) *PublicKey { - mock := &PublicKey{ctrl: ctrl} - mock.recorder = &PublicKeyMockRecorder{mock} +// NewMockPublicKey creates a new mock instance. +func NewMockPublicKey(ctrl *gomock.Controller) *MockPublicKey { + mock := &MockPublicKey{ctrl: ctrl} + mock.recorder = &MockPublicKeyMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. -func (m *PublicKey) EXPECT() *PublicKeyMockRecorder { +func (m *MockPublicKey) EXPECT() *MockPublicKeyMockRecorder { return m.recorder } // Aggregate mocks base method. -func (m *PublicKey) Aggregate(p2 common.PublicKey) common.PublicKey { +func (m *MockPublicKey) Aggregate(p2 common.PublicKey) common.PublicKey { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Aggregate", p2) ret0, _ := ret[0].(common.PublicKey) @@ -108,13 +108,13 @@ func (m *PublicKey) Aggregate(p2 common.PublicKey) common.PublicKey { } // Aggregate indicates an expected call of Aggregate. -func (mr *PublicKeyMockRecorder) Aggregate(p2 interface{}) *gomock.Call { +func (mr *MockPublicKeyMockRecorder) Aggregate(p2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Aggregate", reflect.TypeOf((*PublicKey)(nil).Aggregate), p2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Aggregate", reflect.TypeOf((*MockPublicKey)(nil).Aggregate), p2) } // Copy mocks base method. -func (m *PublicKey) Copy() common.PublicKey { +func (m *MockPublicKey) Copy() common.PublicKey { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Copy") ret0, _ := ret[0].(common.PublicKey) @@ -122,13 +122,13 @@ func (m *PublicKey) Copy() common.PublicKey { } // Copy indicates an expected call of Copy. -func (mr *PublicKeyMockRecorder) Copy() *gomock.Call { +func (mr *MockPublicKeyMockRecorder) Copy() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Copy", reflect.TypeOf((*PublicKey)(nil).Copy)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Copy", reflect.TypeOf((*MockPublicKey)(nil).Copy)) } // Equals mocks base method. -func (m *PublicKey) Equals(p2 common.PublicKey) bool { +func (m *MockPublicKey) Equals(p2 common.PublicKey) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Equals", p2) ret0, _ := ret[0].(bool) @@ -136,13 +136,13 @@ func (m *PublicKey) Equals(p2 common.PublicKey) bool { } // Equals indicates an expected call of Equals. -func (mr *PublicKeyMockRecorder) Equals(p2 interface{}) *gomock.Call { +func (mr *MockPublicKeyMockRecorder) Equals(p2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Equals", reflect.TypeOf((*PublicKey)(nil).Equals), p2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Equals", reflect.TypeOf((*MockPublicKey)(nil).Equals), p2) } // IsInfinite mocks base method. -func (m *PublicKey) IsInfinite() bool { +func (m *MockPublicKey) IsInfinite() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsInfinite") ret0, _ := ret[0].(bool) @@ -150,13 +150,13 @@ func (m *PublicKey) IsInfinite() bool { } // IsInfinite indicates an expected call of IsInfinite. -func (mr *PublicKeyMockRecorder) IsInfinite() *gomock.Call { +func (mr *MockPublicKeyMockRecorder) IsInfinite() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsInfinite", reflect.TypeOf((*PublicKey)(nil).IsInfinite)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsInfinite", reflect.TypeOf((*MockPublicKey)(nil).IsInfinite)) } // Marshal mocks base method. -func (m *PublicKey) Marshal() []byte { +func (m *MockPublicKey) Marshal() []byte { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Marshal") ret0, _ := ret[0].([]byte) @@ -164,36 +164,36 @@ func (m *PublicKey) Marshal() []byte { } // Marshal indicates an expected call of Marshal. -func (mr *PublicKeyMockRecorder) Marshal() *gomock.Call { +func (mr *MockPublicKeyMockRecorder) Marshal() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Marshal", reflect.TypeOf((*PublicKey)(nil).Marshal)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Marshal", reflect.TypeOf((*MockPublicKey)(nil).Marshal)) } // MockSignature is a mock of Signature interface. -type Signature struct { +type MockSignature struct { ctrl *gomock.Controller - recorder *SignatureMockRecorder + recorder *MockSignatureMockRecorder } // MockSignatureMockRecorder is the mock recorder for MockSignature. -type SignatureMockRecorder struct { - mock *Signature +type MockSignatureMockRecorder struct { + mock *MockSignature } -// NewSignature creates a new mock instance. -func NewSignature(ctrl *gomock.Controller) *Signature { - mock := &Signature{ctrl: ctrl} - mock.recorder = &SignatureMockRecorder{mock} +// NewMockSignature creates a new mock instance. +func NewMockSignature(ctrl *gomock.Controller) *MockSignature { + mock := &MockSignature{ctrl: ctrl} + mock.recorder = &MockSignatureMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. -func (m *Signature) EXPECT() *SignatureMockRecorder { +func (m *MockSignature) EXPECT() *MockSignatureMockRecorder { return m.recorder } // AggregateVerify mocks base method. -func (m *Signature) AggregateVerify(pubKeys []common.PublicKey, msgs [][32]byte) bool { +func (m *MockSignature) AggregateVerify(pubKeys []common.PublicKey, msgs [][32]byte) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AggregateVerify", pubKeys, msgs) ret0, _ := ret[0].(bool) @@ -201,13 +201,13 @@ func (m *Signature) AggregateVerify(pubKeys []common.PublicKey, msgs [][32]byte) } // AggregateVerify indicates an expected call of AggregateVerify. -func (mr *SignatureMockRecorder) AggregateVerify(pubKeys, msgs interface{}) *gomock.Call { +func (mr *MockSignatureMockRecorder) AggregateVerify(pubKeys, msgs interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AggregateVerify", reflect.TypeOf((*Signature)(nil).AggregateVerify), pubKeys, msgs) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AggregateVerify", reflect.TypeOf((*MockSignature)(nil).AggregateVerify), pubKeys, msgs) } // Copy mocks base method. -func (m *Signature) Copy() common.Signature { +func (m *MockSignature) Copy() common.Signature { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Copy") ret0, _ := ret[0].(common.Signature) @@ -215,13 +215,13 @@ func (m *Signature) Copy() common.Signature { } // Copy indicates an expected call of Copy. -func (mr *SignatureMockRecorder) Copy() *gomock.Call { +func (mr *MockSignatureMockRecorder) Copy() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Copy", reflect.TypeOf((*Signature)(nil).Copy)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Copy", reflect.TypeOf((*MockSignature)(nil).Copy)) } // Eth2FastAggregateVerify mocks base method. -func (m *Signature) Eth2FastAggregateVerify(pubKeys []common.PublicKey, msg [32]byte) bool { +func (m *MockSignature) Eth2FastAggregateVerify(pubKeys []common.PublicKey, msg [32]byte) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Eth2FastAggregateVerify", pubKeys, msg) ret0, _ := ret[0].(bool) @@ -229,13 +229,13 @@ func (m *Signature) Eth2FastAggregateVerify(pubKeys []common.PublicKey, msg [32] } // Eth2FastAggregateVerify indicates an expected call of Eth2FastAggregateVerify. -func (mr *SignatureMockRecorder) Eth2FastAggregateVerify(pubKeys, msg interface{}) *gomock.Call { +func (mr *MockSignatureMockRecorder) Eth2FastAggregateVerify(pubKeys, msg interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Eth2FastAggregateVerify", reflect.TypeOf((*Signature)(nil).Eth2FastAggregateVerify), pubKeys, msg) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Eth2FastAggregateVerify", reflect.TypeOf((*MockSignature)(nil).Eth2FastAggregateVerify), pubKeys, msg) } // FastAggregateVerify mocks base method. -func (m *Signature) FastAggregateVerify(pubKeys []common.PublicKey, msg [32]byte) bool { +func (m *MockSignature) FastAggregateVerify(pubKeys []common.PublicKey, msg [32]byte) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "FastAggregateVerify", pubKeys, msg) ret0, _ := ret[0].(bool) @@ -243,13 +243,13 @@ func (m *Signature) FastAggregateVerify(pubKeys []common.PublicKey, msg [32]byte } // FastAggregateVerify indicates an expected call of FastAggregateVerify. -func (mr *SignatureMockRecorder) FastAggregateVerify(pubKeys, msg interface{}) *gomock.Call { +func (mr *MockSignatureMockRecorder) FastAggregateVerify(pubKeys, msg interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FastAggregateVerify", reflect.TypeOf((*Signature)(nil).FastAggregateVerify), pubKeys, msg) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FastAggregateVerify", reflect.TypeOf((*MockSignature)(nil).FastAggregateVerify), pubKeys, msg) } // Marshal mocks base method. -func (m *Signature) Marshal() []byte { +func (m *MockSignature) Marshal() []byte { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Marshal") ret0, _ := ret[0].([]byte) @@ -257,13 +257,13 @@ func (m *Signature) Marshal() []byte { } // Marshal indicates an expected call of Marshal. -func (mr *SignatureMockRecorder) Marshal() *gomock.Call { +func (mr *MockSignatureMockRecorder) Marshal() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Marshal", reflect.TypeOf((*Signature)(nil).Marshal)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Marshal", reflect.TypeOf((*MockSignature)(nil).Marshal)) } // Verify mocks base method. -func (m *Signature) Verify(pubKey common.PublicKey, msg []byte) bool { +func (m *MockSignature) Verify(pubKey common.PublicKey, msg []byte) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Verify", pubKey, msg) ret0, _ := ret[0].(bool) @@ -271,7 +271,7 @@ func (m *Signature) Verify(pubKey common.PublicKey, msg []byte) bool { } // Verify indicates an expected call of Verify. -func (mr *SignatureMockRecorder) Verify(pubKey, msg interface{}) *gomock.Call { +func (mr *MockSignatureMockRecorder) Verify(pubKey, msg interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Verify", reflect.TypeOf((*Signature)(nil).Verify), pubKey, msg) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Verify", reflect.TypeOf((*MockSignature)(nil).Verify), pubKey, msg) } diff --git a/testing/validator-mock/validator_client_mock.go b/testing/validator-mock/validator_client_mock.go index d71baea2b21b..5cee2646ed9a 100644 --- a/testing/validator-mock/validator_client_mock.go +++ b/testing/validator-mock/validator_client_mock.go @@ -9,6 +9,7 @@ import ( reflect "reflect" gomock "github.com/golang/mock/gomock" + shared "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" primitives "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives" eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" emptypb "google.golang.org/protobuf/types/known/emptypb" @@ -81,6 +82,21 @@ func (mr *MockValidatorClientMockRecorder) EventStreamIsRunning() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EventStreamIsRunning", reflect.TypeOf((*MockValidatorClient)(nil).EventStreamIsRunning)) } +// GetAggregatedSelections mocks base method. +func (m *MockValidatorClient) GetAggregatedSelections(arg0 context.Context, arg1 []shared.BeaconCommitteeSelection) ([]shared.BeaconCommitteeSelection, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAggregatedSelections", arg0, arg1) + ret0, _ := ret[0].([]shared.BeaconCommitteeSelection) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAggregatedSelections indicates an expected call of GetAggregatedSelections. +func (mr *MockValidatorClientMockRecorder) GetAggregatedSelections(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAggregatedSelections", reflect.TypeOf((*MockValidatorClient)(nil).GetAggregatedSelections), arg0, arg1) +} + // GetAttestationData mocks base method. func (m *MockValidatorClient) GetAttestationData(arg0 context.Context, arg1 *eth.AttestationDataRequest) (*eth.AttestationData, error) { m.ctrl.T.Helper() diff --git a/validator/client/BUILD.bazel b/validator/client/BUILD.bazel index 268d0afd603c..799c89e79469 100644 --- a/validator/client/BUILD.bazel +++ b/validator/client/BUILD.bazel @@ -31,6 +31,7 @@ go_library( "//beacon-chain/builder:go_default_library", "//beacon-chain/core/altair:go_default_library", "//beacon-chain/core/signing:go_default_library", + "//beacon-chain/rpc/eth/shared:go_default_library", "//cache/lru:go_default_library", "//cmd:go_default_library", "//config/features:go_default_library", diff --git a/validator/client/aggregate.go b/validator/client/aggregate.go index 37cc1d2aeb5d..ef112ee0341d 100644 --- a/validator/client/aggregate.go +++ b/validator/client/aggregate.go @@ -53,13 +53,25 @@ func (v *validator) SubmitAggregateAndProof(ctx context.Context, slot primitives v.aggregatedSlotCommitteeIDCache.Add(k, true) v.aggregatedSlotCommitteeIDCacheLock.Unlock() - slotSig, err := v.signSlotWithSelectionProof(ctx, pubKey, slot) - if err != nil { - log.WithError(err).Error("Could not sign slot") - if v.emitAccountMetrics { - ValidatorAggFailVec.WithLabelValues(fmtKey).Inc() + var slotSig []byte + if v.distributed { + slotSig, err = v.getAttSelection(attSelectionKey{slot: slot, index: duty.ValidatorIndex}) + if err != nil { + log.WithError(err).Error("Could not find aggregated selection proof") + if v.emitAccountMetrics { + ValidatorAggFailVec.WithLabelValues(fmtKey).Inc() + } + return + } + } else { + slotSig, err = v.signSlotWithSelectionProof(ctx, pubKey, slot) + if err != nil { + log.WithError(err).Error("Could not sign slot") + if v.emitAccountMetrics { + ValidatorAggFailVec.WithLabelValues(fmtKey).Inc() + } + return } - return } // As specified in spec, an aggregator should wait until two thirds of the way through slot diff --git a/validator/client/aggregate_test.go b/validator/client/aggregate_test.go index 93f0032f10c0..75970ef6f2b6 100644 --- a/validator/client/aggregate_test.go +++ b/validator/client/aggregate_test.go @@ -5,6 +5,8 @@ import ( "errors" "testing" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" + "github.com/golang/mock/gomock" "github.com/prysmaticlabs/go-bitfield" fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams" @@ -116,6 +118,63 @@ func TestSubmitAggregateAndProof_Ok(t *testing.T) { validator.SubmitAggregateAndProof(context.Background(), 0, pubKey) } +func TestSubmitAggregateAndProof_Distributed(t *testing.T) { + validatorIdx := primitives.ValidatorIndex(123) + slot := primitives.Slot(456) + ctx := context.Background() + + validator, m, validatorKey, finish := setup(t) + defer finish() + + var pubKey [fieldparams.BLSPubkeyLength]byte + copy(pubKey[:], validatorKey.PublicKey().Marshal()) + validator.duties = ðpb.DutiesResponse{ + Duties: []*ethpb.DutiesResponse_Duty{ + { + PublicKey: validatorKey.PublicKey().Marshal(), + ValidatorIndex: validatorIdx, + AttesterSlot: slot, + }, + }, + } + + validator.distributed = true + validator.attSelections = make(map[attSelectionKey]shared.BeaconCommitteeSelection) + validator.attSelections[attSelectionKey{ + slot: slot, + index: 123, + }] = shared.BeaconCommitteeSelection{ + SelectionProof: make([]byte, 96), + Slot: slot, + ValidatorIndex: validatorIdx, + } + + m.validatorClient.EXPECT().SubmitAggregateSelectionProof( + gomock.Any(), // ctx + gomock.AssignableToTypeOf(ðpb.AggregateSelectionRequest{}), + ).Return(ðpb.AggregateSelectionResponse{ + AggregateAndProof: ðpb.AggregateAttestationAndProof{ + AggregatorIndex: 0, + Aggregate: util.HydrateAttestation(ðpb.Attestation{ + AggregationBits: make([]byte, 1), + }), + SelectionProof: make([]byte, 96), + }, + }, nil) + + m.validatorClient.EXPECT().DomainData( + gomock.Any(), // ctx + gomock.Any(), // epoch + ).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/) + + m.validatorClient.EXPECT().SubmitSignedAggregateSelectionProof( + gomock.Any(), // ctx + gomock.AssignableToTypeOf(ðpb.SignedAggregateSubmitRequest{}), + ).Return(ðpb.SignedAggregateSubmitResponse{AttestationDataRoot: make([]byte, 32)}, nil) + + validator.SubmitAggregateAndProof(ctx, slot, pubKey) +} + func TestWaitForSlotTwoThird_WaitCorrectly(t *testing.T) { validator, _, _, finish := setup(t) defer finish() diff --git a/validator/client/beacon-api/BUILD.bazel b/validator/client/beacon-api/BUILD.bazel index fad6d0ea3bb0..b98a8cb90dab 100644 --- a/validator/client/beacon-api/BUILD.bazel +++ b/validator/client/beacon-api/BUILD.bazel @@ -12,6 +12,7 @@ go_library( "beacon_block_converter.go", "beacon_block_json_helpers.go", "beacon_block_proto_helpers.go", + "beacon_committee_selections.go", "domain_data.go", "doppelganger.go", "duties.go", diff --git a/validator/client/beacon-api/activation_test.go b/validator/client/beacon-api/activation_test.go index dd1b8fb637bd..7b3ffd868412 100644 --- a/validator/client/beacon-api/activation_test.go +++ b/validator/client/beacon-api/activation_test.go @@ -1,9 +1,8 @@ package beacon_api import ( - "bytes" "context" - "encoding/json" + "net/url" "testing" "time" @@ -111,20 +110,25 @@ func TestActivation_Nominal(t *testing.T) { Ids: stringPubKeys, Statuses: []string{}, } - reqBytes, err := json.Marshal(req) - require.NoError(t, err) + queryParams := url.Values{} + for _, id := range req.Ids { + queryParams.Add("id", id) + } + for _, st := range req.Statuses { + queryParams.Add("status", st) + } + + query := buildURL("/eth/v1/beacon/states/head/validators", queryParams) // Get does not return any result for non existing key - jsonRestHandler.EXPECT().Post( + jsonRestHandler.EXPECT().Get( ctx, - "/eth/v1/beacon/states/head/validators", - nil, - bytes.NewBuffer(reqBytes), + query, &stateValidatorsResponseJson, ).Return( nil, ).SetArg( - 4, + 2, beacon.GetValidatorsResponse{ Data: []*beacon.ValidatorContainer{ { @@ -239,16 +243,14 @@ func TestActivation_InvalidData(t *testing.T) { ctx := context.Background() jsonRestHandler := mock.NewMockJsonRestHandler(ctrl) - jsonRestHandler.EXPECT().Post( + jsonRestHandler.EXPECT().Get( ctx, gomock.Any(), gomock.Any(), - gomock.Any(), - gomock.Any(), ).Return( nil, ).SetArg( - 4, + 2, beacon.GetValidatorsResponse{ Data: testCase.data, }, @@ -280,12 +282,10 @@ func TestActivation_JsonResponseError(t *testing.T) { ctx := context.Background() jsonRestHandler := mock.NewMockJsonRestHandler(ctrl) - jsonRestHandler.EXPECT().Post( + jsonRestHandler.EXPECT().Get( ctx, gomock.Any(), gomock.Any(), - gomock.Any(), - gomock.Any(), ).Return( errors.New("some specific json error"), ).Times(1) diff --git a/validator/client/beacon-api/beacon_api_validator_client.go b/validator/client/beacon-api/beacon_api_validator_client.go index df7fc6a06333..db8050e4d789 100644 --- a/validator/client/beacon-api/beacon_api_validator_client.go +++ b/validator/client/beacon-api/beacon_api_validator_client.go @@ -4,6 +4,8 @@ import ( "context" "time" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/golang/protobuf/ptypes/empty" "github.com/pkg/errors" @@ -175,3 +177,7 @@ func (c *beaconApiValidatorClient) StartEventStream(ctx context.Context) error { func (c *beaconApiValidatorClient) EventStreamIsRunning() bool { return c.eventHandler.running } + +func (c *beaconApiValidatorClient) GetAggregatedSelections(ctx context.Context, selections []shared.BeaconCommitteeSelection) ([]shared.BeaconCommitteeSelection, error) { + return c.getAggregatedSelection(ctx, selections) +} diff --git a/validator/client/beacon-api/beacon_committee_selections.go b/validator/client/beacon-api/beacon_committee_selections.go new file mode 100644 index 000000000000..f1424192de14 --- /dev/null +++ b/validator/client/beacon-api/beacon_committee_selections.go @@ -0,0 +1,40 @@ +package beacon_api + +import ( + "bytes" + "context" + "encoding/json" + + "github.com/pkg/errors" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" +) + +type aggregatedSelectionResponse struct { + Data []shared.BeaconCommitteeSelection `json:"data"` +} + +func (c *beaconApiValidatorClient) getAggregatedSelection(ctx context.Context, selections []shared.BeaconCommitteeSelection) ([]shared.BeaconCommitteeSelection, error) { + body, err := json.Marshal(selections) + if err != nil { + return nil, errors.Wrap(err, "marshal request body selections") + } + + var resp aggregatedSelectionResponse + errJson, err := c.jsonRestHandler.Post(ctx, "/eth/v1/validator/beacon_committee_selections", nil, bytes.NewBuffer(body), &resp) + if err != nil { + return nil, errors.Wrap(err, "error calling post endpoint") + } + if errJson != nil { + return nil, errJson + } + + if len(resp.Data) == 0 { + return nil, errors.New("no aggregated selection returned") + } + + if len(selections) != len(resp.Data) { + return nil, errors.New("mismatching number of selections") + } + + return resp.Data, nil +} diff --git a/validator/client/beacon-api/beacon_committee_selections_test.go b/validator/client/beacon-api/beacon_committee_selections_test.go new file mode 100644 index 000000000000..97e5f59e3b51 --- /dev/null +++ b/validator/client/beacon-api/beacon_committee_selections_test.go @@ -0,0 +1,124 @@ +package beacon_api + +import ( + "bytes" + "context" + "encoding/json" + "testing" + + "github.com/golang/mock/gomock" + "github.com/pkg/errors" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" + "github.com/prysmaticlabs/prysm/v4/testing/require" + "github.com/prysmaticlabs/prysm/v4/validator/client/beacon-api/mock" + test_helpers "github.com/prysmaticlabs/prysm/v4/validator/client/beacon-api/test-helpers" +) + +func TestGetAggregatedSelections(t *testing.T) { + testcases := []struct { + name string + req []shared.BeaconCommitteeSelection + res []shared.BeaconCommitteeSelection + endpointError error + expectedErrorMessage string + }{ + { + name: "valid", + req: []shared.BeaconCommitteeSelection{ + { + SelectionProof: test_helpers.FillByteSlice(96, 82), + Slot: 75, + ValidatorIndex: 76, + }, + }, + res: []shared.BeaconCommitteeSelection{ + { + SelectionProof: test_helpers.FillByteSlice(96, 100), + Slot: 75, + ValidatorIndex: 76, + }, + }, + }, + { + name: "endpoint error", + req: []shared.BeaconCommitteeSelection{ + { + SelectionProof: test_helpers.FillByteSlice(96, 82), + Slot: 75, + ValidatorIndex: 76, + }, + }, + endpointError: errors.New("bad request"), + expectedErrorMessage: "bad request", + }, + { + name: "no response error", + req: []shared.BeaconCommitteeSelection{ + { + SelectionProof: test_helpers.FillByteSlice(96, 82), + Slot: 75, + ValidatorIndex: 76, + }, + }, + expectedErrorMessage: "no aggregated selection returned", + }, + { + name: "mismatch response", + req: []shared.BeaconCommitteeSelection{ + { + SelectionProof: test_helpers.FillByteSlice(96, 82), + Slot: 75, + ValidatorIndex: 76, + }, + { + SelectionProof: test_helpers.FillByteSlice(96, 102), + Slot: 75, + ValidatorIndex: 79, + }, + }, + res: []shared.BeaconCommitteeSelection{ + { + SelectionProof: test_helpers.FillByteSlice(96, 100), + Slot: 75, + ValidatorIndex: 76, + }, + }, + expectedErrorMessage: "mismatching number of selections", + }, + } + + for _, test := range testcases { + t.Run(test.name, func(t *testing.T) { + ctrl := gomock.NewController(t) + jsonRestHandler := mock.NewMockJsonRestHandler(ctrl) + + reqBody, err := json.Marshal(test.req) + require.NoError(t, err) + + ctx := context.Background() + jsonRestHandler.EXPECT().Post( + ctx, + "/eth/v1/validator/beacon_committee_selections", + nil, + bytes.NewBuffer(reqBody), + &aggregatedSelectionResponse{}, + ).SetArg( + 4, + aggregatedSelectionResponse{Data: test.res}, + ).Return( + nil, + test.endpointError, + ).Times(1) + + validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler} + res, err := validatorClient.GetAggregatedSelections(ctx, test.req) + if test.expectedErrorMessage != "" { + require.ErrorContains(t, test.expectedErrorMessage, err) + return + } + + require.NoError(t, err) + require.DeepEqual(t, test.res, res) + }) + } +} diff --git a/validator/client/beacon-api/index_test.go b/validator/client/beacon-api/index_test.go index 8ec1f0923077..f2202bace5c6 100644 --- a/validator/client/beacon-api/index_test.go +++ b/validator/client/beacon-api/index_test.go @@ -1,9 +1,8 @@ package beacon_api import ( - "bytes" "context" - "encoding/json" + "net/url" "testing" "github.com/ethereum/go-ethereum/common/hexutil" @@ -19,38 +18,43 @@ import ( const stringPubKey = "0x8000091c2ae64ee414a54c1cc1fc67dec663408bc636cb86756e0200e41a75c8f86603f104f02c856983d2783116be13" -func getPubKeyAndReqBuffer(t *testing.T) ([]byte, *bytes.Buffer) { +func getPubKeyAndQueryPath(t *testing.T) ([]byte, string) { pubKey, err := hexutil.Decode(stringPubKey) require.NoError(t, err) req := beacon.GetValidatorsRequest{ Ids: []string{stringPubKey}, Statuses: []string{}, } - reqBytes, err := json.Marshal(req) - require.NoError(t, err) - return pubKey, bytes.NewBuffer(reqBytes) + + queryParams := url.Values{} + for _, id := range req.Ids { + queryParams.Add("id", id) + } + for _, st := range req.Statuses { + queryParams.Add("status", st) + } + + return pubKey, buildURL("/eth/v1/beacon/states/head/validators", queryParams) } func TestIndex_Nominal(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - pubKey, reqBuffer := getPubKeyAndReqBuffer(t) + pubKey, query := getPubKeyAndQueryPath(t) ctx := context.Background() stateValidatorsResponseJson := beacon.GetValidatorsResponse{} jsonRestHandler := mock.NewMockJsonRestHandler(ctrl) - jsonRestHandler.EXPECT().Post( + jsonRestHandler.EXPECT().Get( ctx, - "/eth/v1/beacon/states/head/validators", - nil, - reqBuffer, + query, &stateValidatorsResponseJson, ).Return( nil, ).SetArg( - 4, + 2, beacon.GetValidatorsResponse{ Data: []*beacon.ValidatorContainer{ { @@ -85,22 +89,20 @@ func TestIndex_UnexistingValidator(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - pubKey, reqBuffer := getPubKeyAndReqBuffer(t) + pubKey, query := getPubKeyAndQueryPath(t) ctx := context.Background() stateValidatorsResponseJson := beacon.GetValidatorsResponse{} jsonRestHandler := mock.NewMockJsonRestHandler(ctrl) - jsonRestHandler.EXPECT().Post( + jsonRestHandler.EXPECT().Get( ctx, - "/eth/v1/beacon/states/head/validators", - nil, - reqBuffer, + query, &stateValidatorsResponseJson, ).Return( nil, ).SetArg( - 4, + 2, beacon.GetValidatorsResponse{ Data: []*beacon.ValidatorContainer{}, }, @@ -127,22 +129,20 @@ func TestIndex_BadIndexError(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - pubKey, reqBuffer := getPubKeyAndReqBuffer(t) + pubKey, query := getPubKeyAndQueryPath(t) ctx := context.Background() stateValidatorsResponseJson := beacon.GetValidatorsResponse{} jsonRestHandler := mock.NewMockJsonRestHandler(ctrl) - jsonRestHandler.EXPECT().Post( + jsonRestHandler.EXPECT().Get( ctx, - "/eth/v1/beacon/states/head/validators", - nil, - reqBuffer, + query, &stateValidatorsResponseJson, ).Return( nil, ).SetArg( - 4, + 2, beacon.GetValidatorsResponse{ Data: []*beacon.ValidatorContainer{ { @@ -176,17 +176,15 @@ func TestIndex_JsonResponseError(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - pubKey, reqBuffer := getPubKeyAndReqBuffer(t) + pubKey, query := getPubKeyAndQueryPath(t) ctx := context.Background() stateValidatorsResponseJson := beacon.GetValidatorsResponse{} jsonRestHandler := mock.NewMockJsonRestHandler(ctrl) - jsonRestHandler.EXPECT().Post( + jsonRestHandler.EXPECT().Get( ctx, - "/eth/v1/beacon/states/head/validators", - nil, - reqBuffer, + query, &stateValidatorsResponseJson, ).Return( errors.New("some specific json error"), diff --git a/validator/client/beacon-api/mock/beacon_block_converter_mock.go b/validator/client/beacon-api/mock/beacon_block_converter_mock.go index 07ee9d9254cd..cc7b1c4eea07 100644 --- a/validator/client/beacon-api/mock/beacon_block_converter_mock.go +++ b/validator/client/beacon-api/mock/beacon_block_converter_mock.go @@ -9,7 +9,7 @@ import ( gomock "github.com/golang/mock/gomock" shared "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" - eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" + v1alpha1 "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" ) // MockBeaconBlockConverter is a mock of BeaconBlockConverter interface. @@ -36,10 +36,10 @@ func (m *MockBeaconBlockConverter) EXPECT() *MockBeaconBlockConverterMockRecorde } // ConvertRESTAltairBlockToProto mocks base method. -func (m *MockBeaconBlockConverter) ConvertRESTAltairBlockToProto(block *shared.BeaconBlockAltair) (*eth.BeaconBlockAltair, error) { +func (m *MockBeaconBlockConverter) ConvertRESTAltairBlockToProto(block *shared.BeaconBlockAltair) (*v1alpha1.BeaconBlockAltair, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ConvertRESTAltairBlockToProto", block) - ret0, _ := ret[0].(*eth.BeaconBlockAltair) + ret0, _ := ret[0].(*v1alpha1.BeaconBlockAltair) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -51,10 +51,10 @@ func (mr *MockBeaconBlockConverterMockRecorder) ConvertRESTAltairBlockToProto(bl } // ConvertRESTBellatrixBlockToProto mocks base method. -func (m *MockBeaconBlockConverter) ConvertRESTBellatrixBlockToProto(block *shared.BeaconBlockBellatrix) (*eth.BeaconBlockBellatrix, error) { +func (m *MockBeaconBlockConverter) ConvertRESTBellatrixBlockToProto(block *shared.BeaconBlockBellatrix) (*v1alpha1.BeaconBlockBellatrix, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ConvertRESTBellatrixBlockToProto", block) - ret0, _ := ret[0].(*eth.BeaconBlockBellatrix) + ret0, _ := ret[0].(*v1alpha1.BeaconBlockBellatrix) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -66,10 +66,10 @@ func (mr *MockBeaconBlockConverterMockRecorder) ConvertRESTBellatrixBlockToProto } // ConvertRESTCapellaBlockToProto mocks base method. -func (m *MockBeaconBlockConverter) ConvertRESTCapellaBlockToProto(block *shared.BeaconBlockCapella) (*eth.BeaconBlockCapella, error) { +func (m *MockBeaconBlockConverter) ConvertRESTCapellaBlockToProto(block *shared.BeaconBlockCapella) (*v1alpha1.BeaconBlockCapella, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ConvertRESTCapellaBlockToProto", block) - ret0, _ := ret[0].(*eth.BeaconBlockCapella) + ret0, _ := ret[0].(*v1alpha1.BeaconBlockCapella) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -81,10 +81,10 @@ func (mr *MockBeaconBlockConverterMockRecorder) ConvertRESTCapellaBlockToProto(b } // ConvertRESTPhase0BlockToProto mocks base method. -func (m *MockBeaconBlockConverter) ConvertRESTPhase0BlockToProto(block *shared.BeaconBlock) (*eth.BeaconBlock, error) { +func (m *MockBeaconBlockConverter) ConvertRESTPhase0BlockToProto(block *shared.BeaconBlock) (*v1alpha1.BeaconBlock, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ConvertRESTPhase0BlockToProto", block) - ret0, _ := ret[0].(*eth.BeaconBlock) + ret0, _ := ret[0].(*v1alpha1.BeaconBlock) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/validator/client/beacon-api/mock/genesis_mock.go b/validator/client/beacon-api/mock/genesis_mock.go index caa7aa74d391..6dfb8a74ed1c 100644 --- a/validator/client/beacon-api/mock/genesis_mock.go +++ b/validator/client/beacon-api/mock/genesis_mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/prysmaticlabs/prysm/v4/validator/client/beacon-api (interfaces: GenesisProvider) +// Source: validator/client/beacon-api/genesis.go // Package mock is a generated GoMock package. package mock @@ -36,16 +36,16 @@ func (m *MockGenesisProvider) EXPECT() *MockGenesisProviderMockRecorder { } // GetGenesis mocks base method. -func (m *MockGenesisProvider) GetGenesis(arg0 context.Context) (*beacon.Genesis, error) { +func (m *MockGenesisProvider) GetGenesis(ctx context.Context) (*beacon.Genesis, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetGenesis", arg0) + ret := m.ctrl.Call(m, "GetGenesis", ctx) ret0, _ := ret[0].(*beacon.Genesis) ret1, _ := ret[1].(error) return ret0, ret1 } // GetGenesis indicates an expected call of GetGenesis. -func (mr *MockGenesisProviderMockRecorder) GetGenesis(arg0 interface{}) *gomock.Call { +func (mr *MockGenesisProviderMockRecorder) GetGenesis(ctx interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGenesis", reflect.TypeOf((*MockGenesisProvider)(nil).GetGenesis), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGenesis", reflect.TypeOf((*MockGenesisProvider)(nil).GetGenesis), ctx) } diff --git a/validator/client/beacon-api/mock/json_rest_handler_mock.go b/validator/client/beacon-api/mock/json_rest_handler_mock.go index ae95ecd74ffd..b939c1221b1c 100644 --- a/validator/client/beacon-api/mock/json_rest_handler_mock.go +++ b/validator/client/beacon-api/mock/json_rest_handler_mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/prysmaticlabs/prysm/v4/validator/client/beacon-api (interfaces: JsonRestHandler) +// Source: validator/client/beacon-api/json_rest_handler.go // Package mock is a generated GoMock package. package mock @@ -36,29 +36,29 @@ func (m *MockJsonRestHandler) EXPECT() *MockJsonRestHandlerMockRecorder { } // Get mocks base method. -func (m *MockJsonRestHandler) Get(arg0 context.Context, arg1 string, arg2 interface{}) error { +func (m *MockJsonRestHandler) Get(ctx context.Context, endpoint string, resp interface{}) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Get", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "Get", ctx, endpoint, resp) ret0, _ := ret[0].(error) return ret0 } // Get indicates an expected call of Get. -func (mr *MockJsonRestHandlerMockRecorder) Get(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockJsonRestHandlerMockRecorder) Get(ctx, endpoint, resp interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockJsonRestHandler)(nil).Get), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockJsonRestHandler)(nil).Get), ctx, endpoint, resp) } // Post mocks base method. -func (m *MockJsonRestHandler) Post(arg0 context.Context, arg1 string, arg2 map[string]string, arg3 *bytes.Buffer, arg4 interface{}) error { +func (m *MockJsonRestHandler) Post(ctx context.Context, endpoint string, headers map[string]string, data *bytes.Buffer, resp interface{}) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Post", arg0, arg1, arg2, arg3, arg4) + ret := m.ctrl.Call(m, "Post", ctx, endpoint, headers, data, resp) ret0, _ := ret[0].(error) return ret0 } // Post indicates an expected call of Post. -func (mr *MockJsonRestHandlerMockRecorder) Post(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { +func (mr *MockJsonRestHandlerMockRecorder) Post(ctx, endpoint, headers, data, resp interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Post", reflect.TypeOf((*MockJsonRestHandler)(nil).Post), arg0, arg1, arg2, arg3, arg4) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Post", reflect.TypeOf((*MockJsonRestHandler)(nil).Post), ctx, endpoint, headers, data, resp) } diff --git a/validator/client/beacon-api/state_validators.go b/validator/client/beacon-api/state_validators.go index 165ca0e40c01..5f276136a6b7 100644 --- a/validator/client/beacon-api/state_validators.go +++ b/validator/client/beacon-api/state_validators.go @@ -1,10 +1,9 @@ package beacon_api import ( - "bytes" "context" - "encoding/json" "fmt" + "net/url" "strconv" "github.com/pkg/errors" @@ -86,12 +85,19 @@ func (c beaconApiStateValidatorsProvider) getStateValidatorsHelper( } } - reqBytes, err := json.Marshal(req) - if err != nil { - return nil, errors.Wrapf(err, "failed to marshal request into JSON") + queryParams := url.Values{} + for _, id := range req.Ids { + queryParams.Add("id", id) + } + for _, st := range req.Statuses { + queryParams.Add("status", st) } + + query := buildURL(endpoint, queryParams) stateValidatorsJson := &beacon.GetValidatorsResponse{} - if err = c.jsonRestHandler.Post(ctx, endpoint, nil, bytes.NewBuffer(reqBytes), stateValidatorsJson); err != nil { + + err := c.jsonRestHandler.Get(ctx, query, stateValidatorsJson) + if err != nil { return nil, err } diff --git a/validator/client/beacon-api/state_validators_test.go b/validator/client/beacon-api/state_validators_test.go index 066ecd03a47a..660cff792d16 100644 --- a/validator/client/beacon-api/state_validators_test.go +++ b/validator/client/beacon-api/state_validators_test.go @@ -1,9 +1,8 @@ package beacon_api import ( - "bytes" "context" - "encoding/json" + "net/url" "testing" "github.com/golang/mock/gomock" @@ -29,8 +28,6 @@ func TestGetStateValidators_Nominal(t *testing.T) { }, Statuses: []string{"active_ongoing", "active_exiting", "exited_slashed", "exited_unslashed"}, } - reqBytes, err := json.Marshal(req) - require.NoError(t, err) stateValidatorsResponseJson := beacon.GetValidatorsResponse{} jsonRestHandler := mock.NewMockJsonRestHandler(ctrl) @@ -68,16 +65,24 @@ func TestGetStateValidators_Nominal(t *testing.T) { ctx := context.Background() - jsonRestHandler.EXPECT().Post( + queryParams := url.Values{} + for _, id := range req.Ids { + queryParams.Add("id", id) + } + for _, st := range req.Statuses { + queryParams.Add("status", st) + } + + query := buildURL("/eth/v1/beacon/states/head/validators", queryParams) + + jsonRestHandler.EXPECT().Get( ctx, - "/eth/v1/beacon/states/head/validators", - nil, - bytes.NewBuffer(reqBytes), + query, &stateValidatorsResponseJson, ).Return( nil, ).SetArg( - 4, + 2, beacon.GetValidatorsResponse{ Data: wanted, }, @@ -109,26 +114,32 @@ func TestGetStateValidators_GetRestJsonResponseOnError(t *testing.T) { Ids: []string{"0x8000091c2ae64ee414a54c1cc1fc67dec663408bc636cb86756e0200e41a75c8f86603f104f02c856983d2783116be13"}, Statuses: []string{}, } - reqBytes, err := json.Marshal(req) - require.NoError(t, err) stateValidatorsResponseJson := beacon.GetValidatorsResponse{} jsonRestHandler := mock.NewMockJsonRestHandler(ctrl) ctx := context.Background() - jsonRestHandler.EXPECT().Post( + queryParams := url.Values{} + for _, id := range req.Ids { + queryParams.Add("id", id) + } + for _, st := range req.Statuses { + queryParams.Add("status", st) + } + + query := buildURL("/eth/v1/beacon/states/head/validators", queryParams) + + jsonRestHandler.EXPECT().Get( ctx, - "/eth/v1/beacon/states/head/validators", - nil, - bytes.NewBuffer(reqBytes), + query, &stateValidatorsResponseJson, ).Return( errors.New("an error"), ).Times(1) stateValidatorsProvider := beaconApiStateValidatorsProvider{jsonRestHandler: jsonRestHandler} - _, err = stateValidatorsProvider.GetStateValidators(ctx, []string{ + _, err := stateValidatorsProvider.GetStateValidators(ctx, []string{ "0x8000091c2ae64ee414a54c1cc1fc67dec663408bc636cb86756e0200e41a75c8f86603f104f02c856983d2783116be13", // active_ongoing }, nil, @@ -145,29 +156,36 @@ func TestGetStateValidators_DataIsNil(t *testing.T) { Ids: []string{"0x8000091c2ae64ee414a54c1cc1fc67dec663408bc636cb86756e0200e41a75c8f86603f104f02c856983d2783116be13"}, Statuses: []string{}, } - reqBytes, err := json.Marshal(req) - require.NoError(t, err) ctx := context.Background() stateValidatorsResponseJson := beacon.GetValidatorsResponse{} jsonRestHandler := mock.NewMockJsonRestHandler(ctrl) - jsonRestHandler.EXPECT().Post( + queryParams := url.Values{} + for _, id := range req.Ids { + queryParams.Add("id", id) + } + for _, st := range req.Statuses { + queryParams.Add("status", st) + } + + query := buildURL("/eth/v1/beacon/states/head/validators", queryParams) + + jsonRestHandler.EXPECT().Get( ctx, - "/eth/v1/beacon/states/head/validators", - nil, bytes.NewBuffer(reqBytes), + query, &stateValidatorsResponseJson, ).Return( nil, ).SetArg( - 4, + 2, beacon.GetValidatorsResponse{ Data: nil, }, ).Times(1) stateValidatorsProvider := beaconApiStateValidatorsProvider{jsonRestHandler: jsonRestHandler} - _, err = stateValidatorsProvider.GetStateValidators(ctx, []string{ + _, err := stateValidatorsProvider.GetStateValidators(ctx, []string{ "0x8000091c2ae64ee414a54c1cc1fc67dec663408bc636cb86756e0200e41a75c8f86603f104f02c856983d2783116be13", // active_ongoing }, nil, diff --git a/validator/client/grpc-api/BUILD.bazel b/validator/client/grpc-api/BUILD.bazel index 8558091f7ab0..c0660d8659b0 100644 --- a/validator/client/grpc-api/BUILD.bazel +++ b/validator/client/grpc-api/BUILD.bazel @@ -12,6 +12,7 @@ go_library( visibility = ["//validator:__subpackages__"], deps = [ "//beacon-chain/rpc/eth/helpers:go_default_library", + "//beacon-chain/rpc/eth/shared:go_default_library", "//beacon-chain/state/state-native:go_default_library", "//consensus-types/primitives:go_default_library", "//consensus-types/validator:go_default_library", diff --git a/validator/client/grpc-api/grpc_validator_client.go b/validator/client/grpc-api/grpc_validator_client.go index c97e1a621709..d67e5975a086 100644 --- a/validator/client/grpc-api/grpc_validator_client.go +++ b/validator/client/grpc-api/grpc_validator_client.go @@ -3,6 +3,8 @@ package grpc_api import ( "context" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" + "github.com/golang/protobuf/ptypes/empty" "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives" @@ -138,6 +140,10 @@ func (c *grpcValidatorClient) AggregatedSigAndAggregationBits( return c.beaconNodeValidatorClient.AggregatedSigAndAggregationBits(ctx, in) } +func (grpcValidatorClient) GetAggregatedSelections(context.Context, []shared.BeaconCommitteeSelection) ([]shared.BeaconCommitteeSelection, error) { + return nil, iface.ErrNotSupported +} + func NewGrpcValidatorClient(cc grpc.ClientConnInterface) iface.ValidatorClient { return &grpcValidatorClient{ethpb.NewBeaconNodeValidatorClient(cc)} } diff --git a/validator/client/iface/BUILD.bazel b/validator/client/iface/BUILD.bazel index 4acfedda99f1..4fb9cae140a1 100644 --- a/validator/client/iface/BUILD.bazel +++ b/validator/client/iface/BUILD.bazel @@ -12,6 +12,7 @@ go_library( importpath = "github.com/prysmaticlabs/prysm/v4/validator/client/iface", visibility = ["//visibility:public"], deps = [ + "//beacon-chain/rpc/eth/shared:go_default_library", "//config/fieldparams:go_default_library", "//config/validator/service:go_default_library", "//consensus-types/primitives:go_default_library", diff --git a/validator/client/iface/validator_client.go b/validator/client/iface/validator_client.go index bb04fb3f72bf..255709bf9309 100644 --- a/validator/client/iface/validator_client.go +++ b/validator/client/iface/validator_client.go @@ -3,6 +3,8 @@ package iface import ( "context" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" + "github.com/golang/protobuf/ptypes/empty" "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives" ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" @@ -36,4 +38,5 @@ type ValidatorClient interface { SubmitValidatorRegistrations(ctx context.Context, in *ethpb.SignedValidatorRegistrationsV1) (*empty.Empty, error) StartEventStream(ctx context.Context) error EventStreamIsRunning() bool + GetAggregatedSelections(ctx context.Context, selections []shared.BeaconCommitteeSelection) ([]shared.BeaconCommitteeSelection, error) } diff --git a/validator/client/service.go b/validator/client/service.go index d10d3a80c2b1..6b064080e9f3 100644 --- a/validator/client/service.go +++ b/validator/client/service.go @@ -6,6 +6,8 @@ import ( "strings" "time" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" + "github.com/dgraph-io/ristretto" middleware "github.com/grpc-ecosystem/go-grpc-middleware" grpcretry "github.com/grpc-ecosystem/go-grpc-middleware/retry" @@ -76,6 +78,7 @@ type ValidatorService struct { Web3SignerConfig *remoteweb3signer.SetupConfig proposerSettings *validatorserviceconfig.ProposerSettings validatorsRegBatchSize int + distributed bool } // Config for the validator service. @@ -102,6 +105,7 @@ type Config struct { BeaconApiEndpoint string BeaconApiTimeout time.Duration ValidatorsRegBatchSize int + Distributed bool } // NewValidatorService creates a new validator service for the service @@ -131,6 +135,7 @@ func NewValidatorService(ctx context.Context, cfg *Config) (*ValidatorService, e Web3SignerConfig: cfg.Web3SignerConfig, proposerSettings: cfg.ProposerSettings, validatorsRegBatchSize: cfg.ValidatorsRegBatchSize, + distributed: cfg.Distributed, } dialOpts := ConstructDialOptions( @@ -230,6 +235,8 @@ func (v *ValidatorService) Start() { proposerSettings: v.proposerSettings, walletInitializedChannel: make(chan *wallet.Wallet, 1), validatorsRegBatchSize: v.validatorsRegBatchSize, + distributed: v.distributed, + attSelections: make(map[attSelectionKey]shared.BeaconCommitteeSelection), } v.validator = valStruct diff --git a/validator/client/validator.go b/validator/client/validator.go index 629fb9bc056c..44d720e5469f 100644 --- a/validator/client/validator.go +++ b/validator/client/validator.go @@ -15,6 +15,8 @@ import ( "sync" "time" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" + "github.com/dgraph-io/ristretto" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -105,6 +107,9 @@ type validator struct { proposerSettings *validatorserviceconfig.ProposerSettings walletInitializedChannel chan *wallet.Wallet validatorsRegBatchSize int + distributed bool + attSelectionLock sync.Mutex + attSelections map[attSelectionKey]shared.BeaconCommitteeSelection } type validatorStatus struct { @@ -113,6 +118,11 @@ type validatorStatus struct { index primitives.ValidatorIndex } +type attSelectionKey struct { + slot primitives.Slot + index primitives.ValidatorIndex +} + // Done cleans up the validator. func (v *validator) Done() { v.ticker.Done() @@ -629,6 +639,13 @@ func (v *validator) subscribeToSubnets(ctx context.Context, res *ethpb.DutiesRes subscribeValidatorIndices := make([]primitives.ValidatorIndex, 0, len(res.CurrentEpochDuties)+len(res.NextEpochDuties)) alreadySubscribed := make(map[[64]byte]bool) + if v.distributed { + // Get aggregated selection proofs to calculate isAggregator. + if err := v.getAggregatedSelectionProofs(ctx, res); err != nil { + return errors.Wrap(err, "could not get aggregated selection proofs") + } + } + for _, duty := range res.CurrentEpochDuties { pk := bytesutil.ToBytes48(duty.PublicKey) if duty.Status == ethpb.ValidatorStatus_ACTIVE || duty.Status == ethpb.ValidatorStatus_EXITING { @@ -641,7 +658,7 @@ func (v *validator) subscribeToSubnets(ctx context.Context, res *ethpb.DutiesRes continue } - aggregator, err := v.isAggregator(ctx, duty.Committee, attesterSlot, pk) + aggregator, err := v.isAggregator(ctx, duty.Committee, attesterSlot, pk, validatorIndex) if err != nil { return errors.Wrap(err, "could not check if a validator is an aggregator") } @@ -667,7 +684,7 @@ func (v *validator) subscribeToSubnets(ctx context.Context, res *ethpb.DutiesRes continue } - aggregator, err := v.isAggregator(ctx, duty.Committee, attesterSlot, bytesutil.ToBytes48(duty.PublicKey)) + aggregator, err := v.isAggregator(ctx, duty.Committee, attesterSlot, bytesutil.ToBytes48(duty.PublicKey), validatorIndex) if err != nil { return errors.Wrap(err, "could not check if a validator is an aggregator") } @@ -718,7 +735,7 @@ func (v *validator) RolesAt(ctx context.Context, slot primitives.Slot) (map[[fie if duty.AttesterSlot == slot { roles = append(roles, iface.RoleAttester) - aggregator, err := v.isAggregator(ctx, duty.Committee, slot, bytesutil.ToBytes48(duty.PublicKey)) + aggregator, err := v.isAggregator(ctx, duty.Committee, slot, bytesutil.ToBytes48(duty.PublicKey), duty.ValidatorIndex) if err != nil { return nil, errors.Wrap(err, "could not check if a validator is an aggregator") } @@ -773,15 +790,26 @@ func (v *validator) Keymanager() (keymanager.IKeymanager, error) { // isAggregator checks if a validator is an aggregator of a given slot and committee, // it uses a modulo calculated by validator count in committee and samples randomness around it. -func (v *validator) isAggregator(ctx context.Context, committee []primitives.ValidatorIndex, slot primitives.Slot, pubKey [fieldparams.BLSPubkeyLength]byte) (bool, error) { +func (v *validator) isAggregator(ctx context.Context, committee []primitives.ValidatorIndex, slot primitives.Slot, pubKey [fieldparams.BLSPubkeyLength]byte, validatorIndex primitives.ValidatorIndex) (bool, error) { modulo := uint64(1) if len(committee)/int(params.BeaconConfig().TargetAggregatorsPerCommittee) > 1 { modulo = uint64(len(committee)) / params.BeaconConfig().TargetAggregatorsPerCommittee } - slotSig, err := v.signSlotWithSelectionProof(ctx, pubKey, slot) - if err != nil { - return false, err + var ( + slotSig []byte + err error + ) + if v.distributed { + slotSig, err = v.getAttSelection(attSelectionKey{slot: slot, index: validatorIndex}) + if err != nil { + return false, err + } + } else { + slotSig, err = v.signSlotWithSelectionProof(ctx, pubKey, slot) + if err != nil { + return false, err + } } b := hash.Hash(slotSig) @@ -1230,6 +1258,89 @@ func (v *validator) validatorIndex(ctx context.Context, pubkey [fieldparams.BLSP return resp.Index, true, nil } +func (v *validator) getAggregatedSelectionProofs(ctx context.Context, duties *ethpb.DutiesResponse) error { + // Create new instance of attestation selections map. + v.newAttSelections() + + var req []shared.BeaconCommitteeSelection + for _, duty := range duties.CurrentEpochDuties { + if duty.Status != ethpb.ValidatorStatus_ACTIVE && duty.Status != ethpb.ValidatorStatus_EXITING { + continue + } + + pk := bytesutil.ToBytes48(duty.PublicKey) + slotSig, err := v.signSlotWithSelectionProof(ctx, pk, duty.AttesterSlot) + if err != nil { + return err + } + + req = append(req, shared.BeaconCommitteeSelection{ + SelectionProof: slotSig, + Slot: duty.AttesterSlot, + ValidatorIndex: duty.ValidatorIndex, + }) + } + + for _, duty := range duties.NextEpochDuties { + if duty.Status != ethpb.ValidatorStatus_ACTIVE && duty.Status != ethpb.ValidatorStatus_EXITING { + continue + } + + pk := bytesutil.ToBytes48(duty.PublicKey) + slotSig, err := v.signSlotWithSelectionProof(ctx, pk, duty.AttesterSlot) + if err != nil { + return err + } + + req = append(req, shared.BeaconCommitteeSelection{ + SelectionProof: slotSig, + Slot: duty.AttesterSlot, + ValidatorIndex: duty.ValidatorIndex, + }) + } + + resp, err := v.validatorClient.GetAggregatedSelections(ctx, req) + if err != nil { + return err + } + + // Store aggregated selection proofs in state. + v.addAttSelections(resp) + + return nil +} + +func (v *validator) addAttSelections(selections []shared.BeaconCommitteeSelection) { + v.attSelectionLock.Lock() + defer v.attSelectionLock.Unlock() + + for _, s := range selections { + v.attSelections[attSelectionKey{ + slot: s.Slot, + index: s.ValidatorIndex, + }] = s + } +} + +func (v *validator) newAttSelections() { + v.attSelectionLock.Lock() + defer v.attSelectionLock.Unlock() + + v.attSelections = make(map[attSelectionKey]shared.BeaconCommitteeSelection) +} + +func (v *validator) getAttSelection(key attSelectionKey) ([]byte, error) { + v.attSelectionLock.Lock() + defer v.attSelectionLock.Unlock() + + s, ok := v.attSelections[key] + if !ok { + return nil, errors.Errorf("selection proof not found for the given slot=%d and validator_index=%d", key.slot, key.index) + } + + return s.SelectionProof, nil +} + // This constructs a validator subscribed key, it's used to track // which subnet has already been pending requested. func validatorSubscribeKey(slot primitives.Slot, committeeID primitives.CommitteeIndex) [64]byte { diff --git a/validator/client/validator_test.go b/validator/client/validator_test.go index d041a3d87b15..3778bbdb80c5 100644 --- a/validator/client/validator_test.go +++ b/validator/client/validator_test.go @@ -11,6 +11,8 @@ import ( "testing" "time" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/golang/mock/gomock" @@ -639,6 +641,92 @@ func TestUpdateDuties_AllValidatorsExited(t *testing.T) { } +func TestUpdateDuties_Distributed(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + client := validatormock.NewMockValidatorClient(ctrl) + + // Start of third epoch. + slot := 2 * params.BeaconConfig().SlotsPerEpoch + keys := randKeypair(t) + resp := ðpb.DutiesResponse{ + CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{ + { + AttesterSlot: slot, // First slot in epoch. + ValidatorIndex: 200, + CommitteeIndex: 100, + PublicKey: keys.pub[:], + Status: ethpb.ValidatorStatus_ACTIVE, + }, + }, + NextEpochDuties: []*ethpb.DutiesResponse_Duty{ + { + AttesterSlot: slot + params.BeaconConfig().SlotsPerEpoch, // First slot in next epoch. + ValidatorIndex: 200, + CommitteeIndex: 100, + PublicKey: keys.pub[:], + Status: ethpb.ValidatorStatus_ACTIVE, + }, + }, + } + + v := validator{ + keyManager: newMockKeymanager(t, keys), + validatorClient: client, + distributed: true, + } + + sigDomain := make([]byte, 32) + + client.EXPECT().GetDuties( + gomock.Any(), + gomock.Any(), + ).Return(resp, nil) + + client.EXPECT().DomainData( + gomock.Any(), // ctx + gomock.Any(), // epoch + ).Return( + ðpb.DomainResponse{SignatureDomain: sigDomain}, + nil, /*err*/ + ).Times(2) + + client.EXPECT().GetAggregatedSelections( + gomock.Any(), + gomock.Any(), // fill this properly + ).Return( + []shared.BeaconCommitteeSelection{ + { + SelectionProof: make([]byte, 32), + Slot: slot, + ValidatorIndex: 200, + }, + { + SelectionProof: make([]byte, 32), + Slot: slot + params.BeaconConfig().SlotsPerEpoch, + ValidatorIndex: 200, + }, + }, + nil, + ) + + var wg sync.WaitGroup + wg.Add(1) + + client.EXPECT().SubscribeCommitteeSubnets( + gomock.Any(), + gomock.Any(), + gomock.Any(), + ).DoAndReturn(func(_ context.Context, _ *ethpb.CommitteeSubnetsSubscribeRequest, _ []primitives.ValidatorIndex) (*emptypb.Empty, error) { + wg.Done() + return nil, nil + }) + + require.NoError(t, v.UpdateDuties(context.Background(), slot), "Could not update assignments") + util.WaitTimeout(&wg, 2*time.Second) + require.Equal(t, 2, len(v.attSelections)) +} + func TestRolesAt_OK(t *testing.T) { v, m, validatorKey, finish := setup(t) defer finish() diff --git a/validator/node/node.go b/validator/node/node.go index dc33784abf3f..b343c34189bf 100644 --- a/validator/node/node.go +++ b/validator/node/node.go @@ -497,6 +497,7 @@ func (c *ValidatorClient) registerValidatorService(cliCtx *cli.Context) error { BeaconApiTimeout: time.Second * 30, BeaconApiEndpoint: c.cliCtx.String(flags.BeaconRESTApiProviderFlag.Name), ValidatorsRegBatchSize: c.cliCtx.Int(flags.ValidatorsRegistrationBatchSizeFlag.Name), + Distributed: c.cliCtx.Bool(flags.EnableDistributed.Name), }) if err != nil { return errors.Wrap(err, "could not initialize validator service") From 718122b47a5d782fc5f39ba1a41630bf597c6573 Mon Sep 17 00:00:00 2001 From: Dhruv Bodani Date: Tue, 23 Jan 2024 17:41:26 +0530 Subject: [PATCH 02/19] fix build --- validator/client/beacon-api/beacon_committee_selections.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/validator/client/beacon-api/beacon_committee_selections.go b/validator/client/beacon-api/beacon_committee_selections.go index f1424192de14..94a9c230a322 100644 --- a/validator/client/beacon-api/beacon_committee_selections.go +++ b/validator/client/beacon-api/beacon_committee_selections.go @@ -20,13 +20,10 @@ func (c *beaconApiValidatorClient) getAggregatedSelection(ctx context.Context, s } var resp aggregatedSelectionResponse - errJson, err := c.jsonRestHandler.Post(ctx, "/eth/v1/validator/beacon_committee_selections", nil, bytes.NewBuffer(body), &resp) + err := c.jsonRestHandler.Post(ctx, "/eth/v1/validator/beacon_committee_selections", nil, bytes.NewBuffer(body), &resp) if err != nil { return nil, errors.Wrap(err, "error calling post endpoint") } - if errJson != nil { - return nil, errJson - } if len(resp.Data) == 0 { return nil, errors.New("no aggregated selection returned") From 5ec3ee50eff96774c118d2078d291a0723c4ac69 Mon Sep 17 00:00:00 2001 From: Dhruv Bodani Date: Tue, 23 Jan 2024 17:45:06 +0530 Subject: [PATCH 03/19] fix lint --- validator/client/beacon-api/beacon_committee_selections.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator/client/beacon-api/beacon_committee_selections.go b/validator/client/beacon-api/beacon_committee_selections.go index 94a9c230a322..90485fdcf623 100644 --- a/validator/client/beacon-api/beacon_committee_selections.go +++ b/validator/client/beacon-api/beacon_committee_selections.go @@ -20,7 +20,7 @@ func (c *beaconApiValidatorClient) getAggregatedSelection(ctx context.Context, s } var resp aggregatedSelectionResponse - err := c.jsonRestHandler.Post(ctx, "/eth/v1/validator/beacon_committee_selections", nil, bytes.NewBuffer(body), &resp) + err = c.jsonRestHandler.Post(ctx, "/eth/v1/validator/beacon_committee_selections", nil, bytes.NewBuffer(body), &resp) if err != nil { return nil, errors.Wrap(err, "error calling post endpoint") } From cb68c7afe430957a3181404846e131e27f914efa Mon Sep 17 00:00:00 2001 From: Dhruv Bodani Date: Tue, 23 Jan 2024 17:48:38 +0530 Subject: [PATCH 04/19] fix lint --- validator/client/validator_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/validator/client/validator_test.go b/validator/client/validator_test.go index 3778bbdb80c5..00f08acded53 100644 --- a/validator/client/validator_test.go +++ b/validator/client/validator_test.go @@ -2261,7 +2261,7 @@ func TestValidator_buildSignedRegReqs_DefaultConfigDisabled(t *testing.T) { ctx := context.Background() client := validatormock.NewMockValidatorClient(ctrl) - signature := blsmock.NewSignature(ctrl) + signature := blsmock.NewMockSignature(ctrl) signature.EXPECT().Marshal().Return([]byte{}) v := validator{ @@ -2346,7 +2346,7 @@ func TestValidator_buildSignedRegReqs_DefaultConfigEnabled(t *testing.T) { ctx := context.Background() client := validatormock.NewMockValidatorClient(ctrl) - signature := blsmock.NewSignature(ctrl) + signature := blsmock.NewMockSignature(ctrl) signature.EXPECT().Marshal().Return([]byte{}).Times(2) v := validator{ @@ -2468,7 +2468,7 @@ func TestValidator_buildSignedRegReqs_TimestampBeforeGenesis(t *testing.T) { ctx := context.Background() client := validatormock.NewMockValidatorClient(ctrl) - signature := blsmock.NewSignature(ctrl) + signature := blsmock.NewMockSignature(ctrl) v := validator{ signedValidatorRegistrations: map[[48]byte]*ethpb.SignedValidatorRegistrationV1{}, From a3b367bece97bf4753e7de5e73159fac57dc02d1 Mon Sep 17 00:00:00 2001 From: Dhruv Bodani Date: Mon, 29 Jan 2024 21:30:18 +0530 Subject: [PATCH 05/19] Update beacon-chain/rpc/eth/shared/structs.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: RadosÅ‚aw Kapka --- beacon-chain/rpc/eth/shared/structs.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beacon-chain/rpc/eth/shared/structs.go b/beacon-chain/rpc/eth/shared/structs.go index d61e433f50fa..264ce43a89e5 100644 --- a/beacon-chain/rpc/eth/shared/structs.go +++ b/beacon-chain/rpc/eth/shared/structs.go @@ -241,7 +241,7 @@ func (b *BeaconCommitteeSelection) UnmarshalJSON(input []byte) error { var bjson beaconCommitteeSelectionJson err := json.Unmarshal(input, &bjson) if err != nil { - return errors.Wrap(err, "unmarshal beacon committee selection") + return errors.Wrap(err, "failed to unmarshal beacon committee selection") } slot, err := strconv.ParseUint(bjson.Slot, 10, 64) From 09d182a3adc6fb4260cb5b5ec08692171e418a60 Mon Sep 17 00:00:00 2001 From: Dhruv Bodani Date: Mon, 29 Jan 2024 21:30:30 +0530 Subject: [PATCH 06/19] Update validator/client/beacon-api/beacon_committee_selections.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: RadosÅ‚aw Kapka --- validator/client/beacon-api/beacon_committee_selections.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator/client/beacon-api/beacon_committee_selections.go b/validator/client/beacon-api/beacon_committee_selections.go index 90485fdcf623..3f0bb23f3d34 100644 --- a/validator/client/beacon-api/beacon_committee_selections.go +++ b/validator/client/beacon-api/beacon_committee_selections.go @@ -16,7 +16,7 @@ type aggregatedSelectionResponse struct { func (c *beaconApiValidatorClient) getAggregatedSelection(ctx context.Context, selections []shared.BeaconCommitteeSelection) ([]shared.BeaconCommitteeSelection, error) { body, err := json.Marshal(selections) if err != nil { - return nil, errors.Wrap(err, "marshal request body selections") + return nil, errors.Wrap(err, "failed to marshal selections") } var resp aggregatedSelectionResponse From 80d1e99523bbc553780adadd071138dff3dee7fa Mon Sep 17 00:00:00 2001 From: Dhruv Bodani Date: Mon, 29 Jan 2024 21:30:36 +0530 Subject: [PATCH 07/19] Update validator/client/beacon-api/beacon_committee_selections.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: RadosÅ‚aw Kapka --- validator/client/beacon-api/beacon_committee_selections.go | 1 - 1 file changed, 1 deletion(-) diff --git a/validator/client/beacon-api/beacon_committee_selections.go b/validator/client/beacon-api/beacon_committee_selections.go index 3f0bb23f3d34..140fc5108d2b 100644 --- a/validator/client/beacon-api/beacon_committee_selections.go +++ b/validator/client/beacon-api/beacon_committee_selections.go @@ -28,7 +28,6 @@ func (c *beaconApiValidatorClient) getAggregatedSelection(ctx context.Context, s if len(resp.Data) == 0 { return nil, errors.New("no aggregated selection returned") } - if len(selections) != len(resp.Data) { return nil, errors.New("mismatching number of selections") } From 7ee986a520c85441477dfe22d6a1c32e9d6a392f Mon Sep 17 00:00:00 2001 From: Dhruv Bodani Date: Mon, 29 Jan 2024 21:30:43 +0530 Subject: [PATCH 08/19] Update validator/client/beacon-api/beacon_committee_selections.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: RadosÅ‚aw Kapka --- validator/client/beacon-api/beacon_committee_selections.go | 1 - 1 file changed, 1 deletion(-) diff --git a/validator/client/beacon-api/beacon_committee_selections.go b/validator/client/beacon-api/beacon_committee_selections.go index 140fc5108d2b..5aa7117ba436 100644 --- a/validator/client/beacon-api/beacon_committee_selections.go +++ b/validator/client/beacon-api/beacon_committee_selections.go @@ -24,7 +24,6 @@ func (c *beaconApiValidatorClient) getAggregatedSelection(ctx context.Context, s if err != nil { return nil, errors.Wrap(err, "error calling post endpoint") } - if len(resp.Data) == 0 { return nil, errors.New("no aggregated selection returned") } From f02e9e5feeaa17194bcd92d83ebb0292daba0309 Mon Sep 17 00:00:00 2001 From: Dhruv Bodani Date: Wed, 31 Jan 2024 14:38:00 +0530 Subject: [PATCH 09/19] move beacon committee selection structs to validator module --- beacon-chain/rpc/eth/shared/structs.go | 58 ------------------- beacon-chain/rpc/eth/validator/structs.go | 54 +++++++++++++++++ .../validator-mock/validator_client_mock.go | 6 +- validator/client/aggregate_test.go | 6 +- .../beacon-api/beacon_api_validator_client.go | 4 +- .../beacon-api/beacon_committee_selections.go | 7 ++- .../beacon_committee_selections_test.go | 19 +++--- .../client/grpc-api/grpc_validator_client.go | 4 +- validator/client/iface/validator_client.go | 4 +- validator/client/service.go | 4 +- validator/client/validator.go | 14 ++--- validator/client/validator_test.go | 4 +- 12 files changed, 91 insertions(+), 93 deletions(-) diff --git a/beacon-chain/rpc/eth/shared/structs.go b/beacon-chain/rpc/eth/shared/structs.go index 264ce43a89e5..93fb926ecf5a 100644 --- a/beacon-chain/rpc/eth/shared/structs.go +++ b/beacon-chain/rpc/eth/shared/structs.go @@ -1,14 +1,5 @@ package shared -import ( - "encoding/json" - "strconv" - - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/pkg/errors" - "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives" -) - type Validator struct { PublicKey string `json:"pubkey"` WithdrawalCredentials string `json:"withdrawal_credentials"` @@ -216,52 +207,3 @@ type Withdrawal struct { ExecutionAddress string `json:"address"` Amount string `json:"amount"` } - -type BeaconCommitteeSelection struct { - SelectionProof []byte - Slot primitives.Slot - ValidatorIndex primitives.ValidatorIndex -} - -type beaconCommitteeSelectionJson struct { - SelectionProof string `json:"selection_proof"` - Slot string `json:"slot"` - ValidatorIndex string `json:"validator_index"` -} - -func (b BeaconCommitteeSelection) MarshalJSON() ([]byte, error) { - return json.Marshal(beaconCommitteeSelectionJson{ - SelectionProof: hexutil.Encode(b.SelectionProof), - Slot: strconv.FormatUint(uint64(b.Slot), 10), - ValidatorIndex: strconv.FormatUint(uint64(b.ValidatorIndex), 10), - }) -} - -func (b *BeaconCommitteeSelection) UnmarshalJSON(input []byte) error { - var bjson beaconCommitteeSelectionJson - err := json.Unmarshal(input, &bjson) - if err != nil { - return errors.Wrap(err, "failed to unmarshal beacon committee selection") - } - - slot, err := strconv.ParseUint(bjson.Slot, 10, 64) - if err != nil { - return errors.Wrap(err, "failed to parse slot") - } - - vIdx, err := strconv.ParseUint(bjson.ValidatorIndex, 10, 64) - if err != nil { - return errors.Wrap(err, "failed to parse validator index") - } - - selectionProof, err := hexutil.Decode(bjson.SelectionProof) - if err != nil { - return errors.Wrap(err, "failed to parse selection proof") - } - - b.Slot = primitives.Slot(slot) - b.SelectionProof = selectionProof - b.ValidatorIndex = primitives.ValidatorIndex(vIdx) - - return nil -} diff --git a/beacon-chain/rpc/eth/validator/structs.go b/beacon-chain/rpc/eth/validator/structs.go index c58bac7f0fd8..31c8e55b18d6 100644 --- a/beacon-chain/rpc/eth/validator/structs.go +++ b/beacon-chain/rpc/eth/validator/structs.go @@ -2,6 +2,11 @@ package validator import ( "encoding/json" + "strconv" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/pkg/errors" + "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" ) @@ -90,3 +95,52 @@ type Liveness struct { Index string `json:"index"` IsLive bool `json:"is_live"` } + +type BeaconCommitteeSelection struct { + SelectionProof []byte + Slot primitives.Slot + ValidatorIndex primitives.ValidatorIndex +} + +type beaconCommitteeSelectionJson struct { + SelectionProof string `json:"selection_proof"` + Slot string `json:"slot"` + ValidatorIndex string `json:"validator_index"` +} + +func (b BeaconCommitteeSelection) MarshalJSON() ([]byte, error) { + return json.Marshal(beaconCommitteeSelectionJson{ + SelectionProof: hexutil.Encode(b.SelectionProof), + Slot: strconv.FormatUint(uint64(b.Slot), 10), + ValidatorIndex: strconv.FormatUint(uint64(b.ValidatorIndex), 10), + }) +} + +func (b *BeaconCommitteeSelection) UnmarshalJSON(input []byte) error { + var bjson beaconCommitteeSelectionJson + err := json.Unmarshal(input, &bjson) + if err != nil { + return errors.Wrap(err, "failed to unmarshal beacon committee selection") + } + + slot, err := strconv.ParseUint(bjson.Slot, 10, 64) + if err != nil { + return errors.Wrap(err, "failed to parse slot") + } + + vIdx, err := strconv.ParseUint(bjson.ValidatorIndex, 10, 64) + if err != nil { + return errors.Wrap(err, "failed to parse validator index") + } + + selectionProof, err := hexutil.Decode(bjson.SelectionProof) + if err != nil { + return errors.Wrap(err, "failed to parse selection proof") + } + + b.Slot = primitives.Slot(slot) + b.SelectionProof = selectionProof + b.ValidatorIndex = primitives.ValidatorIndex(vIdx) + + return nil +} diff --git a/testing/validator-mock/validator_client_mock.go b/testing/validator-mock/validator_client_mock.go index 5cee2646ed9a..398cd48027f6 100644 --- a/testing/validator-mock/validator_client_mock.go +++ b/testing/validator-mock/validator_client_mock.go @@ -9,7 +9,7 @@ import ( reflect "reflect" gomock "github.com/golang/mock/gomock" - shared "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/validator" primitives "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives" eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" emptypb "google.golang.org/protobuf/types/known/emptypb" @@ -83,10 +83,10 @@ func (mr *MockValidatorClientMockRecorder) EventStreamIsRunning() *gomock.Call { } // GetAggregatedSelections mocks base method. -func (m *MockValidatorClient) GetAggregatedSelections(arg0 context.Context, arg1 []shared.BeaconCommitteeSelection) ([]shared.BeaconCommitteeSelection, error) { +func (m *MockValidatorClient) GetAggregatedSelections(arg0 context.Context, arg1 []validator.BeaconCommitteeSelection) ([]validator.BeaconCommitteeSelection, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAggregatedSelections", arg0, arg1) - ret0, _ := ret[0].([]shared.BeaconCommitteeSelection) + ret0, _ := ret[0].([]validator.BeaconCommitteeSelection) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/validator/client/aggregate_test.go b/validator/client/aggregate_test.go index 75970ef6f2b6..8c6fface96a4 100644 --- a/validator/client/aggregate_test.go +++ b/validator/client/aggregate_test.go @@ -5,7 +5,7 @@ import ( "errors" "testing" - "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" + validator2 "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/validator" "github.com/golang/mock/gomock" "github.com/prysmaticlabs/go-bitfield" @@ -139,11 +139,11 @@ func TestSubmitAggregateAndProof_Distributed(t *testing.T) { } validator.distributed = true - validator.attSelections = make(map[attSelectionKey]shared.BeaconCommitteeSelection) + validator.attSelections = make(map[attSelectionKey]validator2.BeaconCommitteeSelection) validator.attSelections[attSelectionKey{ slot: slot, index: 123, - }] = shared.BeaconCommitteeSelection{ + }] = validator2.BeaconCommitteeSelection{ SelectionProof: make([]byte, 96), Slot: slot, ValidatorIndex: validatorIdx, diff --git a/validator/client/beacon-api/beacon_api_validator_client.go b/validator/client/beacon-api/beacon_api_validator_client.go index db8050e4d789..1943db5cf3b6 100644 --- a/validator/client/beacon-api/beacon_api_validator_client.go +++ b/validator/client/beacon-api/beacon_api_validator_client.go @@ -4,7 +4,7 @@ import ( "context" "time" - "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/validator" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/golang/protobuf/ptypes/empty" @@ -178,6 +178,6 @@ func (c *beaconApiValidatorClient) EventStreamIsRunning() bool { return c.eventHandler.running } -func (c *beaconApiValidatorClient) GetAggregatedSelections(ctx context.Context, selections []shared.BeaconCommitteeSelection) ([]shared.BeaconCommitteeSelection, error) { +func (c *beaconApiValidatorClient) GetAggregatedSelections(ctx context.Context, selections []validator.BeaconCommitteeSelection) ([]validator.BeaconCommitteeSelection, error) { return c.getAggregatedSelection(ctx, selections) } diff --git a/validator/client/beacon-api/beacon_committee_selections.go b/validator/client/beacon-api/beacon_committee_selections.go index 5aa7117ba436..3c37de5cf14d 100644 --- a/validator/client/beacon-api/beacon_committee_selections.go +++ b/validator/client/beacon-api/beacon_committee_selections.go @@ -5,15 +5,16 @@ import ( "context" "encoding/json" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/validator" + "github.com/pkg/errors" - "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" ) type aggregatedSelectionResponse struct { - Data []shared.BeaconCommitteeSelection `json:"data"` + Data []validator.BeaconCommitteeSelection `json:"data"` } -func (c *beaconApiValidatorClient) getAggregatedSelection(ctx context.Context, selections []shared.BeaconCommitteeSelection) ([]shared.BeaconCommitteeSelection, error) { +func (c *beaconApiValidatorClient) getAggregatedSelection(ctx context.Context, selections []validator.BeaconCommitteeSelection) ([]validator.BeaconCommitteeSelection, error) { body, err := json.Marshal(selections) if err != nil { return nil, errors.Wrap(err, "failed to marshal selections") diff --git a/validator/client/beacon-api/beacon_committee_selections_test.go b/validator/client/beacon-api/beacon_committee_selections_test.go index 97e5f59e3b51..8800d2bc2fe6 100644 --- a/validator/client/beacon-api/beacon_committee_selections_test.go +++ b/validator/client/beacon-api/beacon_committee_selections_test.go @@ -6,9 +6,10 @@ import ( "encoding/json" "testing" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/validator" + "github.com/golang/mock/gomock" "github.com/pkg/errors" - "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" "github.com/prysmaticlabs/prysm/v4/testing/require" "github.com/prysmaticlabs/prysm/v4/validator/client/beacon-api/mock" test_helpers "github.com/prysmaticlabs/prysm/v4/validator/client/beacon-api/test-helpers" @@ -17,21 +18,21 @@ import ( func TestGetAggregatedSelections(t *testing.T) { testcases := []struct { name string - req []shared.BeaconCommitteeSelection - res []shared.BeaconCommitteeSelection + req []validator.BeaconCommitteeSelection + res []validator.BeaconCommitteeSelection endpointError error expectedErrorMessage string }{ { name: "valid", - req: []shared.BeaconCommitteeSelection{ + req: []validator.BeaconCommitteeSelection{ { SelectionProof: test_helpers.FillByteSlice(96, 82), Slot: 75, ValidatorIndex: 76, }, }, - res: []shared.BeaconCommitteeSelection{ + res: []validator.BeaconCommitteeSelection{ { SelectionProof: test_helpers.FillByteSlice(96, 100), Slot: 75, @@ -41,7 +42,7 @@ func TestGetAggregatedSelections(t *testing.T) { }, { name: "endpoint error", - req: []shared.BeaconCommitteeSelection{ + req: []validator.BeaconCommitteeSelection{ { SelectionProof: test_helpers.FillByteSlice(96, 82), Slot: 75, @@ -53,7 +54,7 @@ func TestGetAggregatedSelections(t *testing.T) { }, { name: "no response error", - req: []shared.BeaconCommitteeSelection{ + req: []validator.BeaconCommitteeSelection{ { SelectionProof: test_helpers.FillByteSlice(96, 82), Slot: 75, @@ -64,7 +65,7 @@ func TestGetAggregatedSelections(t *testing.T) { }, { name: "mismatch response", - req: []shared.BeaconCommitteeSelection{ + req: []validator.BeaconCommitteeSelection{ { SelectionProof: test_helpers.FillByteSlice(96, 82), Slot: 75, @@ -76,7 +77,7 @@ func TestGetAggregatedSelections(t *testing.T) { ValidatorIndex: 79, }, }, - res: []shared.BeaconCommitteeSelection{ + res: []validator.BeaconCommitteeSelection{ { SelectionProof: test_helpers.FillByteSlice(96, 100), Slot: 75, diff --git a/validator/client/grpc-api/grpc_validator_client.go b/validator/client/grpc-api/grpc_validator_client.go index d67e5975a086..6b2349d01885 100644 --- a/validator/client/grpc-api/grpc_validator_client.go +++ b/validator/client/grpc-api/grpc_validator_client.go @@ -3,7 +3,7 @@ package grpc_api import ( "context" - "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/validator" "github.com/golang/protobuf/ptypes/empty" "github.com/pkg/errors" @@ -140,7 +140,7 @@ func (c *grpcValidatorClient) AggregatedSigAndAggregationBits( return c.beaconNodeValidatorClient.AggregatedSigAndAggregationBits(ctx, in) } -func (grpcValidatorClient) GetAggregatedSelections(context.Context, []shared.BeaconCommitteeSelection) ([]shared.BeaconCommitteeSelection, error) { +func (grpcValidatorClient) GetAggregatedSelections(context.Context, []validator.BeaconCommitteeSelection) ([]validator.BeaconCommitteeSelection, error) { return nil, iface.ErrNotSupported } diff --git a/validator/client/iface/validator_client.go b/validator/client/iface/validator_client.go index 255709bf9309..134b4c2b21fe 100644 --- a/validator/client/iface/validator_client.go +++ b/validator/client/iface/validator_client.go @@ -3,7 +3,7 @@ package iface import ( "context" - "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/validator" "github.com/golang/protobuf/ptypes/empty" "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives" @@ -38,5 +38,5 @@ type ValidatorClient interface { SubmitValidatorRegistrations(ctx context.Context, in *ethpb.SignedValidatorRegistrationsV1) (*empty.Empty, error) StartEventStream(ctx context.Context) error EventStreamIsRunning() bool - GetAggregatedSelections(ctx context.Context, selections []shared.BeaconCommitteeSelection) ([]shared.BeaconCommitteeSelection, error) + GetAggregatedSelections(ctx context.Context, selections []validator.BeaconCommitteeSelection) ([]validator.BeaconCommitteeSelection, error) } diff --git a/validator/client/service.go b/validator/client/service.go index 6b064080e9f3..459aefabbd78 100644 --- a/validator/client/service.go +++ b/validator/client/service.go @@ -6,7 +6,7 @@ import ( "strings" "time" - "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" + validator2 "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/validator" "github.com/dgraph-io/ristretto" middleware "github.com/grpc-ecosystem/go-grpc-middleware" @@ -236,7 +236,7 @@ func (v *ValidatorService) Start() { walletInitializedChannel: make(chan *wallet.Wallet, 1), validatorsRegBatchSize: v.validatorsRegBatchSize, distributed: v.distributed, - attSelections: make(map[attSelectionKey]shared.BeaconCommitteeSelection), + attSelections: make(map[attSelectionKey]validator2.BeaconCommitteeSelection), } v.validator = valStruct diff --git a/validator/client/validator.go b/validator/client/validator.go index 44d720e5469f..13e42fb0955d 100644 --- a/validator/client/validator.go +++ b/validator/client/validator.go @@ -15,7 +15,7 @@ import ( "sync" "time" - "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" + validator2 "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/validator" "github.com/dgraph-io/ristretto" "github.com/ethereum/go-ethereum/common" @@ -109,7 +109,7 @@ type validator struct { validatorsRegBatchSize int distributed bool attSelectionLock sync.Mutex - attSelections map[attSelectionKey]shared.BeaconCommitteeSelection + attSelections map[attSelectionKey]validator2.BeaconCommitteeSelection } type validatorStatus struct { @@ -1262,7 +1262,7 @@ func (v *validator) getAggregatedSelectionProofs(ctx context.Context, duties *et // Create new instance of attestation selections map. v.newAttSelections() - var req []shared.BeaconCommitteeSelection + var req []validator2.BeaconCommitteeSelection for _, duty := range duties.CurrentEpochDuties { if duty.Status != ethpb.ValidatorStatus_ACTIVE && duty.Status != ethpb.ValidatorStatus_EXITING { continue @@ -1274,7 +1274,7 @@ func (v *validator) getAggregatedSelectionProofs(ctx context.Context, duties *et return err } - req = append(req, shared.BeaconCommitteeSelection{ + req = append(req, validator2.BeaconCommitteeSelection{ SelectionProof: slotSig, Slot: duty.AttesterSlot, ValidatorIndex: duty.ValidatorIndex, @@ -1292,7 +1292,7 @@ func (v *validator) getAggregatedSelectionProofs(ctx context.Context, duties *et return err } - req = append(req, shared.BeaconCommitteeSelection{ + req = append(req, validator2.BeaconCommitteeSelection{ SelectionProof: slotSig, Slot: duty.AttesterSlot, ValidatorIndex: duty.ValidatorIndex, @@ -1310,7 +1310,7 @@ func (v *validator) getAggregatedSelectionProofs(ctx context.Context, duties *et return nil } -func (v *validator) addAttSelections(selections []shared.BeaconCommitteeSelection) { +func (v *validator) addAttSelections(selections []validator2.BeaconCommitteeSelection) { v.attSelectionLock.Lock() defer v.attSelectionLock.Unlock() @@ -1326,7 +1326,7 @@ func (v *validator) newAttSelections() { v.attSelectionLock.Lock() defer v.attSelectionLock.Unlock() - v.attSelections = make(map[attSelectionKey]shared.BeaconCommitteeSelection) + v.attSelections = make(map[attSelectionKey]validator2.BeaconCommitteeSelection) } func (v *validator) getAttSelection(key attSelectionKey) ([]byte, error) { diff --git a/validator/client/validator_test.go b/validator/client/validator_test.go index 00f08acded53..aa3ee3cf421f 100644 --- a/validator/client/validator_test.go +++ b/validator/client/validator_test.go @@ -11,7 +11,7 @@ import ( "testing" "time" - "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" + validator2 "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/validator" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -695,7 +695,7 @@ func TestUpdateDuties_Distributed(t *testing.T) { gomock.Any(), gomock.Any(), // fill this properly ).Return( - []shared.BeaconCommitteeSelection{ + []validator2.BeaconCommitteeSelection{ { SelectionProof: make([]byte, 32), Slot: slot, From 5f7409b248ae0d3338911b680c90c019407c5065 Mon Sep 17 00:00:00 2001 From: Dhruv Bodani Date: Wed, 31 Jan 2024 14:45:23 +0530 Subject: [PATCH 10/19] fix bazel build files --- testing/validator-mock/BUILD.bazel | 1 + validator/client/BUILD.bazel | 3 ++- validator/client/beacon-api/BUILD.bazel | 1 + validator/client/grpc-api/BUILD.bazel | 2 +- validator/client/iface/BUILD.bazel | 2 +- 5 files changed, 6 insertions(+), 3 deletions(-) diff --git a/testing/validator-mock/BUILD.bazel b/testing/validator-mock/BUILD.bazel index 28be37b91c37..225a0107fa1e 100644 --- a/testing/validator-mock/BUILD.bazel +++ b/testing/validator-mock/BUILD.bazel @@ -13,6 +13,7 @@ go_library( importpath = "github.com/prysmaticlabs/prysm/v4/testing/validator-mock", visibility = ["//visibility:public"], deps = [ + "//beacon-chain/rpc/eth/validator:go_default_library", "//consensus-types/primitives:go_default_library", "//consensus-types/validator:go_default_library", "//proto/prysm/v1alpha1:go_default_library", diff --git a/validator/client/BUILD.bazel b/validator/client/BUILD.bazel index 799c89e79469..c6f5ec923c43 100644 --- a/validator/client/BUILD.bazel +++ b/validator/client/BUILD.bazel @@ -31,7 +31,7 @@ go_library( "//beacon-chain/builder:go_default_library", "//beacon-chain/core/altair:go_default_library", "//beacon-chain/core/signing:go_default_library", - "//beacon-chain/rpc/eth/shared:go_default_library", + "//beacon-chain/rpc/eth/validator:go_default_library", "//cache/lru:go_default_library", "//cmd:go_default_library", "//config/features:go_default_library", @@ -123,6 +123,7 @@ go_test( deps = [ "//async/event:go_default_library", "//beacon-chain/core/signing:go_default_library", + "//beacon-chain/rpc/eth/validator:go_default_library", "//cache/lru:go_default_library", "//config/features:go_default_library", "//config/fieldparams:go_default_library", diff --git a/validator/client/beacon-api/BUILD.bazel b/validator/client/beacon-api/BUILD.bazel index b98a8cb90dab..c60a927dc290 100644 --- a/validator/client/beacon-api/BUILD.bazel +++ b/validator/client/beacon-api/BUILD.bazel @@ -83,6 +83,7 @@ go_test( "beacon_block_converter_test.go", "beacon_block_json_helpers_test.go", "beacon_block_proto_helpers_test.go", + "beacon_committee_selections_test.go", "domain_data_test.go", "doppelganger_test.go", "duties_test.go", diff --git a/validator/client/grpc-api/BUILD.bazel b/validator/client/grpc-api/BUILD.bazel index c0660d8659b0..61fc55fd326c 100644 --- a/validator/client/grpc-api/BUILD.bazel +++ b/validator/client/grpc-api/BUILD.bazel @@ -12,7 +12,7 @@ go_library( visibility = ["//validator:__subpackages__"], deps = [ "//beacon-chain/rpc/eth/helpers:go_default_library", - "//beacon-chain/rpc/eth/shared:go_default_library", + "//beacon-chain/rpc/eth/validator:go_default_library", "//beacon-chain/state/state-native:go_default_library", "//consensus-types/primitives:go_default_library", "//consensus-types/validator:go_default_library", diff --git a/validator/client/iface/BUILD.bazel b/validator/client/iface/BUILD.bazel index 4fb9cae140a1..d0a722f735b2 100644 --- a/validator/client/iface/BUILD.bazel +++ b/validator/client/iface/BUILD.bazel @@ -12,7 +12,7 @@ go_library( importpath = "github.com/prysmaticlabs/prysm/v4/validator/client/iface", visibility = ["//visibility:public"], deps = [ - "//beacon-chain/rpc/eth/shared:go_default_library", + "//beacon-chain/rpc/eth/validator:go_default_library", "//config/fieldparams:go_default_library", "//config/validator/service:go_default_library", "//consensus-types/primitives:go_default_library", From b7bcb539cff5139fb6d8274f4de8bdd5039df643 Mon Sep 17 00:00:00 2001 From: Dhruv Bodani Date: Thu, 1 Feb 2024 16:30:47 +0530 Subject: [PATCH 11/19] add support for POST and GET endpoints for get state validators query --- .../client/beacon-api/activation_test.go | 38 ++-- .../beacon_committee_selections_test.go | 1 - validator/client/beacon-api/index_test.go | 73 +++++--- .../client/beacon-api/state_validators.go | 24 ++- .../beacon-api/state_validators_test.go | 177 +++++++++++++++++- .../submit_aggregate_selection_proof_test.go | 22 +++ .../client/beacon-api/sync_committee_test.go | 22 +++ 7 files changed, 311 insertions(+), 46 deletions(-) diff --git a/validator/client/beacon-api/activation_test.go b/validator/client/beacon-api/activation_test.go index 7b3ffd868412..ef337572f0a6 100644 --- a/validator/client/beacon-api/activation_test.go +++ b/validator/client/beacon-api/activation_test.go @@ -1,8 +1,9 @@ package beacon_api import ( + "bytes" "context" - "net/url" + "encoding/json" "testing" "time" @@ -110,25 +111,20 @@ func TestActivation_Nominal(t *testing.T) { Ids: stringPubKeys, Statuses: []string{}, } - queryParams := url.Values{} - for _, id := range req.Ids { - queryParams.Add("id", id) - } - for _, st := range req.Statuses { - queryParams.Add("status", st) - } - - query := buildURL("/eth/v1/beacon/states/head/validators", queryParams) + reqBytes, err := json.Marshal(req) + require.NoError(t, err) // Get does not return any result for non existing key - jsonRestHandler.EXPECT().Get( + jsonRestHandler.EXPECT().Post( ctx, - query, + "/eth/v1/beacon/states/head/validators", + nil, + bytes.NewBuffer(reqBytes), &stateValidatorsResponseJson, ).Return( nil, ).SetArg( - 2, + 4, beacon.GetValidatorsResponse{ Data: []*beacon.ValidatorContainer{ { @@ -243,14 +239,16 @@ func TestActivation_InvalidData(t *testing.T) { ctx := context.Background() jsonRestHandler := mock.NewMockJsonRestHandler(ctrl) - jsonRestHandler.EXPECT().Get( + jsonRestHandler.EXPECT().Post( ctx, gomock.Any(), gomock.Any(), + gomock.Any(), + gomock.Any(), ).Return( nil, ).SetArg( - 2, + 4, beacon.GetValidatorsResponse{ Data: testCase.data, }, @@ -282,6 +280,16 @@ func TestActivation_JsonResponseError(t *testing.T) { ctx := context.Background() jsonRestHandler := mock.NewMockJsonRestHandler(ctrl) + jsonRestHandler.EXPECT().Post( + ctx, + gomock.Any(), + gomock.Any(), + gomock.Any(), + gomock.Any(), + ).Return( + errors.New("some specific json error"), + ).Times(1) + jsonRestHandler.EXPECT().Get( ctx, gomock.Any(), diff --git a/validator/client/beacon-api/beacon_committee_selections_test.go b/validator/client/beacon-api/beacon_committee_selections_test.go index 8800d2bc2fe6..c1859a0302a1 100644 --- a/validator/client/beacon-api/beacon_committee_selections_test.go +++ b/validator/client/beacon-api/beacon_committee_selections_test.go @@ -107,7 +107,6 @@ func TestGetAggregatedSelections(t *testing.T) { 4, aggregatedSelectionResponse{Data: test.res}, ).Return( - nil, test.endpointError, ).Times(1) diff --git a/validator/client/beacon-api/index_test.go b/validator/client/beacon-api/index_test.go index f2202bace5c6..b06b59eeca13 100644 --- a/validator/client/beacon-api/index_test.go +++ b/validator/client/beacon-api/index_test.go @@ -1,7 +1,9 @@ package beacon_api import ( + "bytes" "context" + "encoding/json" "net/url" "testing" @@ -18,7 +20,7 @@ import ( const stringPubKey = "0x8000091c2ae64ee414a54c1cc1fc67dec663408bc636cb86756e0200e41a75c8f86603f104f02c856983d2783116be13" -func getPubKeyAndQueryPath(t *testing.T) ([]byte, string) { +func getPubKeyAndReqBuffer(t *testing.T) ([]byte, *bytes.Buffer) { pubKey, err := hexutil.Decode(stringPubKey) require.NoError(t, err) req := beacon.GetValidatorsRequest{ @@ -26,35 +28,31 @@ func getPubKeyAndQueryPath(t *testing.T) ([]byte, string) { Statuses: []string{}, } - queryParams := url.Values{} - for _, id := range req.Ids { - queryParams.Add("id", id) - } - for _, st := range req.Statuses { - queryParams.Add("status", st) - } - - return pubKey, buildURL("/eth/v1/beacon/states/head/validators", queryParams) + reqBytes, err := json.Marshal(req) + require.NoError(t, err) + return pubKey, bytes.NewBuffer(reqBytes) } func TestIndex_Nominal(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - pubKey, query := getPubKeyAndQueryPath(t) + pubKey, reqBuffer := getPubKeyAndReqBuffer(t) ctx := context.Background() stateValidatorsResponseJson := beacon.GetValidatorsResponse{} jsonRestHandler := mock.NewMockJsonRestHandler(ctrl) - jsonRestHandler.EXPECT().Get( + jsonRestHandler.EXPECT().Post( ctx, - query, + "/eth/v1/beacon/states/head/validators", + nil, + reqBuffer, &stateValidatorsResponseJson, ).Return( nil, ).SetArg( - 2, + 4, beacon.GetValidatorsResponse{ Data: []*beacon.ValidatorContainer{ { @@ -89,20 +87,22 @@ func TestIndex_UnexistingValidator(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - pubKey, query := getPubKeyAndQueryPath(t) + pubKey, reqBuffer := getPubKeyAndReqBuffer(t) ctx := context.Background() stateValidatorsResponseJson := beacon.GetValidatorsResponse{} jsonRestHandler := mock.NewMockJsonRestHandler(ctrl) - jsonRestHandler.EXPECT().Get( + jsonRestHandler.EXPECT().Post( ctx, - query, + "/eth/v1/beacon/states/head/validators", + nil, + reqBuffer, &stateValidatorsResponseJson, ).Return( nil, ).SetArg( - 2, + 4, beacon.GetValidatorsResponse{ Data: []*beacon.ValidatorContainer{}, }, @@ -129,20 +129,22 @@ func TestIndex_BadIndexError(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - pubKey, query := getPubKeyAndQueryPath(t) + pubKey, reqBuffer := getPubKeyAndReqBuffer(t) ctx := context.Background() stateValidatorsResponseJson := beacon.GetValidatorsResponse{} jsonRestHandler := mock.NewMockJsonRestHandler(ctrl) - jsonRestHandler.EXPECT().Get( + jsonRestHandler.EXPECT().Post( ctx, - query, + "/eth/v1/beacon/states/head/validators", + nil, + reqBuffer, &stateValidatorsResponseJson, ).Return( nil, ).SetArg( - 2, + 4, beacon.GetValidatorsResponse{ Data: []*beacon.ValidatorContainer{ { @@ -176,15 +178,38 @@ func TestIndex_JsonResponseError(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - pubKey, query := getPubKeyAndQueryPath(t) + pubKey, reqBuffer := getPubKeyAndReqBuffer(t) ctx := context.Background() stateValidatorsResponseJson := beacon.GetValidatorsResponse{} jsonRestHandler := mock.NewMockJsonRestHandler(ctrl) + jsonRestHandler.EXPECT().Post( + ctx, + "/eth/v1/beacon/states/head/validators", + nil, + reqBuffer, + &stateValidatorsResponseJson, + ).Return( + errors.New("some specific json error"), + ).Times(1) + + req := beacon.GetValidatorsRequest{ + Ids: []string{stringPubKey}, + Statuses: []string{}, + } + + queryParams := url.Values{} + for _, id := range req.Ids { + queryParams.Add("id", id) + } + for _, st := range req.Statuses { + queryParams.Add("status", st) + } + jsonRestHandler.EXPECT().Get( ctx, - query, + buildURL("/eth/v1/beacon/states/head/validators", queryParams), &stateValidatorsResponseJson, ).Return( errors.New("some specific json error"), diff --git a/validator/client/beacon-api/state_validators.go b/validator/client/beacon-api/state_validators.go index 5f276136a6b7..2354bf59de6a 100644 --- a/validator/client/beacon-api/state_validators.go +++ b/validator/client/beacon-api/state_validators.go @@ -1,7 +1,9 @@ package beacon_api import ( + "bytes" "context" + "encoding/json" "fmt" "net/url" "strconv" @@ -85,6 +87,25 @@ func (c beaconApiStateValidatorsProvider) getStateValidatorsHelper( } } + reqBytes, err := json.Marshal(req) + if err != nil { + return nil, errors.Wrapf(err, "failed to marshal request into JSON") + } + + stateValidatorsJson := &beacon.GetValidatorsResponse{} + // First try POST endpoint to check whether it is supported by the beacon node. + if err = c.jsonRestHandler.Post(ctx, endpoint, nil, bytes.NewBuffer(reqBytes), stateValidatorsJson); err == nil { + if stateValidatorsJson.Data == nil { + return nil, errors.New("stateValidatorsJson.Data is nil") + } + + return stateValidatorsJson, nil + } + + // Re-initialise the response just in case. + stateValidatorsJson = &beacon.GetValidatorsResponse{} + + // Seems like POST isn't supported by the beacon node, let's try the GET one. queryParams := url.Values{} for _, id := range req.Ids { queryParams.Add("id", id) @@ -94,9 +115,8 @@ func (c beaconApiStateValidatorsProvider) getStateValidatorsHelper( } query := buildURL(endpoint, queryParams) - stateValidatorsJson := &beacon.GetValidatorsResponse{} - err := c.jsonRestHandler.Get(ctx, query, stateValidatorsJson) + err = c.jsonRestHandler.Get(ctx, query, stateValidatorsJson) if err != nil { return nil, err } diff --git a/validator/client/beacon-api/state_validators_test.go b/validator/client/beacon-api/state_validators_test.go index 660cff792d16..959b20896372 100644 --- a/validator/client/beacon-api/state_validators_test.go +++ b/validator/client/beacon-api/state_validators_test.go @@ -1,7 +1,9 @@ package beacon_api import ( + "bytes" "context" + "encoding/json" "net/url" "testing" @@ -14,7 +16,7 @@ import ( "github.com/prysmaticlabs/prysm/v4/validator/client/beacon-api/mock" ) -func TestGetStateValidators_Nominal(t *testing.T) { +func TestGetStateValidators_Nominal_POST(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -28,6 +30,94 @@ func TestGetStateValidators_Nominal(t *testing.T) { }, Statuses: []string{"active_ongoing", "active_exiting", "exited_slashed", "exited_unslashed"}, } + reqBytes, err := json.Marshal(req) + require.NoError(t, err) + + stateValidatorsResponseJson := beacon.GetValidatorsResponse{} + jsonRestHandler := mock.NewMockJsonRestHandler(ctrl) + + wanted := []*beacon.ValidatorContainer{ + { + Index: "12345", + Status: "active_ongoing", + Validator: &beacon.Validator{ + Pubkey: "0x8000091c2ae64ee414a54c1cc1fc67dec663408bc636cb86756e0200e41a75c8f86603f104f02c856983d2783116be19", + }, + }, + { + Index: "55293", + Status: "active_ongoing", + Validator: &beacon.Validator{ + Pubkey: "0x8000091c2ae64ee414a54c1cc1fc67dec663408bc636cb86756e0200e41a75c8f86603f104f02c856983d2783116be13", + }, + }, + { + Index: "55294", + Status: "active_exiting", + Validator: &beacon.Validator{ + Pubkey: "0x80000e851c0f53c3246ff726d7ff7766661ca5e12a07c45c114d208d54f0f8233d4380b2e9aff759d69795d1df905526", + }, + }, + { + Index: "55295", + Status: "exited_slashed", + Validator: &beacon.Validator{ + Pubkey: "0x800015473bdc3a7f45ef8eb8abc598bc20021e55ad6e6ad1d745aaef9730dd2c28ec08bf42df18451de94dd4a6d24ec5", + }, + }, + } + + ctx := context.Background() + + jsonRestHandler.EXPECT().Post( + ctx, + "/eth/v1/beacon/states/head/validators", + nil, + bytes.NewBuffer(reqBytes), + &stateValidatorsResponseJson, + ).Return( + nil, + ).SetArg( + 4, + beacon.GetValidatorsResponse{ + Data: wanted, + }, + ).Times(1) + + stateValidatorsProvider := beaconApiStateValidatorsProvider{jsonRestHandler: jsonRestHandler} + actual, err := stateValidatorsProvider.GetStateValidators(ctx, []string{ + "0x8000091c2ae64ee414a54c1cc1fc67dec663408bc636cb86756e0200e41a75c8f86603f104f02c856983d2783116be13", // active_ongoing + "0x80000e851c0f53c3246ff726d7ff7766661ca5e12a07c45c114d208d54f0f8233d4380b2e9aff759d69795d1df905526", // active_exiting + "0x424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242", // does not exist + "0x8000091c2ae64ee414a54c1cc1fc67dec663408bc636cb86756e0200e41a75c8f86603f104f02c856983d2783116be13", // active_ongoing - duplicate + "0x800015473bdc3a7f45ef8eb8abc598bc20021e55ad6e6ad1d745aaef9730dd2c28ec08bf42df18451de94dd4a6d24ec5", // exited_slashed + }, + []primitives.ValidatorIndex{ + 12345, // active_ongoing + 12345, // active_ongoing - duplicate + }, + []string{"active_ongoing", "active_exiting", "exited_slashed", "exited_unslashed"}, + ) + require.NoError(t, err) + assert.DeepEqual(t, wanted, actual.Data) +} + +func TestGetStateValidators_Nominal_GET(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + req := &beacon.GetValidatorsRequest{ + Ids: []string{ + "12345", + "0x8000091c2ae64ee414a54c1cc1fc67dec663408bc636cb86756e0200e41a75c8f86603f104f02c856983d2783116be13", + "0x80000e851c0f53c3246ff726d7ff7766661ca5e12a07c45c114d208d54f0f8233d4380b2e9aff759d69795d1df905526", + "0x424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242", + "0x800015473bdc3a7f45ef8eb8abc598bc20021e55ad6e6ad1d745aaef9730dd2c28ec08bf42df18451de94dd4a6d24ec5", + }, + Statuses: []string{"active_ongoing", "active_exiting", "exited_slashed", "exited_unslashed"}, + } + reqBytes, err := json.Marshal(req) + require.NoError(t, err) stateValidatorsResponseJson := beacon.GetValidatorsResponse{} jsonRestHandler := mock.NewMockJsonRestHandler(ctrl) @@ -65,6 +155,18 @@ func TestGetStateValidators_Nominal(t *testing.T) { ctx := context.Background() + // First return an error from POST call. + jsonRestHandler.EXPECT().Post( + ctx, + "/eth/v1/beacon/states/head/validators", + nil, + bytes.NewBuffer(reqBytes), + &stateValidatorsResponseJson, + ).Return( + errors.New("an error"), + ).Times(1) + + // Then try the GET call which will be successful. queryParams := url.Values{} for _, id := range req.Ids { queryParams.Add("id", id) @@ -114,12 +216,26 @@ func TestGetStateValidators_GetRestJsonResponseOnError(t *testing.T) { Ids: []string{"0x8000091c2ae64ee414a54c1cc1fc67dec663408bc636cb86756e0200e41a75c8f86603f104f02c856983d2783116be13"}, Statuses: []string{}, } + reqBytes, err := json.Marshal(req) + require.NoError(t, err) stateValidatorsResponseJson := beacon.GetValidatorsResponse{} jsonRestHandler := mock.NewMockJsonRestHandler(ctrl) ctx := context.Background() + // First call POST. + jsonRestHandler.EXPECT().Post( + ctx, + "/eth/v1/beacon/states/head/validators", + nil, + bytes.NewBuffer(reqBytes), + &stateValidatorsResponseJson, + ).Return( + errors.New("an error"), + ).Times(1) + + // Call to GET endpoint upon receiving error from POST call. queryParams := url.Values{} for _, id := range req.Ids { queryParams.Add("id", id) @@ -139,7 +255,7 @@ func TestGetStateValidators_GetRestJsonResponseOnError(t *testing.T) { ).Times(1) stateValidatorsProvider := beaconApiStateValidatorsProvider{jsonRestHandler: jsonRestHandler} - _, err := stateValidatorsProvider.GetStateValidators(ctx, []string{ + _, err = stateValidatorsProvider.GetStateValidators(ctx, []string{ "0x8000091c2ae64ee414a54c1cc1fc67dec663408bc636cb86756e0200e41a75c8f86603f104f02c856983d2783116be13", // active_ongoing }, nil, @@ -148,7 +264,46 @@ func TestGetStateValidators_GetRestJsonResponseOnError(t *testing.T) { assert.ErrorContains(t, "an error", err) } -func TestGetStateValidators_DataIsNil(t *testing.T) { +func TestGetStateValidators_DataIsNil_POST(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + req := &beacon.GetValidatorsRequest{ + Ids: []string{"0x8000091c2ae64ee414a54c1cc1fc67dec663408bc636cb86756e0200e41a75c8f86603f104f02c856983d2783116be13"}, + Statuses: []string{}, + } + reqBytes, err := json.Marshal(req) + require.NoError(t, err) + + ctx := context.Background() + stateValidatorsResponseJson := beacon.GetValidatorsResponse{} + jsonRestHandler := mock.NewMockJsonRestHandler(ctrl) + + jsonRestHandler.EXPECT().Post( + ctx, + "/eth/v1/beacon/states/head/validators", + nil, bytes.NewBuffer(reqBytes), + &stateValidatorsResponseJson, + ).Return( + nil, + ).SetArg( + 4, + beacon.GetValidatorsResponse{ + Data: nil, + }, + ).Times(1) + + stateValidatorsProvider := beaconApiStateValidatorsProvider{jsonRestHandler: jsonRestHandler} + _, err = stateValidatorsProvider.GetStateValidators(ctx, []string{ + "0x8000091c2ae64ee414a54c1cc1fc67dec663408bc636cb86756e0200e41a75c8f86603f104f02c856983d2783116be13", // active_ongoing + }, + nil, + nil, + ) + assert.ErrorContains(t, "stateValidatorsJson.Data is nil", err) +} + +func TestGetStateValidators_DataIsNil_GET(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -156,11 +311,25 @@ func TestGetStateValidators_DataIsNil(t *testing.T) { Ids: []string{"0x8000091c2ae64ee414a54c1cc1fc67dec663408bc636cb86756e0200e41a75c8f86603f104f02c856983d2783116be13"}, Statuses: []string{}, } + reqBytes, err := json.Marshal(req) + require.NoError(t, err) ctx := context.Background() stateValidatorsResponseJson := beacon.GetValidatorsResponse{} jsonRestHandler := mock.NewMockJsonRestHandler(ctrl) + // First call POST which will return an error. + jsonRestHandler.EXPECT().Post( + ctx, + "/eth/v1/beacon/states/head/validators", + nil, + bytes.NewBuffer(reqBytes), + &stateValidatorsResponseJson, + ).Return( + errors.New("an error"), + ).Times(1) + + // Then call GET which returns nil Data. queryParams := url.Values{} for _, id := range req.Ids { queryParams.Add("id", id) @@ -185,7 +354,7 @@ func TestGetStateValidators_DataIsNil(t *testing.T) { ).Times(1) stateValidatorsProvider := beaconApiStateValidatorsProvider{jsonRestHandler: jsonRestHandler} - _, err := stateValidatorsProvider.GetStateValidators(ctx, []string{ + _, err = stateValidatorsProvider.GetStateValidators(ctx, []string{ "0x8000091c2ae64ee414a54c1cc1fc67dec663408bc636cb86756e0200e41a75c8f86603f104f02c856983d2783116be13", // active_ongoing }, nil, diff --git a/validator/client/beacon-api/submit_aggregate_selection_proof_test.go b/validator/client/beacon-api/submit_aggregate_selection_proof_test.go index 010709fe323f..46e1de1d694b 100644 --- a/validator/client/beacon-api/submit_aggregate_selection_proof_test.go +++ b/validator/client/beacon-api/submit_aggregate_selection_proof_test.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "fmt" + "net/url" "testing" "github.com/ethereum/go-ethereum/common/hexutil" @@ -203,6 +204,27 @@ func TestSubmitAggregateSelectionProof(t *testing.T) { test.validatorsErr, ).Times(test.validatorsCalled) + if test.validatorsErr != nil { + // Then try the GET call which will also return error. + queryParams := url.Values{} + for _, id := range valsReq.Ids { + queryParams.Add("id", id) + } + for _, st := range valsReq.Statuses { + queryParams.Add("status", st) + } + + query := buildURL("/eth/v1/beacon/states/head/validators", queryParams) + + jsonRestHandler.EXPECT().Get( + ctx, + query, + &beacon.GetValidatorsResponse{}, + ).Return( + test.validatorsErr, + ).Times(1) + } + // Call attester duties endpoint to get attester duties. validatorIndicesBytes, err := json.Marshal([]string{validatorIndex}) require.NoError(t, err) diff --git a/validator/client/beacon-api/sync_committee_test.go b/validator/client/beacon-api/sync_committee_test.go index bb8a9e33041b..125d92235092 100644 --- a/validator/client/beacon-api/sync_committee_test.go +++ b/validator/client/beacon-api/sync_committee_test.go @@ -5,6 +5,7 @@ import ( "context" "encoding/json" "fmt" + "net/url" "testing" "github.com/ethereum/go-ethereum/common/hexutil" @@ -339,6 +340,27 @@ func TestGetSyncSubCommitteeIndex(t *testing.T) { test.validatorsErr, ).Times(1) + if test.validatorsErr != nil { + // Then try the GET call which will also return error. + queryParams := url.Values{} + for _, id := range valsReq.Ids { + queryParams.Add("id", id) + } + for _, st := range valsReq.Statuses { + queryParams.Add("status", st) + } + + query := buildURL("/eth/v1/beacon/states/head/validators", queryParams) + + jsonRestHandler.EXPECT().Get( + ctx, + query, + &beacon.GetValidatorsResponse{}, + ).Return( + test.validatorsErr, + ).Times(1) + } + validatorIndicesBytes, err := json.Marshal([]string{validatorIndex}) require.NoError(t, err) From 9849659d4f6398fe84b9fa1ae79d29b7888d651a Mon Sep 17 00:00:00 2001 From: Dhruv Bodani Date: Thu, 1 Feb 2024 17:40:36 +0530 Subject: [PATCH 12/19] add a handler to return error from beacon node --- beacon-chain/rpc/eth/validator/handlers.go | 6 ++++++ beacon-chain/rpc/service.go | 1 + 2 files changed, 7 insertions(+) diff --git a/beacon-chain/rpc/eth/validator/handlers.go b/beacon-chain/rpc/eth/validator/handlers.go index b94fa289e6ff..80194b1c3d4d 100644 --- a/beacon-chain/rpc/eth/validator/handlers.go +++ b/beacon-chain/rpc/eth/validator/handlers.go @@ -1085,6 +1085,12 @@ func (s *Server) GetLiveness(w http.ResponseWriter, r *http.Request) { httputil.WriteJson(w, resp) } +// BeaconCommitteeSelections responds with appropriate message and status code according the spec: +// https://ethereum.github.io/beacon-APIs/#/Validator/submitBeaconCommitteeSelections. +func (s *Server) BeaconCommitteeSelections(w http.ResponseWriter, _ *http.Request) { + httputil.HandleError(w, "Endpoint not implemented", 501) +} + // attestationDependentRoot is get_block_root_at_slot(state, compute_start_slot_at_epoch(epoch - 1) - 1) // or the genesis block root in the case of underflow. func attestationDependentRoot(s state.BeaconState, epoch primitives.Epoch) ([]byte, error) { diff --git a/beacon-chain/rpc/service.go b/beacon-chain/rpc/service.go index f89d2b4302d8..752223f1ae7f 100644 --- a/beacon-chain/rpc/service.go +++ b/beacon-chain/rpc/service.go @@ -228,6 +228,7 @@ func (s *Service) initializeValidatorServerRoutes(validatorServer *validator.Ser s.cfg.Router.HandleFunc("/eth/v2/validator/blocks/{slot}", validatorServer.ProduceBlockV2).Methods(http.MethodGet) s.cfg.Router.HandleFunc("/eth/v1/validator/blinded_blocks/{slot}", validatorServer.ProduceBlindedBlock).Methods(http.MethodGet) s.cfg.Router.HandleFunc("/eth/v3/validator/blocks/{slot}", validatorServer.ProduceBlockV3).Methods(http.MethodGet) + s.cfg.Router.HandleFunc("/eth/v1/validator/beacon_committee_selections", validatorServer.BeaconCommitteeSelections).Methods(http.MethodPost) } func (s *Service) initializeNodeServerRoutes(nodeServer *node.Server) { From b354bdcc8763960726c07b891662200aeae2a09f Mon Sep 17 00:00:00 2001 From: Dhruv Bodani Date: Fri, 2 Feb 2024 21:56:31 +0530 Subject: [PATCH 13/19] move beacon committee selection to validator top-level module --- beacon-chain/rpc/eth/validator/structs.go | 54 ------------------ .../validator-mock/validator_client_mock.go | 7 ++- validator/client/aggregate_test.go | 6 +- .../beacon-api/beacon_api_validator_client.go | 4 +- .../beacon-api/beacon_committee_selections.go | 6 +- .../beacon_committee_selections_test.go | 18 +++--- .../client/grpc-api/grpc_validator_client.go | 4 +- validator/client/iface/validator_client.go | 56 ++++++++++++++++++- validator/client/service.go | 4 +- validator/client/validator.go | 14 ++--- validator/client/validator_test.go | 4 +- 11 files changed, 83 insertions(+), 94 deletions(-) diff --git a/beacon-chain/rpc/eth/validator/structs.go b/beacon-chain/rpc/eth/validator/structs.go index 31c8e55b18d6..c58bac7f0fd8 100644 --- a/beacon-chain/rpc/eth/validator/structs.go +++ b/beacon-chain/rpc/eth/validator/structs.go @@ -2,11 +2,6 @@ package validator import ( "encoding/json" - "strconv" - - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/pkg/errors" - "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" ) @@ -95,52 +90,3 @@ type Liveness struct { Index string `json:"index"` IsLive bool `json:"is_live"` } - -type BeaconCommitteeSelection struct { - SelectionProof []byte - Slot primitives.Slot - ValidatorIndex primitives.ValidatorIndex -} - -type beaconCommitteeSelectionJson struct { - SelectionProof string `json:"selection_proof"` - Slot string `json:"slot"` - ValidatorIndex string `json:"validator_index"` -} - -func (b BeaconCommitteeSelection) MarshalJSON() ([]byte, error) { - return json.Marshal(beaconCommitteeSelectionJson{ - SelectionProof: hexutil.Encode(b.SelectionProof), - Slot: strconv.FormatUint(uint64(b.Slot), 10), - ValidatorIndex: strconv.FormatUint(uint64(b.ValidatorIndex), 10), - }) -} - -func (b *BeaconCommitteeSelection) UnmarshalJSON(input []byte) error { - var bjson beaconCommitteeSelectionJson - err := json.Unmarshal(input, &bjson) - if err != nil { - return errors.Wrap(err, "failed to unmarshal beacon committee selection") - } - - slot, err := strconv.ParseUint(bjson.Slot, 10, 64) - if err != nil { - return errors.Wrap(err, "failed to parse slot") - } - - vIdx, err := strconv.ParseUint(bjson.ValidatorIndex, 10, 64) - if err != nil { - return errors.Wrap(err, "failed to parse validator index") - } - - selectionProof, err := hexutil.Decode(bjson.SelectionProof) - if err != nil { - return errors.Wrap(err, "failed to parse selection proof") - } - - b.Slot = primitives.Slot(slot) - b.SelectionProof = selectionProof - b.ValidatorIndex = primitives.ValidatorIndex(vIdx) - - return nil -} diff --git a/testing/validator-mock/validator_client_mock.go b/testing/validator-mock/validator_client_mock.go index 398cd48027f6..b5822c91cd1f 100644 --- a/testing/validator-mock/validator_client_mock.go +++ b/testing/validator-mock/validator_client_mock.go @@ -8,8 +8,9 @@ import ( context "context" reflect "reflect" + "github.com/prysmaticlabs/prysm/v4/validator/client/iface" + gomock "github.com/golang/mock/gomock" - "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/validator" primitives "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives" eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" emptypb "google.golang.org/protobuf/types/known/emptypb" @@ -83,10 +84,10 @@ func (mr *MockValidatorClientMockRecorder) EventStreamIsRunning() *gomock.Call { } // GetAggregatedSelections mocks base method. -func (m *MockValidatorClient) GetAggregatedSelections(arg0 context.Context, arg1 []validator.BeaconCommitteeSelection) ([]validator.BeaconCommitteeSelection, error) { +func (m *MockValidatorClient) GetAggregatedSelections(arg0 context.Context, arg1 []iface.BeaconCommitteeSelection) ([]iface.BeaconCommitteeSelection, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAggregatedSelections", arg0, arg1) - ret0, _ := ret[0].([]validator.BeaconCommitteeSelection) + ret0, _ := ret[0].([]iface.BeaconCommitteeSelection) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/validator/client/aggregate_test.go b/validator/client/aggregate_test.go index 8c6fface96a4..e2bc6f21858a 100644 --- a/validator/client/aggregate_test.go +++ b/validator/client/aggregate_test.go @@ -5,7 +5,7 @@ import ( "errors" "testing" - validator2 "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/validator" + "github.com/prysmaticlabs/prysm/v4/validator/client/iface" "github.com/golang/mock/gomock" "github.com/prysmaticlabs/go-bitfield" @@ -139,11 +139,11 @@ func TestSubmitAggregateAndProof_Distributed(t *testing.T) { } validator.distributed = true - validator.attSelections = make(map[attSelectionKey]validator2.BeaconCommitteeSelection) + validator.attSelections = make(map[attSelectionKey]iface.BeaconCommitteeSelection) validator.attSelections[attSelectionKey{ slot: slot, index: 123, - }] = validator2.BeaconCommitteeSelection{ + }] = iface.BeaconCommitteeSelection{ SelectionProof: make([]byte, 96), Slot: slot, ValidatorIndex: validatorIdx, diff --git a/validator/client/beacon-api/beacon_api_validator_client.go b/validator/client/beacon-api/beacon_api_validator_client.go index 1943db5cf3b6..db03e4184f64 100644 --- a/validator/client/beacon-api/beacon_api_validator_client.go +++ b/validator/client/beacon-api/beacon_api_validator_client.go @@ -4,8 +4,6 @@ import ( "context" "time" - "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/validator" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/golang/protobuf/ptypes/empty" "github.com/pkg/errors" @@ -178,6 +176,6 @@ func (c *beaconApiValidatorClient) EventStreamIsRunning() bool { return c.eventHandler.running } -func (c *beaconApiValidatorClient) GetAggregatedSelections(ctx context.Context, selections []validator.BeaconCommitteeSelection) ([]validator.BeaconCommitteeSelection, error) { +func (c *beaconApiValidatorClient) GetAggregatedSelections(ctx context.Context, selections []iface.BeaconCommitteeSelection) ([]iface.BeaconCommitteeSelection, error) { return c.getAggregatedSelection(ctx, selections) } diff --git a/validator/client/beacon-api/beacon_committee_selections.go b/validator/client/beacon-api/beacon_committee_selections.go index 3c37de5cf14d..c6c74883606e 100644 --- a/validator/client/beacon-api/beacon_committee_selections.go +++ b/validator/client/beacon-api/beacon_committee_selections.go @@ -5,16 +5,16 @@ import ( "context" "encoding/json" - "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/validator" + "github.com/prysmaticlabs/prysm/v4/validator/client/iface" "github.com/pkg/errors" ) type aggregatedSelectionResponse struct { - Data []validator.BeaconCommitteeSelection `json:"data"` + Data []iface.BeaconCommitteeSelection `json:"data"` } -func (c *beaconApiValidatorClient) getAggregatedSelection(ctx context.Context, selections []validator.BeaconCommitteeSelection) ([]validator.BeaconCommitteeSelection, error) { +func (c *beaconApiValidatorClient) getAggregatedSelection(ctx context.Context, selections []iface.BeaconCommitteeSelection) ([]iface.BeaconCommitteeSelection, error) { body, err := json.Marshal(selections) if err != nil { return nil, errors.Wrap(err, "failed to marshal selections") diff --git a/validator/client/beacon-api/beacon_committee_selections_test.go b/validator/client/beacon-api/beacon_committee_selections_test.go index c1859a0302a1..344fece74598 100644 --- a/validator/client/beacon-api/beacon_committee_selections_test.go +++ b/validator/client/beacon-api/beacon_committee_selections_test.go @@ -6,7 +6,7 @@ import ( "encoding/json" "testing" - "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/validator" + "github.com/prysmaticlabs/prysm/v4/validator/client/iface" "github.com/golang/mock/gomock" "github.com/pkg/errors" @@ -18,21 +18,21 @@ import ( func TestGetAggregatedSelections(t *testing.T) { testcases := []struct { name string - req []validator.BeaconCommitteeSelection - res []validator.BeaconCommitteeSelection + req []iface.BeaconCommitteeSelection + res []iface.BeaconCommitteeSelection endpointError error expectedErrorMessage string }{ { name: "valid", - req: []validator.BeaconCommitteeSelection{ + req: []iface.BeaconCommitteeSelection{ { SelectionProof: test_helpers.FillByteSlice(96, 82), Slot: 75, ValidatorIndex: 76, }, }, - res: []validator.BeaconCommitteeSelection{ + res: []iface.BeaconCommitteeSelection{ { SelectionProof: test_helpers.FillByteSlice(96, 100), Slot: 75, @@ -42,7 +42,7 @@ func TestGetAggregatedSelections(t *testing.T) { }, { name: "endpoint error", - req: []validator.BeaconCommitteeSelection{ + req: []iface.BeaconCommitteeSelection{ { SelectionProof: test_helpers.FillByteSlice(96, 82), Slot: 75, @@ -54,7 +54,7 @@ func TestGetAggregatedSelections(t *testing.T) { }, { name: "no response error", - req: []validator.BeaconCommitteeSelection{ + req: []iface.BeaconCommitteeSelection{ { SelectionProof: test_helpers.FillByteSlice(96, 82), Slot: 75, @@ -65,7 +65,7 @@ func TestGetAggregatedSelections(t *testing.T) { }, { name: "mismatch response", - req: []validator.BeaconCommitteeSelection{ + req: []iface.BeaconCommitteeSelection{ { SelectionProof: test_helpers.FillByteSlice(96, 82), Slot: 75, @@ -77,7 +77,7 @@ func TestGetAggregatedSelections(t *testing.T) { ValidatorIndex: 79, }, }, - res: []validator.BeaconCommitteeSelection{ + res: []iface.BeaconCommitteeSelection{ { SelectionProof: test_helpers.FillByteSlice(96, 100), Slot: 75, diff --git a/validator/client/grpc-api/grpc_validator_client.go b/validator/client/grpc-api/grpc_validator_client.go index 6b2349d01885..4036396a1f85 100644 --- a/validator/client/grpc-api/grpc_validator_client.go +++ b/validator/client/grpc-api/grpc_validator_client.go @@ -3,8 +3,6 @@ package grpc_api import ( "context" - "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/validator" - "github.com/golang/protobuf/ptypes/empty" "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives" @@ -140,7 +138,7 @@ func (c *grpcValidatorClient) AggregatedSigAndAggregationBits( return c.beaconNodeValidatorClient.AggregatedSigAndAggregationBits(ctx, in) } -func (grpcValidatorClient) GetAggregatedSelections(context.Context, []validator.BeaconCommitteeSelection) ([]validator.BeaconCommitteeSelection, error) { +func (grpcValidatorClient) GetAggregatedSelections(context.Context, []iface.BeaconCommitteeSelection) ([]iface.BeaconCommitteeSelection, error) { return nil, iface.ErrNotSupported } diff --git a/validator/client/iface/validator_client.go b/validator/client/iface/validator_client.go index 134b4c2b21fe..473afe5301f6 100644 --- a/validator/client/iface/validator_client.go +++ b/validator/client/iface/validator_client.go @@ -2,14 +2,66 @@ package iface import ( "context" + "encoding/json" + "strconv" - "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/validator" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/pkg/errors" "github.com/golang/protobuf/ptypes/empty" "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives" ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" ) +type BeaconCommitteeSelection struct { + SelectionProof []byte + Slot primitives.Slot + ValidatorIndex primitives.ValidatorIndex +} + +type beaconCommitteeSelectionJson struct { + SelectionProof string `json:"selection_proof"` + Slot string `json:"slot"` + ValidatorIndex string `json:"validator_index"` +} + +func (b BeaconCommitteeSelection) MarshalJSON() ([]byte, error) { + return json.Marshal(beaconCommitteeSelectionJson{ + SelectionProof: hexutil.Encode(b.SelectionProof), + Slot: strconv.FormatUint(uint64(b.Slot), 10), + ValidatorIndex: strconv.FormatUint(uint64(b.ValidatorIndex), 10), + }) +} + +func (b *BeaconCommitteeSelection) UnmarshalJSON(input []byte) error { + var bjson beaconCommitteeSelectionJson + err := json.Unmarshal(input, &bjson) + if err != nil { + return errors.Wrap(err, "failed to unmarshal beacon committee selection") + } + + slot, err := strconv.ParseUint(bjson.Slot, 10, 64) + if err != nil { + return errors.Wrap(err, "failed to parse slot") + } + + vIdx, err := strconv.ParseUint(bjson.ValidatorIndex, 10, 64) + if err != nil { + return errors.Wrap(err, "failed to parse validator index") + } + + selectionProof, err := hexutil.Decode(bjson.SelectionProof) + if err != nil { + return errors.Wrap(err, "failed to parse selection proof") + } + + b.Slot = primitives.Slot(slot) + b.SelectionProof = selectionProof + b.ValidatorIndex = primitives.ValidatorIndex(vIdx) + + return nil +} + type ValidatorClient interface { GetDuties(ctx context.Context, in *ethpb.DutiesRequest) (*ethpb.DutiesResponse, error) DomainData(ctx context.Context, in *ethpb.DomainRequest) (*ethpb.DomainResponse, error) @@ -38,5 +90,5 @@ type ValidatorClient interface { SubmitValidatorRegistrations(ctx context.Context, in *ethpb.SignedValidatorRegistrationsV1) (*empty.Empty, error) StartEventStream(ctx context.Context) error EventStreamIsRunning() bool - GetAggregatedSelections(ctx context.Context, selections []validator.BeaconCommitteeSelection) ([]validator.BeaconCommitteeSelection, error) + GetAggregatedSelections(ctx context.Context, selections []BeaconCommitteeSelection) ([]BeaconCommitteeSelection, error) } diff --git a/validator/client/service.go b/validator/client/service.go index 459aefabbd78..6e75a5a1606a 100644 --- a/validator/client/service.go +++ b/validator/client/service.go @@ -6,8 +6,6 @@ import ( "strings" "time" - validator2 "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/validator" - "github.com/dgraph-io/ristretto" middleware "github.com/grpc-ecosystem/go-grpc-middleware" grpcretry "github.com/grpc-ecosystem/go-grpc-middleware/retry" @@ -236,7 +234,7 @@ func (v *ValidatorService) Start() { walletInitializedChannel: make(chan *wallet.Wallet, 1), validatorsRegBatchSize: v.validatorsRegBatchSize, distributed: v.distributed, - attSelections: make(map[attSelectionKey]validator2.BeaconCommitteeSelection), + attSelections: make(map[attSelectionKey]iface.BeaconCommitteeSelection), } v.validator = valStruct diff --git a/validator/client/validator.go b/validator/client/validator.go index 13e42fb0955d..1ad6bfa8cce7 100644 --- a/validator/client/validator.go +++ b/validator/client/validator.go @@ -15,8 +15,6 @@ import ( "sync" "time" - validator2 "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/validator" - "github.com/dgraph-io/ristretto" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -109,7 +107,7 @@ type validator struct { validatorsRegBatchSize int distributed bool attSelectionLock sync.Mutex - attSelections map[attSelectionKey]validator2.BeaconCommitteeSelection + attSelections map[attSelectionKey]iface.BeaconCommitteeSelection } type validatorStatus struct { @@ -1262,7 +1260,7 @@ func (v *validator) getAggregatedSelectionProofs(ctx context.Context, duties *et // Create new instance of attestation selections map. v.newAttSelections() - var req []validator2.BeaconCommitteeSelection + var req []iface.BeaconCommitteeSelection for _, duty := range duties.CurrentEpochDuties { if duty.Status != ethpb.ValidatorStatus_ACTIVE && duty.Status != ethpb.ValidatorStatus_EXITING { continue @@ -1274,7 +1272,7 @@ func (v *validator) getAggregatedSelectionProofs(ctx context.Context, duties *et return err } - req = append(req, validator2.BeaconCommitteeSelection{ + req = append(req, iface.BeaconCommitteeSelection{ SelectionProof: slotSig, Slot: duty.AttesterSlot, ValidatorIndex: duty.ValidatorIndex, @@ -1292,7 +1290,7 @@ func (v *validator) getAggregatedSelectionProofs(ctx context.Context, duties *et return err } - req = append(req, validator2.BeaconCommitteeSelection{ + req = append(req, iface.BeaconCommitteeSelection{ SelectionProof: slotSig, Slot: duty.AttesterSlot, ValidatorIndex: duty.ValidatorIndex, @@ -1310,7 +1308,7 @@ func (v *validator) getAggregatedSelectionProofs(ctx context.Context, duties *et return nil } -func (v *validator) addAttSelections(selections []validator2.BeaconCommitteeSelection) { +func (v *validator) addAttSelections(selections []iface.BeaconCommitteeSelection) { v.attSelectionLock.Lock() defer v.attSelectionLock.Unlock() @@ -1326,7 +1324,7 @@ func (v *validator) newAttSelections() { v.attSelectionLock.Lock() defer v.attSelectionLock.Unlock() - v.attSelections = make(map[attSelectionKey]validator2.BeaconCommitteeSelection) + v.attSelections = make(map[attSelectionKey]iface.BeaconCommitteeSelection) } func (v *validator) getAttSelection(key attSelectionKey) ([]byte, error) { diff --git a/validator/client/validator_test.go b/validator/client/validator_test.go index aa3ee3cf421f..7b45d7c26b4c 100644 --- a/validator/client/validator_test.go +++ b/validator/client/validator_test.go @@ -11,8 +11,6 @@ import ( "testing" "time" - validator2 "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/validator" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/golang/mock/gomock" @@ -695,7 +693,7 @@ func TestUpdateDuties_Distributed(t *testing.T) { gomock.Any(), gomock.Any(), // fill this properly ).Return( - []validator2.BeaconCommitteeSelection{ + []iface.BeaconCommitteeSelection{ { SelectionProof: make([]byte, 32), Slot: slot, From a3c027af5b68820817582b5e936600aa273e8caf Mon Sep 17 00:00:00 2001 From: Dhruv Bodani Date: Fri, 2 Feb 2024 23:44:29 +0530 Subject: [PATCH 14/19] fix bazel --- testing/validator-mock/BUILD.bazel | 1 - validator/client/BUILD.bazel | 2 -- validator/client/grpc-api/BUILD.bazel | 1 - validator/client/iface/BUILD.bazel | 2 +- 4 files changed, 1 insertion(+), 5 deletions(-) diff --git a/testing/validator-mock/BUILD.bazel b/testing/validator-mock/BUILD.bazel index 225a0107fa1e..28be37b91c37 100644 --- a/testing/validator-mock/BUILD.bazel +++ b/testing/validator-mock/BUILD.bazel @@ -13,7 +13,6 @@ go_library( importpath = "github.com/prysmaticlabs/prysm/v4/testing/validator-mock", visibility = ["//visibility:public"], deps = [ - "//beacon-chain/rpc/eth/validator:go_default_library", "//consensus-types/primitives:go_default_library", "//consensus-types/validator:go_default_library", "//proto/prysm/v1alpha1:go_default_library", diff --git a/validator/client/BUILD.bazel b/validator/client/BUILD.bazel index c6f5ec923c43..268d0afd603c 100644 --- a/validator/client/BUILD.bazel +++ b/validator/client/BUILD.bazel @@ -31,7 +31,6 @@ go_library( "//beacon-chain/builder:go_default_library", "//beacon-chain/core/altair:go_default_library", "//beacon-chain/core/signing:go_default_library", - "//beacon-chain/rpc/eth/validator:go_default_library", "//cache/lru:go_default_library", "//cmd:go_default_library", "//config/features:go_default_library", @@ -123,7 +122,6 @@ go_test( deps = [ "//async/event:go_default_library", "//beacon-chain/core/signing:go_default_library", - "//beacon-chain/rpc/eth/validator:go_default_library", "//cache/lru:go_default_library", "//config/features:go_default_library", "//config/fieldparams:go_default_library", diff --git a/validator/client/grpc-api/BUILD.bazel b/validator/client/grpc-api/BUILD.bazel index 61fc55fd326c..8558091f7ab0 100644 --- a/validator/client/grpc-api/BUILD.bazel +++ b/validator/client/grpc-api/BUILD.bazel @@ -12,7 +12,6 @@ go_library( visibility = ["//validator:__subpackages__"], deps = [ "//beacon-chain/rpc/eth/helpers:go_default_library", - "//beacon-chain/rpc/eth/validator:go_default_library", "//beacon-chain/state/state-native:go_default_library", "//consensus-types/primitives:go_default_library", "//consensus-types/validator:go_default_library", diff --git a/validator/client/iface/BUILD.bazel b/validator/client/iface/BUILD.bazel index d0a722f735b2..8d6cc9805beb 100644 --- a/validator/client/iface/BUILD.bazel +++ b/validator/client/iface/BUILD.bazel @@ -12,7 +12,6 @@ go_library( importpath = "github.com/prysmaticlabs/prysm/v4/validator/client/iface", visibility = ["//visibility:public"], deps = [ - "//beacon-chain/rpc/eth/validator:go_default_library", "//config/fieldparams:go_default_library", "//config/validator/service:go_default_library", "//consensus-types/primitives:go_default_library", @@ -21,6 +20,7 @@ go_library( "//proto/prysm/v1alpha1:go_default_library", "//proto/prysm/v1alpha1/validator-client:go_default_library", "//validator/keymanager:go_default_library", + "@com_github_ethereum_go_ethereum//common/hexutil:go_default_library", "@com_github_golang_protobuf//ptypes/empty", "@com_github_pkg_errors//:go_default_library", ], From 9cae79f975f3b7a78f79483c20291d35cf4448f7 Mon Sep 17 00:00:00 2001 From: Dhruv Bodani Date: Fri, 2 Feb 2024 23:58:13 +0530 Subject: [PATCH 15/19] re-arrange fields to fix lint --- validator/client/service.go | 4 ++-- validator/client/validator.go | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/validator/client/service.go b/validator/client/service.go index 6e75a5a1606a..c3124f047536 100644 --- a/validator/client/service.go +++ b/validator/client/service.go @@ -56,6 +56,7 @@ type ValidatorService struct { useWeb bool emitAccountMetrics bool logValidatorBalances bool + distributed bool interopKeysConfig *local.InteropKeymanagerConfig conn validatorHelpers.NodeConnection grpcRetryDelay time.Duration @@ -76,7 +77,6 @@ type ValidatorService struct { Web3SignerConfig *remoteweb3signer.SetupConfig proposerSettings *validatorserviceconfig.ProposerSettings validatorsRegBatchSize int - distributed bool } // Config for the validator service. @@ -84,6 +84,7 @@ type Config struct { UseWeb bool LogValidatorBalances bool EmitAccountMetrics bool + Distributed bool InteropKeysConfig *local.InteropKeymanagerConfig Wallet *wallet.Wallet WalletInitializedFeed *event.Feed @@ -103,7 +104,6 @@ type Config struct { BeaconApiEndpoint string BeaconApiTimeout time.Duration ValidatorsRegBatchSize int - Distributed bool } // NewValidatorService creates a new validator service for the service diff --git a/validator/client/validator.go b/validator/client/validator.go index 1ad6bfa8cce7..92e1b81b0bfb 100644 --- a/validator/client/validator.go +++ b/validator/client/validator.go @@ -67,12 +67,14 @@ type validator struct { logValidatorBalances bool useWeb bool emitAccountMetrics bool + distributed bool domainDataLock sync.RWMutex attLogsLock sync.Mutex aggregatedSlotCommitteeIDCacheLock sync.Mutex highestValidSlotLock sync.Mutex prevBalanceLock sync.RWMutex slashableKeysLock sync.RWMutex + attSelectionLock sync.Mutex eipImportBlacklistedPublicKeys map[[fieldparams.BLSPubkeyLength]byte]bool walletInitializedFeed *event.Feed attLogs map[[32]byte]*attSubmitted @@ -82,6 +84,7 @@ type validator struct { prevBalance map[[fieldparams.BLSPubkeyLength]byte]uint64 pubkeyToValidatorIndex map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex signedValidatorRegistrations map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1 + attSelections map[attSelectionKey]iface.BeaconCommitteeSelection graffitiOrderedIndex uint64 aggregatedSlotCommitteeIDCache *lru.Cache domainDataCache *ristretto.Cache @@ -105,9 +108,6 @@ type validator struct { proposerSettings *validatorserviceconfig.ProposerSettings walletInitializedChannel chan *wallet.Wallet validatorsRegBatchSize int - distributed bool - attSelectionLock sync.Mutex - attSelections map[attSelectionKey]iface.BeaconCommitteeSelection } type validatorStatus struct { From c23400134f6fd0f5a7714bd46c55b3693e3a4f3f Mon Sep 17 00:00:00 2001 From: Dhruv Bodani Date: Mon, 5 Feb 2024 14:59:42 +0530 Subject: [PATCH 16/19] fix TestServer_InitializeRoutes --- beacon-chain/rpc/service_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/beacon-chain/rpc/service_test.go b/beacon-chain/rpc/service_test.go index a24399663d1e..852acaf75342 100644 --- a/beacon-chain/rpc/service_test.go +++ b/beacon-chain/rpc/service_test.go @@ -152,8 +152,8 @@ func TestServer_InitializeRoutes(t *testing.T) { "/eth/v1/validator/aggregate_and_proofs": {http.MethodPost}, "/eth/v1/validator/beacon_committee_subscriptions": {http.MethodPost}, "/eth/v1/validator/sync_committee_subscriptions": {http.MethodPost}, - //"/eth/v1/validator/beacon_committee_selections": {http.MethodPost}, // not implemented - "/eth/v1/validator/sync_committee_contribution": {http.MethodGet}, + "/eth/v1/validator/beacon_committee_selections": {http.MethodPost}, + "/eth/v1/validator/sync_committee_contribution": {http.MethodGet}, //"/eth/v1/validator/sync_committee_selections": {http.MethodPost}, // not implemented "/eth/v1/validator/contribution_and_proofs": {http.MethodPost}, "/eth/v1/validator/prepare_beacon_proposer": {http.MethodPost}, From 179f6cb892a2d858f71aab314a7dda66d293b148 Mon Sep 17 00:00:00 2001 From: Dhruv Bodani Date: Mon, 5 Feb 2024 15:17:32 +0530 Subject: [PATCH 17/19] fix build and lint --- validator/client/beacon-api/index_test.go | 2 +- .../beacon-api/state_validators_test.go | 22 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/validator/client/beacon-api/index_test.go b/validator/client/beacon-api/index_test.go index ec4bd9942a72..91102c64038e 100644 --- a/validator/client/beacon-api/index_test.go +++ b/validator/client/beacon-api/index_test.go @@ -194,7 +194,7 @@ func TestIndex_JsonResponseError(t *testing.T) { errors.New("some specific json error"), ).Times(1) - req := beacon.GetValidatorsRequest{ + req := structs.GetValidatorsRequest{ Ids: []string{stringPubKey}, Statuses: []string{}, } diff --git a/validator/client/beacon-api/state_validators_test.go b/validator/client/beacon-api/state_validators_test.go index d86502faccec..6a8521aa35ac 100644 --- a/validator/client/beacon-api/state_validators_test.go +++ b/validator/client/beacon-api/state_validators_test.go @@ -106,7 +106,7 @@ func TestGetStateValidators_Nominal_GET(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - req := &beacon.GetValidatorsRequest{ + req := &structs.GetValidatorsRequest{ Ids: []string{ "12345", "0x8000091c2ae64ee414a54c1cc1fc67dec663408bc636cb86756e0200e41a75c8f86603f104f02c856983d2783116be13", @@ -119,35 +119,35 @@ func TestGetStateValidators_Nominal_GET(t *testing.T) { reqBytes, err := json.Marshal(req) require.NoError(t, err) - stateValidatorsResponseJson := beacon.GetValidatorsResponse{} + stateValidatorsResponseJson := structs.GetValidatorsResponse{} jsonRestHandler := mock.NewMockJsonRestHandler(ctrl) - wanted := []*beacon.ValidatorContainer{ + wanted := []*structs.ValidatorContainer{ { Index: "12345", Status: "active_ongoing", - Validator: &beacon.Validator{ + Validator: &structs.Validator{ Pubkey: "0x8000091c2ae64ee414a54c1cc1fc67dec663408bc636cb86756e0200e41a75c8f86603f104f02c856983d2783116be19", }, }, { Index: "55293", Status: "active_ongoing", - Validator: &beacon.Validator{ + Validator: &structs.Validator{ Pubkey: "0x8000091c2ae64ee414a54c1cc1fc67dec663408bc636cb86756e0200e41a75c8f86603f104f02c856983d2783116be13", }, }, { Index: "55294", Status: "active_exiting", - Validator: &beacon.Validator{ + Validator: &structs.Validator{ Pubkey: "0x80000e851c0f53c3246ff726d7ff7766661ca5e12a07c45c114d208d54f0f8233d4380b2e9aff759d69795d1df905526", }, }, { Index: "55295", Status: "exited_slashed", - Validator: &beacon.Validator{ + Validator: &structs.Validator{ Pubkey: "0x800015473bdc3a7f45ef8eb8abc598bc20021e55ad6e6ad1d745aaef9730dd2c28ec08bf42df18451de94dd4a6d24ec5", }, }, @@ -185,7 +185,7 @@ func TestGetStateValidators_Nominal_GET(t *testing.T) { nil, ).SetArg( 2, - beacon.GetValidatorsResponse{ + structs.GetValidatorsResponse{ Data: wanted, }, ).Times(1) @@ -307,7 +307,7 @@ func TestGetStateValidators_DataIsNil_GET(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - req := &beacon.GetValidatorsRequest{ + req := &structs.GetValidatorsRequest{ Ids: []string{"0x8000091c2ae64ee414a54c1cc1fc67dec663408bc636cb86756e0200e41a75c8f86603f104f02c856983d2783116be13"}, Statuses: []string{}, } @@ -315,7 +315,7 @@ func TestGetStateValidators_DataIsNil_GET(t *testing.T) { require.NoError(t, err) ctx := context.Background() - stateValidatorsResponseJson := beacon.GetValidatorsResponse{} + stateValidatorsResponseJson := structs.GetValidatorsResponse{} jsonRestHandler := mock.NewMockJsonRestHandler(ctrl) // First call POST which will return an error. @@ -348,7 +348,7 @@ func TestGetStateValidators_DataIsNil_GET(t *testing.T) { nil, ).SetArg( 2, - beacon.GetValidatorsResponse{ + structs.GetValidatorsResponse{ Data: nil, }, ).Times(1) From e57597927e76407f7db3b8d4f112889abe4901fa Mon Sep 17 00:00:00 2001 From: Dhruv Bodani Date: Mon, 5 Feb 2024 15:24:13 +0530 Subject: [PATCH 18/19] fix build and lint --- .../client/beacon-api/submit_aggregate_selection_proof_test.go | 2 +- validator/client/beacon-api/sync_committee_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/validator/client/beacon-api/submit_aggregate_selection_proof_test.go b/validator/client/beacon-api/submit_aggregate_selection_proof_test.go index 470321050367..4f3eb7e7c6a8 100644 --- a/validator/client/beacon-api/submit_aggregate_selection_proof_test.go +++ b/validator/client/beacon-api/submit_aggregate_selection_proof_test.go @@ -217,7 +217,7 @@ func TestSubmitAggregateSelectionProof(t *testing.T) { jsonRestHandler.EXPECT().Get( ctx, query, - &beacon.GetValidatorsResponse{}, + &structs.GetValidatorsResponse{}, ).Return( test.validatorsErr, ).Times(1) diff --git a/validator/client/beacon-api/sync_committee_test.go b/validator/client/beacon-api/sync_committee_test.go index a03176a905b2..6c10c4c18d9b 100644 --- a/validator/client/beacon-api/sync_committee_test.go +++ b/validator/client/beacon-api/sync_committee_test.go @@ -353,7 +353,7 @@ func TestGetSyncSubCommitteeIndex(t *testing.T) { jsonRestHandler.EXPECT().Get( ctx, query, - &beacon.GetValidatorsResponse{}, + &structs.GetValidatorsResponse{}, ).Return( test.validatorsErr, ).Times(1) From 8af0e9dc1ca25e6fbdc5c0fe10232fdddaaecdbf Mon Sep 17 00:00:00 2001 From: Dhruv Bodani Date: Mon, 5 Feb 2024 17:44:17 +0530 Subject: [PATCH 19/19] fix TestSubmitAggregateAndProof_Distributed --- validator/client/aggregate_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator/client/aggregate_test.go b/validator/client/aggregate_test.go index e2bc6f21858a..a9839846c5bb 100644 --- a/validator/client/aggregate_test.go +++ b/validator/client/aggregate_test.go @@ -129,7 +129,7 @@ func TestSubmitAggregateAndProof_Distributed(t *testing.T) { var pubKey [fieldparams.BLSPubkeyLength]byte copy(pubKey[:], validatorKey.PublicKey().Marshal()) validator.duties = ðpb.DutiesResponse{ - Duties: []*ethpb.DutiesResponse_Duty{ + CurrentEpochDuties: []*ethpb.DutiesResponse_Duty{ { PublicKey: validatorKey.PublicKey().Marshal(), ValidatorIndex: validatorIdx,