diff --git a/testing/validator-mock/BUILD.bazel b/testing/validator-mock/BUILD.bazel index 8a32955b7684..e7df6fed845f 100644 --- a/testing/validator-mock/BUILD.bazel +++ b/testing/validator-mock/BUILD.bazel @@ -15,6 +15,7 @@ go_library( deps = [ "//api/client/beacon:go_default_library", "//api/client/event:go_default_library", + "//consensus-types/primitives:go_default_library", "//consensus-types/validator:go_default_library", "//proto/prysm/v1alpha1:go_default_library", "//validator/client/iface:go_default_library", diff --git a/testing/validator-mock/validator_client_mock.go b/testing/validator-mock/validator_client_mock.go index a63834d10126..7af1aee9a0de 100644 --- a/testing/validator-mock/validator_client_mock.go +++ b/testing/validator-mock/validator_client_mock.go @@ -13,8 +13,8 @@ import ( context "context" reflect "reflect" - "github.com/prysmaticlabs/prysm/v5/api/client/beacon" - "github.com/prysmaticlabs/prysm/v5/api/client/event" + event "github.com/prysmaticlabs/prysm/v5/api/client/event" + primitives "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" iface "github.com/prysmaticlabs/prysm/v5/validator/client/iface" gomock "go.uber.org/mock/gomock" @@ -113,7 +113,7 @@ func (m *MockValidatorClient) GetAggregatedSyncSelections(arg0 context.Context, } // GetAggregatedSyncSelections indicates an expected call of GetAggregatedSyncSelections. -func (mr *MockValidatorClientMockRecorder) GetAggregatedSyncSelections(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockValidatorClientMockRecorder) GetAggregatedSyncSelections(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAggregatedSyncSelections", reflect.TypeOf((*MockValidatorClient)(nil).GetAggregatedSyncSelections), arg0, arg1) } @@ -299,72 +299,30 @@ func (mr *MockValidatorClientMockRecorder) ProposeExit(arg0, arg1 any) *gomock.C } // StartEventStream mocks base method. -func (m *MockValidatorClient) StartEventStream(arg0 context.Context, arg1 []string, arg2 chan<- *event.Event){ +func (m *MockValidatorClient) StartEventStream(arg0 context.Context, arg1 []string, arg2 chan<- *event.Event) { m.ctrl.T.Helper() - _ = m.ctrl.Call(m, "StartEventStream", arg0,arg1,arg2) + m.ctrl.Call(m, "StartEventStream", arg0, arg1, arg2) } // StartEventStream indicates an expected call of StartEventStream. -func (mr *MockValidatorClientMockRecorder) StartEventStream(arg0,arg1,arg2 interface{}) *gomock.Call { +func (mr *MockValidatorClientMockRecorder) StartEventStream(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartEventStream", reflect.TypeOf((*MockValidatorClient)(nil).StartEventStream), arg0, arg1, arg2) } -// ProcessEvent mocks base method. -func (m *MockValidatorClient) ProcessEvent(arg0 *event.Event) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ProcessEvent", arg0) - ret0, _ := ret[0].(error) - return ret0 -} - -// ProcessEvent indicates an expected call of ProcessEvent. -func (mr *MockValidatorClientMockRecorder) ProcessEvent(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProcessEvent", reflect.TypeOf((*MockValidatorClient)(nil).ProcessEvent), arg0) -} - -// NodeIsHealthy mocks base method. -func (m *MockValidatorClient) NodeIsHealthy(arg0 context.Context) bool { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NodeIsHealthy",arg0) - ret0, _ := ret[0].(bool) - return ret0 -} - -// NodeIsHealthy indicates an expected call of NodeIsHealthy. -func (mr *MockValidatorClientMockRecorder) NodeIsHealthy(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NodeIsHealthy", reflect.TypeOf((*MockValidatorClient)(nil).NodeIsHealthy), arg0) -} - -// NodeHealthTracker mocks base method. -func (m *MockValidatorClient) NodeHealthTracker() *beacon.NodeHealthTracker { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NodeHealthTracker") - ret0, _ := ret[0].(*beacon.NodeHealthTracker) - return ret0 -} - -// NodeHealthTracker indicates an expected call of NodeHealthTracker. -func (mr *MockValidatorClientMockRecorder) NodeHealthTracker() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NodeHealthTracker", reflect.TypeOf((*MockValidatorClient)(nil).NodeHealthTracker)) -} - // SubmitAggregateSelectionProof mocks base method. -func (m *MockValidatorClient) SubmitAggregateSelectionProof(arg0 context.Context, arg1 *eth.AggregateSelectionRequest) (*eth.AggregateSelectionResponse, error) { +func (m *MockValidatorClient) SubmitAggregateSelectionProof(arg0 context.Context, arg1 *eth.AggregateSelectionRequest, arg2 primitives.ValidatorIndex, arg3 uint64) (*eth.AggregateSelectionResponse, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubmitAggregateSelectionProof", arg0, arg1) + ret := m.ctrl.Call(m, "SubmitAggregateSelectionProof", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*eth.AggregateSelectionResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // SubmitAggregateSelectionProof indicates an expected call of SubmitAggregateSelectionProof. -func (mr *MockValidatorClientMockRecorder) SubmitAggregateSelectionProof(arg0, arg1 any) *gomock.Call { +func (mr *MockValidatorClientMockRecorder) SubmitAggregateSelectionProof(arg0, arg1, arg2, arg3 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitAggregateSelectionProof", reflect.TypeOf((*MockValidatorClient)(nil).SubmitAggregateSelectionProof), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitAggregateSelectionProof", reflect.TypeOf((*MockValidatorClient)(nil).SubmitAggregateSelectionProof), arg0, arg1, arg2, arg3) } // SubmitSignedAggregateSelectionProof mocks base method. diff --git a/validator/client/aggregate.go b/validator/client/aggregate.go index 1bd42aa96d58..ab4b532f7282 100644 --- a/validator/client/aggregate.go +++ b/validator/client/aggregate.go @@ -84,7 +84,7 @@ func (v *validator) SubmitAggregateAndProof(ctx context.Context, slot primitives CommitteeIndex: duty.CommitteeIndex, PublicKey: pubKey[:], SlotSignature: slotSig, - }) + }, duty.ValidatorIndex, uint64(len(duty.Committee))) if err != nil { // handle grpc not found s, ok := status.FromError(err) diff --git a/validator/client/aggregate_test.go b/validator/client/aggregate_test.go index 4c2799f82963..7951a744a2f0 100644 --- a/validator/client/aggregate_test.go +++ b/validator/client/aggregate_test.go @@ -63,6 +63,8 @@ func TestSubmitAggregateAndProof_SignFails(t *testing.T) { m.validatorClient.EXPECT().SubmitAggregateSelectionProof( gomock.Any(), // ctx gomock.AssignableToTypeOf(ðpb.AggregateSelectionRequest{}), + gomock.Any(), + gomock.Any(), ).Return(ðpb.AggregateSelectionResponse{ AggregateAndProof: ðpb.AggregateAttestationAndProof{ AggregatorIndex: 0, @@ -106,6 +108,8 @@ func TestSubmitAggregateAndProof_Ok(t *testing.T) { m.validatorClient.EXPECT().SubmitAggregateSelectionProof( gomock.Any(), // ctx gomock.AssignableToTypeOf(ðpb.AggregateSelectionRequest{}), + gomock.Any(), + gomock.Any(), ).Return(ðpb.AggregateSelectionResponse{ AggregateAndProof: ðpb.AggregateAttestationAndProof{ AggregatorIndex: 0, @@ -166,6 +170,8 @@ func TestSubmitAggregateAndProof_Distributed(t *testing.T) { m.validatorClient.EXPECT().SubmitAggregateSelectionProof( gomock.Any(), // ctx gomock.AssignableToTypeOf(ðpb.AggregateSelectionRequest{}), + gomock.Any(), + gomock.Any(), ).Return(ðpb.AggregateSelectionResponse{ AggregateAndProof: ðpb.AggregateAttestationAndProof{ AggregatorIndex: 0, diff --git a/validator/client/beacon-api/beacon_api_validator_client.go b/validator/client/beacon-api/beacon_api_validator_client.go index 12463e05e5d3..c5817f654a90 100644 --- a/validator/client/beacon-api/beacon_api_validator_client.go +++ b/validator/client/beacon-api/beacon_api_validator_client.go @@ -8,6 +8,7 @@ import ( "github.com/golang/protobuf/ptypes/empty" "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v5/api/client/event" + "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v5/validator/client/iface" @@ -135,9 +136,9 @@ func (c *beaconApiValidatorClient) StreamBlocksAltair(ctx context.Context, in *e return c.streamBlocks(ctx, in, time.Second), nil } -func (c *beaconApiValidatorClient) SubmitAggregateSelectionProof(ctx context.Context, in *ethpb.AggregateSelectionRequest) (*ethpb.AggregateSelectionResponse, error) { +func (c *beaconApiValidatorClient) SubmitAggregateSelectionProof(ctx context.Context, in *ethpb.AggregateSelectionRequest, index primitives.ValidatorIndex, committeeLength uint64) (*ethpb.AggregateSelectionResponse, error) { return wrapInMetrics[*ethpb.AggregateSelectionResponse]("SubmitAggregateSelectionProof", func() (*ethpb.AggregateSelectionResponse, error) { - return c.submitAggregateSelectionProof(ctx, in) + return c.submitAggregateSelectionProof(ctx, in, index, committeeLength) }) } diff --git a/validator/client/beacon-api/submit_aggregate_selection_proof.go b/validator/client/beacon-api/submit_aggregate_selection_proof.go index bac592f38225..2bc7e4230d5a 100644 --- a/validator/client/beacon-api/submit_aggregate_selection_proof.go +++ b/validator/client/beacon-api/submit_aggregate_selection_proof.go @@ -11,10 +11,14 @@ import ( "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" - "github.com/prysmaticlabs/prysm/v5/time/slots" ) -func (c *beaconApiValidatorClient) submitAggregateSelectionProof(ctx context.Context, in *ethpb.AggregateSelectionRequest) (*ethpb.AggregateSelectionResponse, error) { +func (c *beaconApiValidatorClient) submitAggregateSelectionProof( + ctx context.Context, + in *ethpb.AggregateSelectionRequest, + index primitives.ValidatorIndex, + committeeLength uint64, +) (*ethpb.AggregateSelectionResponse, error) { isOptimistic, err := c.isOptimistic(ctx) if err != nil { return nil, err @@ -25,29 +29,7 @@ func (c *beaconApiValidatorClient) submitAggregateSelectionProof(ctx context.Con return nil, errors.New("the node is currently optimistic and cannot serve validators") } - validatorIndexResponse, err := c.validatorIndex(ctx, ðpb.ValidatorIndexRequest{PublicKey: in.PublicKey}) - if err != nil { - return nil, errors.Wrap(err, "failed to get validator index") - } - - attesterDuties, err := c.dutiesProvider.GetAttesterDuties(ctx, slots.ToEpoch(in.Slot), []primitives.ValidatorIndex{validatorIndexResponse.Index}) - if err != nil { - return nil, errors.Wrap(err, "failed to get attester duties") - } - - if len(attesterDuties) == 0 { - return nil, errors.Errorf("no attester duty for the given slot %d", in.Slot) - } - - // First attester duty is required since we requested attester duties for one validator index. - attesterDuty := attesterDuties[0] - - committeeLen, err := strconv.ParseUint(attesterDuty.CommitteeLength, 10, 64) - if err != nil { - return nil, errors.Wrap(err, "failed to parse committee length") - } - - isAggregator, err := helpers.IsAggregator(committeeLen, in.SlotSignature) + isAggregator, err := helpers.IsAggregator(committeeLength, in.SlotSignature) if err != nil { return nil, errors.Wrap(err, "failed to get aggregator status") } @@ -77,7 +59,7 @@ func (c *beaconApiValidatorClient) submitAggregateSelectionProof(ctx context.Con return ðpb.AggregateSelectionResponse{ AggregateAndProof: ðpb.AggregateAttestationAndProof{ - AggregatorIndex: validatorIndexResponse.Index, + AggregatorIndex: index, Aggregate: aggregatedAttestation, SelectionProof: in.SlotSignature, }, 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 26adfa6fc458..8e20bb0fb25d 100644 --- a/validator/client/beacon-api/submit_aggregate_selection_proof_test.go +++ b/validator/client/beacon-api/submit_aggregate_selection_proof_test.go @@ -1,12 +1,9 @@ package beacon_api import ( - "bytes" "context" - "encoding/json" "errors" "fmt" - "net/url" "testing" "github.com/ethereum/go-ethereum/common/hexutil" @@ -15,7 +12,6 @@ import ( ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v5/testing/assert" "github.com/prysmaticlabs/prysm/v5/testing/require" - "github.com/prysmaticlabs/prysm/v5/time/slots" "github.com/prysmaticlabs/prysm/v5/validator/client/beacon-api/mock" test_helpers "github.com/prysmaticlabs/prysm/v5/validator/client/beacon-api/test-helpers" "go.uber.org/mock/gomock" @@ -25,26 +21,15 @@ func TestSubmitAggregateSelectionProof(t *testing.T) { const ( pubkeyStr = "0x8000091c2ae64ee414a54c1cc1fc67dec663408bc636cb86756e0200e41a75c8f86603f104f02c856983d2783116be13" syncingEndpoint = "/eth/v1/node/syncing" - attesterDutiesEndpoint = "/eth/v1/validator/duties/attester" - validatorsEndpoint = "/eth/v1/beacon/states/head/validators" attestationDataEndpoint = "/eth/v1/validator/attestation_data" aggregateAttestationEndpoint = "/eth/v1/validator/aggregate_attestation" - validatorIndex = "55293" + validatorIndex = primitives.ValidatorIndex(55293) slotSignature = "0x8776a37d6802c4797d113169c5fcfda50e68a32058eb6356a6f00d06d7da64c841a00c7c38b9b94a204751eca53707bd03523ce4797827d9bacff116a6e776a20bbccff4b683bf5201b610797ed0502557a58a65c8395f8a1649b976c3112d15" slot = primitives.Slot(123) committeeIndex = primitives.CommitteeIndex(1) + committeesAtSlot = uint64(1) ) - attesterDuties := []*structs.AttesterDuty{ - { - Pubkey: pubkeyStr, - ValidatorIndex: validatorIndex, - Slot: "123", - CommitteeIndex: "1", - CommitteeLength: "3", - }, - } - attestationDataResponse := generateValidAttestation(uint64(slot), uint64(committeeIndex)) attestationDataProto, err := attestationDataResponse.Data.ToConsensus() require.NoError(t, err) @@ -64,22 +49,15 @@ func TestSubmitAggregateSelectionProof(t *testing.T) { name string isOptimistic bool syncingErr error - validatorsErr error - dutiesErr error attestationDataErr error aggregateAttestationErr error - duties []*structs.AttesterDuty - validatorsCalled int - attesterDutiesCalled int attestationDataCalled int aggregateAttestationCalled int expectedErrorMsg string + committeesAtSlot uint64 }{ { name: "success", - duties: attesterDuties, - validatorsCalled: 1, - attesterDutiesCalled: 1, attestationDataCalled: 1, aggregateAttestationCalled: 1, }, @@ -93,60 +71,23 @@ func TestSubmitAggregateSelectionProof(t *testing.T) { syncingErr: errors.New("bad request"), expectedErrorMsg: "failed to get syncing status", }, - { - name: "validator index error", - validatorsCalled: 1, - validatorsErr: errors.New("bad request"), - expectedErrorMsg: "failed to get validator index", - }, - { - name: "attester duties error", - duties: attesterDuties, - validatorsCalled: 1, - attesterDutiesCalled: 1, - dutiesErr: errors.New("bad request"), - expectedErrorMsg: "failed to get attester duties", - }, { name: "attestation data error", - duties: attesterDuties, - validatorsCalled: 1, - attesterDutiesCalled: 1, attestationDataCalled: 1, attestationDataErr: errors.New("bad request"), expectedErrorMsg: fmt.Sprintf("failed to get attestation data for slot=%d and committee_index=%d", slot, committeeIndex), }, { name: "aggregate attestation error", - duties: attesterDuties, - validatorsCalled: 1, - attesterDutiesCalled: 1, attestationDataCalled: 1, aggregateAttestationCalled: 1, aggregateAttestationErr: errors.New("bad request"), expectedErrorMsg: "bad request", }, { - name: "validator is not an aggregator", - duties: []*structs.AttesterDuty{ - { - Pubkey: pubkeyStr, - ValidatorIndex: validatorIndex, - Slot: "123", - CommitteeIndex: "1", - CommitteeLength: "64", - }, - }, - validatorsCalled: 1, - attesterDutiesCalled: 1, - expectedErrorMsg: "validator is not an aggregator", - }, - { - name: "no attester duties", - duties: []*structs.AttesterDuty{}, - validatorsCalled: 1, - attesterDutiesCalled: 1, - expectedErrorMsg: fmt.Sprintf("no attester duty for the given slot %d", slot), + name: "validator is not an aggregator", + committeesAtSlot: 64, + expectedErrorMsg: "validator is not an aggregator", }, } @@ -171,76 +112,6 @@ func TestSubmitAggregateSelectionProof(t *testing.T) { test.syncingErr, ).Times(1) - valsReq := &structs.GetValidatorsRequest{ - Ids: []string{stringPubKey}, - Statuses: []string{}, - } - valReqBytes, err := json.Marshal(valsReq) - require.NoError(t, err) - - // Call validators endpoint to get validator index. - jsonRestHandler.EXPECT().Post( - ctx, - validatorsEndpoint, - nil, - bytes.NewBuffer(valReqBytes), - &structs.GetValidatorsResponse{}, - ).SetArg( - 4, - structs.GetValidatorsResponse{ - Data: []*structs.ValidatorContainer{ - { - Index: validatorIndex, - Status: "active_ongoing", - Validator: &structs.Validator{ - Pubkey: pubkeyStr, - }, - }, - }, - }, - ).Return( - 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, - &structs.GetValidatorsResponse{}, - ).Return( - test.validatorsErr, - ).Times(1) - } - - // Call attester duties endpoint to get attester duties. - validatorIndicesBytes, err := json.Marshal([]string{validatorIndex}) - require.NoError(t, err) - jsonRestHandler.EXPECT().Post( - ctx, - fmt.Sprintf("%s/%d", attesterDutiesEndpoint, slots.ToEpoch(slot)), - nil, - bytes.NewBuffer(validatorIndicesBytes), - &structs.GetAttesterDutiesResponse{}, - ).SetArg( - 4, - structs.GetAttesterDutiesResponse{ - Data: test.duties, - }, - ).Return( - test.dutiesErr, - ).Times(test.attesterDutiesCalled) - // Call attestation data to get attestation data root to query aggregate attestation. jsonRestHandler.EXPECT().Get( ctx, @@ -290,12 +161,17 @@ func TestSubmitAggregateSelectionProof(t *testing.T) { jsonRestHandler: jsonRestHandler, }, } + + committees := committeesAtSlot + if test.committeesAtSlot != 0 { + committees = test.committeesAtSlot + } actualResponse, err := validatorClient.submitAggregateSelectionProof(ctx, ðpb.AggregateSelectionRequest{ Slot: slot, CommitteeIndex: committeeIndex, PublicKey: pubkey, SlotSignature: slotSignatureBytes, - }) + }, validatorIndex, committees) if test.expectedErrorMsg == "" { require.NoError(t, err) assert.DeepEqual(t, expectedResponse, actualResponse) diff --git a/validator/client/grpc-api/grpc_validator_client.go b/validator/client/grpc-api/grpc_validator_client.go index 639bd8e3978b..cb87cc174756 100644 --- a/validator/client/grpc-api/grpc_validator_client.go +++ b/validator/client/grpc-api/grpc_validator_client.go @@ -10,6 +10,7 @@ import ( "github.com/prysmaticlabs/prysm/v5/api/client" eventClient "github.com/prysmaticlabs/prysm/v5/api/client/event" "github.com/prysmaticlabs/prysm/v5/api/server/structs" + "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v5/validator/client/iface" log "github.com/sirupsen/logrus" @@ -82,7 +83,7 @@ func (c *grpcValidatorClient) StreamBlocksAltair(ctx context.Context, in *ethpb. return c.beaconNodeValidatorClient.StreamBlocksAltair(ctx, in) } -func (c *grpcValidatorClient) SubmitAggregateSelectionProof(ctx context.Context, in *ethpb.AggregateSelectionRequest) (*ethpb.AggregateSelectionResponse, error) { +func (c *grpcValidatorClient) SubmitAggregateSelectionProof(ctx context.Context, in *ethpb.AggregateSelectionRequest, _ primitives.ValidatorIndex, _ uint64) (*ethpb.AggregateSelectionResponse, error) { return c.beaconNodeValidatorClient.SubmitAggregateSelectionProof(ctx, in) } diff --git a/validator/client/iface/validator_client.go b/validator/client/iface/validator_client.go index 13cf67c4db78..3f9cbe8fd22f 100644 --- a/validator/client/iface/validator_client.go +++ b/validator/client/iface/validator_client.go @@ -135,7 +135,7 @@ type ValidatorClient interface { GetFeeRecipientByPubKey(ctx context.Context, in *ethpb.FeeRecipientByPubKeyRequest) (*ethpb.FeeRecipientByPubKeyResponse, error) GetAttestationData(ctx context.Context, in *ethpb.AttestationDataRequest) (*ethpb.AttestationData, error) ProposeAttestation(ctx context.Context, in *ethpb.Attestation) (*ethpb.AttestResponse, error) - SubmitAggregateSelectionProof(ctx context.Context, in *ethpb.AggregateSelectionRequest) (*ethpb.AggregateSelectionResponse, error) + SubmitAggregateSelectionProof(ctx context.Context, in *ethpb.AggregateSelectionRequest, index primitives.ValidatorIndex, committeeLength uint64) (*ethpb.AggregateSelectionResponse, error) SubmitSignedAggregateSelectionProof(ctx context.Context, in *ethpb.SignedAggregateSubmitRequest) (*ethpb.SignedAggregateSubmitResponse, error) ProposeExit(ctx context.Context, in *ethpb.SignedVoluntaryExit) (*ethpb.ProposeExitResponse, error) SubscribeCommitteeSubnets(ctx context.Context, in *ethpb.CommitteeSubnetsSubscribeRequest, duties []*ethpb.DutiesResponse_Duty) (*empty.Empty, error)