-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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 /eth/v2/validator/aggregate_attestation
#14481
Changes from 10 commits
97aa3ec
f9903c4
cbd5dd4
607cd31
fb8e325
f189e41
cb62038
f94403f
8f8ef67
99558f4
3a4acbd
f1a1591
55f78d9
29fc4f7
3048541
b980b91
8f88cb4
a358698
3ee2e54
7d168da
ead84cd
2813978
c6e8ac6
a7f5d8a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -31,6 +31,8 @@ import ( | |||||||||
"github.com/prysmaticlabs/prysm/v5/monitoring/tracing/trace" | ||||||||||
"github.com/prysmaticlabs/prysm/v5/network/httputil" | ||||||||||
ethpbalpha "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" | ||||||||||
"github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1/attestation/aggregation/attestations" | ||||||||||
"github.com/prysmaticlabs/prysm/v5/runtime/version" | ||||||||||
"github.com/prysmaticlabs/prysm/v5/time/slots" | ||||||||||
"github.com/sirupsen/logrus" | ||||||||||
"google.golang.org/grpc/codes" | ||||||||||
|
@@ -52,61 +54,133 @@ func (s *Server) GetAggregateAttestation(w http.ResponseWriter, r *http.Request) | |||||||||
return | ||||||||||
} | ||||||||||
|
||||||||||
match := s.aggregateAttestation(w, primitives.Slot(slot), "", attDataRoot) | ||||||||||
if match == nil { | ||||||||||
return | ||||||||||
} | ||||||||||
|
||||||||||
matchedAtt, ok := match.(*ethpbalpha.Attestation) | ||||||||||
if !ok { | ||||||||||
httputil.HandleError(w, "Match is not of type Attestation", http.StatusInternalServerError) | ||||||||||
return | ||||||||||
} | ||||||||||
att := structs.AttFromConsensus(matchedAtt) | ||||||||||
data, err := json.Marshal(att) | ||||||||||
if err != nil { | ||||||||||
httputil.HandleError(w, "Could not marshal attestation: "+err.Error(), http.StatusInternalServerError) | ||||||||||
return | ||||||||||
} | ||||||||||
httputil.WriteJson(w, &structs.AggregateAttestationResponse{Data: data}) | ||||||||||
} | ||||||||||
|
||||||||||
// GetAggregateAttestationV2 aggregates all attestations matching the given attestation data root and slot, returning the aggregated result. | ||||||||||
func (s *Server) GetAggregateAttestationV2(w http.ResponseWriter, r *http.Request) { | ||||||||||
_, span := trace.StartSpan(r.Context(), "validator.GetAggregateAttestationV2") | ||||||||||
defer span.End() | ||||||||||
|
||||||||||
_, attDataRoot, ok := shared.HexFromQuery(w, r, "attestation_data_root", fieldparams.RootLength, true) | ||||||||||
if !ok { | ||||||||||
return | ||||||||||
} | ||||||||||
_, slot, ok := shared.UintFromQuery(w, r, "slot", true) | ||||||||||
if !ok { | ||||||||||
return | ||||||||||
} | ||||||||||
_, index, ok := shared.UintFromQuery(w, r, "committee_index", true) | ||||||||||
if !ok { | ||||||||||
return | ||||||||||
} | ||||||||||
i := strconv.FormatUint(index, 10) | ||||||||||
match := s.aggregateAttestation(w, primitives.Slot(slot), i, attDataRoot) | ||||||||||
if match == nil { | ||||||||||
return | ||||||||||
} | ||||||||||
resp := &structs.AggregateAttestationResponse{ | ||||||||||
Version: version.String(match.Version()), | ||||||||||
} | ||||||||||
if match.Version() >= version.Electra { | ||||||||||
attPostElectra, ok := match.(*ethpbalpha.AttestationElectra) | ||||||||||
if !ok { | ||||||||||
httputil.HandleError(w, "Match is not of type AttestationElectra", http.StatusInternalServerError) | ||||||||||
return | ||||||||||
} | ||||||||||
att := structs.AttElectraFromConsensus(attPostElectra) | ||||||||||
data, err := json.Marshal(att) | ||||||||||
if err != nil { | ||||||||||
httputil.HandleError(w, "Could not marshal attestation: "+err.Error(), http.StatusInternalServerError) | ||||||||||
return | ||||||||||
} | ||||||||||
resp.Data = data | ||||||||||
} else { | ||||||||||
attPreElectra, ok := match.(*ethpbalpha.Attestation) | ||||||||||
if !ok { | ||||||||||
httputil.HandleError(w, "Match is not of type Attestation", http.StatusInternalServerError) | ||||||||||
return | ||||||||||
} | ||||||||||
att := structs.AttFromConsensus(attPreElectra) | ||||||||||
data, err := json.Marshal(att) | ||||||||||
if err != nil { | ||||||||||
httputil.HandleError(w, "Could not marshal attestation: "+err.Error(), http.StatusInternalServerError) | ||||||||||
return | ||||||||||
} | ||||||||||
resp.Data = data | ||||||||||
} | ||||||||||
httputil.WriteJson(w, resp) | ||||||||||
} | ||||||||||
|
||||||||||
func (s *Server) aggregateAttestation(w http.ResponseWriter, slot primitives.Slot, index string, attDataRoot []byte) ethpbalpha.Att { | ||||||||||
var match ethpbalpha.Att | ||||||||||
var err error | ||||||||||
|
||||||||||
match, err = matchingAtt(s.AttestationsPool.AggregatedAttestations(), primitives.Slot(slot), attDataRoot) | ||||||||||
match, err = matchingAtt(s.AttestationsPool.AggregatedAttestations(), slot, attDataRoot, index) | ||||||||||
if err != nil { | ||||||||||
httputil.HandleError(w, "Could not get matching attestation: "+err.Error(), http.StatusInternalServerError) | ||||||||||
return | ||||||||||
return nil | ||||||||||
} | ||||||||||
if match == nil { | ||||||||||
atts, err := s.AttestationsPool.UnaggregatedAttestations() | ||||||||||
if err != nil { | ||||||||||
httputil.HandleError(w, "Could not get unaggregated attestations: "+err.Error(), http.StatusInternalServerError) | ||||||||||
return | ||||||||||
return nil | ||||||||||
} | ||||||||||
match, err = matchingAtt(atts, primitives.Slot(slot), attDataRoot) | ||||||||||
match, err = matchingAtt(atts, slot, attDataRoot, index) | ||||||||||
if err != nil { | ||||||||||
httputil.HandleError(w, "Could not get matching attestation: "+err.Error(), http.StatusInternalServerError) | ||||||||||
return | ||||||||||
return nil | ||||||||||
} | ||||||||||
if match == nil { | ||||||||||
httputil.HandleError(w, "No matching attestation found", http.StatusNotFound) | ||||||||||
return nil | ||||||||||
} | ||||||||||
_, err = attestations.Aggregate([]ethpbalpha.Att{match}) | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
If they cannot be aggregate to just one ( |
||||||||||
if err != nil { | ||||||||||
httputil.HandleError(w, "Could not aggregate the matched unaggregated attestation: "+err.Error(), http.StatusInternalServerError) | ||||||||||
return nil | ||||||||||
} | ||||||||||
} | ||||||||||
if match == nil { | ||||||||||
httputil.HandleError(w, "No matching attestation found", http.StatusNotFound) | ||||||||||
return | ||||||||||
} | ||||||||||
|
||||||||||
response := &structs.AggregateAttestationResponse{ | ||||||||||
Data: &structs.Attestation{ | ||||||||||
AggregationBits: hexutil.Encode(match.GetAggregationBits()), | ||||||||||
Data: &structs.AttestationData{ | ||||||||||
Slot: strconv.FormatUint(uint64(match.GetData().Slot), 10), | ||||||||||
CommitteeIndex: strconv.FormatUint(uint64(match.GetData().CommitteeIndex), 10), | ||||||||||
BeaconBlockRoot: hexutil.Encode(match.GetData().BeaconBlockRoot), | ||||||||||
Source: &structs.Checkpoint{ | ||||||||||
Epoch: strconv.FormatUint(uint64(match.GetData().Source.Epoch), 10), | ||||||||||
Root: hexutil.Encode(match.GetData().Source.Root), | ||||||||||
}, | ||||||||||
Target: &structs.Checkpoint{ | ||||||||||
Epoch: strconv.FormatUint(uint64(match.GetData().Target.Epoch), 10), | ||||||||||
Root: hexutil.Encode(match.GetData().Target.Root), | ||||||||||
}, | ||||||||||
}, | ||||||||||
Signature: hexutil.Encode(match.GetSignature()), | ||||||||||
}} | ||||||||||
httputil.WriteJson(w, response) | ||||||||||
return match | ||||||||||
} | ||||||||||
|
||||||||||
func matchingAtt(atts []ethpbalpha.Att, slot primitives.Slot, attDataRoot []byte) (ethpbalpha.Att, error) { | ||||||||||
func matchingAtt(atts []ethpbalpha.Att, slot primitives.Slot, attDataRoot []byte, index string) (ethpbalpha.Att, error) { | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If there are multiple that match, return the best one by most CommitteeBitsVal().Count(), assuming they cannot be further aggregated. Or consider returning all matching atts. func matchingAtts(atts []ethpbalpha.Att, slot primitives.Slot, attDataRoot []byte, index string) ([]ethpbalpha.Att, error) |
||||||||||
for _, att := range atts { | ||||||||||
if att.GetData().Slot == slot { | ||||||||||
root, err := att.GetData().HashTreeRoot() | ||||||||||
if err != nil { | ||||||||||
return nil, errors.Wrap(err, "could not get attestation data root") | ||||||||||
} | ||||||||||
if bytes.Equal(root[:], attDataRoot) { | ||||||||||
return att, nil | ||||||||||
if index == "" { | ||||||||||
saolyn marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
if bytes.Equal(root[:], attDataRoot) { | ||||||||||
return att, nil | ||||||||||
} | ||||||||||
} else { | ||||||||||
i, err := strconv.ParseUint(index, 10, 64) | ||||||||||
if err != nil { | ||||||||||
return att, err | ||||||||||
} | ||||||||||
bits := att.CommitteeBitsVal().BitAt(i) | ||||||||||
saolyn marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
if bytes.Equal(root[:], attDataRoot) && bits { | ||||||||||
return att, nil | ||||||||||
} | ||||||||||
} | ||||||||||
} | ||||||||||
} | ||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These should return a 400 / bad request since they are required fields.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They already do, within
shared.UintFromQuery
we haveValidateUint
which handles the http error, same thing applies toshared.HexFromQuery