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)) } } }