From 2b7791865d5d6112afd182cd546b628683372794 Mon Sep 17 00:00:00 2001 From: Jim McDonald Date: Fri, 15 Mar 2024 01:40:43 +0000 Subject: [PATCH] Add unblind block. --- errors.go | 19 ++ services/blockunblinder/mock/erroring.go | 39 +++ services/blockunblinder/mock/service.go | 38 +++ services/blockunblinder/service.go | 10 +- services/daemon/rest/builderbid.go | 10 +- services/daemon/rest/parameters.go | 14 +- services/daemon/rest/service.go | 13 +- services/daemon/rest/service_test.go | 24 +- services/daemon/rest/unblindblock.go | 267 ++++++++++++++++++ .../daemon/rest/validatorregistrations.go | 15 +- .../validatorregistrations_internal_test.go | 4 + 11 files changed, 438 insertions(+), 15 deletions(-) create mode 100644 errors.go create mode 100644 services/blockunblinder/mock/erroring.go create mode 100644 services/blockunblinder/mock/service.go create mode 100644 services/daemon/rest/unblindblock.go diff --git a/errors.go b/errors.go new file mode 100644 index 0000000..ead46b7 --- /dev/null +++ b/errors.go @@ -0,0 +1,19 @@ +// Copyright © 2024 Attestant Limited. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package client + +import "errors" + +// ErrInvalidOptions is returned when a request is made with invalid options. +var ErrInvalidOptions = errors.New("invalid options") diff --git a/services/blockunblinder/mock/erroring.go b/services/blockunblinder/mock/erroring.go new file mode 100644 index 0000000..c2d919e --- /dev/null +++ b/services/blockunblinder/mock/erroring.go @@ -0,0 +1,39 @@ +// Copyright © 2024 Attestant Limited. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mock + +import ( + "context" + "errors" + + "github.com/attestantio/go-eth2-client/api" +) + +// ErroringService is a mock block unblinder. +type ErroringService struct{} + +// NewErroring creates a new mock block unblinder. +func NewErroring() *ErroringService { + return &ErroringService{} +} + +// UnblindBlock unblinds the given block. +func (s *ErroringService) UnblindBlock(_ context.Context, + _ *api.VersionedSignedBlindedBeaconBlock, +) ( + *api.VersionedSignedProposal, + error, +) { + return nil, errors.New("error") +} diff --git a/services/blockunblinder/mock/service.go b/services/blockunblinder/mock/service.go new file mode 100644 index 0000000..3082589 --- /dev/null +++ b/services/blockunblinder/mock/service.go @@ -0,0 +1,38 @@ +// Copyright © 2024 Attestant Limited. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mock + +import ( + "context" + + "github.com/attestantio/go-eth2-client/api" +) + +// Service is a mock block auctioneer. +type Service struct{} + +// New creates a new mock block auctioneer. +func New() *Service { + return &Service{} +} + +// UnblindBlock unblinds the given block. +func (s *Service) UnblindBlock(_ context.Context, + _ *api.VersionedSignedBlindedBeaconBlock, +) ( + *api.VersionedSignedProposal, + error, +) { + return &api.VersionedSignedProposal{}, nil +} diff --git a/services/blockunblinder/service.go b/services/blockunblinder/service.go index 36e5cb7..1dca9a0 100644 --- a/services/blockunblinder/service.go +++ b/services/blockunblinder/service.go @@ -1,4 +1,4 @@ -// Copyright © 2022 Attestant Limited. +// Copyright © 2022, 2024 Attestant Limited. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -17,19 +17,15 @@ import ( "context" "github.com/attestantio/go-eth2-client/api" - "github.com/attestantio/go-eth2-client/spec" ) // Service defines the block unblinder service. -type Service interface{} - -// BlockUnblinder is the interface for unblinding blocks. -type BlockUnblinder interface { +type Service interface { // UnblindBlock unblinds the given block. UnblindBlock(ctx context.Context, block *api.VersionedSignedBlindedBeaconBlock, ) ( - *spec.VersionedSignedBeaconBlock, + *api.VersionedSignedProposal, error, ) } diff --git a/services/daemon/rest/builderbid.go b/services/daemon/rest/builderbid.go index f3547e7..3c097a6 100644 --- a/services/daemon/rest/builderbid.go +++ b/services/daemon/rest/builderbid.go @@ -1,4 +1,4 @@ -// Copyright © 2022, 204 Attestant Limited. +// Copyright © 2022, 2024 Attestant Limited. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -15,11 +15,13 @@ package rest import ( "encoding/hex" + "errors" "fmt" "net/http" "strconv" "strings" + relay "github.com/attestantio/go-block-relay" "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/gorilla/mux" ) @@ -70,9 +72,13 @@ func (s *Service) getBuilderBid(w http.ResponseWriter, r *http.Request) { bid, err := s.builderBidProvider.BuilderBid(r.Context(), slot, parentHash, pubkey) if err != nil { + code := http.StatusInternalServerError + if errors.Is(err, relay.ErrInvalidOptions) { + code = http.StatusBadRequest + } s.log.Error().Err(err).Msg("Failed to obtain bid") s.sendResponse(w, http.StatusInternalServerError, &APIResponse{ - Code: http.StatusInternalServerError, + Code: code, Message: "Failed to obtain bid", }) monitorRequestHandled("builder bid", "failure") diff --git a/services/daemon/rest/parameters.go b/services/daemon/rest/parameters.go index ea36352..54fbc75 100644 --- a/services/daemon/rest/parameters.go +++ b/services/daemon/rest/parameters.go @@ -1,4 +1,4 @@ -// Copyright © 2022 Attestant Limited. +// Copyright © 2022, 2024 Attestant Limited. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -17,6 +17,7 @@ import ( "errors" "github.com/attestantio/go-block-relay/services/blockauctioneer" + "github.com/attestantio/go-block-relay/services/blockunblinder" "github.com/attestantio/go-block-relay/services/builderbidprovider" "github.com/attestantio/go-block-relay/services/metrics" nullmetrics "github.com/attestantio/go-block-relay/services/metrics/null" @@ -32,6 +33,7 @@ type parameters struct { validatorRegistrar validatorregistrar.Service blockAuctioneer blockauctioneer.Service builderBidProvider builderbidprovider.Service + blockUnblinder blockunblinder.Service } // Parameter is the interface for service parameters. @@ -94,6 +96,13 @@ func WithBlockAuctioneer(blockAuctioneer blockauctioneer.Service) Parameter { }) } +// WithBlockUnblinder sets the block unblinder. +func WithBlockUnblinder(blockUnblinder blockunblinder.Service) Parameter { + return parameterFunc(func(p *parameters) { + p.blockUnblinder = blockUnblinder + }) +} + // parseAndCheckParameters parses and checks parameters to ensure that mandatory parameters are present and correct. func parseAndCheckParameters(params ...Parameter) (*parameters, error) { parameters := parameters{ @@ -122,6 +131,9 @@ func parseAndCheckParameters(params ...Parameter) (*parameters, error) { if parameters.blockAuctioneer == nil { return nil, errors.New("no block auctioneer specified") } + if parameters.blockUnblinder == nil { + return nil, errors.New("no block unblinder specified") + } if parameters.builderBidProvider == nil { return nil, errors.New("no builder bid provider specified") } diff --git a/services/daemon/rest/service.go b/services/daemon/rest/service.go index b340b0c..86447d4 100644 --- a/services/daemon/rest/service.go +++ b/services/daemon/rest/service.go @@ -1,4 +1,4 @@ -// Copyright © 2022 Attestant Limited. +// Copyright © 2022, 2024 Attestant Limited. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -22,6 +22,7 @@ import ( "time" "github.com/attestantio/go-block-relay/loggers" + "github.com/attestantio/go-block-relay/services/blockunblinder" "github.com/attestantio/go-block-relay/services/builderbidprovider" "github.com/attestantio/go-block-relay/services/validatorregistrar" "github.com/gin-gonic/gin" @@ -37,6 +38,7 @@ type Service struct { srv *http.Server validatorRegistrar validatorregistrar.Service builderBidProvider builderbidprovider.Service + blockUnblinder blockunblinder.Service } // New creates a new REST daemon service. @@ -60,6 +62,7 @@ func New(ctx context.Context, params ...Parameter) (*Service, error) { log: log, validatorRegistrar: parameters.validatorRegistrar, builderBidProvider: parameters.builderBidProvider, + blockUnblinder: parameters.blockUnblinder, } if err := s.startServer(ctx, parameters.serverName, parameters.listenAddress); err != nil { @@ -88,6 +91,8 @@ func (s *Service) startServer(ctx context.Context, router.HandleFunc("/eth/v1/builder/validators", s.postValidatorRegistrations).Methods("POST") router.HandleFunc("/eth/v1/builder/header/{slot}/{parenthash}/{pubkey}", s.getBuilderBid).Methods("GET") router.HandleFunc("/eth/v1/builder/status", s.getStatus).Methods("GET") + router.HandleFunc("/eth/v1/builder/blinded_blocks", s.postUnblindBlock).Methods("POST") + router.PathPrefix("/").Handler(s) s.srv = &http.Server{ Addr: listenAddress, @@ -180,3 +185,9 @@ func (s *Service) sigloop(ctx context.Context) { } } } + +func (s *Service) ServeHTTP(w http.ResponseWriter, r *http.Request) { + s.log.Debug().Str("method", r.Method).Stringer("url", r.URL).Msg("Unhandled request") + + w.WriteHeader(http.StatusNotFound) +} diff --git a/services/daemon/rest/service_test.go b/services/daemon/rest/service_test.go index 3bb4c48..614c3ec 100644 --- a/services/daemon/rest/service_test.go +++ b/services/daemon/rest/service_test.go @@ -1,4 +1,4 @@ -// Copyright © 2022 Attestant Limited. +// Copyright © 2022, 2024 Attestant Limited. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -19,6 +19,7 @@ import ( "time" mockauctioneer "github.com/attestantio/go-block-relay/services/blockauctioneer/mock" + mockblockunblinder "github.com/attestantio/go-block-relay/services/blockunblinder/mock" mockbuilderbidprovider "github.com/attestantio/go-block-relay/services/builderbidprovider/mock" restdaemon "github.com/attestantio/go-block-relay/services/daemon/rest" nullmetrics "github.com/attestantio/go-block-relay/services/metrics/null" @@ -33,6 +34,7 @@ func TestService(t *testing.T) { registrar := mockregistrar.New() auctioneer := mockauctioneer.New() monitor := nullmetrics.New() + unblinder := mockblockunblinder.New() builderBidProvider := mockbuilderbidprovider.New() tests := []struct { @@ -48,6 +50,7 @@ func TestService(t *testing.T) { restdaemon.WithListenAddress(":14734"), restdaemon.WithValidatorRegistrar(registrar), restdaemon.WithBlockAuctioneer(auctioneer), + restdaemon.WithBlockUnblinder(unblinder), restdaemon.WithBuilderBidProvider(builderBidProvider), }, err: "problem with parameters: no monitor specified", @@ -59,6 +62,7 @@ func TestService(t *testing.T) { restdaemon.WithMonitor(monitor), restdaemon.WithValidatorRegistrar(registrar), restdaemon.WithBlockAuctioneer(auctioneer), + restdaemon.WithBlockUnblinder(unblinder), restdaemon.WithBuilderBidProvider(builderBidProvider), }, err: "problem with parameters: no listen address specified", @@ -70,6 +74,7 @@ func TestService(t *testing.T) { restdaemon.WithMonitor(monitor), restdaemon.WithListenAddress(":14734"), restdaemon.WithBlockAuctioneer(auctioneer), + restdaemon.WithBlockUnblinder(unblinder), restdaemon.WithBuilderBidProvider(builderBidProvider), }, err: "problem with parameters: no validator registrar specified", @@ -81,6 +86,7 @@ func TestService(t *testing.T) { restdaemon.WithMonitor(monitor), restdaemon.WithListenAddress(":14734"), restdaemon.WithValidatorRegistrar(registrar), + restdaemon.WithBlockUnblinder(unblinder), restdaemon.WithBuilderBidProvider(builderBidProvider), }, err: "problem with parameters: no block auctioneer specified", @@ -93,9 +99,22 @@ func TestService(t *testing.T) { restdaemon.WithListenAddress(":14734"), restdaemon.WithValidatorRegistrar(registrar), restdaemon.WithBlockAuctioneer(auctioneer), + restdaemon.WithBlockUnblinder(unblinder), }, err: "problem with parameters: no builder bid provider specified", }, + { + name: "BlockUnblindedMissing", + params: []restdaemon.Parameter{ + restdaemon.WithLogLevel(zerolog.Disabled), + restdaemon.WithMonitor(monitor), + restdaemon.WithListenAddress(":14734"), + restdaemon.WithValidatorRegistrar(registrar), + restdaemon.WithBlockAuctioneer(auctioneer), + restdaemon.WithBuilderBidProvider(builderBidProvider), + }, + err: "problem with parameters: no block unblinder specified", + }, { name: "Good", params: []restdaemon.Parameter{ @@ -104,6 +123,7 @@ func TestService(t *testing.T) { restdaemon.WithListenAddress(":14734"), restdaemon.WithValidatorRegistrar(registrar), restdaemon.WithBlockAuctioneer(auctioneer), + restdaemon.WithBlockUnblinder(unblinder), restdaemon.WithBuilderBidProvider(builderBidProvider), }, }, @@ -125,6 +145,7 @@ func TestShutdown(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) registrar := mockregistrar.New() auctioneer := mockauctioneer.New() + unblinder := mockblockunblinder.New() monitor := nullmetrics.New() builderBidProvider := mockbuilderbidprovider.New() @@ -134,6 +155,7 @@ func TestShutdown(t *testing.T) { restdaemon.WithListenAddress(":14734"), restdaemon.WithValidatorRegistrar(registrar), restdaemon.WithBlockAuctioneer(auctioneer), + restdaemon.WithBlockUnblinder(unblinder), restdaemon.WithBuilderBidProvider(builderBidProvider), ) require.NoError(t, err) diff --git a/services/daemon/rest/unblindblock.go b/services/daemon/rest/unblindblock.go new file mode 100644 index 0000000..7cb1316 --- /dev/null +++ b/services/daemon/rest/unblindblock.go @@ -0,0 +1,267 @@ +// Copyright © 2024 Attestant Limited. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package rest + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "strings" + + relay "github.com/attestantio/go-block-relay" + "github.com/attestantio/go-eth2-client/api" + apiv1bellatrix "github.com/attestantio/go-eth2-client/api/v1/bellatrix" + apiv1capella "github.com/attestantio/go-eth2-client/api/v1/capella" + apiv1deneb "github.com/attestantio/go-eth2-client/api/v1/deneb" + "github.com/attestantio/go-eth2-client/spec" + "github.com/attestantio/go-eth2-client/spec/deneb" + "github.com/pkg/errors" +) + +func (s *Service) postUnblindBlock(w http.ResponseWriter, r *http.Request) { + s.log.Trace().Msg("unblindBlock called") + ctx := r.Context() + + signedBlindedBeaconBlock, err := s.obtainUnblindedBlock(ctx, r) + if err != nil { + s.log.Error().Err(err).Msg("Supplied with invalid block") + s.sendResponse(w, http.StatusInternalServerError, &APIResponse{ + Code: http.StatusBadRequest, + Message: "Invalid blinded block", + }) + monitorRequestHandled("unblind block", "failure") + + return + } + + signedProposal, err := s.blockUnblinder.UnblindBlock(r.Context(), signedBlindedBeaconBlock) + if err != nil { + code := http.StatusInternalServerError + if errors.Is(err, relay.ErrInvalidOptions) { + code = http.StatusBadRequest + } + s.log.Error().Err(err).Msg("Failed to unblind block") + s.sendResponse(w, http.StatusInternalServerError, &APIResponse{ + Code: code, + Message: "Failed to unblind block", + }) + monitorRequestHandled("unblind block", "failure") + + return + } + + if signedProposal == nil { + s.sendResponse(w, http.StatusNoContent, nil) + monitorRequestHandled("unblind block", "success") + + return + } + + data, err := s.outputUnblindedBlock(ctx, signedProposal) + if err != nil { + s.log.Error().Err(err).Msg("Failed to generate output") + s.sendResponse(w, http.StatusInternalServerError, &APIResponse{ + Code: http.StatusInternalServerError, + Message: "Failed to unblind block", + }) + monitorRequestHandled("unblind block", "failure") + + return + } + + monitorRequestHandled("unblind block", "success") + s.sendResponse(w, http.StatusOK, data) +} + +func (s *Service) obtainUnblindedBlock(ctx context.Context, + r *http.Request, +) ( + *api.VersionedSignedBlindedBeaconBlock, + error, +) { + // Obtain the content type so we know what we have to unmarshal. + contentTypes, exists := r.Header["Content-Type"] + var contentType string + if !exists || len(contentTypes) == 0 { + s.log.Debug().Msg("No content type header; assuming JSON") + contentType = "application/json" + } else { + contentType = contentTypes[0] + } + + for k, v := range r.Header { + s.log.Info().Str("key", k).Strs("values", v).Msg("Header") + } + + // Obtain the consensus version so we know what we have to unmarshal to. + consensusVersions, exists := r.Header["Eth-Consensus-Version"] + var consensusVersion string + if !exists || len(consensusVersions) == 0 { + // Various consensus clients do not provide this header. Remove + // assumption when they do. + s.log.Debug().Msg("No consensus version header; assuming deneb") + consensusVersion = "deneb" + // s.log.Error().Msg("No Eth-Consensus-Version header") + // s.sendResponse(w, http.StatusInternalServerError, &APIResponse{ + // Code: http.StatusBadRequest, + // Message: "No Eth-Consensus-Version header", + // }) + // monitorRequestHandled("unblind block", "failure") + // + // return + } else { + consensusVersion = consensusVersions[0] + } + + signedBlindedBeaconBlock, err := s.unmarshalBlindedBlock(ctx, contentType, consensusVersion, r) + if err != nil { + return nil, err + } + + return signedBlindedBeaconBlock, nil +} + +func (s *Service) unmarshalBlindedBlock(ctx context.Context, + contentType string, + consensusVersion string, + r *http.Request, +) ( + *api.VersionedSignedBlindedBeaconBlock, + error, +) { + signedBlindedBeaconBlock := &api.VersionedSignedBlindedBeaconBlock{} + + switch strings.ToLower(consensusVersion) { + case "bellatrix": + signedBlindedBeaconBlock.Version = spec.DataVersionBellatrix + signedBlindedBeaconBlock.Bellatrix = &apiv1bellatrix.SignedBlindedBeaconBlock{} + case "capella": + signedBlindedBeaconBlock.Version = spec.DataVersionCapella + signedBlindedBeaconBlock.Capella = &apiv1capella.SignedBlindedBeaconBlock{} + case "deneb": + signedBlindedBeaconBlock.Version = spec.DataVersionDeneb + signedBlindedBeaconBlock.Deneb = &apiv1deneb.SignedBlindedBeaconBlock{} + default: + return nil, fmt.Errorf("unknown block version %v", consensusVersion) + } + + switch strings.ToLower(contentType) { + case "application/octet-stream": + return s.unmarshalBlindedBlockSSZ(ctx, signedBlindedBeaconBlock, r.Body) + case "application/json": + return s.unmarshalBlindedBlockJSON(ctx, signedBlindedBeaconBlock, r.Body) + default: + return nil, fmt.Errorf("unsupported content type %s", contentType) + } +} + +func (s *Service) unmarshalBlindedBlockJSON(_ context.Context, + signedBlindedBeaconBlock *api.VersionedSignedBlindedBeaconBlock, + body io.Reader, +) ( + *api.VersionedSignedBlindedBeaconBlock, + error, +) { + var err error + + switch signedBlindedBeaconBlock.Version { + case spec.DataVersionBellatrix: + err = json.NewDecoder(body).Decode(signedBlindedBeaconBlock.Bellatrix) + case spec.DataVersionCapella: + err = json.NewDecoder(body).Decode(signedBlindedBeaconBlock.Capella) + case spec.DataVersionDeneb: + err = json.NewDecoder(body).Decode(signedBlindedBeaconBlock.Deneb) + default: + err = fmt.Errorf("unsupported block version %v", signedBlindedBeaconBlock.Version) + } + + if err != nil { + return nil, err + } + + return signedBlindedBeaconBlock, nil +} + +func (s *Service) unmarshalBlindedBlockSSZ(_ context.Context, + signedBlindedBeaconBlock *api.VersionedSignedBlindedBeaconBlock, + body io.Reader, +) ( + *api.VersionedSignedBlindedBeaconBlock, + error, +) { + data, err := io.ReadAll(body) + if err != nil { + return nil, errors.Wrap(err, "failed to read body") + } + + switch signedBlindedBeaconBlock.Version { + case spec.DataVersionBellatrix: + err = signedBlindedBeaconBlock.Bellatrix.UnmarshalSSZ(data) + case spec.DataVersionCapella: + err = signedBlindedBeaconBlock.Capella.UnmarshalSSZ(data) + case spec.DataVersionDeneb: + err = signedBlindedBeaconBlock.Deneb.UnmarshalSSZ(data) + default: + err = fmt.Errorf("unsupported block version %v", signedBlindedBeaconBlock.Version) + } + + if err != nil { + return nil, err + } + + return signedBlindedBeaconBlock, nil +} + +type unblindBlockResponse struct { + Version spec.DataVersion `json:"version"` + Data *unblindBlockResponseData `json:"data"` +} + +type unblindBlockResponseData struct { + ExecutionPayload *deneb.ExecutionPayload `json:"execution_payload"` + BlobsBundle *unblindBlockResponseBlobsBundle `json:"blobs_bundle"` +} + +type unblindBlockResponseBlobsBundle struct { + Commitments []deneb.KZGCommitment `json:"commitments"` + Proofs []deneb.KZGProof `json:"proofs"` + Blobs []deneb.Blob `json:"blobs"` +} + +func (s *Service) outputUnblindedBlock(_ context.Context, + proposal *api.VersionedSignedProposal, +) ( + *unblindBlockResponse, + error, +) { + resp := &unblindBlockResponse{} + resp.Version = proposal.Version + switch resp.Version { + case spec.DataVersionDeneb: + resp.Data = &unblindBlockResponseData{ + ExecutionPayload: proposal.Deneb.SignedBlock.Message.Body.ExecutionPayload, + BlobsBundle: &unblindBlockResponseBlobsBundle{ + Commitments: proposal.Deneb.SignedBlock.Message.Body.BlobKZGCommitments, + Proofs: proposal.Deneb.KZGProofs, + Blobs: proposal.Deneb.Blobs, + }, + } + default: + return nil, fmt.Errorf("unsupported version %v", resp.Version) + } + + return resp, nil +} diff --git a/services/daemon/rest/validatorregistrations.go b/services/daemon/rest/validatorregistrations.go index b385cca..b8f0f12 100644 --- a/services/daemon/rest/validatorregistrations.go +++ b/services/daemon/rest/validatorregistrations.go @@ -1,4 +1,4 @@ -// Copyright © 2022 Attestant Limited. +// Copyright © 2022, 2024 Attestant Limited. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -19,6 +19,7 @@ import ( "net/http" "strings" + relay "github.com/attestantio/go-block-relay" "github.com/attestantio/go-block-relay/services/validatorregistrar" "github.com/attestantio/go-block-relay/types" "github.com/pkg/errors" @@ -73,8 +74,12 @@ func (s *Service) postValidatorRegistrationsPassthrough(ctx context.Context, registrationErrors, err := provider.ValidatorRegistrationsPassthrough(ctx, r.Body) if err != nil { s.log.Error().Err(err).Msg("Failed to register validators with passthrough") + code := http.StatusInternalServerError + if errors.Is(err, relay.ErrInvalidOptions) { + code = http.StatusBadRequest + } - return http.StatusInternalServerError, nil, errors.New("failed to register validators") + return code, nil, errors.New("failed to register validators") } return http.StatusOK, registrationErrors, nil @@ -99,8 +104,12 @@ func (s *Service) postValidatorRegistrationsHandler(ctx context.Context, registrationErrors, err := provider.ValidatorRegistrations(ctx, registrations) if err != nil { s.log.Error().Err(err).Msg("Failed to register validators") + code := http.StatusInternalServerError + if errors.Is(err, relay.ErrInvalidOptions) { + code = http.StatusBadRequest + } - return http.StatusInternalServerError, nil, errors.Wrap(err, "failed to register validators") + return code, nil, errors.Wrap(err, "failed to register validators") } return http.StatusOK, registrationErrors, nil diff --git a/services/daemon/rest/validatorregistrations_internal_test.go b/services/daemon/rest/validatorregistrations_internal_test.go index c7990c9..58ba3ff 100644 --- a/services/daemon/rest/validatorregistrations_internal_test.go +++ b/services/daemon/rest/validatorregistrations_internal_test.go @@ -20,6 +20,7 @@ import ( "testing" mockauctioneer "github.com/attestantio/go-block-relay/services/blockauctioneer/mock" + mockblockunblinder "github.com/attestantio/go-block-relay/services/blockunblinder/mock" mockbuilderbidprovider "github.com/attestantio/go-block-relay/services/builderbidprovider/mock" nullmetrics "github.com/attestantio/go-block-relay/services/metrics/null" mockvalidatorregistrar "github.com/attestantio/go-block-relay/services/validatorregistrar/mock" @@ -32,6 +33,7 @@ func TestValidatorRegistrations(t *testing.T) { registrar := mockvalidatorregistrar.New() auctioneer := mockauctioneer.New() + unblinder := mockblockunblinder.New() monitor := nullmetrics.New() builderBidProvider := mockbuilderbidprovider.New() @@ -42,6 +44,7 @@ func TestValidatorRegistrations(t *testing.T) { WithListenAddress(":14734"), WithValidatorRegistrar(registrar), WithBlockAuctioneer(auctioneer), + WithBlockUnblinder(unblinder), WithBuilderBidProvider(builderBidProvider), ) require.NoError(t, err) @@ -55,6 +58,7 @@ func TestValidatorRegistrations(t *testing.T) { WithValidatorRegistrar(registrar), WithValidatorRegistrar(erroringRegistrar), WithBlockAuctioneer(auctioneer), + WithBlockUnblinder(unblinder), WithBuilderBidProvider(builderBidProvider), ) require.NoError(t, err)