prysm-pulse/beacon-chain/rpc/eth/validator/handlers.go
Radosław Kapka a9b003e1fe
HTTP Beacon API: /eth/v1/validator/contribution_and_proofs (#12660)
* HTTP Beacon API: `/eth/v1/validator/contribution_and_proofs`

* add comment to invalid test case

* fix validation and test

* review

---------

Co-authored-by: james-prysm <90280386+james-prysm@users.noreply.github.com>
2023-07-31 17:32:39 +00:00

114 lines
4.0 KiB
Go

package validator
import (
"bytes"
"encoding/json"
"net/http"
"strconv"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/go-playground/validator/v10"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/core"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
http2 "github.com/prysmaticlabs/prysm/v4/network/http"
ethpbalpha "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
)
// GetAggregateAttestation aggregates all attestations matching the given attestation data root and slot, returning the aggregated result.
func (s *Server) GetAggregateAttestation(w http.ResponseWriter, r *http.Request) {
attDataRoot := r.URL.Query().Get("attestation_data_root")
valid := shared.ValidateHex(w, "Attestation data root", attDataRoot)
if !valid {
return
}
rawSlot := r.URL.Query().Get("slot")
slot, valid := shared.ValidateUint(w, "Slot", rawSlot)
if !valid {
return
}
if err := s.AttestationsPool.AggregateUnaggregatedAttestations(r.Context()); err != nil {
http2.HandleError(w, "Could not aggregate unaggregated attestations: "+err.Error(), http.StatusBadRequest)
return
}
allAtts := s.AttestationsPool.AggregatedAttestations()
var bestMatchingAtt *ethpbalpha.Attestation
for _, att := range allAtts {
if att.Data.Slot == primitives.Slot(slot) {
root, err := att.Data.HashTreeRoot()
if err != nil {
http2.HandleError(w, "Could not get attestation data root: "+err.Error(), http.StatusInternalServerError)
return
}
attDataRootBytes, err := hexutil.Decode(attDataRoot)
if err != nil {
http2.HandleError(w, "Could not decode attestation data root into bytes: "+err.Error(), http.StatusBadRequest)
return
}
if bytes.Equal(root[:], attDataRootBytes) {
if bestMatchingAtt == nil || len(att.AggregationBits) > len(bestMatchingAtt.AggregationBits) {
bestMatchingAtt = att
}
}
}
}
if bestMatchingAtt == nil {
http2.HandleError(w, "No matching attestation found", http.StatusNotFound)
return
}
response := &AggregateAttestationResponse{
Data: shared.Attestation{
AggregationBits: hexutil.Encode(bestMatchingAtt.AggregationBits),
Data: shared.AttestationData{
Slot: strconv.FormatUint(uint64(bestMatchingAtt.Data.Slot), 10),
CommitteeIndex: strconv.FormatUint(uint64(bestMatchingAtt.Data.CommitteeIndex), 10),
BeaconBlockRoot: hexutil.Encode(bestMatchingAtt.Data.BeaconBlockRoot),
Source: shared.Checkpoint{
Epoch: strconv.FormatUint(uint64(bestMatchingAtt.Data.Source.Epoch), 10),
Root: hexutil.Encode(bestMatchingAtt.Data.Source.Root),
},
Target: shared.Checkpoint{
Epoch: strconv.FormatUint(uint64(bestMatchingAtt.Data.Target.Epoch), 10),
Root: hexutil.Encode(bestMatchingAtt.Data.Target.Root),
},
},
Signature: hexutil.Encode(bestMatchingAtt.Signature),
}}
http2.WriteJson(w, response)
}
// SubmitContributionAndProofs publishes multiple signed sync committee contribution and proofs.
func (s *Server) SubmitContributionAndProofs(w http.ResponseWriter, r *http.Request) {
var req SubmitContributionAndProofsRequest
if r.Body == http.NoBody {
http2.HandleError(w, "No data submitted", http.StatusBadRequest)
return
}
if err := json.NewDecoder(r.Body).Decode(&req.Data); err != nil {
http2.HandleError(w, "Could not decode request body: "+err.Error(), http.StatusBadRequest)
return
}
validate := validator.New()
for _, item := range req.Data {
if err := validate.Struct(item); err != nil {
http2.HandleError(w, err.Error(), http.StatusBadRequest)
return
}
consensusItem, err := item.ToConsensus()
if err != nil {
http2.HandleError(w, "Could not convert request contribution to consensus contribution: "+err.Error(), http.StatusBadRequest)
return
}
rpcError := core.SubmitSignedContributionAndProof(r.Context(), consensusItem, s.Broadcaster, s.SyncCommitteePool, s.OperationNotifier)
if rpcError != nil {
http2.HandleError(w, rpcError.Err.Error(), core.ErrorReasonToHTTP(rpcError.Reason))
}
}
}