max_agg_comm_consensus_data.go constructs aggregation_bits for the max Phase0/Electra attestation fixtures by allocating MAX_VALIDATORS_PER_* bytes, where the SSZ spec defines the field as a Bitlist whose size parameter is in bits:
Per consensus-specs, aggregation_bits is Bitlist[MAX_VALIDATORS_PER_COMMITTEE] (Phase0) and Bitlist[MAX_VALIDATORS_PER_COMMITTEE * MAX_COMMITTEES_PER_SLOT] (Electra), with MAX_VALIDATORS_PER_COMMITTEE = 2048, MAX_COMMITTEES_PER_SLOT = 64. The SSZ Bitlist encoding packs N data bits plus a single delimiter bit into ⌈(N+1)/8⌉ bytes:
| Bitlist |
Max canonical encoded bytes |
Bitlist[2048] |
257 |
Bitlist[131072] |
16385 |
So the spec-correct max attestation sizes are:
| Type |
Spec-correct max |
Constant in ssv-spec |
Inflation |
phase0.Attestation |
4 + 128 + 96 + 257 = 485 |
MaxSizePhase0Attestation = 2276 |
~4.7x |
electra.Attestation |
4 + 128 + 96 + 8 + 16385 = 16621 |
MaxSizeElectraAttestation = 131308 |
~7.9x |
The bytes produced are also non-canonical: [0x01, 0x00, …, 0x00] puts the delimiter at bit 0 (length = 0 bits) followed by 2047/131071 trailing zero bytes. Strict SSZ decoders reject this — including fastssz's own ValidateBitlist, so even go-eth2-client's generated Attestation.UnmarshalSSZ (which ssv-spec uses) would fail to round-trip these fixtures. The current test only calls Object.Encode(), so it never hits the decoder check.
Two related causes let this through:
- The fastssz-generated encoder for
bitfield.Bitlist checks len(bytes) <= 2048, treating the ssz-max:"2048" tag as a byte limit. The SSZ-spec interpretation is bits — the encoder check is the same dimension confusion.
checkSSZTags compares Len() (byte count for bitfield.Bitlist / []byte) against the ssz-max tag — same confusion in the validator.
Also affects MaxSizeAggregatorCommitteeConsensusData = 8970524, since AggregatedAttestations is budgeted as 64 × MaxSizeElectraAttestation. With the corrected per-attestation max, the aggregated-attestations region shrinks by ~8x.
Suggested fix (Phase0; analogous for Electra)
var aggbits [257]byte
for i := range aggbits[:256] { aggbits[i] = 0xFF }
aggbits[256] = 0x01 // delimiter at bit 2048
Blast radius
grep finds no production callers of these three constants — they're only used inside this fixture file. So no on-wire impact. But downstream consumers (other client impls, ops/budgeting docs) reading these as the canonical max-message sizes would over-allocate by ~8x, and any client that tries to round-trip these fixtures through a strict decoder will fail (which is how I found this — Anchor's strict SSZ decoder rejects the AggregationBits payload with InvalidByteCount { given: 2048, expected: 1 }).
max_agg_comm_consensus_data.goconstructsaggregation_bitsfor the max Phase0/Electra attestation fixtures by allocatingMAX_VALIDATORS_PER_*bytes, where the SSZ spec defines the field as aBitlistwhose size parameter is in bits:max_agg_comm_consensus_data.go#L105—aggbits := [2048]byte{1}(Phase0)max_agg_comm_consensus_data.go#L128—aggbits := [131072]byte{1}(Electra)Per consensus-specs,
aggregation_bitsisBitlist[MAX_VALIDATORS_PER_COMMITTEE](Phase0) andBitlist[MAX_VALIDATORS_PER_COMMITTEE * MAX_COMMITTEES_PER_SLOT](Electra), withMAX_VALIDATORS_PER_COMMITTEE = 2048,MAX_COMMITTEES_PER_SLOT = 64. The SSZ Bitlist encoding packs N data bits plus a single delimiter bit into⌈(N+1)/8⌉bytes:Bitlist[2048]Bitlist[131072]So the spec-correct max attestation sizes are:
phase0.AttestationMaxSizePhase0Attestation = 2276electra.AttestationMaxSizeElectraAttestation = 131308The bytes produced are also non-canonical:
[0x01, 0x00, …, 0x00]puts the delimiter at bit 0 (length = 0 bits) followed by 2047/131071 trailing zero bytes. Strict SSZ decoders reject this — including fastssz's ownValidateBitlist, so evengo-eth2-client's generatedAttestation.UnmarshalSSZ(which ssv-spec uses) would fail to round-trip these fixtures. The current test only callsObject.Encode(), so it never hits the decoder check.Two related causes let this through:
bitfield.Bitlistcheckslen(bytes) <= 2048, treating thessz-max:"2048"tag as a byte limit. The SSZ-spec interpretation is bits — the encoder check is the same dimension confusion.checkSSZTagscomparesLen()(byte count forbitfield.Bitlist/[]byte) against thessz-maxtag — same confusion in the validator.Also affects
MaxSizeAggregatorCommitteeConsensusData = 8970524, sinceAggregatedAttestationsis budgeted as64 × MaxSizeElectraAttestation. With the corrected per-attestation max, the aggregated-attestations region shrinks by ~8x.Suggested fix (Phase0; analogous for Electra)
Blast radius
grepfinds no production callers of these three constants — they're only used inside this fixture file. So no on-wire impact. But downstream consumers (other client impls, ops/budgeting docs) reading these as the canonical max-message sizes would over-allocate by ~8x, and any client that tries to round-trip these fixtures through a strict decoder will fail (which is how I found this — Anchor's strict SSZ decoder rejects the AggregationBits payload withInvalidByteCount { given: 2048, expected: 1 }).