Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add REST implementation for SubmitAggregateSelectionProof #11980

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions validator/client/beacon-api/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ go_library(
"state_validators.go",
"status.go",
"stream_blocks.go",
"submit_aggregate_selection_proof.go",
"submit_signed_aggregate_proof.go",
"submit_signed_contribution_and_proof.go",
"subscribe_committee_subnets.go",
Expand All @@ -35,6 +36,7 @@ go_library(
visibility = ["//validator:__subpackages__"],
deps = [
"//api/gateway/apimiddleware:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/signing:go_default_library",
"//beacon-chain/rpc/apimiddleware:go_default_library",
"//config/params:go_default_library",
Expand Down Expand Up @@ -86,6 +88,7 @@ go_test(
"state_validators_test.go",
"status_test.go",
"stream_blocks_test.go",
"submit_aggregate_selection_proof_test.go",
"submit_signed_aggregate_proof_test.go",
"submit_signed_contribution_and_proof_test.go",
"subscribe_committee_subnets_test.go",
Expand Down
9 changes: 9 additions & 0 deletions validator/client/beacon-api/beacon_api_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,12 @@ func (c *beaconApiValidatorClient) isSyncing(ctx context.Context) (bool, error)

return response.Data.IsSyncing, err
}

func (c *beaconApiValidatorClient) isOptimistic(ctx context.Context) (bool, error) {
response, err := c.getSyncing(ctx)
if err != nil || response == nil || response.Data == nil {
return true, errors.Wrapf(err, "failed to get syncing status")
}

return response.Data.IsOptimistic, err
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,7 @@ func (c *beaconApiValidatorClient) StreamDuties(ctx context.Context, in *ethpb.D
}

func (c *beaconApiValidatorClient) SubmitAggregateSelectionProof(ctx context.Context, in *ethpb.AggregateSelectionRequest) (*ethpb.AggregateSelectionResponse, error) {
if c.fallbackClient != nil {
return c.fallbackClient.SubmitAggregateSelectionProof(ctx, in)
}

// TODO: Implement me
panic("beaconApiValidatorClient.SubmitAggregateSelectionProof is not implemented. To use a fallback client, create this validator with NewBeaconApiValidatorClientWithFallback instead.")
return c.submitAggregateSelectionProof(ctx, in)
}

func (c *beaconApiValidatorClient) SubmitSignedAggregateSelectionProof(ctx context.Context, in *ethpb.SignedAggregateSubmitRequest) (*ethpb.SignedAggregateSubmitResponse, error) {
Expand Down
56 changes: 34 additions & 22 deletions validator/client/beacon-api/beacon_block_proto_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,12 @@ func convertAttesterSlashingsToProto(jsonAttesterSlashings []*apimiddleware.Atte
return nil, errors.Errorf("attester slashing at index `%d` is nil", index)
}

attestation1, err := convertAttestationToProto(jsonAttesterSlashing.Attestation_1)
attestation1, err := convertIndexedAttestationToProto(jsonAttesterSlashing.Attestation_1)
if err != nil {
return nil, errors.Wrap(err, "failed to get attestation 1")
}

attestation2, err := convertAttestationToProto(jsonAttesterSlashing.Attestation_2)
attestation2, err := convertIndexedAttestationToProto(jsonAttesterSlashing.Attestation_2)
if err != nil {
return nil, errors.Wrap(err, "failed to get attestation 2")
}
Expand All @@ -116,7 +116,7 @@ func convertAttesterSlashingsToProto(jsonAttesterSlashings []*apimiddleware.Atte
return attesterSlashings, nil
}

func convertAttestationToProto(jsonAttestation *apimiddleware.IndexedAttestationJson) (*ethpb.IndexedAttestation, error) {
func convertIndexedAttestationToProto(jsonAttestation *apimiddleware.IndexedAttestationJson) (*ethpb.IndexedAttestation, error) {
if jsonAttestation == nil {
return nil, errors.New("indexed attestation is nil")
}
Expand Down Expand Up @@ -170,34 +170,46 @@ func convertCheckpointToProto(jsonCheckpoint *apimiddleware.CheckpointJson) (*et
}, nil
}

func convertAttestationsToProto(jsonAttestations []*apimiddleware.AttestationJson) ([]*ethpb.Attestation, error) {
attestations := make([]*ethpb.Attestation, len(jsonAttestations))
func convertAttestationToProto(jsonAttestation *apimiddleware.AttestationJson) (*ethpb.Attestation, error) {
if jsonAttestation == nil {
return nil, errors.New("json attestation is nil")
}

aggregationBits, err := hexutil.Decode(jsonAttestation.AggregationBits)
if err != nil {
return nil, errors.Wrapf(err, "failed to decode aggregation bits `%s`", jsonAttestation.AggregationBits)
}

attestationData, err := convertAttestationDataToProto(jsonAttestation.Data)
if err != nil {
return nil, errors.Wrap(err, "failed to get attestation data")
}

signature, err := hexutil.Decode(jsonAttestation.Signature)
if err != nil {
return nil, errors.Wrapf(err, "failed to decode attestation signature `%s`", jsonAttestation.Signature)
}

return &ethpb.Attestation{
AggregationBits: aggregationBits,
Data: attestationData,
Signature: signature,
}, nil
}

func convertAttestationsToProto(jsonAttestations []*apimiddleware.AttestationJson) ([]*ethpb.Attestation, error) {
var attestations []*ethpb.Attestation
for index, jsonAttestation := range jsonAttestations {
if jsonAttestation == nil {
return nil, errors.Errorf("attestation at index `%d` is nil", index)
}

aggregationBits, err := hexutil.Decode(jsonAttestation.AggregationBits)
attestation, err := convertAttestationToProto(jsonAttestation)
if err != nil {
return nil, errors.Wrapf(err, "failed to decode aggregation bits `%s`", jsonAttestation.AggregationBits)
return nil, errors.Wrapf(err, "failed to convert json attestation to proto at index %d", index)
}

attestationData, err := convertAttestationDataToProto(jsonAttestation.Data)
if err != nil {
return nil, errors.Wrap(err, "failed to get attestation data")
}

signature, err := hexutil.Decode(jsonAttestation.Signature)
if err != nil {
return nil, errors.Wrapf(err, "failed to decode attestation signature `%s`", jsonAttestation.Signature)
}

attestations[index] = &ethpb.Attestation{
AggregationBits: aggregationBits,
Data: attestationData,
Signature: signature,
}
attestations = append(attestations, attestation)
}

return attestations, nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ func TestBeaconBlockProtoHelpers_ConvertAttestationToProto(t *testing.T) {

for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
result, err := convertAttestationToProto(testCase.generateInput())
result, err := convertIndexedAttestationToProto(testCase.generateInput())

if testCase.expectedResult != nil {
require.NoError(t, err)
Expand Down
99 changes: 99 additions & 0 deletions validator/client/beacon-api/submit_aggregate_selection_proof.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package beacon_api

import (
"context"
"net/url"
"strconv"

"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/rpc/apimiddleware"
"github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v3/time/slots"
)

func (c *beaconApiValidatorClient) submitAggregateSelectionProof(ctx context.Context, in *ethpb.AggregateSelectionRequest) (*ethpb.AggregateSelectionResponse, error) {
isOptimistic, err := c.isOptimistic(ctx)
if err != nil {
return nil, err
}

// An optimistic validator MUST NOT participate in attestation. (i.e., sign across the DOMAIN_BEACON_ATTESTER, DOMAIN_SELECTION_PROOF or DOMAIN_AGGREGATE_AND_PROOF domains).
if isOptimistic {
return nil, errors.New("the node is currently optimistic and cannot serve validators")
}

validatorIndexResponse, err := c.validatorIndex(ctx, &ethpb.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)
if err != nil {
return nil, errors.Wrap(err, "failed to get aggregator status")
}
if !isAggregator {
return nil, errors.New("validator is not an aggregator")
}
dB2510 marked this conversation as resolved.
Show resolved Hide resolved

attestationData, err := c.getAttestationData(ctx, in.Slot, in.CommitteeIndex)
if err != nil {
return nil, errors.Wrapf(err, "failed to get attestation data for slot=%d and committee_index=%d", in.Slot, in.CommitteeIndex)
}

attestationDataRoot, err := attestationData.HashTreeRoot()
if err != nil {
return nil, errors.Wrap(err, "failed to calculate attestation data root")
}

aggregateAttestationResponse, err := c.getAggregateAttestation(ctx, in.Slot, attestationDataRoot[:])
if err != nil {
return nil, err
}

aggregatedAttestation, err := convertAttestationToProto(aggregateAttestationResponse.Data)
if err != nil {
return nil, errors.Wrap(err, "failed to convert aggregate attestation json to proto")
}

return &ethpb.AggregateSelectionResponse{
AggregateAndProof: &ethpb.AggregateAttestationAndProof{
AggregatorIndex: validatorIndexResponse.Index,
Aggregate: aggregatedAttestation,
SelectionProof: in.SlotSignature,
},
}, nil
}

func (c *beaconApiValidatorClient) getAggregateAttestation(ctx context.Context, slot primitives.Slot, attestationDataRoot []byte) (*apimiddleware.AggregateAttestationResponseJson, error) {
params := url.Values{}
params.Add("slot", strconv.FormatUint(uint64(slot), 10))
params.Add("attestation_data_root", hexutil.Encode(attestationDataRoot))
endpoint := buildURL("/eth/v1/validator/aggregate_attestation", params)

var aggregateAttestationResponse apimiddleware.AggregateAttestationResponseJson
if _, err := c.jsonRestHandler.GetRestJsonResponse(ctx, endpoint, &aggregateAttestationResponse); err != nil {
return nil, errors.Wrap(err, "failed to get aggregate attestation")
}

return &aggregateAttestationResponse, nil
}
Loading