Skip to content

Commit 57cc495

Browse files
saolynrkapka
andauthored
Add /eth/v2/beacon/blocks/{block_id}/attestations (#14478)
* add endpoint * changelog * fix response * improvement * fix test * James' review * fix test * fix version check * add test for non electra V2 * Review items * James' review * Radek' review * Update CHANGELOG.md --------- Co-authored-by: Radosław Kapka <rkapka@wp.pl>
1 parent 2c981d5 commit 57cc495

File tree

6 files changed

+391
-127
lines changed

6 files changed

+391
-127
lines changed

CHANGELOG.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,16 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve
1616
- Light client support: Implement `BlockToLightClientHeader` function.
1717
- Light client support: Consensus types.
1818
- GetBeaconStateV2: add Electra case.
19-
- Implement [consensus-specs/3875](https://github.com/ethereum/consensus-specs/pull/3875)
20-
- Tests to ensure sepolia config matches the official upstream yaml
21-
- HTTP endpoint for PublishBlobs
19+
- Implement [consensus-specs/3875](https://github.com/ethereum/consensus-specs/pull/3875).
20+
- Tests to ensure sepolia config matches the official upstream yaml.
21+
- HTTP endpoint for PublishBlobs.
2222
- GetBlockV2, GetBlindedBlock, ProduceBlockV2, ProduceBlockV3: add Electra case.
23-
- Add Electra support and tests for light client functions
23+
- Add Electra support and tests for light client functions.
2424
- fastssz version bump (better error messages).
2525
- SSE implementation that sheds stuck clients. [pr](https://github.com/prysmaticlabs/prysm/pull/14413)
26-
- Add Bellatrix tests for light client functions
27-
- Add Discovery Rebooter Feature
28-
26+
- Add Bellatrix tests for light client functions.
27+
- Add Discovery Rebooter Feature.
28+
- Added GetBlockAttestationsV2 endpoint.
2929

3030
### Changed
3131

api/server/structs/endpoints_beacon.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,13 @@ type GetBlockAttestationsResponse struct {
133133
Data []*Attestation `json:"data"`
134134
}
135135

136+
type GetBlockAttestationsV2Response struct {
137+
Version string `json:"version"`
138+
ExecutionOptimistic bool `json:"execution_optimistic"`
139+
Finalized bool `json:"finalized"`
140+
Data json.RawMessage `json:"data"` // Accepts both `Attestation` and `AttestationElectra` types
141+
}
142+
136143
type GetStateRootResponse struct {
137144
ExecutionOptimistic bool `json:"execution_optimistic"`
138145
Finalized bool `json:"finalized"`

beacon-chain/rpc/endpoints.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,15 @@ func (s *Service) beaconEndpoints(
585585
handler: server.GetBlockAttestations,
586586
methods: []string{http.MethodGet},
587587
},
588+
{
589+
template: "/eth/v2/beacon/blocks/{block_id}/attestations",
590+
name: namespace + ".GetBlockAttestationsV2",
591+
middleware: []middleware.Middleware{
592+
middleware.AcceptHeaderHandler([]string{api.JsonMediaType}),
593+
},
594+
handler: server.GetBlockAttestations,
595+
methods: []string{http.MethodGet},
596+
},
588597
{
589598
template: "/eth/v1/beacon/blinded_blocks/{block_id}",
590599
name: namespace + ".GetBlindedBlock",

beacon-chain/rpc/endpoints_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ func Test_endpoints(t *testing.T) {
3636
"/eth/v2/beacon/blocks/{block_id}": {http.MethodGet},
3737
"/eth/v1/beacon/blocks/{block_id}/root": {http.MethodGet},
3838
"/eth/v1/beacon/blocks/{block_id}/attestations": {http.MethodGet},
39+
"/eth/v2/beacon/blocks/{block_id}/attestations": {http.MethodGet},
3940
"/eth/v1/beacon/blob_sidecars/{block_id}": {http.MethodGet},
4041
"/eth/v1/beacon/deposit_snapshot": {http.MethodGet},
4142
"/eth/v1/beacon/blinded_blocks/{block_id}": {http.MethodGet},

beacon-chain/rpc/eth/beacon/handlers.go

Lines changed: 72 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -200,16 +200,10 @@ func (s *Server) GetBlockAttestations(w http.ResponseWriter, r *http.Request) {
200200
ctx, span := trace.StartSpan(r.Context(), "beacon.GetBlockAttestations")
201201
defer span.End()
202202

203-
blockId := r.PathValue("block_id")
204-
if blockId == "" {
205-
httputil.HandleError(w, "block_id is required in URL params", http.StatusBadRequest)
206-
return
207-
}
208-
blk, err := s.Blocker.Block(ctx, []byte(blockId))
209-
if !shared.WriteBlockFetchError(w, blk, err) {
203+
blk, isOptimistic, root := s.blockData(ctx, w, r)
204+
if blk == nil {
210205
return
211206
}
212-
213207
consensusAtts := blk.Block().Body().Attestations()
214208
atts := make([]*structs.Attestation, len(consensusAtts))
215209
for i, att := range consensusAtts {
@@ -221,25 +215,87 @@ func (s *Server) GetBlockAttestations(w http.ResponseWriter, r *http.Request) {
221215
return
222216
}
223217
}
224-
root, err := blk.Block().HashTreeRoot()
225-
if err != nil {
226-
httputil.HandleError(w, "Could not get block root: "+err.Error(), http.StatusInternalServerError)
218+
resp := &structs.GetBlockAttestationsResponse{
219+
Data: atts,
220+
ExecutionOptimistic: isOptimistic,
221+
Finalized: s.FinalizationFetcher.IsFinalized(ctx, root),
222+
}
223+
httputil.WriteJson(w, resp)
224+
}
225+
226+
// GetBlockAttestationsV2 retrieves attestation included in requested block.
227+
func (s *Server) GetBlockAttestationsV2(w http.ResponseWriter, r *http.Request) {
228+
ctx, span := trace.StartSpan(r.Context(), "beacon.GetBlockAttestationsV2")
229+
defer span.End()
230+
231+
blk, isOptimistic, root := s.blockData(ctx, w, r)
232+
if blk == nil {
227233
return
228234
}
229-
isOptimistic, err := s.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
235+
consensusAtts := blk.Block().Body().Attestations()
236+
237+
v := blk.Block().Version()
238+
var attStructs []interface{}
239+
if v >= version.Electra {
240+
for _, att := range consensusAtts {
241+
a, ok := att.(*eth.AttestationElectra)
242+
if !ok {
243+
httputil.HandleError(w, fmt.Sprintf("unable to convert consensus attestations electra of type %T", att), http.StatusInternalServerError)
244+
return
245+
}
246+
attStruct := structs.AttElectraFromConsensus(a)
247+
attStructs = append(attStructs, attStruct)
248+
}
249+
} else {
250+
for _, att := range consensusAtts {
251+
a, ok := att.(*eth.Attestation)
252+
if !ok {
253+
httputil.HandleError(w, fmt.Sprintf("unable to convert consensus attestation of type %T", att), http.StatusInternalServerError)
254+
return
255+
}
256+
attStruct := structs.AttFromConsensus(a)
257+
attStructs = append(attStructs, attStruct)
258+
}
259+
}
260+
261+
attBytes, err := json.Marshal(attStructs)
230262
if err != nil {
231-
httputil.HandleError(w, "Could not check if block is optimistic: "+err.Error(), http.StatusInternalServerError)
263+
httputil.HandleError(w, fmt.Sprintf("failed to marshal attestations: %v", err), http.StatusInternalServerError)
232264
return
233265
}
234-
235-
resp := &structs.GetBlockAttestationsResponse{
236-
Data: atts,
266+
resp := &structs.GetBlockAttestationsV2Response{
267+
Version: version.String(v),
237268
ExecutionOptimistic: isOptimistic,
238269
Finalized: s.FinalizationFetcher.IsFinalized(ctx, root),
270+
Data: attBytes,
239271
}
240272
httputil.WriteJson(w, resp)
241273
}
242274

275+
func (s *Server) blockData(ctx context.Context, w http.ResponseWriter, r *http.Request) (interfaces.ReadOnlySignedBeaconBlock, bool, [32]byte) {
276+
blockId := r.PathValue("block_id")
277+
if blockId == "" {
278+
httputil.HandleError(w, "block_id is required in URL params", http.StatusBadRequest)
279+
return nil, false, [32]byte{}
280+
}
281+
blk, err := s.Blocker.Block(ctx, []byte(blockId))
282+
if !shared.WriteBlockFetchError(w, blk, err) {
283+
return nil, false, [32]byte{}
284+
}
285+
286+
root, err := blk.Block().HashTreeRoot()
287+
if err != nil {
288+
httputil.HandleError(w, "Could not get block root: "+err.Error(), http.StatusInternalServerError)
289+
return nil, false, [32]byte{}
290+
}
291+
isOptimistic, err := s.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
292+
if err != nil {
293+
httputil.HandleError(w, "Could not check if block is optimistic: "+err.Error(), http.StatusInternalServerError)
294+
return nil, false, [32]byte{}
295+
}
296+
return blk, isOptimistic, root
297+
}
298+
243299
// PublishBlindedBlock instructs the beacon node to use the components of the `SignedBlindedBeaconBlock` to construct
244300
// and publish a SignedBeaconBlock by swapping out the transactions_root for the corresponding full list of `transactions`.
245301
// The beacon node should broadcast a newly constructed SignedBeaconBlock to the beacon network, to be included in the

0 commit comments

Comments
 (0)