mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 15:37:56 -05:00
fix: /eth/v2/beacon/blocks post api to handle electra and fulu blocks correctly (#14897)
* adding fix and changelog * adding no lint gocognit for now * fixing linting * Update beacon-chain/rpc/eth/beacon/handlers.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update beacon-chain/rpc/eth/beacon/handlers.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update beacon-chain/rpc/eth/beacon/handlers.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * updating based on kasey's suggestions * preston's comments --------- Co-authored-by: Radosław Kapka <rkapka@wp.pl>
This commit is contained in:
@@ -724,6 +724,48 @@ func (s *Server) PublishBlockV2(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
type signedBlockContentPeeker struct {
|
||||
Block json.RawMessage `json:"signed_block"`
|
||||
}
|
||||
type slotPeeker struct {
|
||||
Block struct {
|
||||
Slot primitives.Slot `json:"slot,string"`
|
||||
} `json:"message"`
|
||||
}
|
||||
|
||||
func versionHeaderFromRequest(body []byte) (string, error) {
|
||||
// check is required for post deneb fork blocks contents
|
||||
p := &signedBlockContentPeeker{}
|
||||
if err := json.Unmarshal(body, p); err != nil {
|
||||
return "", errors.Wrap(err, "unable to peek slot from block contents")
|
||||
}
|
||||
data := body
|
||||
if len(p.Block) > 0 {
|
||||
data = p.Block
|
||||
}
|
||||
sp := &slotPeeker{}
|
||||
if err := json.Unmarshal(data, sp); err != nil {
|
||||
return "", errors.Wrap(err, "unable to peek slot from block")
|
||||
}
|
||||
ce := slots.ToEpoch(sp.Block.Slot)
|
||||
if ce >= params.BeaconConfig().FuluForkEpoch {
|
||||
return version.String(version.Fulu), nil
|
||||
} else if ce >= params.BeaconConfig().ElectraForkEpoch {
|
||||
return version.String(version.Electra), nil
|
||||
} else if ce >= params.BeaconConfig().DenebForkEpoch {
|
||||
return version.String(version.Deneb), nil
|
||||
} else if ce >= params.BeaconConfig().CapellaForkEpoch {
|
||||
return version.String(version.Capella), nil
|
||||
} else if ce >= params.BeaconConfig().BellatrixForkEpoch {
|
||||
return version.String(version.Bellatrix), nil
|
||||
} else if ce >= params.BeaconConfig().AltairForkEpoch {
|
||||
return version.String(version.Altair), nil
|
||||
} else {
|
||||
return version.String(version.Phase0), nil
|
||||
}
|
||||
}
|
||||
|
||||
// nolint:gocognit
|
||||
func (s *Server) publishBlockSSZ(ctx context.Context, w http.ResponseWriter, r *http.Request, versionRequired bool) { // nolint:gocognit
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
@@ -735,9 +777,28 @@ func (s *Server) publishBlockSSZ(ctx context.Context, w http.ResponseWriter, r *
|
||||
httputil.HandleError(w, api.VersionHeader+" header is required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if !versionRequired && versionHeader == "" {
|
||||
versionHeader, err = versionHeaderFromRequest(body)
|
||||
if err != nil {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body for version header: %s", err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if versionHeader == version.String(version.Fulu) {
|
||||
fuluBlock := ð.SignedBeaconBlockContentsFulu{}
|
||||
if err = fuluBlock.UnmarshalSSZ(body); err != nil {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Fulu), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
fuluBlock := ð.SignedBeaconBlockContentsFulu{}
|
||||
if err = fuluBlock.UnmarshalSSZ(body); err == nil {
|
||||
genericBlock := ð.GenericSignedBeaconBlock{
|
||||
Block: ð.GenericSignedBeaconBlock_Fulu{
|
||||
Fulu: fuluBlock,
|
||||
@@ -760,17 +821,17 @@ func (s *Server) publishBlockSSZ(ctx context.Context, w http.ResponseWriter, r *
|
||||
s.proposeBlock(ctx, w, genericBlock)
|
||||
return
|
||||
}
|
||||
if versionHeader == version.String(version.Fulu) {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Fulu), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
electraBlock := ð.SignedBeaconBlockContentsElectra{}
|
||||
if err = electraBlock.UnmarshalSSZ(body); err == nil {
|
||||
if versionHeader == version.String(version.Electra) {
|
||||
electraBlock := ð.SignedBeaconBlockContentsElectra{}
|
||||
if err = electraBlock.UnmarshalSSZ(body); err != nil {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Electra), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
genericBlock := ð.GenericSignedBeaconBlock{
|
||||
Block: ð.GenericSignedBeaconBlock_Electra{
|
||||
Electra: electraBlock,
|
||||
@@ -793,17 +854,17 @@ func (s *Server) publishBlockSSZ(ctx context.Context, w http.ResponseWriter, r *
|
||||
s.proposeBlock(ctx, w, genericBlock)
|
||||
return
|
||||
}
|
||||
if versionHeader == version.String(version.Electra) {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Electra), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
denebBlock := ð.SignedBeaconBlockContentsDeneb{}
|
||||
if err = denebBlock.UnmarshalSSZ(body); err == nil {
|
||||
if versionHeader == version.String(version.Deneb) {
|
||||
denebBlock := ð.SignedBeaconBlockContentsDeneb{}
|
||||
if err = denebBlock.UnmarshalSSZ(body); err != nil {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Deneb), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
genericBlock := ð.GenericSignedBeaconBlock{
|
||||
Block: ð.GenericSignedBeaconBlock_Deneb{
|
||||
Deneb: denebBlock,
|
||||
@@ -826,17 +887,18 @@ func (s *Server) publishBlockSSZ(ctx context.Context, w http.ResponseWriter, r *
|
||||
s.proposeBlock(ctx, w, genericBlock)
|
||||
return
|
||||
}
|
||||
if versionHeader == version.String(version.Deneb) {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Deneb), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
capellaBlock := ð.SignedBeaconBlockCapella{}
|
||||
if err = capellaBlock.UnmarshalSSZ(body); err == nil {
|
||||
if versionHeader == version.String(version.Capella) {
|
||||
capellaBlock := ð.SignedBeaconBlockCapella{}
|
||||
if err = capellaBlock.UnmarshalSSZ(body); err != nil {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Capella), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
genericBlock := ð.GenericSignedBeaconBlock{
|
||||
Block: ð.GenericSignedBeaconBlock_Capella{
|
||||
Capella: capellaBlock,
|
||||
@@ -849,17 +911,17 @@ func (s *Server) publishBlockSSZ(ctx context.Context, w http.ResponseWriter, r *
|
||||
s.proposeBlock(ctx, w, genericBlock)
|
||||
return
|
||||
}
|
||||
if versionHeader == version.String(version.Capella) {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Capella), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
bellatrixBlock := ð.SignedBeaconBlockBellatrix{}
|
||||
if err = bellatrixBlock.UnmarshalSSZ(body); err == nil {
|
||||
if versionHeader == version.String(version.Bellatrix) {
|
||||
bellatrixBlock := ð.SignedBeaconBlockBellatrix{}
|
||||
if err = bellatrixBlock.UnmarshalSSZ(body); err != nil {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Bellatrix), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
genericBlock := ð.GenericSignedBeaconBlock{
|
||||
Block: ð.GenericSignedBeaconBlock_Bellatrix{
|
||||
Bellatrix: bellatrixBlock,
|
||||
@@ -872,17 +934,18 @@ func (s *Server) publishBlockSSZ(ctx context.Context, w http.ResponseWriter, r *
|
||||
s.proposeBlock(ctx, w, genericBlock)
|
||||
return
|
||||
}
|
||||
if versionHeader == version.String(version.Bellatrix) {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Bellatrix), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
altairBlock := ð.SignedBeaconBlockAltair{}
|
||||
if err = altairBlock.UnmarshalSSZ(body); err == nil {
|
||||
if versionHeader == version.String(version.Altair) {
|
||||
altairBlock := ð.SignedBeaconBlockAltair{}
|
||||
if err = altairBlock.UnmarshalSSZ(body); err != nil {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Altair), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
genericBlock := ð.GenericSignedBeaconBlock{
|
||||
Block: ð.GenericSignedBeaconBlock_Altair{
|
||||
Altair: altairBlock,
|
||||
@@ -895,17 +958,17 @@ func (s *Server) publishBlockSSZ(ctx context.Context, w http.ResponseWriter, r *
|
||||
s.proposeBlock(ctx, w, genericBlock)
|
||||
return
|
||||
}
|
||||
if versionHeader == version.String(version.Altair) {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Altair), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
phase0Block := ð.SignedBeaconBlock{}
|
||||
if err = phase0Block.UnmarshalSSZ(body); err == nil {
|
||||
if versionHeader == version.String(version.Phase0) {
|
||||
phase0Block := ð.SignedBeaconBlock{}
|
||||
if err = phase0Block.UnmarshalSSZ(body); err != nil {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Phase0), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
genericBlock := ð.GenericSignedBeaconBlock{
|
||||
Block: ð.GenericSignedBeaconBlock_Phase0{
|
||||
Phase0: phase0Block,
|
||||
@@ -918,18 +981,11 @@ func (s *Server) publishBlockSSZ(ctx context.Context, w http.ResponseWriter, r *
|
||||
s.proposeBlock(ctx, w, genericBlock)
|
||||
return
|
||||
}
|
||||
if versionHeader == version.String(version.Phase0) {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Phase0), err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
httputil.HandleError(w, "Body does not represent a valid block type", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
// nolint:gocognit
|
||||
func (s *Server) publishBlock(ctx context.Context, w http.ResponseWriter, r *http.Request, versionRequired bool) { // nolint:gocognit
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
@@ -941,32 +997,41 @@ func (s *Server) publishBlock(ctx context.Context, w http.ResponseWriter, r *htt
|
||||
httputil.HandleError(w, api.VersionHeader+" header is required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var consensusBlock *eth.GenericSignedBeaconBlock
|
||||
|
||||
var fuluBlockContents *structs.SignedBeaconBlockContentsFulu
|
||||
if err = unmarshalStrict(body, &fuluBlockContents); err == nil {
|
||||
consensusBlock, err = fuluBlockContents.ToGeneric()
|
||||
if err == nil {
|
||||
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
|
||||
if errors.Is(err, errEquivocatedBlock) {
|
||||
b, err := blocks.NewSignedBeaconBlock(consensusBlock)
|
||||
if err != nil {
|
||||
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if err := s.broadcastSeenBlockSidecars(ctx, b, consensusBlock.GetFulu().Blobs, consensusBlock.GetFulu().KzgProofs); err != nil {
|
||||
log.WithError(err).Error("Failed to broadcast blob sidecars")
|
||||
}
|
||||
}
|
||||
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
s.proposeBlock(ctx, w, consensusBlock)
|
||||
return
|
||||
if !versionRequired && versionHeader == "" {
|
||||
versionHeader, err = versionHeaderFromRequest(body)
|
||||
if err != nil {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body for version header: %s", err.Error()),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
var consensusBlock *eth.GenericSignedBeaconBlock
|
||||
if versionHeader == version.String(version.Fulu) {
|
||||
var fuluBlockContents *structs.SignedBeaconBlockContentsFulu
|
||||
if err = unmarshalStrict(body, &fuluBlockContents); err == nil {
|
||||
consensusBlock, err = fuluBlockContents.ToGeneric()
|
||||
if err == nil {
|
||||
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
|
||||
if errors.Is(err, errEquivocatedBlock) {
|
||||
b, err := blocks.NewSignedBeaconBlock(consensusBlock)
|
||||
if err != nil {
|
||||
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if err := s.broadcastSeenBlockSidecars(ctx, b, consensusBlock.GetFulu().Blobs, consensusBlock.GetFulu().KzgProofs); err != nil {
|
||||
log.WithError(err).Error("Failed to broadcast blob sidecars")
|
||||
}
|
||||
}
|
||||
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
s.proposeBlock(ctx, w, consensusBlock)
|
||||
return
|
||||
}
|
||||
}
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Fulu), err.Error()),
|
||||
@@ -975,29 +1040,29 @@ func (s *Server) publishBlock(ctx context.Context, w http.ResponseWriter, r *htt
|
||||
return
|
||||
}
|
||||
|
||||
var electraBlockContents *structs.SignedBeaconBlockContentsElectra
|
||||
if err = unmarshalStrict(body, &electraBlockContents); err == nil {
|
||||
consensusBlock, err = electraBlockContents.ToGeneric()
|
||||
if err == nil {
|
||||
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
|
||||
if errors.Is(err, errEquivocatedBlock) {
|
||||
b, err := blocks.NewSignedBeaconBlock(consensusBlock)
|
||||
if err != nil {
|
||||
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if err := s.broadcastSeenBlockSidecars(ctx, b, consensusBlock.GetElectra().Blobs, consensusBlock.GetElectra().KzgProofs); err != nil {
|
||||
log.WithError(err).Error("Failed to broadcast blob sidecars")
|
||||
if versionHeader == version.String(version.Electra) {
|
||||
var electraBlockContents *structs.SignedBeaconBlockContentsElectra
|
||||
if err = unmarshalStrict(body, &electraBlockContents); err == nil {
|
||||
consensusBlock, err = electraBlockContents.ToGeneric()
|
||||
if err == nil {
|
||||
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
|
||||
if errors.Is(err, errEquivocatedBlock) {
|
||||
b, err := blocks.NewSignedBeaconBlock(consensusBlock)
|
||||
if err != nil {
|
||||
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if err := s.broadcastSeenBlockSidecars(ctx, b, consensusBlock.GetElectra().Blobs, consensusBlock.GetElectra().KzgProofs); err != nil {
|
||||
log.WithError(err).Error("Failed to broadcast blob sidecars")
|
||||
}
|
||||
}
|
||||
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
|
||||
s.proposeBlock(ctx, w, consensusBlock)
|
||||
return
|
||||
}
|
||||
s.proposeBlock(ctx, w, consensusBlock)
|
||||
return
|
||||
}
|
||||
}
|
||||
if versionHeader == version.String(version.Electra) {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Electra), err.Error()),
|
||||
@@ -1006,29 +1071,29 @@ func (s *Server) publishBlock(ctx context.Context, w http.ResponseWriter, r *htt
|
||||
return
|
||||
}
|
||||
|
||||
var denebBlockContents *structs.SignedBeaconBlockContentsDeneb
|
||||
if err = unmarshalStrict(body, &denebBlockContents); err == nil {
|
||||
consensusBlock, err = denebBlockContents.ToGeneric()
|
||||
if err == nil {
|
||||
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
|
||||
if errors.Is(err, errEquivocatedBlock) {
|
||||
b, err := blocks.NewSignedBeaconBlock(consensusBlock)
|
||||
if err != nil {
|
||||
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if err := s.broadcastSeenBlockSidecars(ctx, b, consensusBlock.GetDeneb().Blobs, consensusBlock.GetDeneb().KzgProofs); err != nil {
|
||||
log.WithError(err).Error("Failed to broadcast blob sidecars")
|
||||
if versionHeader == version.String(version.Deneb) {
|
||||
var denebBlockContents *structs.SignedBeaconBlockContentsDeneb
|
||||
if err = unmarshalStrict(body, &denebBlockContents); err == nil {
|
||||
consensusBlock, err = denebBlockContents.ToGeneric()
|
||||
if err == nil {
|
||||
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
|
||||
if errors.Is(err, errEquivocatedBlock) {
|
||||
b, err := blocks.NewSignedBeaconBlock(consensusBlock)
|
||||
if err != nil {
|
||||
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if err := s.broadcastSeenBlockSidecars(ctx, b, consensusBlock.GetDeneb().Blobs, consensusBlock.GetDeneb().KzgProofs); err != nil {
|
||||
log.WithError(err).Error("Failed to broadcast blob sidecars")
|
||||
}
|
||||
}
|
||||
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
|
||||
s.proposeBlock(ctx, w, consensusBlock)
|
||||
return
|
||||
}
|
||||
s.proposeBlock(ctx, w, consensusBlock)
|
||||
return
|
||||
}
|
||||
}
|
||||
if versionHeader == version.String(version.Deneb) {
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Deneb), err.Error()),
|
||||
@@ -1037,19 +1102,20 @@ func (s *Server) publishBlock(ctx context.Context, w http.ResponseWriter, r *htt
|
||||
return
|
||||
}
|
||||
|
||||
var capellaBlock *structs.SignedBeaconBlockCapella
|
||||
if err = unmarshalStrict(body, &capellaBlock); err == nil {
|
||||
consensusBlock, err = capellaBlock.ToGeneric()
|
||||
if err == nil {
|
||||
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
|
||||
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
|
||||
if versionHeader == version.String(version.Capella) {
|
||||
var capellaBlock *structs.SignedBeaconBlockCapella
|
||||
if err = unmarshalStrict(body, &capellaBlock); err == nil {
|
||||
consensusBlock, err = capellaBlock.ToGeneric()
|
||||
if err == nil {
|
||||
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
|
||||
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
s.proposeBlock(ctx, w, consensusBlock)
|
||||
return
|
||||
}
|
||||
s.proposeBlock(ctx, w, consensusBlock)
|
||||
return
|
||||
}
|
||||
}
|
||||
if versionHeader == version.String(version.Capella) {
|
||||
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Capella), err.Error()),
|
||||
@@ -1058,19 +1124,20 @@ func (s *Server) publishBlock(ctx context.Context, w http.ResponseWriter, r *htt
|
||||
return
|
||||
}
|
||||
|
||||
var bellatrixBlock *structs.SignedBeaconBlockBellatrix
|
||||
if err = unmarshalStrict(body, &bellatrixBlock); err == nil {
|
||||
consensusBlock, err = bellatrixBlock.ToGeneric()
|
||||
if err == nil {
|
||||
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
|
||||
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
|
||||
if versionHeader == version.String(version.Bellatrix) {
|
||||
var bellatrixBlock *structs.SignedBeaconBlockBellatrix
|
||||
if err = unmarshalStrict(body, &bellatrixBlock); err == nil {
|
||||
consensusBlock, err = bellatrixBlock.ToGeneric()
|
||||
if err == nil {
|
||||
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
|
||||
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
s.proposeBlock(ctx, w, consensusBlock)
|
||||
return
|
||||
}
|
||||
s.proposeBlock(ctx, w, consensusBlock)
|
||||
return
|
||||
}
|
||||
}
|
||||
if versionHeader == version.String(version.Bellatrix) {
|
||||
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Bellatrix), err.Error()),
|
||||
@@ -1079,19 +1146,20 @@ func (s *Server) publishBlock(ctx context.Context, w http.ResponseWriter, r *htt
|
||||
return
|
||||
}
|
||||
|
||||
var altairBlock *structs.SignedBeaconBlockAltair
|
||||
if err = unmarshalStrict(body, &altairBlock); err == nil {
|
||||
consensusBlock, err = altairBlock.ToGeneric()
|
||||
if err == nil {
|
||||
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
|
||||
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
|
||||
if versionHeader == version.String(version.Altair) {
|
||||
var altairBlock *structs.SignedBeaconBlockAltair
|
||||
if err = unmarshalStrict(body, &altairBlock); err == nil {
|
||||
consensusBlock, err = altairBlock.ToGeneric()
|
||||
if err == nil {
|
||||
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
|
||||
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
s.proposeBlock(ctx, w, consensusBlock)
|
||||
return
|
||||
}
|
||||
s.proposeBlock(ctx, w, consensusBlock)
|
||||
return
|
||||
}
|
||||
}
|
||||
if versionHeader == version.String(version.Altair) {
|
||||
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Altair), err.Error()),
|
||||
@@ -1100,19 +1168,20 @@ func (s *Server) publishBlock(ctx context.Context, w http.ResponseWriter, r *htt
|
||||
return
|
||||
}
|
||||
|
||||
var phase0Block *structs.SignedBeaconBlock
|
||||
if err = unmarshalStrict(body, &phase0Block); err == nil {
|
||||
consensusBlock, err = phase0Block.ToGeneric()
|
||||
if err == nil {
|
||||
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
|
||||
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
|
||||
if versionHeader == version.String(version.Phase0) {
|
||||
var phase0Block *structs.SignedBeaconBlock
|
||||
if err = unmarshalStrict(body, &phase0Block); err == nil {
|
||||
consensusBlock, err = phase0Block.ToGeneric()
|
||||
if err == nil {
|
||||
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
|
||||
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
s.proposeBlock(ctx, w, consensusBlock)
|
||||
return
|
||||
}
|
||||
s.proposeBlock(ctx, w, consensusBlock)
|
||||
return
|
||||
}
|
||||
}
|
||||
if versionHeader == version.String(version.Phase0) {
|
||||
|
||||
httputil.HandleError(
|
||||
w,
|
||||
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Phase0), err.Error()),
|
||||
|
||||
@@ -1407,6 +1407,38 @@ func TestPublishBlock(t *testing.T) {
|
||||
server.PublishBlock(writer, request)
|
||||
assert.Equal(t, http.StatusOK, writer.Code)
|
||||
})
|
||||
t.Run("Capella block without version header succeeds", func(t *testing.T) {
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.CapellaForkEpoch = 4
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
params.SetupTestConfigCleanup(t)
|
||||
var signedblock *structs.SignedBeaconBlockCapella
|
||||
require.NoError(t, json.Unmarshal([]byte(rpctesting.CapellaBlock), &signedblock))
|
||||
signedblock.Message.Slot = fmt.Sprintf("%d", uint64(params.BeaconConfig().SlotsPerEpoch)*uint64(params.BeaconConfig().CapellaForkEpoch))
|
||||
newBlock, err := json.Marshal(signedblock)
|
||||
require.NoError(t, err)
|
||||
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
|
||||
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
|
||||
block, ok := req.Block.(*eth.GenericSignedBeaconBlock_Capella)
|
||||
converted, err := structs.BeaconBlockCapellaFromConsensus(block.Capella.Block)
|
||||
require.NoError(t, err)
|
||||
var signedblock *structs.SignedBeaconBlockCapella
|
||||
err = json.Unmarshal(newBlock, &signedblock)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, converted, signedblock.Message)
|
||||
return ok
|
||||
}))
|
||||
server := &Server{
|
||||
V1Alpha1ValidatorServer: v1alpha1Server,
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
}
|
||||
|
||||
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader(newBlock))
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
server.PublishBlock(writer, request)
|
||||
assert.Equal(t, http.StatusOK, writer.Code)
|
||||
})
|
||||
t.Run("Deneb", func(t *testing.T) {
|
||||
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
|
||||
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
|
||||
@@ -1433,11 +1465,10 @@ func TestPublishBlock(t *testing.T) {
|
||||
t.Run("Electra", func(t *testing.T) {
|
||||
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
|
||||
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
|
||||
// Convert back Fulu to Electra when there is at least one difference between Electra and Fulu blocks.
|
||||
block, ok := req.Block.(*eth.GenericSignedBeaconBlock_Fulu)
|
||||
converted, err := structs.SignedBeaconBlockContentsFuluFromConsensus(block.Fulu)
|
||||
block, ok := req.Block.(*eth.GenericSignedBeaconBlock_Electra)
|
||||
converted, err := structs.SignedBeaconBlockContentsElectraFromConsensus(block.Electra)
|
||||
require.NoError(t, err)
|
||||
var signedblock *structs.SignedBeaconBlockContentsFulu
|
||||
var signedblock *structs.SignedBeaconBlockContentsElectra
|
||||
err = json.Unmarshal([]byte(rpctesting.FuluBlockContents), &signedblock)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, converted, signedblock)
|
||||
@@ -1454,6 +1485,51 @@ func TestPublishBlock(t *testing.T) {
|
||||
server.PublishBlock(writer, request)
|
||||
assert.Equal(t, http.StatusOK, writer.Code)
|
||||
})
|
||||
t.Run("Electra block without version header succeeds", func(t *testing.T) {
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.ElectraForkEpoch = 6
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
params.SetupTestConfigCleanup(t)
|
||||
var signedblock *structs.SignedBeaconBlockContentsElectra
|
||||
require.NoError(t, json.Unmarshal([]byte(rpctesting.ElectraBlockContents), &signedblock))
|
||||
signedblock.SignedBlock.Message.Slot = fmt.Sprintf("%d", uint64(params.BeaconConfig().SlotsPerEpoch)*uint64(params.BeaconConfig().ElectraForkEpoch))
|
||||
newContents, err := json.Marshal(signedblock)
|
||||
require.NoError(t, err)
|
||||
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
|
||||
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
|
||||
block, ok := req.Block.(*eth.GenericSignedBeaconBlock_Electra)
|
||||
converted, err := structs.SignedBeaconBlockContentsElectraFromConsensus(block.Electra)
|
||||
require.NoError(t, err)
|
||||
var signedblock *structs.SignedBeaconBlockContentsElectra
|
||||
err = json.Unmarshal(newContents, &signedblock)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, converted, signedblock)
|
||||
return ok
|
||||
}))
|
||||
server := &Server{
|
||||
V1Alpha1ValidatorServer: v1alpha1Server,
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
}
|
||||
|
||||
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader(newContents))
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
server.PublishBlock(writer, request)
|
||||
assert.Equal(t, http.StatusOK, writer.Code)
|
||||
})
|
||||
t.Run("Electra block without version header on wrong fork", func(t *testing.T) {
|
||||
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
|
||||
server := &Server{
|
||||
V1Alpha1ValidatorServer: v1alpha1Server,
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
}
|
||||
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.ElectraBlockContents)))
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
server.PublishBlock(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
assert.StringContains(t, fmt.Sprintf("Could not decode request body into %s consensus block", version.String(version.Phase0)), writer.Body.String())
|
||||
})
|
||||
t.Run("Fulu", func(t *testing.T) {
|
||||
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
|
||||
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
|
||||
@@ -1521,6 +1597,107 @@ func TestPublishBlock(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestVersionHeaderFromRequest(t *testing.T) {
|
||||
t.Run("Fulu block contents returns fulu header", func(t *testing.T) {
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.FuluForkEpoch = 7
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
params.SetupTestConfigCleanup(t)
|
||||
var signedblock *structs.SignedBeaconBlockContentsFulu
|
||||
require.NoError(t, json.Unmarshal([]byte(rpctesting.FuluBlockContents), &signedblock))
|
||||
signedblock.SignedBlock.Message.Slot = fmt.Sprintf("%d", uint64(params.BeaconConfig().SlotsPerEpoch)*uint64(params.BeaconConfig().FuluForkEpoch))
|
||||
newContents, err := json.Marshal(signedblock)
|
||||
require.NoError(t, err)
|
||||
versionHead, err := versionHeaderFromRequest(newContents)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, version.String(version.Fulu), versionHead)
|
||||
})
|
||||
t.Run("Electra block contents returns electra header", func(t *testing.T) {
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.ElectraForkEpoch = 6
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
params.SetupTestConfigCleanup(t)
|
||||
var signedblock *structs.SignedBeaconBlockContentsElectra
|
||||
require.NoError(t, json.Unmarshal([]byte(rpctesting.ElectraBlockContents), &signedblock))
|
||||
signedblock.SignedBlock.Message.Slot = fmt.Sprintf("%d", uint64(params.BeaconConfig().SlotsPerEpoch)*uint64(params.BeaconConfig().ElectraForkEpoch))
|
||||
newContents, err := json.Marshal(signedblock)
|
||||
require.NoError(t, err)
|
||||
versionHead, err := versionHeaderFromRequest(newContents)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, version.String(version.Electra), versionHead)
|
||||
})
|
||||
t.Run("Deneb block contents returns deneb header", func(t *testing.T) {
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.DenebForkEpoch = 5
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
params.SetupTestConfigCleanup(t)
|
||||
var signedblock *structs.SignedBeaconBlockContentsDeneb
|
||||
require.NoError(t, json.Unmarshal([]byte(rpctesting.DenebBlockContents), &signedblock))
|
||||
signedblock.SignedBlock.Message.Slot = fmt.Sprintf("%d", uint64(params.BeaconConfig().SlotsPerEpoch)*uint64(params.BeaconConfig().DenebForkEpoch))
|
||||
newContents, err := json.Marshal(signedblock)
|
||||
require.NoError(t, err)
|
||||
versionHead, err := versionHeaderFromRequest(newContents)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, version.String(version.Deneb), versionHead)
|
||||
})
|
||||
t.Run("Capella block returns capella header", func(t *testing.T) {
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.CapellaForkEpoch = 4
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
params.SetupTestConfigCleanup(t)
|
||||
var signedblock *structs.SignedBeaconBlockCapella
|
||||
require.NoError(t, json.Unmarshal([]byte(rpctesting.CapellaBlock), &signedblock))
|
||||
signedblock.Message.Slot = fmt.Sprintf("%d", uint64(params.BeaconConfig().SlotsPerEpoch)*uint64(params.BeaconConfig().CapellaForkEpoch))
|
||||
newBlock, err := json.Marshal(signedblock)
|
||||
require.NoError(t, err)
|
||||
versionHead, err := versionHeaderFromRequest(newBlock)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, version.String(version.Capella), versionHead)
|
||||
})
|
||||
t.Run("Bellatrix block returns capella header", func(t *testing.T) {
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.BellatrixForkEpoch = 3
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
params.SetupTestConfigCleanup(t)
|
||||
var signedblock *structs.SignedBeaconBlockBellatrix
|
||||
require.NoError(t, json.Unmarshal([]byte(rpctesting.BellatrixBlock), &signedblock))
|
||||
signedblock.Message.Slot = fmt.Sprintf("%d", uint64(params.BeaconConfig().SlotsPerEpoch)*uint64(params.BeaconConfig().BellatrixForkEpoch))
|
||||
newBlock, err := json.Marshal(signedblock)
|
||||
require.NoError(t, err)
|
||||
versionHead, err := versionHeaderFromRequest(newBlock)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, version.String(version.Bellatrix), versionHead)
|
||||
})
|
||||
t.Run("Altair block returns capella header", func(t *testing.T) {
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.AltairForkEpoch = 2
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
params.SetupTestConfigCleanup(t)
|
||||
var signedblock *structs.SignedBeaconBlockAltair
|
||||
require.NoError(t, json.Unmarshal([]byte(rpctesting.AltairBlock), &signedblock))
|
||||
signedblock.Message.Slot = fmt.Sprintf("%d", uint64(params.BeaconConfig().SlotsPerEpoch)*uint64(params.BeaconConfig().AltairForkEpoch))
|
||||
newBlock, err := json.Marshal(signedblock)
|
||||
require.NoError(t, err)
|
||||
versionHead, err := versionHeaderFromRequest(newBlock)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, version.String(version.Altair), versionHead)
|
||||
})
|
||||
t.Run("Phase0 block returns capella header", func(t *testing.T) {
|
||||
var signedblock *structs.SignedBeaconBlock
|
||||
require.NoError(t, json.Unmarshal([]byte(rpctesting.Phase0Block), &signedblock))
|
||||
newBlock, err := json.Marshal(signedblock)
|
||||
require.NoError(t, err)
|
||||
versionHead, err := versionHeaderFromRequest(newBlock)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, version.String(version.Phase0), versionHead)
|
||||
})
|
||||
t.Run("Malformed json returns error unable to peek slot from block contents", func(t *testing.T) {
|
||||
malformedJSON := []byte(`{"age": 30,}`)
|
||||
_, err := versionHeaderFromRequest(malformedJSON)
|
||||
require.ErrorContains(t, "unable to peek slot", err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPublishBlockSSZ(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
t.Run("Phase 0", func(t *testing.T) {
|
||||
@@ -2352,12 +2529,11 @@ func TestPublishBlockV2(t *testing.T) {
|
||||
t.Run("Electra", func(t *testing.T) {
|
||||
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
|
||||
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
|
||||
// Convert back Fulu to Electra when there is at least one difference between Electra and Fulu blocks.
|
||||
block, ok := req.Block.(*eth.GenericSignedBeaconBlock_Fulu)
|
||||
converted, err := structs.SignedBeaconBlockContentsFuluFromConsensus(block.Fulu)
|
||||
block, ok := req.Block.(*eth.GenericSignedBeaconBlock_Electra)
|
||||
converted, err := structs.SignedBeaconBlockContentsElectraFromConsensus(block.Electra)
|
||||
require.NoError(t, err)
|
||||
var signedblock *structs.SignedBeaconBlockContentsFulu
|
||||
err = json.Unmarshal([]byte(rpctesting.FuluBlockContents), &signedblock)
|
||||
var signedblock *structs.SignedBeaconBlockContentsElectra
|
||||
err = json.Unmarshal([]byte(rpctesting.ElectraBlockContents), &signedblock)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, converted, signedblock)
|
||||
return ok
|
||||
|
||||
3
changelog/james-prysm_fix-block-api-electra.md
Normal file
3
changelog/james-prysm_fix-block-api-electra.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Fixed
|
||||
|
||||
- fix block api endpoint to handle blocks with the same structure but on different forks (i.e. fulu and electra)
|
||||
Reference in New Issue
Block a user