mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-26 05:17:22 +00:00
475 lines
19 KiB
Go
475 lines
19 KiB
Go
package apimiddleware
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/pkg/errors"
|
|
types "github.com/prysmaticlabs/eth2-types"
|
|
"github.com/prysmaticlabs/prysm/api/gateway/apimiddleware"
|
|
"github.com/prysmaticlabs/prysm/config/params"
|
|
ethpbv2 "github.com/prysmaticlabs/prysm/proto/eth/v2"
|
|
"github.com/prysmaticlabs/prysm/time/slots"
|
|
)
|
|
|
|
// https://ethereum.github.io/beacon-apis/#/Validator/prepareBeaconProposer expects posting a top-level array.
|
|
// We make it more proto-friendly by wrapping it in a struct.
|
|
func wrapFeeRecipientsArray(
|
|
endpoint *apimiddleware.Endpoint,
|
|
_ http.ResponseWriter,
|
|
req *http.Request,
|
|
) (apimiddleware.RunDefault, apimiddleware.ErrorJson) {
|
|
if _, ok := endpoint.PostRequest.(*feeRecipientsRequestJSON); !ok {
|
|
return true, nil
|
|
}
|
|
recipients := make([]*feeRecipientJson, 0)
|
|
if err := json.NewDecoder(req.Body).Decode(&recipients); err != nil {
|
|
return false, apimiddleware.InternalServerErrorWithMessage(err, "could not decode body")
|
|
}
|
|
j := &feeRecipientsRequestJSON{Recipients: recipients}
|
|
b, err := json.Marshal(j)
|
|
if err != nil {
|
|
return false, apimiddleware.InternalServerErrorWithMessage(err, "could not marshal wrapped body")
|
|
}
|
|
req.Body = ioutil.NopCloser(bytes.NewReader(b))
|
|
return true, nil
|
|
}
|
|
|
|
// https://ethereum.github.io/beacon-apis/#/Beacon/submitPoolAttestations expects posting a top-level array.
|
|
// We make it more proto-friendly by wrapping it in a struct with a 'data' field.
|
|
func wrapAttestationsArray(
|
|
endpoint *apimiddleware.Endpoint,
|
|
_ http.ResponseWriter,
|
|
req *http.Request,
|
|
) (apimiddleware.RunDefault, apimiddleware.ErrorJson) {
|
|
if _, ok := endpoint.PostRequest.(*submitAttestationRequestJson); ok {
|
|
atts := make([]*attestationJson, 0)
|
|
if err := json.NewDecoder(req.Body).Decode(&atts); err != nil {
|
|
return false, apimiddleware.InternalServerErrorWithMessage(err, "could not decode body")
|
|
}
|
|
j := &submitAttestationRequestJson{Data: atts}
|
|
b, err := json.Marshal(j)
|
|
if err != nil {
|
|
return false, apimiddleware.InternalServerErrorWithMessage(err, "could not marshal wrapped body")
|
|
}
|
|
req.Body = ioutil.NopCloser(bytes.NewReader(b))
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
// Some endpoints e.g. https://ethereum.github.io/beacon-apis/#/Validator/getAttesterDuties expect posting a top-level array.
|
|
// We make it more proto-friendly by wrapping it in a struct with an 'Index' field.
|
|
func wrapValidatorIndicesArray(
|
|
endpoint *apimiddleware.Endpoint,
|
|
_ http.ResponseWriter,
|
|
req *http.Request,
|
|
) (apimiddleware.RunDefault, apimiddleware.ErrorJson) {
|
|
if _, ok := endpoint.PostRequest.(*dutiesRequestJson); ok {
|
|
indices := make([]string, 0)
|
|
if err := json.NewDecoder(req.Body).Decode(&indices); err != nil {
|
|
return false, apimiddleware.InternalServerErrorWithMessage(err, "could not decode body")
|
|
}
|
|
j := &dutiesRequestJson{Index: indices}
|
|
b, err := json.Marshal(j)
|
|
if err != nil {
|
|
return false, apimiddleware.InternalServerErrorWithMessage(err, "could not marshal wrapped body")
|
|
}
|
|
req.Body = ioutil.NopCloser(bytes.NewReader(b))
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
// https://ethereum.github.io/beacon-apis/#/Validator/publishAggregateAndProofs expects posting a top-level array.
|
|
// We make it more proto-friendly by wrapping it in a struct with a 'data' field.
|
|
func wrapSignedAggregateAndProofArray(
|
|
endpoint *apimiddleware.Endpoint,
|
|
_ http.ResponseWriter,
|
|
req *http.Request,
|
|
) (apimiddleware.RunDefault, apimiddleware.ErrorJson) {
|
|
if _, ok := endpoint.PostRequest.(*submitAggregateAndProofsRequestJson); ok {
|
|
data := make([]*signedAggregateAttestationAndProofJson, 0)
|
|
if err := json.NewDecoder(req.Body).Decode(&data); err != nil {
|
|
return false, apimiddleware.InternalServerErrorWithMessage(err, "could not decode body")
|
|
}
|
|
j := &submitAggregateAndProofsRequestJson{Data: data}
|
|
b, err := json.Marshal(j)
|
|
if err != nil {
|
|
return false, apimiddleware.InternalServerErrorWithMessage(err, "could not marshal wrapped body")
|
|
}
|
|
req.Body = ioutil.NopCloser(bytes.NewReader(b))
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
// https://ethereum.github.io/beacon-apis/#/Validator/prepareBeaconCommitteeSubnet expects posting a top-level array.
|
|
// We make it more proto-friendly by wrapping it in a struct with a 'data' field.
|
|
func wrapBeaconCommitteeSubscriptionsArray(
|
|
endpoint *apimiddleware.Endpoint,
|
|
_ http.ResponseWriter,
|
|
req *http.Request,
|
|
) (apimiddleware.RunDefault, apimiddleware.ErrorJson) {
|
|
if _, ok := endpoint.PostRequest.(*submitBeaconCommitteeSubscriptionsRequestJson); ok {
|
|
data := make([]*beaconCommitteeSubscribeJson, 0)
|
|
if err := json.NewDecoder(req.Body).Decode(&data); err != nil {
|
|
return false, apimiddleware.InternalServerErrorWithMessage(err, "could not decode body")
|
|
}
|
|
j := &submitBeaconCommitteeSubscriptionsRequestJson{Data: data}
|
|
b, err := json.Marshal(j)
|
|
if err != nil {
|
|
return false, apimiddleware.InternalServerErrorWithMessage(err, "could not marshal wrapped body")
|
|
}
|
|
req.Body = ioutil.NopCloser(bytes.NewReader(b))
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
// https://ethereum.github.io/beacon-APIs/#/Validator/prepareSyncCommitteeSubnets expects posting a top-level array.
|
|
// We make it more proto-friendly by wrapping it in a struct with a 'data' field.
|
|
func wrapSyncCommitteeSubscriptionsArray(
|
|
endpoint *apimiddleware.Endpoint,
|
|
_ http.ResponseWriter,
|
|
req *http.Request,
|
|
) (apimiddleware.RunDefault, apimiddleware.ErrorJson) {
|
|
if _, ok := endpoint.PostRequest.(*submitSyncCommitteeSubscriptionRequestJson); ok {
|
|
data := make([]*syncCommitteeSubscriptionJson, 0)
|
|
if err := json.NewDecoder(req.Body).Decode(&data); err != nil {
|
|
return false, apimiddleware.InternalServerErrorWithMessage(err, "could not decode body")
|
|
}
|
|
j := &submitSyncCommitteeSubscriptionRequestJson{Data: data}
|
|
b, err := json.Marshal(j)
|
|
if err != nil {
|
|
return false, apimiddleware.InternalServerErrorWithMessage(err, "could not marshal wrapped body")
|
|
}
|
|
req.Body = ioutil.NopCloser(bytes.NewReader(b))
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
// https://ethereum.github.io/beacon-APIs/#/Beacon/submitPoolSyncCommitteeSignatures expects posting a top-level array.
|
|
// We make it more proto-friendly by wrapping it in a struct with a 'data' field.
|
|
func wrapSyncCommitteeSignaturesArray(
|
|
endpoint *apimiddleware.Endpoint,
|
|
_ http.ResponseWriter,
|
|
req *http.Request,
|
|
) (apimiddleware.RunDefault, apimiddleware.ErrorJson) {
|
|
if _, ok := endpoint.PostRequest.(*submitSyncCommitteeSignaturesRequestJson); ok {
|
|
data := make([]*syncCommitteeMessageJson, 0)
|
|
if err := json.NewDecoder(req.Body).Decode(&data); err != nil {
|
|
return false, apimiddleware.InternalServerErrorWithMessage(err, "could not decode body")
|
|
}
|
|
j := &submitSyncCommitteeSignaturesRequestJson{Data: data}
|
|
b, err := json.Marshal(j)
|
|
if err != nil {
|
|
return false, apimiddleware.InternalServerErrorWithMessage(err, "could not marshal wrapped body")
|
|
}
|
|
req.Body = ioutil.NopCloser(bytes.NewReader(b))
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
// https://ethereum.github.io/beacon-APIs/#/Validator/publishContributionAndProofs expects posting a top-level array.
|
|
// We make it more proto-friendly by wrapping it in a struct with a 'data' field.
|
|
func wrapSignedContributionAndProofsArray(
|
|
endpoint *apimiddleware.Endpoint,
|
|
_ http.ResponseWriter,
|
|
req *http.Request,
|
|
) (apimiddleware.RunDefault, apimiddleware.ErrorJson) {
|
|
if _, ok := endpoint.PostRequest.(*submitContributionAndProofsRequestJson); ok {
|
|
data := make([]*signedContributionAndProofJson, 0)
|
|
if err := json.NewDecoder(req.Body).Decode(&data); err != nil {
|
|
return false, apimiddleware.InternalServerErrorWithMessage(err, "could not decode body")
|
|
}
|
|
j := &submitContributionAndProofsRequestJson{Data: data}
|
|
b, err := json.Marshal(j)
|
|
if err != nil {
|
|
return false, apimiddleware.InternalServerErrorWithMessage(err, "could not marshal wrapped body")
|
|
}
|
|
req.Body = ioutil.NopCloser(bytes.NewReader(b))
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
type phase0PublishBlockRequestJson struct {
|
|
Phase0Block *beaconBlockJson `json:"phase0_block"`
|
|
Signature string `json:"signature" hex:"true"`
|
|
}
|
|
|
|
type altairPublishBlockRequestJson struct {
|
|
AltairBlock *beaconBlockAltairJson `json:"altair_block"`
|
|
Signature string `json:"signature" hex:"true"`
|
|
}
|
|
|
|
type bellatrixPublishBlockRequestJson struct {
|
|
BellatrixBlock *beaconBlockBellatrixJson `json:"bellatrix_block"`
|
|
Signature string `json:"signature" hex:"true"`
|
|
}
|
|
|
|
// setInitialPublishBlockPostRequest is triggered before we deserialize the request JSON into a struct.
|
|
// We don't know which version of the block got posted, but we can determine it from the slot.
|
|
// We know that blocks of all versions have a Message field with a Slot field,
|
|
// so we deserialize the request into a struct s, which has the right fields, to obtain the slot.
|
|
// Once we know the slot, we can determine what the PostRequest field of the endpoint should be, and we set it appropriately.
|
|
func setInitialPublishBlockPostRequest(endpoint *apimiddleware.Endpoint,
|
|
_ http.ResponseWriter,
|
|
req *http.Request,
|
|
) (apimiddleware.RunDefault, apimiddleware.ErrorJson) {
|
|
s := struct {
|
|
Message struct {
|
|
Slot string
|
|
}
|
|
}{}
|
|
|
|
buf, err := ioutil.ReadAll(req.Body)
|
|
if err != nil {
|
|
return false, apimiddleware.InternalServerErrorWithMessage(err, "could not read body")
|
|
}
|
|
if err := json.Unmarshal(buf, &s); err != nil {
|
|
return false, apimiddleware.InternalServerErrorWithMessage(err, "could not read slot from body")
|
|
}
|
|
slot, err := strconv.ParseUint(s.Message.Slot, 10, 64)
|
|
if err != nil {
|
|
return false, apimiddleware.InternalServerErrorWithMessage(err, "slot is not an unsigned integer")
|
|
}
|
|
currentEpoch := slots.ToEpoch(types.Slot(slot))
|
|
if currentEpoch < params.BeaconConfig().AltairForkEpoch {
|
|
endpoint.PostRequest = &signedBeaconBlockContainerJson{}
|
|
} else if currentEpoch < params.BeaconConfig().BellatrixForkEpoch {
|
|
endpoint.PostRequest = &signedBeaconBlockAltairContainerJson{}
|
|
} else {
|
|
endpoint.PostRequest = &signedBeaconBlockBellatrixContainerJson{}
|
|
}
|
|
req.Body = ioutil.NopCloser(bytes.NewBuffer(buf))
|
|
return true, nil
|
|
}
|
|
|
|
// In preparePublishedBlock we transform the PostRequest.
|
|
// gRPC expects an XXX_block field in the JSON object, but we have a message field at this point.
|
|
// We do a simple conversion depending on the type of endpoint.PostRequest
|
|
// (which was filled out previously in setInitialPublishBlockPostRequest).
|
|
func preparePublishedBlock(endpoint *apimiddleware.Endpoint, _ http.ResponseWriter, _ *http.Request) apimiddleware.ErrorJson {
|
|
if block, ok := endpoint.PostRequest.(*signedBeaconBlockContainerJson); ok {
|
|
// Prepare post request that can be properly decoded on gRPC side.
|
|
actualPostReq := &phase0PublishBlockRequestJson{
|
|
Phase0Block: block.Message,
|
|
Signature: block.Signature,
|
|
}
|
|
endpoint.PostRequest = actualPostReq
|
|
return nil
|
|
}
|
|
if block, ok := endpoint.PostRequest.(*signedBeaconBlockAltairContainerJson); ok {
|
|
// Prepare post request that can be properly decoded on gRPC side.
|
|
actualPostReq := &altairPublishBlockRequestJson{
|
|
AltairBlock: block.Message,
|
|
Signature: block.Signature,
|
|
}
|
|
endpoint.PostRequest = actualPostReq
|
|
return nil
|
|
}
|
|
if block, ok := endpoint.PostRequest.(*signedBeaconBlockBellatrixContainerJson); ok {
|
|
// Prepare post request that can be properly decoded on gRPC side.
|
|
actualPostReq := &bellatrixPublishBlockRequestJson{
|
|
BellatrixBlock: block.Message,
|
|
Signature: block.Signature,
|
|
}
|
|
endpoint.PostRequest = actualPostReq
|
|
return nil
|
|
}
|
|
return apimiddleware.InternalServerError(errors.New("unsupported block type"))
|
|
}
|
|
|
|
type tempSyncCommitteesResponseJson struct {
|
|
Data *tempSyncCommitteeValidatorsJson `json:"data"`
|
|
}
|
|
|
|
type tempSyncCommitteeValidatorsJson struct {
|
|
Validators []string `json:"validators"`
|
|
ValidatorAggregates []*tempSyncSubcommitteeValidatorsJson `json:"validator_aggregates"`
|
|
}
|
|
|
|
type tempSyncSubcommitteeValidatorsJson struct {
|
|
Validators []string `json:"validators"`
|
|
}
|
|
|
|
// https://ethereum.github.io/beacon-APIs/?urls.primaryName=v2.0.0#/Beacon/getEpochSyncCommittees returns validator_aggregates as a nested array.
|
|
// grpc-gateway returns a struct with nested fields which we have to transform into a plain 2D array.
|
|
func prepareValidatorAggregates(body []byte, responseContainer interface{}) (apimiddleware.RunDefault, apimiddleware.ErrorJson) {
|
|
tempContainer := &tempSyncCommitteesResponseJson{}
|
|
if err := json.Unmarshal(body, tempContainer); err != nil {
|
|
return false, apimiddleware.InternalServerErrorWithMessage(err, "could not unmarshal response into temp container")
|
|
}
|
|
container, ok := responseContainer.(*syncCommitteesResponseJson)
|
|
if !ok {
|
|
return false, apimiddleware.InternalServerError(errors.New("container is not of the correct type"))
|
|
}
|
|
|
|
container.Data = &syncCommitteeValidatorsJson{}
|
|
container.Data.Validators = tempContainer.Data.Validators
|
|
container.Data.ValidatorAggregates = make([][]string, len(tempContainer.Data.ValidatorAggregates))
|
|
for i, srcValAgg := range tempContainer.Data.ValidatorAggregates {
|
|
dstValAgg := make([]string, len(srcValAgg.Validators))
|
|
copy(dstValAgg, tempContainer.Data.ValidatorAggregates[i].Validators)
|
|
container.Data.ValidatorAggregates[i] = dstValAgg
|
|
}
|
|
|
|
return false, nil
|
|
}
|
|
|
|
type phase0BlockResponseJson struct {
|
|
Version string `json:"version"`
|
|
Data *signedBeaconBlockContainerJson `json:"data"`
|
|
}
|
|
|
|
type altairBlockResponseJson struct {
|
|
Version string `json:"version"`
|
|
Data *signedBeaconBlockAltairContainerJson `json:"data"`
|
|
}
|
|
|
|
type bellatrixBlockResponseJson struct {
|
|
Version string `json:"version"`
|
|
Data *signedBeaconBlockBellatrixContainerJson `json:"data"`
|
|
}
|
|
|
|
func serializeV2Block(response interface{}) (apimiddleware.RunDefault, []byte, apimiddleware.ErrorJson) {
|
|
respContainer, ok := response.(*blockV2ResponseJson)
|
|
if !ok {
|
|
return false, nil, apimiddleware.InternalServerError(errors.New("container is not of the correct type"))
|
|
}
|
|
|
|
var actualRespContainer interface{}
|
|
if strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_PHASE0.String())) {
|
|
actualRespContainer = &phase0BlockResponseJson{
|
|
Version: respContainer.Version,
|
|
Data: &signedBeaconBlockContainerJson{
|
|
Message: respContainer.Data.Phase0Block,
|
|
Signature: respContainer.Data.Signature,
|
|
},
|
|
}
|
|
} else if strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_ALTAIR.String())) {
|
|
actualRespContainer = &altairBlockResponseJson{
|
|
Version: respContainer.Version,
|
|
Data: &signedBeaconBlockAltairContainerJson{
|
|
Message: respContainer.Data.AltairBlock,
|
|
Signature: respContainer.Data.Signature,
|
|
},
|
|
}
|
|
} else if strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_BELLATRIX.String())) {
|
|
actualRespContainer = &bellatrixBlockResponseJson{
|
|
Version: respContainer.Version,
|
|
Data: &signedBeaconBlockBellatrixContainerJson{
|
|
Message: respContainer.Data.BellatrixBlock,
|
|
Signature: respContainer.Data.Signature,
|
|
},
|
|
}
|
|
} else {
|
|
return false, nil, apimiddleware.InternalServerError(fmt.Errorf("unsupported block version '%s'", respContainer.Version))
|
|
}
|
|
|
|
j, err := json.Marshal(actualRespContainer)
|
|
if err != nil {
|
|
return false, nil, apimiddleware.InternalServerErrorWithMessage(err, "could not marshal response")
|
|
}
|
|
return false, j, nil
|
|
}
|
|
|
|
type phase0StateResponseJson struct {
|
|
Version string `json:"version"`
|
|
Data *beaconStateJson `json:"data"`
|
|
}
|
|
|
|
type altairStateResponseJson struct {
|
|
Version string `json:"version"`
|
|
Data *beaconStateAltairJson `json:"data"`
|
|
}
|
|
|
|
type bellatrixStateResponseJson struct {
|
|
Version string `json:"version"`
|
|
Data *beaconStateBellatrixJson `json:"data"`
|
|
}
|
|
|
|
func serializeV2State(response interface{}) (apimiddleware.RunDefault, []byte, apimiddleware.ErrorJson) {
|
|
respContainer, ok := response.(*beaconStateV2ResponseJson)
|
|
if !ok {
|
|
return false, nil, apimiddleware.InternalServerError(errors.New("container is not of the correct type"))
|
|
}
|
|
|
|
var actualRespContainer interface{}
|
|
switch {
|
|
case strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_PHASE0.String())):
|
|
actualRespContainer = &phase0StateResponseJson{
|
|
Version: respContainer.Version,
|
|
Data: respContainer.Data.Phase0State,
|
|
}
|
|
case strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_ALTAIR.String())):
|
|
actualRespContainer = &altairStateResponseJson{
|
|
Version: respContainer.Version,
|
|
Data: respContainer.Data.AltairState,
|
|
}
|
|
case strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_BELLATRIX.String())):
|
|
actualRespContainer = &bellatrixStateResponseJson{
|
|
Version: respContainer.Version,
|
|
Data: respContainer.Data.BellatrixState,
|
|
}
|
|
default:
|
|
return false, nil, apimiddleware.InternalServerError(fmt.Errorf("unsupported state version '%s'", respContainer.Version))
|
|
}
|
|
|
|
j, err := json.Marshal(actualRespContainer)
|
|
if err != nil {
|
|
return false, nil, apimiddleware.InternalServerErrorWithMessage(err, "could not marshal response")
|
|
}
|
|
return false, j, nil
|
|
}
|
|
|
|
type phase0ProduceBlockResponseJson struct {
|
|
Version string `json:"version"`
|
|
Data *beaconBlockJson `json:"data"`
|
|
}
|
|
|
|
type altairProduceBlockResponseJson struct {
|
|
Version string `json:"version"`
|
|
Data *beaconBlockAltairJson `json:"data"`
|
|
}
|
|
|
|
type bellatrixProduceBlockResponseJson struct {
|
|
Version string `json:"version"`
|
|
Data *beaconBlockBellatrixJson `json:"data"`
|
|
}
|
|
|
|
func serializeProducedV2Block(response interface{}) (apimiddleware.RunDefault, []byte, apimiddleware.ErrorJson) {
|
|
respContainer, ok := response.(*produceBlockResponseV2Json)
|
|
if !ok {
|
|
return false, nil, apimiddleware.InternalServerError(errors.New("container is not of the correct type"))
|
|
}
|
|
|
|
var actualRespContainer interface{}
|
|
if strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_PHASE0.String())) {
|
|
actualRespContainer = &phase0ProduceBlockResponseJson{
|
|
Version: respContainer.Version,
|
|
Data: respContainer.Data.Phase0Block,
|
|
}
|
|
} else if strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_ALTAIR.String())) {
|
|
actualRespContainer = &altairProduceBlockResponseJson{
|
|
Version: respContainer.Version,
|
|
Data: respContainer.Data.AltairBlock,
|
|
}
|
|
} else if strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_BELLATRIX.String())) {
|
|
actualRespContainer = &bellatrixProduceBlockResponseJson{
|
|
Version: respContainer.Version,
|
|
Data: respContainer.Data.BellatrixBlock,
|
|
}
|
|
} else {
|
|
return false, nil, apimiddleware.InternalServerError(fmt.Errorf("unsupported block version '%s'", respContainer.Version))
|
|
}
|
|
|
|
j, err := json.Marshal(actualRespContainer)
|
|
if err != nil {
|
|
return false, nil, apimiddleware.InternalServerErrorWithMessage(err, "could not marshal response")
|
|
}
|
|
return false, j, nil
|
|
}
|