Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
939ca24
cl/ssz: support progressive Gloas block hashing
domiwei Jul 15, 2026
774faad
cl/state: support progressive Gloas hashing
domiwei Jul 15, 2026
9b67a4c
cl/merkle_tree: use canonical progressive list helper
domiwei Jul 27, 2026
bb24ad9
cl/state: invalidate roots at Gloas fork
domiwei Jul 15, 2026
a65109e
cl/ssz: retain attestation fork version for hashing
domiwei Jul 15, 2026
4f536b2
cl/ssz: retain indexed attestation fork version
domiwei Jul 15, 2026
70f1cb0
cl/ssz: propagate aggregate fork version
domiwei Jul 15, 2026
2a251c1
cl/ssz: hash Gloas execution payload progressively
domiwei Jul 15, 2026
6d94508
cl/ssz: hash Gloas payload containers progressively
domiwei Jul 15, 2026
6872ca4
cl/ssz: decode Gloas execution request progressive lists
domiwei Jul 15, 2026
b04b997
cl/ssz: decode Gloas beacon body progressive lists
domiwei Jul 15, 2026
f26c262
cl/ssz: hash Gloas data columns progressively
domiwei Jul 15, 2026
60e1a2a
cl/ssz: merkleize partial column roots
domiwei Jul 15, 2026
1cd69b1
cl/lightclient: update Gloas proof depths
domiwei Jul 15, 2026
532df02
cl/merkle: build progressive container proofs
domiwei Jul 15, 2026
6415c9c
cl/state: follow alpha.12 builder deposits
domiwei Jul 15, 2026
c9652dd
cl/config: update builder withdrawal delay
domiwei Jul 15, 2026
c77083c
cl/state: test alpha.12 builder credentials
domiwei Jul 15, 2026
f985e7b
cl/transition: bound Gloas execution requests
domiwei Jul 15, 2026
117c1ec
cl/config: update builder deposit request limit
domiwei Jul 15, 2026
ab6ac3b
cl/spectest: cover static helper containers
domiwei Jul 15, 2026
06df6f3
cl/lightclient: build Gloas execution hash proof
domiwei Jul 15, 2026
6f0f870
cl/spectest: run Gloas fork choice cases
domiwei Jul 15, 2026
9e0650d
cl/spectest: run Gloas churn cases
domiwei Jul 15, 2026
4407c46
cl/spectest: run attestation reward cases
domiwei Jul 15, 2026
0215ee9
cl/spectest: make reward deltas decodable
domiwei Jul 15, 2026
fdb227b
cl/spectest: run sync committee gossip cases
domiwei Jul 15, 2026
5c46a5b
cl/spectest: cover phase0 reward deltas
domiwei Jul 15, 2026
3a1c191
cl/spectest: honor gossip fork epochs
domiwei Jul 15, 2026
c3f473f
cl/spectest: update consensus fixtures to alpha.12
domiwei Jul 15, 2026
19584c7
cl/spectest: reject malformed gossip bounds
domiwei Jul 15, 2026
c30d9d3
cl/spectest: test malformed fixture bounds
domiwei Jul 15, 2026
31bc7f8
cl/spectest: use common sha256 helper
domiwei Jul 27, 2026
77e0d9f
cl: harden Gloas progressive boundaries
domiwei Jul 31, 2026
893a3d8
cl: preserve aggregate fork version across JSON
domiwei Jul 31, 2026
77d6098
cl: harden progressive decoding boundaries
domiwei Aug 3, 2026
42ec832
cl: preserve progressive bid decode limits
domiwei Aug 3, 2026
c397add
cl: initialize progressive bids from JSON
domiwei Aug 3, 2026
6cb2eab
cl: address Copilot review findings
domiwei Aug 3, 2026
281153a
cl: address Gloas review findings
domiwei Aug 3, 2026
3cc88f2
cl: harden attestation aggregation state
domiwei Aug 3, 2026
5b45171
cltypes: merge bitlists by logical length
domiwei Aug 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 15 additions & 7 deletions cl/aggregation/pool_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@ func (p *aggregationPoolImpl) AddAttestation(inAtt *solid.Attestation) error {
defer p.aggregatesLock.Unlock()
att, ok := p.aggregates[hashRoot]
if !ok {
p.aggregates[hashRoot] = inAtt.Copy()
storedAttestation := inAtt.Copy()
storedAttestation.SetVersion(clversion)
p.aggregates[hashRoot] = storedAttestation
return nil
}

Expand All @@ -111,19 +113,21 @@ func (p *aggregationPoolImpl) AddAttestation(inAtt *solid.Attestation) error {
return err
}
// update attestation
p.aggregates[hashRoot] = &solid.Attestation{
mergedAttestation := &solid.Attestation{
AggregationBits: mergedBits,
Data: att.Data,
Signature: mergedSig,
}
mergedAttestation.SetVersion(clversion)
p.aggregates[hashRoot] = mergedAttestation
} else {
// Electra and after case, aggregate by committee
p.aggregateByCommittee(inAtt)
return p.aggregateByCommittee(inAtt, clversion)
}
return nil
}

func (p *aggregationPoolImpl) aggregateByCommittee(inAtt *solid.Attestation) error {
func (p *aggregationPoolImpl) aggregateByCommittee(inAtt *solid.Attestation, version clparams.StateVersion) error {
indices := inAtt.CommitteeBits.GetOnIndices()
if len(indices) != 1 {
// it's composed of multiple committees, so ignore
Expand All @@ -140,7 +144,9 @@ func (p *aggregationPoolImpl) aggregateByCommittee(inAtt *solid.Attestation) err
}
att, exist := p.aggregatesInCommittee.Get(key)
if !exist {
p.aggregatesInCommittee.Add(key, inAtt)
storedAttestation := inAtt.Copy()
storedAttestation.SetVersion(version)
p.aggregatesInCommittee.Add(key, storedAttestation)
return nil
}

Expand All @@ -164,12 +170,14 @@ func (p *aggregationPoolImpl) aggregateByCommittee(inAtt *solid.Attestation) err
}
var mergedSig [96]byte
copy(mergedSig[:], merged)
p.aggregatesInCommittee.Add(key, &solid.Attestation{
mergedAttestation := &solid.Attestation{
AggregationBits: mergedAggrBits,
CommitteeBits: att.CommitteeBits,
Data: att.Data,
Signature: mergedSig,
})
}
mergedAttestation.SetVersion(version)
p.aggregatesInCommittee.Add(key, mergedAttestation)
return nil
}

Expand Down
96 changes: 89 additions & 7 deletions cl/aggregation/pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,13 @@ func (t *PoolTestSuite) TestAddAttestationElectra() {
expectedCommitteeBits := solid.NewBitVector(64)
expectedCommitteeBits.SetBitAt(10, true)
expectedCommitteeBits.SetBitAt(10, true)
expected := &solid.Attestation{
AggregationBits: solid.BitlistFromBytes([]byte{0b00001101}, 2048*64),
Data: attData1,
Signature: mockAggrResult,
CommitteeBits: expectedCommitteeBits,
}
expected.SetVersion(clparams.ElectraVersion)

att1 := &solid.Attestation{
AggregationBits: solid.BitlistFromBytes([]byte{0b00001001}, 2048*64),
Expand Down Expand Up @@ -144,12 +151,7 @@ func (t *PoolTestSuite) TestAddAttestationElectra() {
t.mockEthClock.EXPECT().GetEpochAtSlot(gomock.Any()).Return(uint64(1)).Times(2)
t.mockEthClock.EXPECT().StateVersionByEpoch(gomock.Any()).Return(clparams.ElectraVersion).Times(2)
},
expect: &solid.Attestation{
AggregationBits: solid.BitlistFromBytes([]byte{0b00001101}, 2048*64),
Data: attData1,
Signature: mockAggrResult,
CommitteeBits: expectedCommitteeBits,
},
expect: expected,
},
}

Expand All @@ -169,6 +171,84 @@ func (t *PoolTestSuite) TestAddAttestationElectra() {
}
}

func (t *PoolTestSuite) TestMergedGloasAttestationUsesProgressiveHash() {
committeeBits := solid.NewBitVector(64)
committeeBits.SetBitAt(10, true)
att1 := &solid.Attestation{
AggregationBits: solid.BitlistFromBytes([]byte{0b00001001}, 2048*64),
Data: attData1,
Signature: [96]byte{'a'},
CommitteeBits: committeeBits,
}
att2 := att1.Copy()
att2.AggregationBits = solid.BitlistFromBytes([]byte{0b00001100}, 2048*64)
att2.Signature = [96]byte{'b'}
t.mockEthClock.EXPECT().GetEpochAtSlot(gomock.Any()).Return(uint64(1)).Times(2)
t.mockEthClock.EXPECT().StateVersionByEpoch(gomock.Any()).Return(clparams.GloasVersion).Times(2)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
pool := NewAggregationPool(ctx, t.mockBeaconConfig, nil, t.mockEthClock)

t.Require().NoError(pool.AddAttestation(att1))
t.Require().NoError(pool.AddAttestation(att2))
merged := pool.GetAggregatationByRootAndCommittee(attData1Root, 10)
got, err := merged.HashSSZ()
t.Require().NoError(err)
want, err := merged.HashSSZProgressive()
t.Require().NoError(err)
t.Equal(want, got)
}

func (t *PoolTestSuite) TestFirstGloasAttestationIsCopiedAndVersioned() {
committeeBits := solid.NewBitVector(64)
committeeBits.SetBitAt(10, true)
att := &solid.Attestation{
AggregationBits: solid.BitlistFromBytes([]byte{0b00001001}, 2048*64),
Data: attData1,
Signature: [96]byte{'a'},
CommitteeBits: committeeBits,
}
callerRoot, err := att.HashSSZ()
t.Require().NoError(err)
t.mockEthClock.EXPECT().GetEpochAtSlot(gomock.Any()).Return(uint64(1))
t.mockEthClock.EXPECT().StateVersionByEpoch(gomock.Any()).Return(clparams.GloasVersion)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
pool := NewAggregationPool(ctx, t.mockBeaconConfig, nil, t.mockEthClock)

t.Require().NoError(pool.AddAttestation(att))
afterAddRoot, err := att.HashSSZ()
t.Require().NoError(err)
t.Equal(callerRoot, afterAddRoot)
cached := pool.GetAggregatationByRootAndCommittee(attData1Root, 10)
t.NotSame(att, cached)
att.SetVersion(clparams.ElectraVersion)
got, err := cached.HashSSZ()
t.Require().NoError(err)
want, err := cached.HashSSZProgressive()
t.Require().NoError(err)
t.Equal(want, got)
}

func (t *PoolTestSuite) TestElectraAggregationErrorIsReturned() {
committeeBits := solid.NewBitVector(64)
committeeBits.SetBitAt(10, true)
att := &solid.Attestation{
AggregationBits: solid.BitlistFromBytes([]byte{0b00001001}, 2048*64),
Data: attData1,
Signature: [96]byte{'a'},
CommitteeBits: committeeBits,
}
t.mockEthClock.EXPECT().GetEpochAtSlot(gomock.Any()).Return(uint64(1)).Times(2)
t.mockEthClock.EXPECT().StateVersionByEpoch(gomock.Any()).Return(clparams.ElectraVersion).Times(2)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
pool := NewAggregationPool(ctx, t.mockBeaconConfig, nil, t.mockEthClock)

t.Require().NoError(pool.AddAttestation(att))
t.ErrorIs(pool.AddAttestation(att.Copy()), ErrIsSuperset)
}

func (t *PoolTestSuite) TestAddAttestation() {
testcases := []struct {
name string
Expand Down Expand Up @@ -236,7 +316,9 @@ func (t *PoolTestSuite) TestAddAttestation() {
pool.AddAttestation(tc.atts[i])
}
att := pool.GetAggregatationByRoot(tc.hashRoot)
t.Equal(tc.expect, att, tc.name)
expected := tc.expect.Copy()
expected.SetVersion(clparams.DenebVersion)
t.Equal(expected, att, tc.name)
}
}

Expand Down
5 changes: 5 additions & 0 deletions cl/beacon/handler/block_production.go
Original file line number Diff line number Diff line change
Expand Up @@ -1552,6 +1552,11 @@ func (a *ApiHandler) publishBlindedBlocks(w http.ResponseWriter, r *http.Request
if err := validateBlindedBlockRequest(signedBlindedBlock, version); err != nil {
return nil, beaconhttp.NewEndpointError(http.StatusBadRequest, err)
}
if err := solid.RangeErr(signedBlindedBlock.Block.Body.Attestations, func(_ int, attestation *solid.Attestation, _ int) error {
return attestation.ValidateForConfig(a.beaconChainCfg, version)
}); err != nil {
return nil, beaconhttp.NewEndpointError(http.StatusBadRequest, err)
}
if isJSON {
signedBlindedBlock.Block.SetVersion(version)
}
Expand Down
23 changes: 19 additions & 4 deletions cl/beacon/handler/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ func (a *ApiHandler) PostEthV1BeaconPoolBlsToExecutionChanges(w http.ResponseWri
return
}
failures := []poolingFailure{}
for _, v := range req {
for idx, v := range req {
encodedSSZ, err := v.EncodeSSZ(nil)
if err != nil {
beaconhttp.NewEndpointError(http.StatusInternalServerError, err).WriteTo(w)
Expand All @@ -356,7 +356,7 @@ func (a *ApiHandler) PostEthV1BeaconPoolBlsToExecutionChanges(w http.ResponseWri
if err := a.blsToExecutionChangeService.ProcessMessage(r.Context(), nil, &services.SignedBLSToExecutionChangeForGossip{
SignedBLSToExecutionChange: v,
}); err != nil && !errors.Is(err, services.ErrIgnore) {
failures = append(failures, poolingFailure{Index: len(failures), Message: err.Error()})
failures = append(failures, poolingFailure{Index: idx, Message: err.Error()})
continue
}

Expand All @@ -383,7 +383,22 @@ func (a *ApiHandler) PostEthV1ValidatorAggregatesAndProof(w http.ResponseWriter,
}

failures := []poolingFailure{}
for _, v := range req {
for idx, v := range req {
if v == nil || v.Message == nil || v.Message.Aggregate == nil || v.Message.Aggregate.Data == nil || v.Message.Aggregate.AggregationBits == nil {
failures = append(failures, poolingFailure{Index: idx, Message: "invalid aggregate and proof"})
continue
Comment thread
domiwei marked this conversation as resolved.
}
epoch := v.Message.Aggregate.Data.Slot / a.beaconChainCfg.SlotsPerEpoch
version := a.beaconChainCfg.GetCurrentStateVersion(epoch)
if version >= clparams.ElectraVersion && v.Message.Aggregate.CommitteeBits == nil {
failures = append(failures, poolingFailure{Index: idx, Message: "invalid aggregate and proof: missing committee bits"})
continue
}
v.SetVersion(version)
if err := v.Message.Aggregate.ValidateForConfig(a.beaconChainCfg, version); err != nil {
failures = append(failures, poolingFailure{Index: idx, Message: err.Error()})
continue
}
encodedSSZ, err := v.EncodeSSZ(nil)
if err != nil {
beaconhttp.NewEndpointError(http.StatusInternalServerError, err).WriteTo(w)
Expand All @@ -399,7 +414,7 @@ func (a *ApiHandler) PostEthV1ValidatorAggregatesAndProof(w http.ResponseWriter,
log.Debug("[Beacon REST] aggregate ignored", "err", err, "slot", v.Message.Aggregate.Data.Slot)
} else if err != nil {
log.Warn("[Beacon REST] failed to process aggregate", "err", err)
failures = append(failures, poolingFailure{Index: len(failures), Message: err.Error()})
failures = append(failures, poolingFailure{Index: idx, Message: err.Error()})
continue
}
if err := a.gossipManager.Publish(r.Context(), gossip.TopicNameBeaconAggregateAndProof, encodedSSZ); err != nil {
Expand Down
48 changes: 38 additions & 10 deletions cl/beacon/handler/pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,9 @@ import (
)

func TestPoolAttesterSlashings(t *testing.T) {
attesterSlashing := &cltypes.AttesterSlashing{
Attestation_1: &cltypes.IndexedAttestation{
AttestingIndices: solid.NewRawUint64List(2048, []uint64{2, 3, 4, 5, 6}),
Data: &solid.AttestationData{},
},
Attestation_2: &cltypes.IndexedAttestation{
AttestingIndices: solid.NewRawUint64List(2048, []uint64{2, 3, 4, 1, 6}),
Data: &solid.AttestationData{},
},
}
attesterSlashing := cltypes.NewAttesterSlashing(clparams.DenebVersion)
attesterSlashing.Attestation_1.AttestingIndices = solid.NewRawUint64List(2048, []uint64{2, 3, 4, 5, 6})
attesterSlashing.Attestation_2.AttestingIndices = solid.NewRawUint64List(2048, []uint64{2, 3, 4, 1, 6})
// find server
_, _, _, _, _, handler, _, syncedDataMgr, _, _ := setupTestingHandler(t, clparams.Phase0Version, log.Root(), false)
mockBeaconState := &state.CachingBeaconState{BeaconState: raw.New(&clparams.BeaconChainConfig{})}
Expand Down Expand Up @@ -305,6 +298,41 @@ func TestPoolAggregatesAndProofs(t *testing.T) {
require.Equal(t, msg[1].Message.Aggregate, out.Data[1])
}

func TestPoolAggregatesAndProofsReportsRequestIndex(t *testing.T) {
msg := []*cltypes.SignedAggregateAndProof{
{
Message: &cltypes.AggregateAndProof{
Aggregate: &solid.Attestation{
AggregationBits: solid.BitlistFromBytes([]byte{1, 2}, 2048),
Data: &solid.AttestationData{},
Signature: common.Bytes96{3, 45, 6},
},
},
Signature: common.Bytes96{2},
},
nil,
}
_, _, _, _, _, handler, _, syncedDataMgr, _, _ := setupTestingHandler(t, clparams.Phase0Version, log.Root(), false)
mockBeaconState := &state.CachingBeaconState{BeaconState: raw.New(&clparams.BeaconChainConfig{})}
mockBeaconState.SetVersion(clparams.DenebVersion)
syncedDataMgr.(*sync_mock_services.MockSyncedData).EXPECT().ViewHeadState(gomock.Any()).DoAndReturn(func(vhsf synced_data.ViewHeadStateFn) error {
vhsf(mockBeaconState)
return nil
}).AnyTimes()
server := httptest.NewServer(handler.mux)
defer server.Close()
requestBody, err := json.Marshal(msg)
require.NoError(t, err)

resp, err := server.Client().Post(server.URL+"/eth/v1/validator/aggregate_and_proofs", "application/json", bytes.NewBuffer(requestBody))
require.NoError(t, err)
defer resp.Body.Close()
require.Equal(t, 400, resp.StatusCode)
var response poolingError
require.NoError(t, json.NewDecoder(resp.Body).Decode(&response))
require.Equal(t, []poolingFailure{{Index: 1, Message: "invalid aggregate and proof"}}, response.Failures)
}

func TestPoolSyncCommittees(t *testing.T) {
msgs := []*cltypes.SyncCommitteeMessage{
{
Expand Down
6 changes: 3 additions & 3 deletions cl/clparams/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -1096,7 +1096,7 @@ var MainnetBeaconConfig BeaconChainConfig = BeaconChainConfig{
MaxDepositRequestsPerPayload: 8192,
MaxWithdrawalRequestsPerPayload: 16,
MaxConsolidationRequestsPerPayload: 2,
MaxBuilderDepositRequestsPerPayload: 256,
MaxBuilderDepositRequestsPerPayload: 64,
MaxBuilderExitRequestsPerPayload: 16,
MinSlashingPenaltyQuotientElectra: 4096,
WhistleBlowerRewardQuotientElectra: 4096,
Expand Down Expand Up @@ -1130,14 +1130,14 @@ var MainnetBeaconConfig BeaconChainConfig = BeaconChainConfig{
ChurnLimitQuotientGloas: 1 << 15,
ConsolidationChurnLimitQuotient: 1 << 16,
MaxPerEpochActivationChurnLimitGloas: 256_000_000_000,
BuilderWithdrawalPrefix: 0x03,
BuilderWithdrawalPrefix: 0xB0,
PayloadDueBps: 7500,
PtcSize: 512,
MaxPayloadAttestations: 4,
BuilderRegistryLimit: 1 << 40,
BuilderPendingWithdrawalsLimit: 1 << 20,
MaxBuildersPerWithdrawalsSweep: 1 << 14,
MinBuilderWithdrawabilityDelay: 8192,
MinBuilderWithdrawabilityDelay: 64,
}

func mainnetConfig() BeaconChainConfig {
Expand Down
Loading
Loading