From d30336c79231f7242e190ec890e6c3767d56c0db Mon Sep 17 00:00:00 2001 From: Kasey Kirkham Date: Tue, 30 Apr 2024 20:06:34 -0500 Subject: [PATCH] json get block handler refactor --- api/server/structs/BUILD.bazel | 2 + api/server/structs/block.go | 93 ++++ api/server/structs/conversions_block.go | 33 +- beacon-chain/rpc/eth/beacon/handlers.go | 543 +++--------------------- 4 files changed, 180 insertions(+), 491 deletions(-) diff --git a/api/server/structs/BUILD.bazel b/api/server/structs/BUILD.bazel index 7727a44f1b89..f266f16680ee 100644 --- a/api/server/structs/BUILD.bazel +++ b/api/server/structs/BUILD.bazel @@ -26,6 +26,8 @@ go_library( "//api/server:go_default_library", "//beacon-chain/state:go_default_library", "//config/fieldparams:go_default_library", + "//consensus-types/blocks:go_default_library", + "//consensus-types/interfaces:go_default_library", "//consensus-types/primitives:go_default_library", "//consensus-types/validator:go_default_library", "//container/slice:go_default_library", diff --git a/api/server/structs/block.go b/api/server/structs/block.go index b5cfc1f27d1b..6f5b2a870f2b 100644 --- a/api/server/structs/block.go +++ b/api/server/structs/block.go @@ -1,10 +1,33 @@ package structs +import "encoding/json" + +// MessageJsoner describes a signed consensus type wrapper that can return the `.Message` field in a json envelope +// encoded as a []byte, for use as a json.RawMessage value when encoding the outer envelope. +type MessageJsoner interface { + MessageRawJson() ([]byte, error) +} + +type SignedMessageJsoner interface { + MessageJsoner + SigString() string +} + type SignedBeaconBlock struct { Message *BeaconBlock `json:"message"` Signature string `json:"signature"` } +var _ SignedMessageJsoner = &SignedBeaconBlock{} + +func (s *SignedBeaconBlock) MessageRawJson() ([]byte, error) { + return json.Marshal(s.Message) +} + +func (s *SignedBeaconBlock) SigString() string { + return s.Signature +} + type BeaconBlock struct { Slot string `json:"slot"` ProposerIndex string `json:"proposer_index"` @@ -29,6 +52,16 @@ type SignedBeaconBlockAltair struct { Signature string `json:"signature"` } +var _ SignedMessageJsoner = &SignedBeaconBlockAltair{} + +func (s *SignedBeaconBlockAltair) MessageRawJson() ([]byte, error) { + return json.Marshal(s.Message) +} + +func (s *SignedBeaconBlockAltair) SigString() string { + return s.Signature +} + type BeaconBlockAltair struct { Slot string `json:"slot"` ProposerIndex string `json:"proposer_index"` @@ -54,6 +87,16 @@ type SignedBeaconBlockBellatrix struct { Signature string `json:"signature"` } +var _ SignedMessageJsoner = &SignedBeaconBlockBellatrix{} + +func (s *SignedBeaconBlockBellatrix) MessageRawJson() ([]byte, error) { + return json.Marshal(s.Message) +} + +func (s *SignedBeaconBlockBellatrix) SigString() string { + return s.Signature +} + type BeaconBlockBellatrix struct { Slot string `json:"slot"` ProposerIndex string `json:"proposer_index"` @@ -80,6 +123,16 @@ type SignedBlindedBeaconBlockBellatrix struct { Signature string `json:"signature"` } +var _ SignedMessageJsoner = &SignedBlindedBeaconBlockBellatrix{} + +func (s *SignedBlindedBeaconBlockBellatrix) MessageRawJson() ([]byte, error) { + return json.Marshal(s.Message) +} + +func (s *SignedBlindedBeaconBlockBellatrix) SigString() string { + return s.Signature +} + type BlindedBeaconBlockBellatrix struct { Slot string `json:"slot"` ProposerIndex string `json:"proposer_index"` @@ -106,6 +159,16 @@ type SignedBeaconBlockCapella struct { Signature string `json:"signature"` } +var _ SignedMessageJsoner = &SignedBeaconBlockCapella{} + +func (s *SignedBeaconBlockCapella) MessageRawJson() ([]byte, error) { + return json.Marshal(s.Message) +} + +func (s *SignedBeaconBlockCapella) SigString() string { + return s.Signature +} + type BeaconBlockCapella struct { Slot string `json:"slot"` ProposerIndex string `json:"proposer_index"` @@ -133,6 +196,16 @@ type SignedBlindedBeaconBlockCapella struct { Signature string `json:"signature"` } +var _ SignedMessageJsoner = &SignedBlindedBeaconBlockCapella{} + +func (s *SignedBlindedBeaconBlockCapella) MessageRawJson() ([]byte, error) { + return json.Marshal(s.Message) +} + +func (s *SignedBlindedBeaconBlockCapella) SigString() string { + return s.Signature +} + type BlindedBeaconBlockCapella struct { Slot string `json:"slot"` ProposerIndex string `json:"proposer_index"` @@ -172,6 +245,16 @@ type SignedBeaconBlockDeneb struct { Signature string `json:"signature"` } +var _ SignedMessageJsoner = &SignedBeaconBlockDeneb{} + +func (s *SignedBeaconBlockDeneb) MessageRawJson() ([]byte, error) { + return json.Marshal(s.Message) +} + +func (s *SignedBeaconBlockDeneb) SigString() string { + return s.Signature +} + type BeaconBlockDeneb struct { Slot string `json:"slot"` ProposerIndex string `json:"proposer_index"` @@ -208,6 +291,16 @@ type SignedBlindedBeaconBlockDeneb struct { Signature string `json:"signature"` } +var _ SignedMessageJsoner = &SignedBlindedBeaconBlockDeneb{} + +func (s *SignedBlindedBeaconBlockDeneb) MessageRawJson() ([]byte, error) { + return json.Marshal(s.Message) +} + +func (s *SignedBlindedBeaconBlockDeneb) SigString() string { + return s.Signature +} + type BlindedBeaconBlockBodyDeneb struct { RandaoReveal string `json:"randao_reveal"` Eth1Data *Eth1Data `json:"eth1_data"` diff --git a/api/server/structs/conversions_block.go b/api/server/structs/conversions_block.go index 4349d7d46ddc..6a3f4e912997 100644 --- a/api/server/structs/conversions_block.go +++ b/api/server/structs/conversions_block.go @@ -6,8 +6,10 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v5/api/server" fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" + "github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v5/container/slice" "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" @@ -15,6 +17,8 @@ import ( eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" ) +var ErrUnsupportedConversion = errors.New("Could not determine api struct type to use for value") + func (h *SignedBeaconBlockHeader) ToConsensus() (*eth.SignedBeaconBlockHeader, error) { msg, err := h.Message.ToConsensus() if err != nil { @@ -1852,7 +1856,34 @@ func BeaconBlockFromConsensus(b *eth.BeaconBlock) *BeaconBlock { } } -func SignedBeaconBlockFromConsensus(b *eth.SignedBeaconBlock) *SignedBeaconBlock { +func SignedBeaconBlockMessageJsoner(block interfaces.ReadOnlySignedBeaconBlock) (SignedMessageJsoner, error) { + pb, err := block.Proto() + if err != nil { + return nil, err + } + switch pbStruct := pb.(type) { + case *eth.SignedBeaconBlock: + return SignedBeaconBlockPhase0FromConsensus(pbStruct), nil + case *eth.SignedBeaconBlockAltair: + return SignedBeaconBlockAltairFromConsensus(pbStruct), nil + case *eth.SignedBlindedBeaconBlockBellatrix: + return SignedBlindedBeaconBlockBellatrixFromConsensus(pbStruct) + case *eth.SignedBeaconBlockBellatrix: + return SignedBeaconBlockBellatrixFromConsensus(pbStruct) + case *eth.SignedBlindedBeaconBlockCapella: + return SignedBlindedBeaconBlockCapellaFromConsensus(pbStruct) + case *eth.SignedBeaconBlockCapella: + return SignedBeaconBlockCapellaFromConsensus(pbStruct) + case *eth.SignedBlindedBeaconBlockDeneb: + return SignedBlindedBeaconBlockDenebFromConsensus(pbStruct) + case *eth.SignedBeaconBlockDeneb: + return SignedBeaconBlockDenebFromConsensus(pbStruct) + default: + return nil, ErrUnsupportedConversion + } +} + +func SignedBeaconBlockPhase0FromConsensus(b *eth.SignedBeaconBlock) *SignedBeaconBlock { return &SignedBeaconBlock{ Message: BeaconBlockFromConsensus(b.Block), Signature: hexutil.Encode(b.Signature), diff --git a/beacon-chain/rpc/eth/beacon/handlers.go b/beacon-chain/rpc/eth/beacon/handlers.go index ee8a2698888c..ca2775052e2d 100644 --- a/beacon-chain/rpc/eth/beacon/handlers.go +++ b/beacon-chain/rpc/eth/beacon/handlers.go @@ -25,7 +25,6 @@ import ( "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/prysm/v1alpha1/validator" fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" "github.com/prysmaticlabs/prysm/v5/config/params" - consensus_types "github.com/prysmaticlabs/prysm/v5/consensus-types" "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" "github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" @@ -50,8 +49,6 @@ var ( errMarshalSSZ = errors.New("could not marshal block into SSZ") ) -type handled bool - // GetBlockV2 retrieves block details for given block ID. func (s *Server) GetBlockV2(w http.ResponseWriter, r *http.Request) { ctx, span := trace.StartSpan(r.Context(), "beacon.GetBlockV2") @@ -63,84 +60,25 @@ func (s *Server) GetBlockV2(w http.ResponseWriter, r *http.Request) { return } blk, err := s.Blocker.Block(ctx, []byte(blockId)) - if !shared.WriteBlockFetchError(w, blk, err) { - return - } - - if httputil.RespondWithSsz(r) { - s.getBlockSSZV2(ctx, w, blk) - } else { - s.getBlockV2(ctx, w, blk) - } -} - -// getBlockV2 returns the JSON-serialized version of the beacon block for given block ID. -func (s *Server) getBlockV2(ctx context.Context, w http.ResponseWriter, blk interfaces.ReadOnlySignedBeaconBlock) { - blkRoot, err := blk.Block().HashTreeRoot() if err != nil { - httputil.HandleError(w, "Could not get block root "+err.Error(), http.StatusInternalServerError) + shared.WriteBlockFetchError(w, blk, err) return } - finalized := s.FinalizationFetcher.IsFinalized(ctx, blkRoot) - getBlockHandler := func(get func(ctx context.Context, block interfaces.ReadOnlySignedBeaconBlock) (*structs.GetBlockV2Response, error)) handled { - result, err := get(ctx, blk) - if result != nil { - result.Finalized = finalized - w.Header().Set(api.VersionHeader, result.Version) - httputil.WriteJson(w, result) - return true - } - // ErrUnsupportedField means that we have another block type - if !errors.Is(err, consensus_types.ErrUnsupportedField) { - httputil.HandleError(w, "Could not get signed beacon block: "+err.Error(), http.StatusInternalServerError) - return true + // Deal with block unblinding. + if blk.Version() >= version.Bellatrix && blk.IsBlinded() { + blk, err = s.ExecutionPayloadReconstructor.ReconstructFullBlock(ctx, blk) + if err != nil { + shared.WriteBlockFetchError(w, blk, errors.Wrapf(err, "could not reconstruct full execution payload to create signed beacon block")) + return } - return false - } - - if getBlockHandler(s.getBlockDeneb) { - return - } - if getBlockHandler(s.getBlockCapella) { - return - } - if getBlockHandler(s.getBlockBellatrix) { - return } - if getBlockHandler(s.getBlockAltair) { - return - } - if getBlockHandler(s.getBlockPhase0) { - return - } - httputil.HandleError(w, fmt.Sprintf("Unknown block type %T", blk), http.StatusInternalServerError) -} - -// getBlockSSZV2 returns the SSZ-serialized version of the beacon block for given block ID. -func (s *Server) getBlockSSZV2(ctx context.Context, w http.ResponseWriter, blk interfaces.ReadOnlySignedBeaconBlock) { - result, err := s.getUnblindedBlockRespSSZ(ctx, blk) - if err != nil { - httputil.HandleError(w, "Could not get signed beacon block: "+err.Error(), http.StatusInternalServerError) - } - if result == nil { - httputil.HandleError(w, fmt.Sprintf("Unknown block type %T", blk), http.StatusInternalServerError) - } - w.Header().Set(api.VersionHeader, version.String(blk.Version())) - httputil.WriteSsz(w, result, "beacon_block.ssz") -} -// getBlindedBlockSSZ returns the SSZ-serialized version of the blinded beacon block for given block id. -func (s *Server) getBlindedBlockSSZ(w http.ResponseWriter, blk interfaces.ReadOnlySignedBeaconBlock) { - result, err := s.getBlindedBlockRespSSZ(blk) - if err != nil { - httputil.HandleError(w, "Could not get signed beacon block: "+err.Error(), http.StatusInternalServerError) - } - if result == nil { - httputil.HandleError(w, fmt.Sprintf("Unknown block type %T", blk), http.StatusInternalServerError) + if httputil.RespondWithSsz(r) { + s.getBlockV2Ssz(w, blk) + } else { + s.getBlockV2Json(ctx, w, blk) } - w.Header().Set(api.VersionHeader, version.String(blk.Version())) - httputil.WriteSsz(w, result, "beacon_block.ssz") } // GetBlindedBlock retrieves blinded block for given block id. @@ -158,295 +96,36 @@ func (s *Server) GetBlindedBlock(w http.ResponseWriter, r *http.Request) { return } - if httputil.RespondWithSsz(r) { - s.getBlindedBlockSSZ(w, blk) - } else { - s.getBlindedBlock(ctx, w, blk) - } -} - -// getBlindedBlock returns the JSON-serialized version of the blinded beacon block for given block id. -func (s *Server) getBlindedBlock(ctx context.Context, w http.ResponseWriter, blk interfaces.ReadOnlySignedBeaconBlock) { - blkRoot, err := blk.Block().HashTreeRoot() - if err != nil { - httputil.HandleError(w, "Could not get block root "+err.Error(), http.StatusInternalServerError) - return - } - finalized := s.FinalizationFetcher.IsFinalized(ctx, blkRoot) - - getBlockHandler := func(get func(ctx context.Context, block interfaces.ReadOnlySignedBeaconBlock) (*structs.GetBlockV2Response, error)) handled { - result, err := get(ctx, blk) - if result != nil { - result.Finalized = finalized - w.Header().Set(api.VersionHeader, result.Version) - httputil.WriteJson(w, result) - return true - } - // ErrUnsupportedField means that we have another block type - if !errors.Is(err, consensus_types.ErrUnsupportedField) { - httputil.HandleError(w, "Could not get signed beacon block: "+err.Error(), http.StatusInternalServerError) - return true - } - return false - } - - if getBlockHandler(s.getBlockPhase0) { - return - } - if getBlockHandler(s.getBlockAltair) { - return - } - if getBlockHandler(s.getBlindedBlockBellatrix) { - return - } - if getBlockHandler(s.getBlindedBlockCapella) { - return - } - if getBlockHandler(s.getBlindedBlockDeneb) { - return - } - httputil.HandleError(w, fmt.Sprintf("Unknown block type %T", blk), http.StatusInternalServerError) -} - -func (*Server) getBlockPhase0(_ context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (*structs.GetBlockV2Response, error) { - consensusBlk, err := blk.PbPhase0Block() - if err != nil { - return nil, err - } - if consensusBlk == nil { - return nil, errNilBlock - } - respBlk := structs.SignedBeaconBlockFromConsensus(consensusBlk) - jsonBytes, err := json.Marshal(respBlk.Message) - if err != nil { - return nil, err - } - return &structs.GetBlockV2Response{ - Version: version.String(version.Phase0), - ExecutionOptimistic: false, - Data: &structs.SignedBlock{ - Message: jsonBytes, - Signature: respBlk.Signature, - }, - }, nil -} - -func (*Server) getBlockAltair(_ context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (*structs.GetBlockV2Response, error) { - consensusBlk, err := blk.PbAltairBlock() - if err != nil { - return nil, err - } - if consensusBlk == nil { - return nil, errNilBlock - } - respBlk := structs.SignedBeaconBlockAltairFromConsensus(consensusBlk) - jsonBytes, err := json.Marshal(respBlk.Message) - if err != nil { - return nil, err - } - return &structs.GetBlockV2Response{ - Version: version.String(version.Altair), - ExecutionOptimistic: false, - Data: &structs.SignedBlock{ - Message: jsonBytes, - Signature: respBlk.Signature, - }, - }, nil -} - -func (s *Server) getBlockBellatrix(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (*structs.GetBlockV2Response, error) { - consensusBlk, err := blk.PbBellatrixBlock() - if err != nil { - // ErrUnsupportedField means that we have another block type - if errors.Is(err, consensus_types.ErrUnsupportedField) { - blindedConsensusBlk, err := blk.PbBlindedBellatrixBlock() - if err != nil { - return nil, err - } - if blindedConsensusBlk == nil { - return nil, errNilBlock - } - fullBlk, err := s.ExecutionPayloadReconstructor.ReconstructFullBlock(ctx, blk) - if err != nil { - return nil, errors.Wrapf(err, "could not reconstruct full execution payload to create signed beacon block") - } - consensusBlk, err = fullBlk.PbBellatrixBlock() - if err != nil { - return nil, errors.Wrapf(err, "could not get signed beacon block") - } - } else { - return nil, err - } - } - - if consensusBlk == nil { - return nil, errNilBlock - } - root, err := blk.Block().HashTreeRoot() - if err != nil { - return nil, errors.Wrapf(err, "could not get block root") - } - isOptimistic, err := s.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root) - if err != nil { - return nil, errors.Wrapf(err, "could not check if block is optimistic") - } - respBlk, err := structs.SignedBeaconBlockBellatrixFromConsensus(consensusBlk) - if err != nil { - return nil, err - } - jsonBytes, err := json.Marshal(respBlk.Message) - if err != nil { - return nil, err - } - return &structs.GetBlockV2Response{ - Version: version.String(version.Bellatrix), - ExecutionOptimistic: isOptimistic, - Data: &structs.SignedBlock{ - Message: jsonBytes, - Signature: respBlk.Signature, - }, - }, nil -} - -func (s *Server) getBlockCapella(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (*structs.GetBlockV2Response, error) { - consensusBlk, err := blk.PbCapellaBlock() - if err != nil { - // ErrUnsupportedField means that we have another block type - if errors.Is(err, consensus_types.ErrUnsupportedField) { - blindedConsensusBlk, err := blk.PbBlindedCapellaBlock() - if err != nil { - return nil, err - } - if blindedConsensusBlk == nil { - return nil, errNilBlock - } - fullBlk, err := s.ExecutionPayloadReconstructor.ReconstructFullBlock(ctx, blk) - if err != nil { - return nil, errors.Wrapf(err, "could not reconstruct full execution payload to create signed beacon block") - } - consensusBlk, err = fullBlk.PbCapellaBlock() - if err != nil { - return nil, errors.Wrapf(err, "could not get signed beacon block") - } - } else { - return nil, err - } - } - - if consensusBlk == nil { - return nil, errNilBlock - } - root, err := blk.Block().HashTreeRoot() - if err != nil { - return nil, errors.Wrapf(err, "could not get block root") - } - isOptimistic, err := s.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root) - if err != nil { - return nil, errors.Wrapf(err, "could not check if block is optimistic") - } - respBlk, err := structs.SignedBeaconBlockCapellaFromConsensus(consensusBlk) - if err != nil { - return nil, err - } - jsonBytes, err := json.Marshal(respBlk.Message) - if err != nil { - return nil, err - } - return &structs.GetBlockV2Response{ - Version: version.String(version.Capella), - ExecutionOptimistic: isOptimistic, - Data: &structs.SignedBlock{ - Message: jsonBytes, - Signature: respBlk.Signature, - }, - }, nil -} - -func (s *Server) getBlockDeneb(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (*structs.GetBlockV2Response, error) { - consensusBlk, err := blk.PbDenebBlock() - if err != nil { - // ErrUnsupportedGetter means that we have another block type - if errors.Is(err, consensus_types.ErrUnsupportedField) { - blindedConsensusBlk, err := blk.PbBlindedDenebBlock() - if err != nil { - return nil, err - } - if blindedConsensusBlk == nil { - return nil, errNilBlock - } - fullBlk, err := s.ExecutionPayloadReconstructor.ReconstructFullBlock(ctx, blk) - if err != nil { - return nil, errors.Wrapf(err, "could not reconstruct full execution payload to create signed beacon block") - } - consensusBlk, err = fullBlk.PbDenebBlock() - if err != nil { - return nil, errors.Wrapf(err, "could not get signed beacon block") - } - } else { - return nil, err + // Convert to blinded block (if it's not already). + if blk.Version() >= version.Bellatrix && !blk.IsBlinded() { + blk, err = blk.ToBlinded() + if err != nil { + shared.WriteBlockFetchError(w, blk, errors.Wrapf(err, "could not convert block to blinded block")) + return } } - if consensusBlk == nil { - return nil, errNilBlock - } - root, err := blk.Block().HashTreeRoot() - if err != nil { - return nil, errors.Wrapf(err, "could not get block root") - } - isOptimistic, err := s.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root) - if err != nil { - return nil, errors.Wrapf(err, "could not check if block is optimistic") - } - respBlk, err := structs.SignedBeaconBlockDenebFromConsensus(consensusBlk) - if err != nil { - return nil, err - } - jsonBytes, err := json.Marshal(respBlk.Message) - if err != nil { - return nil, err - } - return &structs.GetBlockV2Response{ - Version: version.String(version.Deneb), - ExecutionOptimistic: isOptimistic, - Data: &structs.SignedBlock{ - Message: jsonBytes, - Signature: respBlk.Signature, - }, - }, nil -} - -// getUnblindedBlockRespSSZ attempts to reconstruct the full block before calling getBlockRespSSZ to finish the job. -func (s *Server) getUnblindedBlockRespSSZ(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) ([]byte, error) { - err := blocks.BeaconBlockIsNil(blk) - if err != nil { - return nil, errNilBlock - } - if blk.Version() >= version.Bellatrix && blk.IsBlinded() { - blk, err = s.ExecutionPayloadReconstructor.ReconstructFullBlock(ctx, blk) - if err != nil { - return nil, errors.Wrapf(err, "could not reconstruct full execution payload to create signed beacon block") - } + if httputil.RespondWithSsz(r) { + s.getBlockV2Ssz(w, blk) + } else { + s.getBlockV2Json(ctx, w, blk) } - return s.getBlockRespSSZ(blk) } -// getUnblindedBlockRespSSZ attempts to reconstruct the full block before calling getBlockRespSSZ to finish the job. -func (s *Server) getBlindedBlockRespSSZ(blk interfaces.ReadOnlySignedBeaconBlock) ([]byte, error) { - err := blocks.BeaconBlockIsNil(blk) +// getBlockV2Ssz returns the SSZ-serialized version of the beacon block for given block ID. +func (s *Server) getBlockV2Ssz(w http.ResponseWriter, blk interfaces.ReadOnlySignedBeaconBlock) { + result, err := s.getBlockResponseBodySsz(blk) if err != nil { - return nil, errNilBlock + httputil.HandleError(w, "Could not get signed beacon block: "+err.Error(), http.StatusInternalServerError) } - if blk.Version() >= version.Bellatrix && !blk.IsBlinded() { - blk, err = blk.ToBlinded() - if err != nil { - return nil, errors.Wrapf(err, "could not convert block to blinded block") - } + if result == nil { + httputil.HandleError(w, fmt.Sprintf("Unknown block type %T", blk), http.StatusInternalServerError) } - return s.getBlockRespSSZ(blk) + w.Header().Set(api.VersionHeader, version.String(blk.Version())) + httputil.WriteSsz(w, result, "beacon_block.ssz") } -func (s *Server) getBlockRespSSZ(blk interfaces.ReadOnlySignedBeaconBlock) ([]byte, error) { +func (s *Server) getBlockResponseBodySsz(blk interfaces.ReadOnlySignedBeaconBlock) ([]byte, error) { err := blocks.BeaconBlockIsNil(blk) if err != nil { return nil, errNilBlock @@ -466,164 +145,48 @@ func (s *Server) getBlockRespSSZ(blk interfaces.ReadOnlySignedBeaconBlock) ([]by return sszData, nil } -func (s *Server) getBlindedBlockBellatrix(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (*structs.GetBlockV2Response, error) { - blindedConsensusBlk, err := blk.PbBlindedBellatrixBlock() - if err != nil { - // ErrUnsupportedField means that we have another block type - if errors.Is(err, consensus_types.ErrUnsupportedField) { - consensusBlk, err := blk.PbBellatrixBlock() - if err != nil { - return nil, err - } - if consensusBlk == nil { - return nil, errNilBlock - } - blkInterface, err := blk.ToBlinded() - if err != nil { - return nil, errors.Wrapf(err, "could not convert block to blinded block") - } - blindedConsensusBlk, err = blkInterface.PbBlindedBellatrixBlock() - if err != nil { - return nil, errors.Wrapf(err, "could not get signed beacon block") - } - } else { - return nil, err - } - } - - if blindedConsensusBlk == nil { - return nil, errNilBlock - } - root, err := blk.Block().HashTreeRoot() - if err != nil { - return nil, errors.Wrapf(err, "could not get block root") - } - isOptimistic, err := s.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root) - if err != nil { - return nil, errors.Wrapf(err, "could not check if block is optimistic") - } - respBlk, err := structs.SignedBlindedBeaconBlockBellatrixFromConsensus(blindedConsensusBlk) - if err != nil { - return nil, err - } - jsonBytes, err := json.Marshal(respBlk.Message) +// getBlockV2Json returns the JSON-serialized version of the beacon block for given block ID. +func (s *Server) getBlockV2Json(ctx context.Context, w http.ResponseWriter, blk interfaces.ReadOnlySignedBeaconBlock) { + result, err := s.getBlockResponseBodyJson(ctx, blk) if err != nil { - return nil, err + httputil.HandleError(w, "Error processing request: "+err.Error(), http.StatusInternalServerError) } - return &structs.GetBlockV2Response{ - Version: version.String(version.Bellatrix), - ExecutionOptimistic: isOptimistic, - Data: &structs.SignedBlock{ - Message: jsonBytes, - Signature: respBlk.Signature, - }, - }, nil + w.Header().Set(api.VersionHeader, result.Version) + httputil.WriteJson(w, result) } -func (s *Server) getBlindedBlockCapella(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (*structs.GetBlockV2Response, error) { - blindedConsensusBlk, err := blk.PbBlindedCapellaBlock() - if err != nil { - // ErrUnsupportedField means that we have another block type - if errors.Is(err, consensus_types.ErrUnsupportedField) { - consensusBlk, err := blk.PbCapellaBlock() - if err != nil { - return nil, err - } - if consensusBlk == nil { - return nil, errNilBlock - } - blkInterface, err := blk.ToBlinded() - if err != nil { - return nil, errors.Wrapf(err, "could not convert block to blinded block") - } - blindedConsensusBlk, err = blkInterface.PbBlindedCapellaBlock() - if err != nil { - return nil, errors.Wrapf(err, "could not get signed beacon block") - } - } else { - return nil, err - } - } - - if blindedConsensusBlk == nil { - return nil, errNilBlock - } - root, err := blk.Block().HashTreeRoot() - if err != nil { - return nil, errors.Wrapf(err, "could not get block root") - } - isOptimistic, err := s.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root) - if err != nil { - return nil, errors.Wrapf(err, "could not check if block is optimistic") - } - respBlk, err := structs.SignedBlindedBeaconBlockCapellaFromConsensus(blindedConsensusBlk) - if err != nil { +func (s *Server) getBlockResponseBodyJson(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (*structs.GetBlockV2Response, error) { + if err := blocks.BeaconBlockIsNil(blk); err != nil { return nil, err } - jsonBytes, err := json.Marshal(respBlk.Message) + blkRoot, err := blk.Block().HashTreeRoot() if err != nil { - return nil, err + return nil, errors.Wrap(err, "Could not get block root") } - return &structs.GetBlockV2Response{ - Version: version.String(version.Capella), - ExecutionOptimistic: isOptimistic, - Data: &structs.SignedBlock{ - Message: jsonBytes, - Signature: respBlk.Signature, - }, - }, nil -} - -func (s *Server) getBlindedBlockDeneb(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (*structs.GetBlockV2Response, error) { - blindedConsensusBlk, err := blk.PbBlindedDenebBlock() - if err != nil { - // ErrUnsupportedGetter means that we have another block type - if errors.Is(err, consensus_types.ErrUnsupportedField) { - consensusBlk, err := blk.PbDenebBlock() - if err != nil { - return nil, err - } - if consensusBlk == nil { - return nil, errNilBlock - } - blkInterface, err := blk.ToBlinded() - if err != nil { - return nil, errors.Wrapf(err, "could not convert block to blinded block") - } - blindedConsensusBlk, err = blkInterface.PbBlindedDenebBlock() - if err != nil { - return nil, errors.Wrapf(err, "could not get signed beacon block") - } - } else { - return nil, err + finalized := s.FinalizationFetcher.IsFinalized(ctx, blkRoot) + isOptimistic := false + if blk.Version() >= version.Bellatrix { + isOptimistic, err = s.OptimisticModeFetcher.IsOptimisticForRoot(ctx, blkRoot) + if err != nil { + return nil, errors.Wrap(err, "could not check if block is optimistic") } } - - if blindedConsensusBlk == nil { - return nil, errNilBlock - } - root, err := blk.Block().HashTreeRoot() - if err != nil { - return nil, errors.Wrapf(err, "could not get block root") - } - isOptimistic, err := s.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root) - if err != nil { - return nil, errors.Wrapf(err, "could not check if block is optimistic") - } - respBlk, err := structs.SignedBlindedBeaconBlockDenebFromConsensus(blindedConsensusBlk) + mj, err := structs.SignedBeaconBlockMessageJsoner(blk) if err != nil { return nil, err } - jsonBytes, err := json.Marshal(respBlk.Message) + jb, err := mj.MessageRawJson() if err != nil { return nil, err } + // Does not include values for optimistic and finalized. This code expects the caller to fill those out. return &structs.GetBlockV2Response{ - Version: version.String(version.Deneb), + Finalized: finalized, ExecutionOptimistic: isOptimistic, + Version: version.String(blk.Version()), Data: &structs.SignedBlock{ - Message: jsonBytes, - Signature: respBlk.Signature, + Message: jb, + Signature: mj.SigString(), }, }, nil }