mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-04-19 03:01:06 -04:00
**What type of PR is this?** Feature **What does this PR do? Why is it needed?** PR is attempts to remove code duplication and process through a map of configurations for get and post block apis. this should simplify maintainability. **Which issues(s) does this PR fix?** Fixes # **Other notes for review** **Acknowledgements** - [x] I have read [CONTRIBUTING.md](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md). - [x] I have included a uniquely named [changelog fragment file](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md#maintaining-changelogmd). - [x] I have added a description with sufficient context for reviewers to understand this PR. - [x] I have tested that my changes work as expected and I added a testing plan to the PR description (if applicable).
271 lines
9.8 KiB
Go
271 lines
9.8 KiB
Go
package beacon_api
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
neturl "net/url"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/OffchainLabs/prysm/v7/api"
|
|
"github.com/OffchainLabs/prysm/v7/api/apiutil"
|
|
"github.com/OffchainLabs/prysm/v7/api/server/structs"
|
|
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
|
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
|
"github.com/OffchainLabs/prysm/v7/runtime/version"
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
func (c *beaconApiValidatorClient) beaconBlock(ctx context.Context, slot primitives.Slot, randaoReveal, graffiti []byte) (*ethpb.GenericBeaconBlock, error) {
|
|
queryParams := neturl.Values{}
|
|
queryParams.Add("randao_reveal", hexutil.Encode(randaoReveal))
|
|
if len(graffiti) > 0 {
|
|
queryParams.Add("graffiti", hexutil.Encode(graffiti))
|
|
}
|
|
queryUrl := apiutil.BuildURL(fmt.Sprintf("/eth/v3/validator/blocks/%d", slot), queryParams)
|
|
data, header, err := c.handler.GetSSZ(ctx, queryUrl)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if strings.Contains(header.Get("Content-Type"), api.OctetStreamMediaType) {
|
|
ver, err := version.FromString(header.Get(api.VersionHeader))
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, fmt.Sprintf("unsupported header version %s", header.Get(api.VersionHeader)))
|
|
}
|
|
isBlindedRaw := header.Get(api.ExecutionPayloadBlindedHeader)
|
|
isBlinded, err := strconv.ParseBool(isBlindedRaw)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return processBlockSSZResponse(ver, data, isBlinded)
|
|
} else {
|
|
decoder := json.NewDecoder(bytes.NewBuffer(data))
|
|
produceBlockV3ResponseJson := structs.ProduceBlockV3Response{}
|
|
if err = decoder.Decode(&produceBlockV3ResponseJson); err != nil {
|
|
return nil, errors.Wrapf(err, "failed to decode response body into json for %s", queryUrl)
|
|
}
|
|
return processBlockJSONResponse(
|
|
produceBlockV3ResponseJson.Version,
|
|
produceBlockV3ResponseJson.ExecutionPayloadBlinded,
|
|
json.NewDecoder(bytes.NewReader(produceBlockV3ResponseJson.Data)),
|
|
)
|
|
}
|
|
}
|
|
|
|
// sszBlockCodec defines SSZ unmarshalers for a fork's block and blinded block types.
|
|
type sszBlockCodec struct {
|
|
unmarshalBlock func([]byte) (*ethpb.GenericBeaconBlock, error)
|
|
unmarshalBlinded func([]byte) (*ethpb.GenericBeaconBlock, error) // nil for Phase0/Altair
|
|
}
|
|
|
|
type sszCodecEntry struct {
|
|
minVersion int
|
|
codec sszBlockCodec
|
|
}
|
|
|
|
// sszCodecs is ordered descending by version so that unknown future versions
|
|
// fall through to the latest known fork (matching the original if-cascade).
|
|
var sszCodecs = []sszCodecEntry{
|
|
{
|
|
minVersion: version.Fulu,
|
|
codec: sszBlockCodec{
|
|
unmarshalBlock: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
|
|
block := ðpb.BeaconBlockContentsFulu{}
|
|
if err := block.UnmarshalSSZ(data); err != nil {
|
|
return nil, err
|
|
}
|
|
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Fulu{Fulu: block}}, nil
|
|
},
|
|
unmarshalBlinded: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
|
|
blindedBlock := ðpb.BlindedBeaconBlockFulu{}
|
|
if err := blindedBlock.UnmarshalSSZ(data); err != nil {
|
|
return nil, err
|
|
}
|
|
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_BlindedFulu{BlindedFulu: blindedBlock}, IsBlinded: true}, nil
|
|
},
|
|
},
|
|
},
|
|
{
|
|
minVersion: version.Electra,
|
|
codec: sszBlockCodec{
|
|
unmarshalBlock: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
|
|
block := ðpb.BeaconBlockContentsElectra{}
|
|
if err := block.UnmarshalSSZ(data); err != nil {
|
|
return nil, err
|
|
}
|
|
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Electra{Electra: block}}, nil
|
|
},
|
|
unmarshalBlinded: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
|
|
blindedBlock := ðpb.BlindedBeaconBlockElectra{}
|
|
if err := blindedBlock.UnmarshalSSZ(data); err != nil {
|
|
return nil, err
|
|
}
|
|
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_BlindedElectra{BlindedElectra: blindedBlock}, IsBlinded: true}, nil
|
|
},
|
|
},
|
|
},
|
|
{
|
|
minVersion: version.Deneb,
|
|
codec: sszBlockCodec{
|
|
unmarshalBlock: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
|
|
block := ðpb.BeaconBlockContentsDeneb{}
|
|
if err := block.UnmarshalSSZ(data); err != nil {
|
|
return nil, err
|
|
}
|
|
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Deneb{Deneb: block}}, nil
|
|
},
|
|
unmarshalBlinded: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
|
|
blindedBlock := ðpb.BlindedBeaconBlockDeneb{}
|
|
if err := blindedBlock.UnmarshalSSZ(data); err != nil {
|
|
return nil, err
|
|
}
|
|
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_BlindedDeneb{BlindedDeneb: blindedBlock}, IsBlinded: true}, nil
|
|
},
|
|
},
|
|
},
|
|
{
|
|
minVersion: version.Capella,
|
|
codec: sszBlockCodec{
|
|
unmarshalBlock: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
|
|
block := ðpb.BeaconBlockCapella{}
|
|
if err := block.UnmarshalSSZ(data); err != nil {
|
|
return nil, err
|
|
}
|
|
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Capella{Capella: block}}, nil
|
|
},
|
|
unmarshalBlinded: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
|
|
blindedBlock := ðpb.BlindedBeaconBlockCapella{}
|
|
if err := blindedBlock.UnmarshalSSZ(data); err != nil {
|
|
return nil, err
|
|
}
|
|
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_BlindedCapella{BlindedCapella: blindedBlock}, IsBlinded: true}, nil
|
|
},
|
|
},
|
|
},
|
|
{
|
|
minVersion: version.Bellatrix,
|
|
codec: sszBlockCodec{
|
|
unmarshalBlock: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
|
|
block := ðpb.BeaconBlockBellatrix{}
|
|
if err := block.UnmarshalSSZ(data); err != nil {
|
|
return nil, err
|
|
}
|
|
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Bellatrix{Bellatrix: block}}, nil
|
|
},
|
|
unmarshalBlinded: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
|
|
blindedBlock := ðpb.BlindedBeaconBlockBellatrix{}
|
|
if err := blindedBlock.UnmarshalSSZ(data); err != nil {
|
|
return nil, err
|
|
}
|
|
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_BlindedBellatrix{BlindedBellatrix: blindedBlock}, IsBlinded: true}, nil
|
|
},
|
|
},
|
|
},
|
|
{
|
|
minVersion: version.Altair,
|
|
codec: sszBlockCodec{
|
|
unmarshalBlock: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
|
|
block := ðpb.BeaconBlockAltair{}
|
|
if err := block.UnmarshalSSZ(data); err != nil {
|
|
return nil, err
|
|
}
|
|
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Altair{Altair: block}}, nil
|
|
},
|
|
},
|
|
},
|
|
{
|
|
minVersion: version.Phase0,
|
|
codec: sszBlockCodec{
|
|
unmarshalBlock: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
|
|
block := ðpb.BeaconBlock{}
|
|
if err := block.UnmarshalSSZ(data); err != nil {
|
|
return nil, err
|
|
}
|
|
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Phase0{Phase0: block}}, nil
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
func processBlockSSZResponse(ver int, data []byte, isBlinded bool) (*ethpb.GenericBeaconBlock, error) {
|
|
for _, entry := range sszCodecs {
|
|
if ver >= entry.minVersion {
|
|
if isBlinded && entry.codec.unmarshalBlinded != nil {
|
|
return entry.codec.unmarshalBlinded(data)
|
|
}
|
|
return entry.codec.unmarshalBlock(data)
|
|
}
|
|
}
|
|
return nil, fmt.Errorf("unsupported block version %s", version.String(ver))
|
|
}
|
|
|
|
func convertBlockToGeneric(decoder *json.Decoder, dest ethpb.GenericConverter, version string, isBlinded bool) (*ethpb.GenericBeaconBlock, error) {
|
|
typeName := version
|
|
if isBlinded {
|
|
typeName = "blinded " + typeName
|
|
}
|
|
|
|
if err := decoder.Decode(dest); err != nil {
|
|
return nil, errors.Wrapf(err, "failed to decode %s block response json", typeName)
|
|
}
|
|
|
|
genericBlock, err := dest.ToGeneric()
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "failed to convert %s block", typeName)
|
|
}
|
|
return genericBlock, nil
|
|
}
|
|
|
|
// jsonBlockTypes defines factory functions for creating block and blinded block structs for JSON decoding.
|
|
type jsonBlockTypes struct {
|
|
newBlock func() ethpb.GenericConverter
|
|
newBlinded func() ethpb.GenericConverter // nil for Phase0/Altair
|
|
}
|
|
|
|
var jsonBlockFactories = map[string]jsonBlockTypes{
|
|
version.String(version.Phase0): {
|
|
newBlock: func() ethpb.GenericConverter { return &structs.BeaconBlock{} },
|
|
},
|
|
version.String(version.Altair): {
|
|
newBlock: func() ethpb.GenericConverter { return &structs.BeaconBlockAltair{} },
|
|
},
|
|
version.String(version.Bellatrix): {
|
|
newBlock: func() ethpb.GenericConverter { return &structs.BeaconBlockBellatrix{} },
|
|
newBlinded: func() ethpb.GenericConverter { return &structs.BlindedBeaconBlockBellatrix{} },
|
|
},
|
|
version.String(version.Capella): {
|
|
newBlock: func() ethpb.GenericConverter { return &structs.BeaconBlockCapella{} },
|
|
newBlinded: func() ethpb.GenericConverter { return &structs.BlindedBeaconBlockCapella{} },
|
|
},
|
|
version.String(version.Deneb): {
|
|
newBlock: func() ethpb.GenericConverter { return &structs.BeaconBlockContentsDeneb{} },
|
|
newBlinded: func() ethpb.GenericConverter { return &structs.BlindedBeaconBlockDeneb{} },
|
|
},
|
|
version.String(version.Electra): {
|
|
newBlock: func() ethpb.GenericConverter { return &structs.BeaconBlockContentsElectra{} },
|
|
newBlinded: func() ethpb.GenericConverter { return &structs.BlindedBeaconBlockElectra{} },
|
|
},
|
|
version.String(version.Fulu): {
|
|
newBlock: func() ethpb.GenericConverter { return &structs.BeaconBlockContentsFulu{} },
|
|
newBlinded: func() ethpb.GenericConverter { return &structs.BlindedBeaconBlockFulu{} },
|
|
},
|
|
}
|
|
|
|
func processBlockJSONResponse(ver string, isBlinded bool, decoder *json.Decoder) (*ethpb.GenericBeaconBlock, error) {
|
|
if decoder == nil {
|
|
return nil, errors.New("no produce block json decoder found")
|
|
}
|
|
|
|
factory, ok := jsonBlockFactories[ver]
|
|
if !ok {
|
|
return nil, errors.Errorf("unsupported consensus version `%s`", ver)
|
|
}
|
|
if isBlinded && factory.newBlinded != nil {
|
|
return convertBlockToGeneric(decoder, factory.newBlinded(), ver, true)
|
|
}
|
|
return convertBlockToGeneric(decoder, factory.newBlock(), ver, false)
|
|
}
|