mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-08 21:08:10 -05:00
adding ssz for get block endpoint (#15390)
* adding get ssz * adding some tests * gaz * adding ssz to e2e * wip ssz * adding in additional check on header type * remove unused * renaming json rest handler, and adding in usage of use ssz debug flag * fixing unit tests * fixing tests * gaz * radek feedback * Update config/features/config.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update config/features/flags.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update config/features/flags.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update validator/client/beacon-api/get_beacon_block.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update validator/client/beacon-api/get_beacon_block.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update validator/client/beacon-api/get_beacon_block.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * addressing feedback * missing import * another missing import * fixing tests * gaz * removing unused * gaz * more radek feedback * fixing context * adding in check for non accepted conent type * reverting to not create more edgecases --------- Co-authored-by: Radosław Kapka <rkapka@wp.pl>
This commit is contained in:
7
changelog/james-prysm_ssz-validator-block.md
Normal file
7
changelog/james-prysm_ssz-validator-block.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
### Added
|
||||||
|
|
||||||
|
- New ssz-only flag for validator client to enable calling rest apis in SSZ, starting with get block endpoint.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- when REST api is enabled the get Block api defaults to requesting and receiving SSZ instead of JSON, JSON is the fallback.
|
||||||
@@ -52,6 +52,7 @@ type Flags struct {
|
|||||||
EnableExperimentalAttestationPool bool // EnableExperimentalAttestationPool enables an experimental attestation pool design.
|
EnableExperimentalAttestationPool bool // EnableExperimentalAttestationPool enables an experimental attestation pool design.
|
||||||
EnableDutiesV2 bool // EnableDutiesV2 sets validator client to use the get Duties V2 endpoint
|
EnableDutiesV2 bool // EnableDutiesV2 sets validator client to use the get Duties V2 endpoint
|
||||||
EnableWeb bool // EnableWeb enables the webui on the validator client
|
EnableWeb bool // EnableWeb enables the webui on the validator client
|
||||||
|
SSZOnly bool // SSZOnly forces the validator client to use SSZ for communication with the beacon node when REST mode is enabled (useful for debugging)
|
||||||
// Logging related toggles.
|
// Logging related toggles.
|
||||||
DisableGRPCConnectionLogs bool // Disables logging when a new grpc client has connected.
|
DisableGRPCConnectionLogs bool // Disables logging when a new grpc client has connected.
|
||||||
EnableFullSSZDataLogging bool // Enables logging for full ssz data on rejected gossip messages
|
EnableFullSSZDataLogging bool // Enables logging for full ssz data on rejected gossip messages
|
||||||
@@ -344,6 +345,11 @@ func ConfigureValidator(ctx *cli.Context) error {
|
|||||||
logEnabled(EnableWebFlag)
|
logEnabled(EnableWebFlag)
|
||||||
cfg.EnableWeb = true
|
cfg.EnableWeb = true
|
||||||
}
|
}
|
||||||
|
if ctx.Bool(SSZOnly.Name) {
|
||||||
|
logEnabled(SSZOnly)
|
||||||
|
cfg.SSZOnly = true
|
||||||
|
}
|
||||||
|
|
||||||
cfg.KeystoreImportDebounceInterval = ctx.Duration(dynamicKeyReloadDebounceInterval.Name)
|
cfg.KeystoreImportDebounceInterval = ctx.Duration(dynamicKeyReloadDebounceInterval.Name)
|
||||||
Init(cfg)
|
Init(cfg)
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -201,6 +201,12 @@ var (
|
|||||||
Usage: "(Work in progress): Enables the web portal for the validator client.",
|
Usage: "(Work in progress): Enables the web portal for the validator client.",
|
||||||
Value: false,
|
Value: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SSZOnly forces the validator client to use SSZ for communication with the beacon node when REST mode is enabled
|
||||||
|
SSZOnly = &cli.BoolFlag{
|
||||||
|
Name: "ssz-only",
|
||||||
|
Usage: "(debug): Forces the validator client to use SSZ for communication with the beacon node when REST mode is enabled",
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// devModeFlags holds list of flags that are set when development mode is on.
|
// devModeFlags holds list of flags that are set when development mode is on.
|
||||||
@@ -223,6 +229,7 @@ var ValidatorFlags = append(deprecatedFlags, []cli.Flag{
|
|||||||
EnableBeaconRESTApi,
|
EnableBeaconRESTApi,
|
||||||
EnableDutiesV2,
|
EnableDutiesV2,
|
||||||
EnableWebFlag,
|
EnableWebFlag,
|
||||||
|
SSZOnly,
|
||||||
}...)
|
}...)
|
||||||
|
|
||||||
// E2EValidatorFlags contains a list of the validator feature flags to be tested in E2E.
|
// E2EValidatorFlags contains a list of the validator feature flags to be tested in E2E.
|
||||||
|
|||||||
@@ -5,6 +5,12 @@ import (
|
|||||||
enginev1 "github.com/OffchainLabs/prysm/v6/proto/engine/v1"
|
enginev1 "github.com/OffchainLabs/prysm/v6/proto/engine/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// GenericConverter defines any struct that can be converted to a generic beacon block.
|
||||||
|
// We assume all your versioned block structs implement this method.
|
||||||
|
type GenericConverter interface {
|
||||||
|
ToGeneric() (*GenericBeaconBlock, error)
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Phase 0
|
// Phase 0
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -248,6 +248,9 @@ func (v *ValidatorNode) Start(ctx context.Context) error {
|
|||||||
args = append(args,
|
args = append(args,
|
||||||
fmt.Sprintf("--%s=http://localhost:%d", flags.BeaconRESTApiProviderFlag.Name, beaconRestApiPort),
|
fmt.Sprintf("--%s=http://localhost:%d", flags.BeaconRESTApiProviderFlag.Name, beaconRestApiPort),
|
||||||
fmt.Sprintf("--%s", features.EnableBeaconRESTApi.Name))
|
fmt.Sprintf("--%s", features.EnableBeaconRESTApi.Name))
|
||||||
|
if v.config.UseSSZOnly {
|
||||||
|
args = append(args, fmt.Sprintf("--%s", features.SSZOnly.Name))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only apply e2e flags to the current branch. New flags may not exist in previous release.
|
// Only apply e2e flags to the current branch. New flags may not exist in previous release.
|
||||||
|
|||||||
@@ -29,6 +29,10 @@ func TestEndToEnd_MinimalConfig_ValidatorRESTApi(t *testing.T) {
|
|||||||
e2eMinimal(t, types.InitForkCfg(version.Bellatrix, version.Electra, params.E2ETestConfig()), types.WithCheckpointSync(), types.WithValidatorRESTApi()).run()
|
e2eMinimal(t, types.InitForkCfg(version.Bellatrix, version.Electra, params.E2ETestConfig()), types.WithCheckpointSync(), types.WithValidatorRESTApi()).run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEndToEnd_MinimalConfig_ValidatorRESTApi_SSZ(t *testing.T) {
|
||||||
|
e2eMinimal(t, types.InitForkCfg(version.Bellatrix, version.Electra, params.E2ETestConfig()), types.WithCheckpointSync(), types.WithValidatorRESTApi(), types.WithSSZOnly()).run()
|
||||||
|
}
|
||||||
|
|
||||||
func TestEndToEnd_ScenarioRun_EEOffline(t *testing.T) {
|
func TestEndToEnd_ScenarioRun_EEOffline(t *testing.T) {
|
||||||
t.Skip("TODO(#10242) Prysm is current unable to handle an offline e2e")
|
t.Skip("TODO(#10242) Prysm is current unable to handle an offline e2e")
|
||||||
cfg := types.InitForkCfg(version.Bellatrix, version.Deneb, params.E2ETestConfig())
|
cfg := types.InitForkCfg(version.Bellatrix, version.Deneb, params.E2ETestConfig())
|
||||||
|
|||||||
@@ -51,6 +51,12 @@ func WithValidatorRESTApi() E2EConfigOpt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithSSZOnly() E2EConfigOpt {
|
||||||
|
return func(cfg *E2EConfig) {
|
||||||
|
cfg.UseSSZOnly = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func WithBuilder() E2EConfigOpt {
|
func WithBuilder() E2EConfigOpt {
|
||||||
return func(cfg *E2EConfig) {
|
return func(cfg *E2EConfig) {
|
||||||
cfg.UseBuilder = true
|
cfg.UseBuilder = true
|
||||||
@@ -70,6 +76,7 @@ type E2EConfig struct {
|
|||||||
UseFixedPeerIDs bool
|
UseFixedPeerIDs bool
|
||||||
UseValidatorCrossClient bool
|
UseValidatorCrossClient bool
|
||||||
UseBeaconRestApi bool
|
UseBeaconRestApi bool
|
||||||
|
UseSSZOnly bool
|
||||||
UseBuilder bool
|
UseBuilder bool
|
||||||
EpochsToRun uint64
|
EpochsToRun uint64
|
||||||
Seed int64
|
Seed int64
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ func (acm *CLIManager) prepareBeaconClients(ctx context.Context) (*iface.Validat
|
|||||||
acm.beaconApiTimeout,
|
acm.beaconApiTimeout,
|
||||||
)
|
)
|
||||||
|
|
||||||
restHandler := beaconApi.NewBeaconApiJsonRestHandler(
|
restHandler := beaconApi.NewBeaconApiRestHandler(
|
||||||
http.Client{Timeout: acm.beaconApiTimeout},
|
http.Client{Timeout: acm.beaconApiTimeout},
|
||||||
acm.beaconApiEndpoint,
|
acm.beaconApiEndpoint,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ go_library(
|
|||||||
"genesis.go",
|
"genesis.go",
|
||||||
"get_beacon_block.go",
|
"get_beacon_block.go",
|
||||||
"index.go",
|
"index.go",
|
||||||
"json_rest_handler.go",
|
|
||||||
"log.go",
|
"log.go",
|
||||||
"metrics.go",
|
"metrics.go",
|
||||||
"prepare_beacon_proposer.go",
|
"prepare_beacon_proposer.go",
|
||||||
@@ -27,6 +26,7 @@ go_library(
|
|||||||
"propose_exit.go",
|
"propose_exit.go",
|
||||||
"prysm_beacon_chain_client.go",
|
"prysm_beacon_chain_client.go",
|
||||||
"registration.go",
|
"registration.go",
|
||||||
|
"rest_handler_client.go",
|
||||||
"state_validators.go",
|
"state_validators.go",
|
||||||
"status.go",
|
"status.go",
|
||||||
"stream_blocks.go",
|
"stream_blocks.go",
|
||||||
@@ -47,6 +47,7 @@ go_library(
|
|||||||
"//api/server/structs:go_default_library",
|
"//api/server/structs:go_default_library",
|
||||||
"//beacon-chain/core/helpers:go_default_library",
|
"//beacon-chain/core/helpers:go_default_library",
|
||||||
"//beacon-chain/core/signing:go_default_library",
|
"//beacon-chain/core/signing:go_default_library",
|
||||||
|
"//config/features:go_default_library",
|
||||||
"//config/fieldparams:go_default_library",
|
"//config/fieldparams:go_default_library",
|
||||||
"//config/params:go_default_library",
|
"//config/params:go_default_library",
|
||||||
"//consensus-types/primitives:go_default_library",
|
"//consensus-types/primitives:go_default_library",
|
||||||
@@ -91,7 +92,6 @@ go_test(
|
|||||||
"genesis_test.go",
|
"genesis_test.go",
|
||||||
"get_beacon_block_test.go",
|
"get_beacon_block_test.go",
|
||||||
"index_test.go",
|
"index_test.go",
|
||||||
"json_rest_handler_test.go",
|
|
||||||
"prepare_beacon_proposer_test.go",
|
"prepare_beacon_proposer_test.go",
|
||||||
"propose_attestation_test.go",
|
"propose_attestation_test.go",
|
||||||
"propose_beacon_block_altair_test.go",
|
"propose_beacon_block_altair_test.go",
|
||||||
@@ -110,6 +110,7 @@ go_test(
|
|||||||
"propose_exit_test.go",
|
"propose_exit_test.go",
|
||||||
"prysm_beacon_chain_client_test.go",
|
"prysm_beacon_chain_client_test.go",
|
||||||
"registration_test.go",
|
"registration_test.go",
|
||||||
|
"rest_handler_client_test.go",
|
||||||
"state_validators_test.go",
|
"state_validators_test.go",
|
||||||
"status_test.go",
|
"status_test.go",
|
||||||
"stream_blocks_test.go",
|
"stream_blocks_test.go",
|
||||||
@@ -128,6 +129,7 @@ go_test(
|
|||||||
"//api/server/structs:go_default_library",
|
"//api/server/structs:go_default_library",
|
||||||
"//beacon-chain/core/helpers:go_default_library",
|
"//beacon-chain/core/helpers:go_default_library",
|
||||||
"//beacon-chain/rpc/eth/shared/testing:go_default_library",
|
"//beacon-chain/rpc/eth/shared/testing:go_default_library",
|
||||||
|
"//config/features:go_default_library",
|
||||||
"//config/params:go_default_library",
|
"//config/params:go_default_library",
|
||||||
"//consensus-types/primitives:go_default_library",
|
"//consensus-types/primitives:go_default_library",
|
||||||
"//consensus-types/validator:go_default_library",
|
"//consensus-types/validator:go_default_library",
|
||||||
@@ -145,6 +147,8 @@ go_test(
|
|||||||
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
|
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
|
||||||
"@com_github_golang_protobuf//ptypes/empty",
|
"@com_github_golang_protobuf//ptypes/empty",
|
||||||
"@com_github_pkg_errors//:go_default_library",
|
"@com_github_pkg_errors//:go_default_library",
|
||||||
|
"@com_github_sirupsen_logrus//:go_default_library",
|
||||||
|
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
|
||||||
"@org_golang_google_protobuf//types/known/emptypb:go_default_library",
|
"@org_golang_google_protobuf//types/known/emptypb:go_default_library",
|
||||||
"@org_golang_google_protobuf//types/known/timestamppb:go_default_library",
|
"@org_golang_google_protobuf//types/known/timestamppb:go_default_library",
|
||||||
"@org_uber_go_mock//gomock:go_default_library",
|
"@org_uber_go_mock//gomock:go_default_library",
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import (
|
|||||||
|
|
||||||
type beaconApiChainClient struct {
|
type beaconApiChainClient struct {
|
||||||
fallbackClient iface.ChainClient
|
fallbackClient iface.ChainClient
|
||||||
jsonRestHandler JsonRestHandler
|
jsonRestHandler RestHandler
|
||||||
stateValidatorsProvider StateValidatorsProvider
|
stateValidatorsProvider StateValidatorsProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -333,7 +333,7 @@ func (c beaconApiChainClient) ValidatorParticipation(ctx context.Context, in *et
|
|||||||
return nil, errors.New("beaconApiChainClient.ValidatorParticipation is not implemented. To use a fallback client, pass a fallback client as the last argument of NewBeaconApiChainClientWithFallback.")
|
return nil, errors.New("beaconApiChainClient.ValidatorParticipation is not implemented. To use a fallback client, pass a fallback client as the last argument of NewBeaconApiChainClientWithFallback.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBeaconApiChainClientWithFallback(jsonRestHandler JsonRestHandler, fallbackClient iface.ChainClient) iface.ChainClient {
|
func NewBeaconApiChainClientWithFallback(jsonRestHandler RestHandler, fallbackClient iface.ChainClient) iface.ChainClient {
|
||||||
return &beaconApiChainClient{
|
return &beaconApiChainClient{
|
||||||
jsonRestHandler: jsonRestHandler,
|
jsonRestHandler: jsonRestHandler,
|
||||||
fallbackClient: fallbackClient,
|
fallbackClient: fallbackClient,
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ var (
|
|||||||
|
|
||||||
type beaconApiNodeClient struct {
|
type beaconApiNodeClient struct {
|
||||||
fallbackClient iface.NodeClient
|
fallbackClient iface.NodeClient
|
||||||
jsonRestHandler JsonRestHandler
|
jsonRestHandler RestHandler
|
||||||
genesisProvider GenesisProvider
|
genesisProvider GenesisProvider
|
||||||
healthTracker health.Tracker
|
healthTracker health.Tracker
|
||||||
}
|
}
|
||||||
@@ -111,7 +111,7 @@ func (c *beaconApiNodeClient) HealthTracker() health.Tracker {
|
|||||||
return c.healthTracker
|
return c.healthTracker
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNodeClientWithFallback(jsonRestHandler JsonRestHandler, fallbackClient iface.NodeClient) iface.NodeClient {
|
func NewNodeClientWithFallback(jsonRestHandler RestHandler, fallbackClient iface.NodeClient) iface.NodeClient {
|
||||||
b := &beaconApiNodeClient{
|
b := &beaconApiNodeClient{
|
||||||
jsonRestHandler: jsonRestHandler,
|
jsonRestHandler: jsonRestHandler,
|
||||||
fallbackClient: fallbackClient,
|
fallbackClient: fallbackClient,
|
||||||
|
|||||||
@@ -22,13 +22,13 @@ type beaconApiValidatorClient struct {
|
|||||||
genesisProvider GenesisProvider
|
genesisProvider GenesisProvider
|
||||||
dutiesProvider dutiesProvider
|
dutiesProvider dutiesProvider
|
||||||
stateValidatorsProvider StateValidatorsProvider
|
stateValidatorsProvider StateValidatorsProvider
|
||||||
jsonRestHandler JsonRestHandler
|
jsonRestHandler RestHandler
|
||||||
beaconBlockConverter BeaconBlockConverter
|
beaconBlockConverter BeaconBlockConverter
|
||||||
prysmChainClient iface.PrysmChainClient
|
prysmChainClient iface.PrysmChainClient
|
||||||
isEventStreamRunning bool
|
isEventStreamRunning bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBeaconApiValidatorClient(jsonRestHandler JsonRestHandler, opts ...ValidatorClientOpt) iface.ValidatorClient {
|
func NewBeaconApiValidatorClient(jsonRestHandler RestHandler, opts ...ValidatorClientOpt) iface.ValidatorClient {
|
||||||
c := &beaconApiValidatorClient{
|
c := &beaconApiValidatorClient{
|
||||||
genesisProvider: &beaconApiGenesisProvider{jsonRestHandler: jsonRestHandler},
|
genesisProvider: &beaconApiGenesisProvider{jsonRestHandler: jsonRestHandler},
|
||||||
dutiesProvider: beaconApiDutiesProvider{jsonRestHandler: jsonRestHandler},
|
dutiesProvider: beaconApiDutiesProvider{jsonRestHandler: jsonRestHandler},
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ type dutiesProvider interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type beaconApiDutiesProvider struct {
|
type beaconApiDutiesProvider struct {
|
||||||
jsonRestHandler JsonRestHandler
|
jsonRestHandler RestHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
type attesterDuty struct {
|
type attesterDuty struct {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ type GenesisProvider interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type beaconApiGenesisProvider struct {
|
type beaconApiGenesisProvider struct {
|
||||||
jsonRestHandler JsonRestHandler
|
jsonRestHandler RestHandler
|
||||||
genesis *structs.Genesis
|
genesis *structs.Genesis
|
||||||
once sync.Once
|
once sync.Once
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,10 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
neturl "net/url"
|
neturl "net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/OffchainLabs/prysm/v6/api"
|
||||||
"github.com/OffchainLabs/prysm/v6/api/apiutil"
|
"github.com/OffchainLabs/prysm/v6/api/apiutil"
|
||||||
"github.com/OffchainLabs/prysm/v6/api/server/structs"
|
"github.com/OffchainLabs/prysm/v6/api/server/structs"
|
||||||
"github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
|
"github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
|
||||||
@@ -22,160 +25,224 @@ func (c *beaconApiValidatorClient) beaconBlock(ctx context.Context, slot primiti
|
|||||||
if len(graffiti) > 0 {
|
if len(graffiti) > 0 {
|
||||||
queryParams.Add("graffiti", hexutil.Encode(graffiti))
|
queryParams.Add("graffiti", hexutil.Encode(graffiti))
|
||||||
}
|
}
|
||||||
|
|
||||||
queryUrl := apiutil.BuildURL(fmt.Sprintf("/eth/v3/validator/blocks/%d", slot), queryParams)
|
queryUrl := apiutil.BuildURL(fmt.Sprintf("/eth/v3/validator/blocks/%d", slot), queryParams)
|
||||||
produceBlockV3ResponseJson := structs.ProduceBlockV3Response{}
|
data, header, err := c.jsonRestHandler.GetSSZ(ctx, queryUrl)
|
||||||
err := c.jsonRestHandler.Get(ctx, queryUrl, &produceBlockV3ResponseJson)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if strings.Contains(header.Get("Content-Type"), api.OctetStreamMediaType) {
|
||||||
return processBlockResponse(
|
ver, err := version.FromString(header.Get(api.VersionHeader))
|
||||||
produceBlockV3ResponseJson.Version,
|
if err != nil {
|
||||||
produceBlockV3ResponseJson.ExecutionPayloadBlinded,
|
return nil, errors.Wrap(err, fmt.Sprintf("unsupported header version %s", header.Get(api.VersionHeader)))
|
||||||
json.NewDecoder(bytes.NewReader(produceBlockV3ResponseJson.Data)),
|
}
|
||||||
)
|
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)),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint: gocognit
|
func processBlockSSZResponse(ver int, data []byte, isBlinded bool) (*ethpb.GenericBeaconBlock, error) {
|
||||||
func processBlockResponse(ver string, isBlinded bool, decoder *json.Decoder) (*ethpb.GenericBeaconBlock, error) {
|
if ver >= version.Fulu {
|
||||||
var response *ethpb.GenericBeaconBlock
|
return processBlockSSZResponseFulu(data, isBlinded)
|
||||||
|
}
|
||||||
|
if ver >= version.Electra {
|
||||||
|
return processBlockSSZResponseElectra(data, isBlinded)
|
||||||
|
}
|
||||||
|
if ver >= version.Deneb {
|
||||||
|
return processBlockSSZResponseDeneb(data, isBlinded)
|
||||||
|
}
|
||||||
|
if ver >= version.Capella {
|
||||||
|
return processBlockSSZResponseCapella(data, isBlinded)
|
||||||
|
}
|
||||||
|
if ver >= version.Bellatrix {
|
||||||
|
return processBlockSSZResponseBellatrix(data, isBlinded)
|
||||||
|
}
|
||||||
|
if ver >= version.Altair {
|
||||||
|
block := ðpb.BeaconBlockAltair{}
|
||||||
|
if err := block.UnmarshalSSZ(data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Altair{Altair: block}}, nil
|
||||||
|
}
|
||||||
|
if ver >= version.Phase0 {
|
||||||
|
block := ðpb.BeaconBlock{}
|
||||||
|
if err := block.UnmarshalSSZ(data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Phase0{Phase0: block}}, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("unsupported block version %s", version.String(ver))
|
||||||
|
}
|
||||||
|
|
||||||
|
func processBlockSSZResponseFulu(data []byte, isBlinded bool) (*ethpb.GenericBeaconBlock, error) {
|
||||||
|
if isBlinded {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
block := ðpb.BeaconBlockContentsFulu{}
|
||||||
|
if err := block.UnmarshalSSZ(data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Fulu{Fulu: block}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func processBlockSSZResponseElectra(data []byte, isBlinded bool) (*ethpb.GenericBeaconBlock, error) {
|
||||||
|
if isBlinded {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
block := ðpb.BeaconBlockContentsElectra{}
|
||||||
|
if err := block.UnmarshalSSZ(data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Electra{Electra: block}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func processBlockSSZResponseDeneb(data []byte, isBlinded bool) (*ethpb.GenericBeaconBlock, error) {
|
||||||
|
if isBlinded {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
block := ðpb.BeaconBlockContentsDeneb{}
|
||||||
|
if err := block.UnmarshalSSZ(data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Deneb{Deneb: block}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func processBlockSSZResponseCapella(data []byte, isBlinded bool) (*ethpb.GenericBeaconBlock, error) {
|
||||||
|
if isBlinded {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
block := ðpb.BeaconBlockCapella{}
|
||||||
|
if err := block.UnmarshalSSZ(data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Capella{Capella: block}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func processBlockSSZResponseBellatrix(data []byte, isBlinded bool) (*ethpb.GenericBeaconBlock, error) {
|
||||||
|
if isBlinded {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
block := ðpb.BeaconBlockBellatrix{}
|
||||||
|
if err := block.UnmarshalSSZ(data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Bellatrix{Bellatrix: block}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func processBlockJSONResponse(ver string, isBlinded bool, decoder *json.Decoder) (*ethpb.GenericBeaconBlock, error) {
|
||||||
if decoder == nil {
|
if decoder == nil {
|
||||||
return nil, errors.New("no produce block json decoder found")
|
return nil, errors.New("no produce block json decoder found")
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ver {
|
switch ver {
|
||||||
case version.String(version.Phase0):
|
case version.String(version.Phase0):
|
||||||
jsonPhase0Block := structs.BeaconBlock{}
|
return convertBlockToGeneric(decoder, &structs.BeaconBlock{}, version.String(version.Phase0), false)
|
||||||
if err := decoder.Decode(&jsonPhase0Block); err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to decode phase0 block response json")
|
|
||||||
}
|
|
||||||
genericBlock, err := jsonPhase0Block.ToGeneric()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to get phase0 block")
|
|
||||||
}
|
|
||||||
response = genericBlock
|
|
||||||
case version.String(version.Altair):
|
case version.String(version.Altair):
|
||||||
jsonAltairBlock := structs.BeaconBlockAltair{}
|
return convertBlockToGeneric(decoder, &structs.BeaconBlockAltair{}, "altair", false)
|
||||||
if err := decoder.Decode(&jsonAltairBlock); err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to decode altair block response json")
|
|
||||||
}
|
|
||||||
genericBlock, err := jsonAltairBlock.ToGeneric()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to get altair block")
|
|
||||||
}
|
|
||||||
response = genericBlock
|
|
||||||
case version.String(version.Bellatrix):
|
case version.String(version.Bellatrix):
|
||||||
if isBlinded {
|
return processBellatrixBlock(decoder, isBlinded)
|
||||||
jsonBellatrixBlock := structs.BlindedBeaconBlockBellatrix{}
|
|
||||||
if err := decoder.Decode(&jsonBellatrixBlock); err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to decode blinded bellatrix block response json")
|
|
||||||
}
|
|
||||||
genericBlock, err := jsonBellatrixBlock.ToGeneric()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to get blinded bellatrix block")
|
|
||||||
}
|
|
||||||
response = genericBlock
|
|
||||||
} else {
|
|
||||||
jsonBellatrixBlock := structs.BeaconBlockBellatrix{}
|
|
||||||
if err := decoder.Decode(&jsonBellatrixBlock); err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to decode bellatrix block response json")
|
|
||||||
}
|
|
||||||
genericBlock, err := jsonBellatrixBlock.ToGeneric()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to get bellatrix block")
|
|
||||||
}
|
|
||||||
response = genericBlock
|
|
||||||
}
|
|
||||||
case version.String(version.Capella):
|
case version.String(version.Capella):
|
||||||
if isBlinded {
|
return processCapellaBlock(decoder, isBlinded)
|
||||||
jsonCapellaBlock := structs.BlindedBeaconBlockCapella{}
|
|
||||||
if err := decoder.Decode(&jsonCapellaBlock); err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to decode blinded capella block response json")
|
|
||||||
}
|
|
||||||
genericBlock, err := jsonCapellaBlock.ToGeneric()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to get blinded capella block")
|
|
||||||
}
|
|
||||||
response = genericBlock
|
|
||||||
} else {
|
|
||||||
jsonCapellaBlock := structs.BeaconBlockCapella{}
|
|
||||||
if err := decoder.Decode(&jsonCapellaBlock); err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to decode capella block response json")
|
|
||||||
}
|
|
||||||
genericBlock, err := jsonCapellaBlock.ToGeneric()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to get capella block")
|
|
||||||
}
|
|
||||||
response = genericBlock
|
|
||||||
}
|
|
||||||
case version.String(version.Deneb):
|
case version.String(version.Deneb):
|
||||||
if isBlinded {
|
return processDenebBlock(decoder, isBlinded)
|
||||||
jsonDenebBlock := structs.BlindedBeaconBlockDeneb{}
|
|
||||||
if err := decoder.Decode(&jsonDenebBlock); err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to decode blinded deneb block response json")
|
|
||||||
}
|
|
||||||
genericBlock, err := jsonDenebBlock.ToGeneric()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to get blinded deneb block")
|
|
||||||
}
|
|
||||||
response = genericBlock
|
|
||||||
} else {
|
|
||||||
jsonDenebBlockContents := structs.BeaconBlockContentsDeneb{}
|
|
||||||
if err := decoder.Decode(&jsonDenebBlockContents); err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to decode deneb block response json")
|
|
||||||
}
|
|
||||||
genericBlock, err := jsonDenebBlockContents.ToGeneric()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to get deneb block")
|
|
||||||
}
|
|
||||||
response = genericBlock
|
|
||||||
}
|
|
||||||
case version.String(version.Electra):
|
case version.String(version.Electra):
|
||||||
if isBlinded {
|
return processElectraBlock(decoder, isBlinded)
|
||||||
jsonElectraBlock := structs.BlindedBeaconBlockElectra{}
|
|
||||||
if err := decoder.Decode(&jsonElectraBlock); err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to decode blinded electra block response json")
|
|
||||||
}
|
|
||||||
genericBlock, err := jsonElectraBlock.ToGeneric()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to get blinded electra block")
|
|
||||||
}
|
|
||||||
response = genericBlock
|
|
||||||
} else {
|
|
||||||
jsonElectraBlockContents := structs.BeaconBlockContentsElectra{}
|
|
||||||
if err := decoder.Decode(&jsonElectraBlockContents); err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to decode electra block response json")
|
|
||||||
}
|
|
||||||
genericBlock, err := jsonElectraBlockContents.ToGeneric()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to get electra block")
|
|
||||||
}
|
|
||||||
response = genericBlock
|
|
||||||
}
|
|
||||||
case version.String(version.Fulu):
|
case version.String(version.Fulu):
|
||||||
if isBlinded {
|
return processFuluBlock(decoder, isBlinded)
|
||||||
jsonFuluBlock := structs.BlindedBeaconBlockFulu{}
|
|
||||||
if err := decoder.Decode(&jsonFuluBlock); err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to decode blinded fulu block response json")
|
|
||||||
}
|
|
||||||
genericBlock, err := jsonFuluBlock.ToGeneric()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to get blinded fulu block")
|
|
||||||
}
|
|
||||||
response = genericBlock
|
|
||||||
} else {
|
|
||||||
jsonFuluBlockContents := structs.BeaconBlockContentsFulu{}
|
|
||||||
if err := decoder.Decode(&jsonFuluBlockContents); err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to decode fulu block response json")
|
|
||||||
}
|
|
||||||
genericBlock, err := jsonFuluBlockContents.ToGeneric()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to get fulu block")
|
|
||||||
}
|
|
||||||
response = genericBlock
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
return nil, errors.Errorf("unsupported consensus version `%s`", ver)
|
return nil, errors.Errorf("unsupported consensus version `%s`", ver)
|
||||||
}
|
}
|
||||||
return response, nil
|
}
|
||||||
|
|
||||||
|
func processBellatrixBlock(decoder *json.Decoder, isBlinded bool) (*ethpb.GenericBeaconBlock, error) {
|
||||||
|
if isBlinded {
|
||||||
|
return convertBlockToGeneric(decoder, &structs.BlindedBeaconBlockBellatrix{}, "bellatrix", true)
|
||||||
|
}
|
||||||
|
return convertBlockToGeneric(decoder, &structs.BeaconBlockBellatrix{}, "bellatrix", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func processCapellaBlock(decoder *json.Decoder, isBlinded bool) (*ethpb.GenericBeaconBlock, error) {
|
||||||
|
if isBlinded {
|
||||||
|
return convertBlockToGeneric(decoder, &structs.BlindedBeaconBlockCapella{}, "capella", true)
|
||||||
|
}
|
||||||
|
return convertBlockToGeneric(decoder, &structs.BeaconBlockCapella{}, "capella", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func processDenebBlock(decoder *json.Decoder, isBlinded bool) (*ethpb.GenericBeaconBlock, error) {
|
||||||
|
if isBlinded {
|
||||||
|
return convertBlockToGeneric(decoder, &structs.BlindedBeaconBlockDeneb{}, "deneb", true)
|
||||||
|
}
|
||||||
|
return convertBlockToGeneric(decoder, &structs.BeaconBlockContentsDeneb{}, "deneb", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func processElectraBlock(decoder *json.Decoder, isBlinded bool) (*ethpb.GenericBeaconBlock, error) {
|
||||||
|
if isBlinded {
|
||||||
|
return convertBlockToGeneric(decoder, &structs.BlindedBeaconBlockElectra{}, "electra", true)
|
||||||
|
}
|
||||||
|
return convertBlockToGeneric(decoder, &structs.BeaconBlockContentsElectra{}, "electra", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func processFuluBlock(decoder *json.Decoder, isBlinded bool) (*ethpb.GenericBeaconBlock, error) {
|
||||||
|
if isBlinded {
|
||||||
|
return convertBlockToGeneric(decoder, &structs.BlindedBeaconBlockFulu{}, "fulu", true)
|
||||||
|
}
|
||||||
|
return convertBlockToGeneric(decoder, &structs.BeaconBlockContentsFulu{}, "fulu", false)
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -56,6 +56,23 @@ func (mr *MockJsonRestHandlerMockRecorder) Get(ctx, endpoint, resp any) *gomock.
|
|||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockJsonRestHandler)(nil).Get), ctx, endpoint, resp)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockJsonRestHandler)(nil).Get), ctx, endpoint, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// GetSSZ mocks base method.
|
||||||
|
func (m *MockJsonRestHandler) GetSSZ(ctx context.Context, endpoint string) ([]byte, http.Header, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetSSZ", ctx, endpoint)
|
||||||
|
ret0, _ := ret[0].([]byte)
|
||||||
|
ret1, _ := ret[1].(http.Header)
|
||||||
|
ret2, _ := ret[2].(error)
|
||||||
|
return ret0, ret1, ret2
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSSZ indicates an expected call of GetSSZ.
|
||||||
|
func (mr *MockJsonRestHandlerMockRecorder) GetSSZ(ctx, endpoint any) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSSZ", reflect.TypeOf((*MockJsonRestHandler)(nil).GetSSZ), ctx, endpoint)
|
||||||
|
}
|
||||||
|
|
||||||
// Host mocks base method.
|
// Host mocks base method.
|
||||||
func (m *MockJsonRestHandler) Host() string {
|
func (m *MockJsonRestHandler) Host() string {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// NewPrysmChainClient returns implementation of iface.PrysmChainClient.
|
// NewPrysmChainClient returns implementation of iface.PrysmChainClient.
|
||||||
func NewPrysmChainClient(jsonRestHandler JsonRestHandler, nodeClient iface.NodeClient) iface.PrysmChainClient {
|
func NewPrysmChainClient(jsonRestHandler RestHandler, nodeClient iface.NodeClient) iface.PrysmChainClient {
|
||||||
return prysmChainClient{
|
return prysmChainClient{
|
||||||
jsonRestHandler: jsonRestHandler,
|
jsonRestHandler: jsonRestHandler,
|
||||||
nodeClient: nodeClient,
|
nodeClient: nodeClient,
|
||||||
@@ -26,7 +26,7 @@ func NewPrysmChainClient(jsonRestHandler JsonRestHandler, nodeClient iface.NodeC
|
|||||||
}
|
}
|
||||||
|
|
||||||
type prysmChainClient struct {
|
type prysmChainClient struct {
|
||||||
jsonRestHandler JsonRestHandler
|
jsonRestHandler RestHandler
|
||||||
nodeClient iface.NodeClient
|
nodeClient iface.NodeClient
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,49 +4,53 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/OffchainLabs/prysm/v6/api"
|
"github.com/OffchainLabs/prysm/v6/api"
|
||||||
|
"github.com/OffchainLabs/prysm/v6/config/features"
|
||||||
"github.com/OffchainLabs/prysm/v6/network/httputil"
|
"github.com/OffchainLabs/prysm/v6/network/httputil"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
type JsonRestHandler interface {
|
type RestHandler interface {
|
||||||
Get(ctx context.Context, endpoint string, resp interface{}) error
|
Get(ctx context.Context, endpoint string, resp interface{}) error
|
||||||
|
GetSSZ(ctx context.Context, endpoint string) ([]byte, http.Header, error)
|
||||||
Post(ctx context.Context, endpoint string, headers map[string]string, data *bytes.Buffer, resp interface{}) error
|
Post(ctx context.Context, endpoint string, headers map[string]string, data *bytes.Buffer, resp interface{}) error
|
||||||
HttpClient() *http.Client
|
HttpClient() *http.Client
|
||||||
Host() string
|
Host() string
|
||||||
SetHost(host string)
|
SetHost(host string)
|
||||||
}
|
}
|
||||||
|
|
||||||
type BeaconApiJsonRestHandler struct {
|
type BeaconApiRestHandler struct {
|
||||||
client http.Client
|
client http.Client
|
||||||
host string
|
host string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBeaconApiJsonRestHandler returns a JsonRestHandler
|
// NewBeaconApiRestHandler returns a RestHandler
|
||||||
func NewBeaconApiJsonRestHandler(client http.Client, host string) JsonRestHandler {
|
func NewBeaconApiRestHandler(client http.Client, host string) RestHandler {
|
||||||
return &BeaconApiJsonRestHandler{
|
return &BeaconApiRestHandler{
|
||||||
client: client,
|
client: client,
|
||||||
host: host,
|
host: host,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// HttpClient returns the underlying HTTP client of the handler
|
// HttpClient returns the underlying HTTP client of the handler
|
||||||
func (c *BeaconApiJsonRestHandler) HttpClient() *http.Client {
|
func (c *BeaconApiRestHandler) HttpClient() *http.Client {
|
||||||
return &c.client
|
return &c.client
|
||||||
}
|
}
|
||||||
|
|
||||||
// Host returns the underlying HTTP host
|
// Host returns the underlying HTTP host
|
||||||
func (c *BeaconApiJsonRestHandler) Host() string {
|
func (c *BeaconApiRestHandler) Host() string {
|
||||||
return c.host
|
return c.host
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get sends a GET request and decodes the response body as a JSON object into the passed in object.
|
// Get sends a GET request and decodes the response body as a JSON object into the passed in object.
|
||||||
// If an HTTP error is returned, the body is decoded as a DefaultJsonError JSON object and returned as the first return value.
|
// If an HTTP error is returned, the body is decoded as a DefaultJsonError JSON object and returned as the first return value.
|
||||||
func (c *BeaconApiJsonRestHandler) Get(ctx context.Context, endpoint string, resp interface{}) error {
|
func (c *BeaconApiRestHandler) Get(ctx context.Context, endpoint string, resp interface{}) error {
|
||||||
url := c.host + endpoint
|
url := c.host + endpoint
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -66,9 +70,61 @@ func (c *BeaconApiJsonRestHandler) Get(ctx context.Context, endpoint string, res
|
|||||||
return decodeResp(httpResp, resp)
|
return decodeResp(httpResp, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *BeaconApiRestHandler) GetSSZ(ctx context.Context, endpoint string) ([]byte, http.Header, error) {
|
||||||
|
url := c.host + endpoint
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, errors.Wrapf(err, "failed to create request for endpoint %s", url)
|
||||||
|
}
|
||||||
|
primaryAcceptType := fmt.Sprintf("%s;q=%s", api.OctetStreamMediaType, "0.95")
|
||||||
|
secondaryAcceptType := fmt.Sprintf("%s;q=%s", api.JsonMediaType, "0.9")
|
||||||
|
acceptHeaderString := fmt.Sprintf("%s,%s", primaryAcceptType, secondaryAcceptType)
|
||||||
|
if features.Get().SSZOnly {
|
||||||
|
acceptHeaderString = api.OctetStreamMediaType
|
||||||
|
}
|
||||||
|
req.Header.Set("Accept", acceptHeaderString)
|
||||||
|
httpResp, err := c.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, errors.Wrapf(err, "failed to perform request for endpoint %s", url)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := httpResp.Body.Close(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
contentType := httpResp.Header.Get("Content-Type")
|
||||||
|
body, err := io.ReadAll(httpResp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, errors.Wrapf(err, "failed to read response body for %s", httpResp.Request.URL)
|
||||||
|
}
|
||||||
|
if !strings.Contains(primaryAcceptType, contentType) {
|
||||||
|
log.WithFields(logrus.Fields{
|
||||||
|
"primaryAcceptType": primaryAcceptType,
|
||||||
|
"secondaryAcceptType": secondaryAcceptType,
|
||||||
|
"receivedAcceptType": contentType,
|
||||||
|
}).Debug("Server responded with non primary accept type")
|
||||||
|
}
|
||||||
|
|
||||||
|
// non-2XX codes are a failure
|
||||||
|
if !strings.HasPrefix(httpResp.Status, "2") {
|
||||||
|
decoder := json.NewDecoder(bytes.NewBuffer(body))
|
||||||
|
errorJson := &httputil.DefaultJsonError{}
|
||||||
|
if err = decoder.Decode(errorJson); err != nil {
|
||||||
|
return nil, nil, errors.Wrapf(err, "failed to decode response body into error json for %s", httpResp.Request.URL)
|
||||||
|
}
|
||||||
|
return nil, nil, errorJson
|
||||||
|
}
|
||||||
|
|
||||||
|
if features.Get().SSZOnly && contentType != api.OctetStreamMediaType {
|
||||||
|
return nil, nil, errors.Errorf("server responded with non primary accept type %s", contentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return body, httpResp.Header, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Post sends a POST request and decodes the response body as a JSON object into the passed in object.
|
// Post sends a POST request and decodes the response body as a JSON object into the passed in object.
|
||||||
// If an HTTP error is returned, the body is decoded as a DefaultJsonError JSON object and returned as the first return value.
|
// If an HTTP error is returned, the body is decoded as a DefaultJsonError JSON object and returned as the first return value.
|
||||||
func (c *BeaconApiJsonRestHandler) Post(
|
func (c *BeaconApiRestHandler) Post(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
apiEndpoint string,
|
apiEndpoint string,
|
||||||
headers map[string]string,
|
headers map[string]string,
|
||||||
@@ -136,6 +192,6 @@ func decodeResp(httpResp *http.Response, resp interface{}) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BeaconApiJsonRestHandler) SetHost(host string) {
|
func (c *BeaconApiRestHandler) SetHost(host string) {
|
||||||
c.host = host
|
c.host = host
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ package beacon_api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -15,6 +16,8 @@ import (
|
|||||||
"github.com/OffchainLabs/prysm/v6/testing/assert"
|
"github.com/OffchainLabs/prysm/v6/testing/assert"
|
||||||
"github.com/OffchainLabs/prysm/v6/testing/require"
|
"github.com/OffchainLabs/prysm/v6/testing/require"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/sirupsen/logrus/hooks/test"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGet(t *testing.T) {
|
func TestGet(t *testing.T) {
|
||||||
@@ -39,7 +42,7 @@ func TestGet(t *testing.T) {
|
|||||||
server := httptest.NewServer(mux)
|
server := httptest.NewServer(mux)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
jsonRestHandler := BeaconApiJsonRestHandler{
|
jsonRestHandler := BeaconApiRestHandler{
|
||||||
client: http.Client{Timeout: time.Second * 5},
|
client: http.Client{Timeout: time.Second * 5},
|
||||||
host: server.URL,
|
host: server.URL,
|
||||||
}
|
}
|
||||||
@@ -48,6 +51,98 @@ func TestGet(t *testing.T) {
|
|||||||
assert.DeepEqual(t, genesisJson, resp)
|
assert.DeepEqual(t, genesisJson, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetSSZ(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
const endpoint = "/example/rest/api/ssz"
|
||||||
|
genesisJson := &structs.GetGenesisResponse{
|
||||||
|
Data: &structs.Genesis{
|
||||||
|
GenesisTime: "123",
|
||||||
|
GenesisValidatorsRoot: "0x456",
|
||||||
|
GenesisForkVersion: "0x789",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("Successful SSZ response", func(t *testing.T) {
|
||||||
|
expectedBody := []byte{10, 20, 30, 40}
|
||||||
|
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.HandleFunc(endpoint, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
assert.StringContains(t, api.OctetStreamMediaType, r.Header.Get("Accept"))
|
||||||
|
w.Header().Set("Content-Type", api.OctetStreamMediaType)
|
||||||
|
_, err := w.Write(expectedBody)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
server := httptest.NewServer(mux)
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
jsonRestHandler := BeaconApiRestHandler{
|
||||||
|
client: http.Client{Timeout: time.Second * 5},
|
||||||
|
host: server.URL,
|
||||||
|
}
|
||||||
|
|
||||||
|
body, header, err := jsonRestHandler.GetSSZ(ctx, endpoint)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.DeepEqual(t, expectedBody, body)
|
||||||
|
require.StringContains(t, api.OctetStreamMediaType, header.Get("Content-Type"))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Json Content-Type response", func(t *testing.T) {
|
||||||
|
logrus.SetLevel(logrus.DebugLevel)
|
||||||
|
defer logrus.SetLevel(logrus.InfoLevel) // reset it afterwards
|
||||||
|
logHook := test.NewGlobal()
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.HandleFunc(endpoint, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
assert.StringContains(t, api.OctetStreamMediaType, r.Header.Get("Accept"))
|
||||||
|
w.Header().Set("Content-Type", api.JsonMediaType)
|
||||||
|
|
||||||
|
marshalledJson, err := json.Marshal(genesisJson)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = w.Write(marshalledJson)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
server := httptest.NewServer(mux)
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
jsonRestHandler := BeaconApiRestHandler{
|
||||||
|
client: http.Client{Timeout: time.Second * 5},
|
||||||
|
host: server.URL,
|
||||||
|
}
|
||||||
|
|
||||||
|
body, header, err := jsonRestHandler.GetSSZ(ctx, endpoint)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.LogsContain(t, logHook, "Server responded with non primary accept type")
|
||||||
|
require.Equal(t, api.JsonMediaType, header.Get("Content-Type"))
|
||||||
|
resp := &structs.GetGenesisResponse{}
|
||||||
|
require.NoError(t, json.Unmarshal(body, resp))
|
||||||
|
require.Equal(t, "123", resp.Data.GenesisTime)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Wrong Content-Type response, doesn't error out and instead handled downstream", func(t *testing.T) {
|
||||||
|
logrus.SetLevel(logrus.DebugLevel)
|
||||||
|
defer logrus.SetLevel(logrus.InfoLevel) // reset it afterwards
|
||||||
|
logHook := test.NewGlobal()
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.HandleFunc(endpoint, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
assert.StringContains(t, api.OctetStreamMediaType, r.Header.Get("Accept"))
|
||||||
|
w.Header().Set("Content-Type", "text/plain") // Invalid content type
|
||||||
|
_, err := w.Write([]byte("some text"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
server := httptest.NewServer(mux)
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
jsonRestHandler := BeaconApiRestHandler{
|
||||||
|
client: http.Client{Timeout: time.Second * 5},
|
||||||
|
host: server.URL,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err := jsonRestHandler.GetSSZ(ctx, endpoint)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.LogsContain(t, logHook, "Server responded with non primary accept type")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestPost(t *testing.T) {
|
func TestPost(t *testing.T) {
|
||||||
ctx := t.Context()
|
ctx := t.Context()
|
||||||
const endpoint = "/example/rest/api/endpoint"
|
const endpoint = "/example/rest/api/endpoint"
|
||||||
@@ -85,7 +180,7 @@ func TestPost(t *testing.T) {
|
|||||||
server := httptest.NewServer(mux)
|
server := httptest.NewServer(mux)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
jsonRestHandler := BeaconApiJsonRestHandler{
|
jsonRestHandler := BeaconApiRestHandler{
|
||||||
client: http.Client{Timeout: time.Second * 5},
|
client: http.Client{Timeout: time.Second * 5},
|
||||||
host: server.URL,
|
host: server.URL,
|
||||||
}
|
}
|
||||||
@@ -21,7 +21,7 @@ type StateValidatorsProvider interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type beaconApiStateValidatorsProvider struct {
|
type beaconApiStateValidatorsProvider struct {
|
||||||
jsonRestHandler JsonRestHandler
|
jsonRestHandler RestHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c beaconApiStateValidatorsProvider) StateValidators(
|
func (c beaconApiStateValidatorsProvider) StateValidators(
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
validatorHelpers "github.com/OffchainLabs/prysm/v6/validator/helpers"
|
validatorHelpers "github.com/OffchainLabs/prysm/v6/validator/helpers"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewChainClient(validatorConn validatorHelpers.NodeConnection, jsonRestHandler beaconApi.JsonRestHandler) iface.ChainClient {
|
func NewChainClient(validatorConn validatorHelpers.NodeConnection, jsonRestHandler beaconApi.RestHandler) iface.ChainClient {
|
||||||
grpcClient := grpcApi.NewGrpcChainClient(validatorConn.GetGrpcClientConn())
|
grpcClient := grpcApi.NewGrpcChainClient(validatorConn.GetGrpcClientConn())
|
||||||
if features.Get().EnableBeaconRESTApi {
|
if features.Get().EnableBeaconRESTApi {
|
||||||
return beaconApi.NewBeaconApiChainClientWithFallback(jsonRestHandler, grpcClient)
|
return beaconApi.NewBeaconApiChainClientWithFallback(jsonRestHandler, grpcClient)
|
||||||
@@ -18,7 +18,7 @@ func NewChainClient(validatorConn validatorHelpers.NodeConnection, jsonRestHandl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPrysmChainClient(validatorConn validatorHelpers.NodeConnection, jsonRestHandler beaconApi.JsonRestHandler) iface.PrysmChainClient {
|
func NewPrysmChainClient(validatorConn validatorHelpers.NodeConnection, jsonRestHandler beaconApi.RestHandler) iface.PrysmChainClient {
|
||||||
if features.Get().EnableBeaconRESTApi {
|
if features.Get().EnableBeaconRESTApi {
|
||||||
return beaconApi.NewPrysmChainClient(jsonRestHandler, nodeClientFactory.NewNodeClient(validatorConn, jsonRestHandler))
|
return beaconApi.NewPrysmChainClient(jsonRestHandler, nodeClientFactory.NewNodeClient(validatorConn, jsonRestHandler))
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
validatorHelpers "github.com/OffchainLabs/prysm/v6/validator/helpers"
|
validatorHelpers "github.com/OffchainLabs/prysm/v6/validator/helpers"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewNodeClient(validatorConn validatorHelpers.NodeConnection, jsonRestHandler beaconApi.JsonRestHandler) iface.NodeClient {
|
func NewNodeClient(validatorConn validatorHelpers.NodeConnection, jsonRestHandler beaconApi.RestHandler) iface.NodeClient {
|
||||||
grpcClient := grpcApi.NewNodeClient(validatorConn.GetGrpcClientConn())
|
grpcClient := grpcApi.NewNodeClient(validatorConn.GetGrpcClientConn())
|
||||||
if features.Get().EnableBeaconRESTApi {
|
if features.Get().EnableBeaconRESTApi {
|
||||||
return beaconApi.NewNodeClientWithFallback(jsonRestHandler, grpcClient)
|
return beaconApi.NewNodeClientWithFallback(jsonRestHandler, grpcClient)
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ func (v *ValidatorService) Start() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
restHandler := beaconApi.NewBeaconApiJsonRestHandler(
|
restHandler := beaconApi.NewBeaconApiRestHandler(
|
||||||
http.Client{Timeout: v.conn.GetBeaconApiTimeout(), Transport: otelhttp.NewTransport(http.DefaultTransport)},
|
http.Client{Timeout: v.conn.GetBeaconApiTimeout(), Transport: otelhttp.NewTransport(http.DefaultTransport)},
|
||||||
hosts[0],
|
hosts[0],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
|
|
||||||
func NewValidatorClient(
|
func NewValidatorClient(
|
||||||
validatorConn validatorHelpers.NodeConnection,
|
validatorConn validatorHelpers.NodeConnection,
|
||||||
jsonRestHandler beaconApi.JsonRestHandler,
|
jsonRestHandler beaconApi.RestHandler,
|
||||||
opt ...beaconApi.ValidatorClientOpt,
|
opt ...beaconApi.ValidatorClientOpt,
|
||||||
) iface.ValidatorClient {
|
) iface.ValidatorClient {
|
||||||
if features.Get().EnableBeaconRESTApi {
|
if features.Get().EnableBeaconRESTApi {
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ func (s *Server) registerBeaconClient() error {
|
|||||||
s.beaconApiTimeout,
|
s.beaconApiTimeout,
|
||||||
)
|
)
|
||||||
|
|
||||||
restHandler := beaconApi.NewBeaconApiJsonRestHandler(
|
restHandler := beaconApi.NewBeaconApiRestHandler(
|
||||||
http.Client{Timeout: s.beaconApiTimeout, Transport: otelhttp.NewTransport(http.DefaultTransport)},
|
http.Client{Timeout: s.beaconApiTimeout, Transport: otelhttp.NewTransport(http.DefaultTransport)},
|
||||||
s.beaconApiEndpoint,
|
s.beaconApiEndpoint,
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user