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

validator REST: attestation v2 #14633

Merged
merged 29 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
22b610f
wip
james-prysm Nov 7, 2024
f9d3c42
fixing tests
james-prysm Nov 7, 2024
4eb8ad0
adding unit tests
james-prysm Nov 7, 2024
e5c2157
fixing tests
james-prysm Nov 8, 2024
22a9d0a
adding back v1 usage
james-prysm Nov 8, 2024
1ff1822
changelog
james-prysm Nov 8, 2024
7caa823
Merge branch 'develop' into validator-rest-electra-attestation
james-prysm Nov 8, 2024
84eb391
rolling back test and adding placeholder
james-prysm Nov 8, 2024
6b5d44a
adding electra tests
james-prysm Nov 8, 2024
f1c677a
Merge branch 'develop' into validator-rest-electra-attestation
james-prysm Nov 8, 2024
94d18c6
Merge branch 'develop' into validator-rest-electra-attestation
james-prysm Nov 12, 2024
b6d96d5
adding attestation nil check based on review
james-prysm Nov 12, 2024
83b3d09
reduce code duplication
james-prysm Nov 13, 2024
f489d4e
Merge branch 'develop' into validator-rest-electra-attestation
james-prysm Nov 13, 2024
299048f
linting
james-prysm Nov 13, 2024
6636965
fixing tests
james-prysm Nov 13, 2024
0059090
based on sammy review
james-prysm Nov 14, 2024
e397dd3
radek feedback
james-prysm Nov 18, 2024
2fdce65
Merge branch 'develop' into validator-rest-electra-attestation
james-prysm Nov 18, 2024
4003c86
Merge branch 'develop' into validator-rest-electra-attestation
james-prysm Nov 19, 2024
736d0d2
adding fall back for pre electra and updated tests
james-prysm Nov 19, 2024
48cb111
fixing api calls and associated tests
james-prysm Nov 19, 2024
5dfbc2f
Merge branch 'develop' into validator-rest-electra-attestation
james-prysm Nov 19, 2024
2855c2e
gaz
james-prysm Nov 19, 2024
42d9e22
Update validator/client/beacon-api/propose_attestation.go
james-prysm Nov 20, 2024
8254f2f
review feedback
james-prysm Nov 20, 2024
bf4577d
add missing fallback
james-prysm Nov 20, 2024
6fc4954
Merge branch 'develop' into validator-rest-electra-attestation
james-prysm Nov 20, 2024
a0cc0c3
fixing tests
james-prysm Nov 20, 2024
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve
- Added SubmitAttestationsV2 endpoint.
- Validator REST mode Electra block support
- Added validator index label to `validator_statuses` metric
- Added Validator REST mode use of Attestation V2 endpoints and Electra attestations

### Changed

Expand Down
1 change: 1 addition & 0 deletions validator/client/aggregate.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ func (v *validator) SubmitAggregateAndProof(ctx context.Context, slot primitives
PublicKey: pubKey[:],
SlotSignature: slotSig,
}
// TODO: look at renaming SubmitAggregateSelectionProof functions as they are GET beacon API
var agg ethpb.AggregateAttAndProof
if postElectra {
res, err := v.validatorClient.SubmitAggregateSelectionProofElectra(ctx, aggSelectionRequest, duty.ValidatorIndex, uint64(len(duty.Committee)))
Expand Down
1 change: 1 addition & 0 deletions validator/client/beacon-api/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ go_test(
"//network/httputil:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//runtime/version:go_default_library",
"//testing/assert:go_default_library",
"//testing/require:go_default_library",
"//time/slots:go_default_library",
Expand Down
21 changes: 18 additions & 3 deletions validator/client/beacon-api/beacon_api_validator_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,12 @@ func (c *beaconApiValidatorClient) ProposeAttestation(ctx context.Context, in *e
}

func (c *beaconApiValidatorClient) ProposeAttestationElectra(ctx context.Context, in *ethpb.AttestationElectra) (*ethpb.AttestResponse, error) {
return nil, errors.New("ProposeAttestationElectra is not implemented")
ctx, span := trace.StartSpan(ctx, "beacon-api.ProposeAttestationElectra")
defer span.End()

return wrapInMetrics[*ethpb.AttestResponse]("ProposeAttestationElectra", func() (*ethpb.AttestResponse, error) {
return c.proposeAttestationElectra(ctx, in)
})
}

func (c *beaconApiValidatorClient) ProposeBeaconBlock(ctx context.Context, in *ethpb.GenericSignedBeaconBlock) (*ethpb.ProposeResponse, error) {
Expand Down Expand Up @@ -190,7 +195,12 @@ func (c *beaconApiValidatorClient) SubmitAggregateSelectionProof(ctx context.Con
}

func (c *beaconApiValidatorClient) SubmitAggregateSelectionProofElectra(ctx context.Context, in *ethpb.AggregateSelectionRequest, index primitives.ValidatorIndex, committeeLength uint64) (*ethpb.AggregateSelectionElectraResponse, error) {
return nil, errors.New("SubmitAggregateSelectionProofElectra is not implemented")
ctx, span := trace.StartSpan(ctx, "beacon-api.SubmitAggregateSelectionProofElectra")
defer span.End()

return wrapInMetrics[*ethpb.AggregateSelectionElectraResponse]("SubmitAggregateSelectionProofElectra", func() (*ethpb.AggregateSelectionElectraResponse, error) {
return c.submitAggregateSelectionProofElectra(ctx, in, index, committeeLength)
})
}

func (c *beaconApiValidatorClient) SubmitSignedAggregateSelectionProof(ctx context.Context, in *ethpb.SignedAggregateSubmitRequest) (*ethpb.SignedAggregateSubmitResponse, error) {
Expand All @@ -203,7 +213,12 @@ func (c *beaconApiValidatorClient) SubmitSignedAggregateSelectionProof(ctx conte
}

func (c *beaconApiValidatorClient) SubmitSignedAggregateSelectionProofElectra(ctx context.Context, in *ethpb.SignedAggregateSubmitElectraRequest) (*ethpb.SignedAggregateSubmitResponse, error) {
return nil, errors.New("SubmitSignedAggregateSelectionProofElectra is not implemented")
ctx, span := trace.StartSpan(ctx, "beacon-api.SubmitSignedAggregateSelectionProofElectra")
defer span.End()

return wrapInMetrics[*ethpb.SignedAggregateSubmitResponse]("SubmitSignedAggregateSelectionProofElectra", func() (*ethpb.SignedAggregateSubmitResponse, error) {
return c.submitSignedAggregateSelectionProofElectra(ctx, in)
})
}

func (c *beaconApiValidatorClient) SubmitSignedContributionAndProof(ctx context.Context, in *ethpb.SignedContributionAndProof) (*empty.Empty, error) {
Expand Down
28 changes: 28 additions & 0 deletions validator/client/beacon-api/beacon_block_json_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ func jsonifyAttestations(attestations []*ethpb.Attestation) []*structs.Attestati
return jsonAttestations
}

func jsonifyAttestationsElectra(attestations []*ethpb.AttestationElectra) []*structs.AttestationElectra {
jsonAttestations := make([]*structs.AttestationElectra, len(attestations))
for index, attestation := range attestations {
jsonAttestations[index] = jsonifyAttestationElectra(attestation)
}
return jsonAttestations
}

func jsonifyAttesterSlashings(attesterSlashings []*ethpb.AttesterSlashing) []*structs.AttesterSlashing {
jsonAttesterSlashings := make([]*structs.AttesterSlashing, len(attesterSlashings))
for index, attesterSlashing := range attesterSlashings {
Expand Down Expand Up @@ -164,6 +172,15 @@ func jsonifyAttestation(attestation *ethpb.Attestation) *structs.Attestation {
}
}

func jsonifyAttestationElectra(attestation *ethpb.AttestationElectra) *structs.AttestationElectra {
return &structs.AttestationElectra{
AggregationBits: hexutil.Encode(attestation.AggregationBits),
Data: jsonifyAttestationData(attestation.Data),
Signature: hexutil.Encode(attestation.Signature),
CommitteeBits: hexutil.Encode(attestation.CommitteeBits),
}
}

func jsonifySignedAggregateAndProof(signedAggregateAndProof *ethpb.SignedAggregateAttestationAndProof) *structs.SignedAggregateAttestationAndProof {
return &structs.SignedAggregateAttestationAndProof{
Message: &structs.AggregateAttestationAndProof{
Expand All @@ -175,6 +192,17 @@ func jsonifySignedAggregateAndProof(signedAggregateAndProof *ethpb.SignedAggrega
}
}

func jsonifySignedAggregateAndProofElectra(signedAggregateAndProof *ethpb.SignedAggregateAttestationAndProofElectra) *structs.SignedAggregateAttestationAndProofElectra {
return &structs.SignedAggregateAttestationAndProofElectra{
Message: &structs.AggregateAttestationAndProofElectra{
AggregatorIndex: uint64ToString(signedAggregateAndProof.Message.AggregatorIndex),
Aggregate: jsonifyAttestationElectra(signedAggregateAndProof.Message.Aggregate),
SelectionProof: hexutil.Encode(signedAggregateAndProof.Message.SelectionProof),
},
Signature: hexutil.Encode(signedAggregateAndProof.Signature),
}
}

func jsonifyWithdrawals(withdrawals []*enginev1.Withdrawal) []*structs.Withdrawal {
jsonWithdrawals := make([]*structs.Withdrawal, len(withdrawals))
for index, withdrawal := range withdrawals {
Expand Down
33 changes: 33 additions & 0 deletions validator/client/beacon-api/beacon_block_proto_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,39 @@ func convertAttestationToProto(jsonAttestation *structs.Attestation) (*ethpb.Att
}, nil
}

func convertAttestationElectraToProto(jsonAttestation *structs.AttestationElectra) (*ethpb.AttestationElectra, 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)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we be checking for lengths?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we can add this in a future PR, but I would not do it here

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

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

return &ethpb.AttestationElectra{
AggregationBits: aggregationBits,
Data: attestationData,
Signature: signature,
CommitteeBits: committeeBits,
}, nil
}

func convertAttestationsToProto(jsonAttestations []*structs.Attestation) ([]*ethpb.Attestation, error) {
var attestations []*ethpb.Attestation
for index, jsonAttestation := range jsonAttestations {
Expand Down
90 changes: 69 additions & 21 deletions validator/client/beacon-api/propose_attestation.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,73 @@ import (
"bytes"
"context"
"encoding/json"
"net/http"

"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v5/network/httputil"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/runtime/version"
)

func (c *beaconApiValidatorClient) proposeAttestation(ctx context.Context, attestation *ethpb.Attestation) (*ethpb.AttestResponse, error) {
if err := checkNilAttestation(attestation); err != nil {
if err := validateNilAttestation(attestation); err != nil {
return nil, err
}

marshalledAttestation, err := json.Marshal(jsonifyAttestations([]*ethpb.Attestation{attestation}))
if err != nil {
return nil, err
}

if err = c.jsonRestHandler.Post(
headers := map[string]string{"Eth-Consensus-Version": version.String(attestation.Version())}
err = c.jsonRestHandler.Post(
ctx,
"/eth/v1/beacon/pool/attestations",
"/eth/v2/beacon/pool/attestations",
headers,
bytes.NewBuffer(marshalledAttestation),
nil,
)
errJson := &httputil.DefaultJsonError{}
if err != nil {
// TODO: remove this when v2 becomes default
if !errors.As(err, &errJson) {
return nil, err
}
if errJson.Code != http.StatusNotFound {
return nil, errJson
}
log.Debug("Endpoint /eth/v2/beacon/pool/attestations is not supported, falling back to older endpoints for submit attestation.")
if err = c.jsonRestHandler.Post(
ctx,
"/eth/v1/beacon/pool/attestations",
nil,
bytes.NewBuffer(marshalledAttestation),
nil,
); err != nil {
return nil, err
}
}

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

return &ethpb.AttestResponse{AttestationDataRoot: attestationDataRoot[:]}, nil
}

func (c *beaconApiValidatorClient) proposeAttestationElectra(ctx context.Context, attestation *ethpb.AttestationElectra) (*ethpb.AttestResponse, error) {
if err := validateNilAttestation(attestation); err != nil {
return nil, err
}
marshalledAttestation, err := json.Marshal(jsonifyAttestationsElectra([]*ethpb.AttestationElectra{attestation}))
if err != nil {
return nil, err
}
headers := map[string]string{"Eth-Consensus-Version": version.String(attestation.Version())}
if err = c.jsonRestHandler.Post(
ctx,
"/eth/v2/beacon/pool/attestations",
headers,
bytes.NewBuffer(marshalledAttestation),
nil,
); err != nil {
Expand All @@ -37,27 +85,27 @@ func (c *beaconApiValidatorClient) proposeAttestation(ctx context.Context, attes
return &ethpb.AttestResponse{AttestationDataRoot: attestationDataRoot[:]}, nil
}

// checkNilAttestation returns error if attestation or any field of attestation is nil.
func checkNilAttestation(attestation *ethpb.Attestation) error {
if attestation == nil {
return errors.New("attestation is nil")
func validateNilAttestation(attestation ethpb.Att) error {
if attestation == nil || attestation.IsNil() {
return errors.New("attestation can't be nil")
}

if attestation.Data == nil {
return errors.New("attestation data is nil")
if attestation.GetData().Source == nil {
return errors.New("attestation's source can't be nil")
}

if attestation.Data.Source == nil || attestation.Data.Target == nil {
return errors.New("source/target in attestation data is nil")
if attestation.GetData().Target == nil {
return errors.New("attestation's target can't be nil")
}

if len(attestation.AggregationBits) == 0 {
return errors.New("attestation aggregation bits is empty")
v := attestation.Version()
if len(attestation.GetAggregationBits()) == 0 {
return errors.New("attestation's bitfield can't be nil")
}

if len(attestation.Signature) == 0 {
return errors.New("attestation signature is empty")
if len(attestation.GetSignature()) == 0 {
return errors.New("attestation signature can't be nil")
}
if v >= version.Electra {
if len(attestation.CommitteeBitsVal().BitIndices()) == 0 {
return errors.New("attestation committee bits can't be nil")
}
}

return nil
}
Loading
Loading