Skip to content

Commit a94561f

Browse files
committed
Merge branch 'develop' into peerDAS
2 parents af875b7 + 543ebe8 commit a94561f

File tree

78 files changed

+3463
-1277
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+3463
-1277
lines changed

CHANGELOG.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,49 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
66

7+
## [v6.0.4](https://github.com/prysmaticlabs/prysm/compare/v6.0.3...v6.0.4) - 2025-06-05
8+
9+
This release has more work on PeerDAS, and light client support. Additionally, we have a few bug fixes:
10+
- Blob cache size now correctly set at startup.
11+
- A fix for slashing protection history exports where the validator database was in a nested folder.
12+
- Corrected behavior of the API call for state committees with an invalid request.
13+
- `/bin/sh` is now symlinked to `/bin/bash` for Prysm docker images.
14+
15+
In the [Hoodi](https://github.com/eth-clients/hoodi) testnet, the default gas limit is raised to 60M gas.
16+
17+
### Added
18+
19+
- Add light client mainnet spec test. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15295)
20+
- Add support for light client req/resp domain. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15281)
21+
- Added /bin/sh simlink to docker images. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15294)
22+
- Added Prysm build data to otel tracing spans. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15302)
23+
- Add light client minimal spec test support for `update_ranking` tests. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15297)
24+
- Add fulu operation and epoch processing spec tests. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15284)
25+
- Updated e2e Beacon API evaluator to support more endpoints, including the ones introduced in Electra. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15304)
26+
- Data column sidecars verification methods. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15232)
27+
- Implement data column sidecars filesystem. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15257)
28+
- Add blob schedule support from https://github.com/ethereum/consensus-specs/pull/4277. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15272)
29+
- random forkchoice spec tests for fulu. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15287)
30+
- Add ability to download nightly test vectors. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15312)
31+
- PeerDAS: Validation pipeline for data column sidecars received via gossip. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15310)
32+
- PeerDAS: Implement P2P. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15347)
33+
- PeerDAS: Implement the blockchain package. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15350)
34+
35+
### Changed
36+
37+
- Update spec tests to v1.6.0-alpha.0. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15306)
38+
- PeerDAS: Refactor the reconstruction pipeline. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15309)
39+
- PeerDAS: `DataColumnStorage.Get` - Exit early no columns are available. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15309)
40+
- Default hoodi testnet builder gas limit to 60M. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15361)
41+
42+
### Fixed
43+
44+
- Fix cyclical dependencies issue when using testing/util package. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15248)
45+
- Set seen blob cache size correctly based on current slot time at start up. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15348)
46+
- Fix `slashing-protection-history export` failing when `validator.db` is in a nested folder like `data/direct/`. (#14954). [[PR]](https://github.com/prysmaticlabs/prysm/pull/15351)
47+
- Made `/eth/v1/beacon/states/{state_id}/committees` endpoint return `400` when slot does not belong to the specified epoch, aligning with the Beacon API spec (#15355). [[PR]](https://github.com/prysmaticlabs/prysm/pull/15356)
48+
- Removed eager validator context cancellation that was causing validator builder registrations to fail occasionally. [[PR]](https://github.com/prysmaticlabs/prysm/pull/15369)
49+
750
## [v6.0.3](https://github.com/prysmaticlabs/prysm/compare/v6.0.2...v6.0.3) - 2025-05-21
851

952
This release has important bugfixes for users of the [Beacon API](https://ethereum.github.io/beacon-APIs/). These fixes include:

api/server/structs/endpoints_node.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,14 @@ type GetPeerResponse struct {
3333
Data *Peer `json:"data"`
3434
}
3535

36+
// Added Meta to align with beacon-api: https://ethereum.github.io/beacon-APIs/#/Node/getPeers
37+
type Meta struct {
38+
Count int `json:"count"`
39+
}
40+
3641
type GetPeersResponse struct {
3742
Data []*Peer `json:"data"`
43+
Meta Meta `json:"meta"`
3844
}
3945

4046
type Peer struct {

beacon-chain/core/helpers/beacon_committee.go

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -277,10 +277,10 @@ type CommitteeAssignment struct {
277277
CommitteeIndex primitives.CommitteeIndex
278278
}
279279

280-
// verifyAssignmentEpoch verifies if the given epoch is valid for assignment based on the provided state.
280+
// VerifyAssignmentEpoch verifies if the given epoch is valid for assignment based on the provided state.
281281
// It checks if the epoch is not greater than the next epoch, and if the start slot of the epoch is greater
282282
// than or equal to the minimum valid start slot calculated based on the state's current slot and historical roots.
283-
func verifyAssignmentEpoch(epoch primitives.Epoch, state state.BeaconState) error {
283+
func VerifyAssignmentEpoch(epoch primitives.Epoch, state state.BeaconState) error {
284284
nextEpoch := time.NextEpoch(state)
285285
if epoch > nextEpoch {
286286
return fmt.Errorf("epoch %d can't be greater than next epoch %d", epoch, nextEpoch)
@@ -308,7 +308,7 @@ func ProposerAssignments(ctx context.Context, state state.BeaconState, epoch pri
308308
defer span.End()
309309

310310
// Verify if the epoch is valid for assignment based on the provided state.
311-
if err := verifyAssignmentEpoch(epoch, state); err != nil {
311+
if err := VerifyAssignmentEpoch(epoch, state); err != nil {
312312
return nil, err
313313
}
314314
startSlot, err := slots.EpochStart(epoch)
@@ -351,6 +351,61 @@ func ProposerAssignments(ctx context.Context, state state.BeaconState, epoch pri
351351
return proposerAssignments, nil
352352
}
353353

354+
// LiteAssignment is a lite version of CommitteeAssignment, and has committee length
355+
// and validator committee index instead of the full committee list
356+
type LiteAssignment struct {
357+
AttesterSlot primitives.Slot // slot in which to attest
358+
CommitteeIndex primitives.CommitteeIndex // position of the committee in the slot
359+
CommitteeLength uint64 // number of members in the committee
360+
ValidatorCommitteeIndex uint64 // validator’s offset inside the committee
361+
}
362+
363+
// PrecomputeCommittees returns an array indexed by (slot-startSlot)
364+
// whose elements are the beacon committees of that slot.
365+
func PrecomputeCommittees(
366+
ctx context.Context,
367+
st state.BeaconState,
368+
startSlot primitives.Slot,
369+
) ([][][]primitives.ValidatorIndex, error) {
370+
cfg := params.BeaconConfig()
371+
out := make([][][]primitives.ValidatorIndex, cfg.SlotsPerEpoch)
372+
373+
for relativeSlot := primitives.Slot(0); relativeSlot < cfg.SlotsPerEpoch; relativeSlot++ {
374+
slot := startSlot + relativeSlot
375+
376+
comms, err := BeaconCommittees(ctx, st, slot)
377+
if err != nil {
378+
return nil, errors.Wrapf(err, "BeaconCommittees failed at slot %d", slot)
379+
}
380+
out[relativeSlot] = comms
381+
}
382+
return out, nil
383+
}
384+
385+
// AssignmentForValidator scans the cached committees once
386+
// and returns the duty for a single validator.
387+
func AssignmentForValidator(
388+
bySlot [][][]primitives.ValidatorIndex,
389+
startSlot primitives.Slot,
390+
vIdx primitives.ValidatorIndex,
391+
) *LiteAssignment {
392+
for relativeSlot, committees := range bySlot {
393+
for cIdx, committee := range committees {
394+
for pos, member := range committee {
395+
if member == vIdx {
396+
return &LiteAssignment{
397+
AttesterSlot: startSlot + primitives.Slot(relativeSlot),
398+
CommitteeIndex: primitives.CommitteeIndex(cIdx),
399+
CommitteeLength: uint64(len(committee)),
400+
ValidatorCommitteeIndex: uint64(pos),
401+
}
402+
}
403+
}
404+
}
405+
}
406+
return nil // validator is not scheduled this epoch
407+
}
408+
354409
// CommitteeAssignments calculates committee assignments for each validator during the specified epoch.
355410
// It retrieves active validator indices, determines the number of committees per slot, and computes
356411
// assignments for each validator based on their presence in the provided validators slice.
@@ -359,7 +414,7 @@ func CommitteeAssignments(ctx context.Context, state state.BeaconState, epoch pr
359414
defer span.End()
360415

361416
// Verify if the epoch is valid for assignment based on the provided state.
362-
if err := verifyAssignmentEpoch(epoch, state); err != nil {
417+
if err := VerifyAssignmentEpoch(epoch, state); err != nil {
363418
return nil, err
364419
}
365420
startSlot, err := slots.EpochStart(epoch)

beacon-chain/core/helpers/beacon_committee_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -871,3 +871,48 @@ func TestBeaconCommitteesFromCache(t *testing.T) {
871871
assert.DeepEqual(t, committees[idx], committee)
872872
}
873873
}
874+
875+
func TestPrecomputeCommittees_HappyPath(t *testing.T) {
876+
cfg := params.BeaconConfig()
877+
start := primitives.Slot(100)
878+
ctx := context.Background()
879+
st, _ := util.DeterministicGenesisState(t, 256)
880+
881+
got, err := helpers.PrecomputeCommittees(ctx, st, start)
882+
883+
require.NoError(t, err)
884+
require.Equal(t, len(got), int(cfg.SlotsPerEpoch), "outer slice length mismatch")
885+
886+
for i := range got {
887+
expSlot := start + primitives.Slot(i)
888+
comms, err := helpers.BeaconCommittees(ctx, st, expSlot)
889+
require.NoError(t, err)
890+
require.DeepEqual(t, comms, got[i])
891+
}
892+
}
893+
894+
func TestAssignmentForValidator(t *testing.T) {
895+
start := primitives.Slot(200)
896+
bySlot := [][][]primitives.ValidatorIndex{
897+
{{1, 2, 3}},
898+
{{7, 8, 9}},
899+
}
900+
vIdx := primitives.ValidatorIndex(8)
901+
902+
got := helpers.AssignmentForValidator(bySlot, start, vIdx)
903+
904+
require.NotNil(t, got)
905+
require.Equal(t, start+1, got.AttesterSlot)
906+
require.Equal(t, primitives.CommitteeIndex(0), got.CommitteeIndex)
907+
require.Equal(t, uint64(3), got.CommitteeLength)
908+
require.Equal(t, uint64(1), got.ValidatorCommitteeIndex)
909+
910+
t.Run("Not Found", func(t *testing.T) {
911+
start = primitives.Slot(300)
912+
bySlot = [][][]primitives.ValidatorIndex{
913+
{{4, 5, 6}},
914+
}
915+
got = helpers.AssignmentForValidator(bySlot, start, primitives.ValidatorIndex(99))
916+
require.IsNil(t, got)
917+
})
918+
}

beacon-chain/rpc/endpoints.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ func (s *Service) validatorEndpoints(
245245
template: "/eth/v2/validator/aggregate_attestation",
246246
name: namespace + ".GetAggregateAttestationV2",
247247
middleware: []middleware.Middleware{
248-
middleware.AcceptHeaderHandler([]string{api.JsonMediaType}),
248+
middleware.AcceptHeaderHandler([]string{api.JsonMediaType, api.OctetStreamMediaType}),
249249
},
250250
handler: server.GetAggregateAttestationV2,
251251
methods: []string{http.MethodGet},
@@ -314,7 +314,7 @@ func (s *Service) validatorEndpoints(
314314
template: "/eth/v1/validator/attestation_data",
315315
name: namespace + ".GetAttestationData",
316316
middleware: []middleware.Middleware{
317-
middleware.AcceptHeaderHandler([]string{api.JsonMediaType}),
317+
middleware.AcceptHeaderHandler([]string{api.JsonMediaType, api.OctetStreamMediaType}),
318318
},
319319
handler: server.GetAttestationData,
320320
methods: []string{http.MethodGet},

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1265,6 +1265,15 @@ func (s *Server) GetCommittees(w http.ResponseWriter, r *http.Request) {
12651265
httputil.HandleError(w, "Could not get epoch end slot: "+err.Error(), http.StatusInternalServerError)
12661266
return
12671267
}
1268+
1269+
// Validate that the provided slot belongs to the specified epoch, as required by the Beacon API spec.
1270+
if rawSlot != "" {
1271+
if sl < uint64(startSlot) || sl > uint64(endSlot) {
1272+
httputil.HandleError(w, fmt.Sprintf("Slot %d does not belong in epoch %d", sl, epoch), http.StatusBadRequest)
1273+
return
1274+
}
1275+
}
1276+
12681277
committeesPerSlot := corehelpers.SlotCommitteeCount(activeCount)
12691278
committees := make([]*structs.Committee, 0)
12701279
for slot := startSlot; slot <= endSlot; slot++ {

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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4078,6 +4078,47 @@ func TestGetCommittees(t *testing.T) {
40784078
require.NoError(t, err)
40794079
assert.Equal(t, true, resp.Finalized)
40804080
})
4081+
4082+
t.Run("Invalid slot for given epoch", func(t *testing.T) {
4083+
cases := []struct {
4084+
name string
4085+
epoch string
4086+
slot string
4087+
expectMsg string
4088+
}{
4089+
{
4090+
name: "Slot after the specified epoch",
4091+
epoch: "10",
4092+
slot: "400",
4093+
expectMsg: "Slot 400 does not belong in epoch 10",
4094+
},
4095+
{
4096+
name: "Slot before the specified epoch",
4097+
epoch: "10",
4098+
slot: "300",
4099+
expectMsg: "Slot 300 does not belong in epoch 10",
4100+
},
4101+
}
4102+
4103+
for _, tc := range cases {
4104+
t.Run(tc.name, func(t *testing.T) {
4105+
query := url + "?epoch=" + tc.epoch + "&slot=" + tc.slot
4106+
request := httptest.NewRequest(http.MethodGet, query, nil)
4107+
request.SetPathValue("state_id", "head")
4108+
writer := httptest.NewRecorder()
4109+
writer.Body = &bytes.Buffer{}
4110+
s.GetCommittees(writer, request)
4111+
assert.Equal(t, http.StatusBadRequest, writer.Code)
4112+
var resp struct {
4113+
Message string `json:"message"`
4114+
Code int `json:"code"`
4115+
}
4116+
err := json.Unmarshal(writer.Body.Bytes(), &resp)
4117+
assert.NoError(t, err)
4118+
assert.Equal(t, tc.expectMsg, resp.Message)
4119+
})
4120+
}
4121+
})
40814122
}
40824123

40834124
func TestGetBlockHeaders(t *testing.T) {

beacon-chain/rpc/eth/node/handlers_peers.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,12 @@ func (s *Server) GetPeers(w http.ResponseWriter, r *http.Request) {
112112
}
113113
allPeers = append(allPeers, p)
114114
}
115-
resp := &structs.GetPeersResponse{Data: allPeers}
115+
resp := &structs.GetPeersResponse{
116+
Data: allPeers,
117+
Meta: structs.Meta{
118+
Count: len(allPeers),
119+
},
120+
}
116121
httputil.WriteJson(w, resp)
117122
return
118123
}
@@ -177,7 +182,12 @@ func (s *Server) GetPeers(w http.ResponseWriter, r *http.Request) {
177182
filteredPeers = append(filteredPeers, p)
178183
}
179184

180-
resp := &structs.GetPeersResponse{Data: filteredPeers}
185+
resp := &structs.GetPeersResponse{
186+
Data: filteredPeers,
187+
Meta: structs.Meta{
188+
Count: len(filteredPeers),
189+
},
190+
}
181191
httputil.WriteJson(w, resp)
182192
}
183193

beacon-chain/rpc/eth/node/handlers_peers_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ func TestGetPeers(t *testing.T) {
145145
resp := &structs.GetPeersResponse{}
146146
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
147147
require.Equal(t, 1, len(resp.Data))
148+
assert.Equal(t, 1, resp.Meta.Count)
148149
returnedPeer := resp.Data[0]
149150
assert.Equal(t, expectedId.String(), returnedPeer.PeerId)
150151
expectedEnr, err := peerStatus.ENR(expectedId)
@@ -229,6 +230,7 @@ func TestGetPeers(t *testing.T) {
229230
resp := &structs.GetPeersResponse{}
230231
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
231232
assert.Equal(t, len(tt.wantIds), len(resp.Data), "Wrong number of peers returned")
233+
assert.Equal(t, len(tt.wantIds), resp.Meta.Count, "meta.count does not match number of returned peers")
232234
for _, id := range tt.wantIds {
233235
expectedId := id.String()
234236
found := false

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

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,39 @@ func (s *Server) GetAggregateAttestationV2(w http.ResponseWriter, r *http.Reques
9898
if agg == nil {
9999
return
100100
}
101+
102+
if httputil.RespondWithSsz(r) {
103+
var data []byte
104+
var err error
105+
if v >= version.Electra {
106+
typedAgg, ok := agg.(*ethpbalpha.AttestationElectra)
107+
if !ok {
108+
httputil.HandleError(w, fmt.Sprintf("Attestation is not of type %T", &ethpbalpha.AttestationElectra{}), http.StatusInternalServerError)
109+
return
110+
}
111+
data, err = typedAgg.MarshalSSZ()
112+
if err != nil {
113+
httputil.HandleError(w, "Could not marshal attestation: "+err.Error(), http.StatusInternalServerError)
114+
return
115+
}
116+
} else {
117+
typedAgg, ok := agg.(*ethpbalpha.Attestation)
118+
if !ok {
119+
httputil.HandleError(w, fmt.Sprintf("Attestation is not of type %T", &ethpbalpha.Attestation{}), http.StatusInternalServerError)
120+
return
121+
}
122+
data, err = typedAgg.MarshalSSZ()
123+
if err != nil {
124+
httputil.HandleError(w, "Could not marshal attestation: "+err.Error(), http.StatusInternalServerError)
125+
return
126+
}
127+
}
128+
129+
w.Header().Set(api.VersionHeader, version.String(v))
130+
httputil.WriteSsz(w, data)
131+
return
132+
}
133+
101134
resp := &structs.AggregateAttestationResponse{
102135
Version: version.String(v),
103136
}
@@ -610,6 +643,16 @@ func (s *Server) GetAttestationData(w http.ResponseWriter, r *http.Request) {
610643
return
611644
}
612645

646+
if httputil.RespondWithSsz(r) {
647+
data, err := attestationData.MarshalSSZ()
648+
if err != nil {
649+
httputil.HandleError(w, "Could not marshal attestation data: "+err.Error(), http.StatusInternalServerError)
650+
return
651+
}
652+
httputil.WriteSsz(w, data)
653+
return
654+
}
655+
613656
response := &structs.GetAttestationDataResponse{
614657
Data: &structs.AttestationData{
615658
Slot: strconv.FormatUint(uint64(attestationData.Slot), 10),

0 commit comments

Comments
 (0)