Skip to content
Merged
Show file tree
Hide file tree
Changes from 71 commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
877ad54
remove aggregatorCommittee from BeaconRole
MatheusFranco99 Dec 17, 2025
6042b46
remove aggregator and scc roles
MatheusFranco99 Dec 17, 2025
43c258c
remove agg and scc from ValidatorConsensusData
MatheusFranco99 Dec 17, 2025
77cafc6
generate ssz encoding
MatheusFranco99 Dec 17, 2025
c29cab4
align testingutils to remove reference to agg and scc alone
MatheusFranco99 Dec 17, 2025
d0fe1b7
add agg committee consensus data tests (and remove agg and scc from v…
MatheusFranco99 Dec 17, 2025
80d2575
generate types JSON tests
MatheusFranco99 Dec 17, 2025
15c0097
drop agg and scc runners; fix agg committee runner issue
MatheusFranco99 Dec 17, 2025
3a90436
align testingutils for agg committee tests
MatheusFranco99 Dec 17, 2025
eb2603b
value check tests
MatheusFranco99 Dec 17, 2025
2f36539
preconsensus tests
MatheusFranco99 Dec 17, 2025
35494fe
post consensus tests
MatheusFranco99 Dec 17, 2025
29f8546
duties tests
MatheusFranco99 Dec 17, 2025
0d94a9f
runner construction tests
MatheusFranco99 Dec 17, 2025
5522634
consensus tests
MatheusFranco99 Dec 17, 2025
1eea4d8
happy flow test
MatheusFranco99 Dec 17, 2025
c95d636
dutyexe tests
MatheusFranco99 Dec 17, 2025
e746f6f
add test docs; fix msg processing test
MatheusFranco99 Dec 17, 2025
e93301b
add all tests
MatheusFranco99 Dec 17, 2025
0ca88b3
generate JSON tests
MatheusFranco99 Dec 17, 2025
cafd39f
drop weird json tests in unintended directory
MatheusFranco99 Dec 20, 2025
2e3c59e
add mixed agg+scc pre-consensus tests
MatheusFranco99 Dec 20, 2025
0b017e7
generate JSON tests
MatheusFranco99 Dec 20, 2025
de5bb1d
add error code
MatheusFranco99 Dec 21, 2025
36b5db0
increase number of Contributors
MatheusFranco99 Dec 21, 2025
a1dcc04
add post-consensus mixed agg committee tests
MatheusFranco99 Dec 21, 2025
c8ced68
generate JSON tests
MatheusFranco99 Dec 21, 2025
983436a
add agg committee duty validation; add psgi msg sorting;
MatheusFranco99 Dec 21, 2025
725412b
add sorting and duty validation tests
MatheusFranco99 Dec 21, 2025
e67f91f
generate JSON tests
MatheusFranco99 Dec 21, 2025
995ff50
fixed remaining mixed tests
MatheusFranco99 Dec 23, 2025
ed7b9f1
generate JSON tests
MatheusFranco99 Dec 23, 2025
e94dd0a
fix lint (remove unused functions)
MatheusFranco99 Dec 23, 2025
49fe3cf
Merge branch 'agg-comm-mixed-duties' into agg-comm-improvements
MatheusFranco99 Dec 23, 2025
b1a471f
change AggregatorCommitteeConsensusData to reduce duplicated data ove…
MatheusFranco99 Dec 23, 2025
32abf8c
align tests
MatheusFranco99 Dec 23, 2025
95d33d9
generate JSON tests
MatheusFranco99 Dec 23, 2025
307ca56
fix maximum ssz sizes
MatheusFranco99 Dec 23, 2025
c6b8215
avoid in-place sorting
MatheusFranco99 Dec 23, 2025
c42950f
add test docs
MatheusFranco99 Dec 23, 2025
603009d
remove unused test docs
MatheusFranco99 Dec 23, 2025
67d01c2
fix maximum-size tests
MatheusFranco99 Dec 23, 2025
28a2857
generate JSON tests
MatheusFranco99 Dec 23, 2025
ed8afc0
Merge branch 'aggregator-committee' into agg-comm-mixed-duties
MatheusFranco99 Dec 24, 2025
20c25bb
Merge branch 'agg-comm-mixed-duties' into agg-comm-improvements
MatheusFranco99 Dec 24, 2025
4b026a6
Merge branch 'aggregator-committee' into agg-comm-improvements
MatheusFranco99 Dec 24, 2025
e055e09
fix test: duty with diff slots
MatheusFranco99 Dec 24, 2025
a049b2f
add max size test for aggCommCD
MatheusFranco99 Dec 24, 2025
69ba19e
add size tests for phase0 and electra attestations; fix ssz max size …
MatheusFranco99 Dec 24, 2025
6129218
fix lint issues
MatheusFranco99 Dec 24, 2025
3689cb7
fix test dir (no multiple duty)
MatheusFranco99 Dec 24, 2025
0eb1e47
fix versions data
MatheusFranco99 Dec 24, 2025
cd761b8
maximum duty possible test
MatheusFranco99 Dec 24, 2025
28ab5c9
fix lint
MatheusFranco99 Dec 24, 2025
b1dde26
apply suggestions (remove sorting feature; remove unused errors; use …
MatheusFranco99 Dec 26, 2025
c6e5fd8
clarify validator sync committee index usage
MatheusFranco99 Dec 26, 2025
6d4decb
change subnetID computation to avoid errors
MatheusFranco99 Dec 26, 2025
4f30d5b
generate JSON tests
MatheusFranco99 Dec 26, 2025
d184645
tests for: invalid quorum; invalid quorum then valid quorum;
MatheusFranco99 Dec 26, 2025
63c6233
generate JSON tests
MatheusFranco99 Dec 26, 2025
172ef5b
Merge branch 'aggregator-committee' into agg-comm-improvements
MatheusFranco99 Dec 26, 2025
1bb7ec3
generate JSON tests with new error numbers
MatheusFranco99 Dec 26, 2025
e2815a7
Merge branch 'aggregator-committee' into agg-comm-improvements
MatheusFranco99 Dec 26, 2025
6839788
remove deprecated partial signature types
MatheusFranco99 Jan 2, 2026
25d060a
generate JSON tests
MatheusFranco99 Jan 2, 2026
18e3f37
generate SSZ files
MatheusFranco99 Jan 2, 2026
89d2fff
fix committee to have agg and comm runners
MatheusFranco99 Jan 2, 2026
1b89dcc
align testing utils. Fix Committee constructor to a common one, and a…
MatheusFranco99 Jan 2, 2026
321e00d
add test for comm + agg comm duties in the same slot
MatheusFranco99 Jan 2, 2026
d0f516d
generate JSON tests
MatheusFranco99 Jan 2, 2026
cac61ae
remove unused function
MatheusFranco99 Jan 2, 2026
97381bb
Merge branch 'aggregator-committee' into committee-runners
MatheusFranco99 Jan 5, 2026
066f73b
add tests for error cases in committee
MatheusFranco99 Jan 5, 2026
dfd6567
add test for mixed duties for multiple slots
MatheusFranco99 Jan 5, 2026
7d51718
remove unused parameter
MatheusFranco99 Jan 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
85 changes: 59 additions & 26 deletions ssv/aggregator_committee.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func (r *AggregatorCommitteeRunner) ProcessPreConsensus(signedMsg *types.Partial

duty := r.BaseRunner.State.StartingDuty.(*types.AggregatorCommitteeDuty)
epoch := r.beacon.GetBeaconNetwork().EstimatedEpochAtSlot(duty.Slot)
aggregatorData := &types.AggregatorCommitteeConsensusData{
consensusData := &types.AggregatorCommitteeConsensusData{
Version: r.beacon.DataVersion(epoch),
}
hasAnyAggregator := false
Expand Down Expand Up @@ -166,7 +166,7 @@ func (r *AggregatorCommitteeRunner) ProcessPreConsensus(signedMsg *types.Partial
case types.BNRoleAggregator:
vDuty := r.findValidatorDuty(validatorIndex, types.BNRoleAggregator)
if vDuty != nil {
isAggregator, err := r.processAggregatorSelectionProof(blsSig, vDuty, aggregatorData)
isAggregator, err := r.processAggregatorSelectionProof(blsSig, vDuty, consensusData)
if err == nil {
if isAggregator {
hasAnyAggregator = true
Expand All @@ -179,7 +179,7 @@ func (r *AggregatorCommitteeRunner) ProcessPreConsensus(signedMsg *types.Partial
case types.BNRoleSyncCommitteeContribution:
vDuty := r.findValidatorDuty(validatorIndex, types.BNRoleSyncCommitteeContribution)
if vDuty != nil {
isAggregator, err := r.processSyncCommitteeSelectionProof(blsSig, metadata.SyncCommitteeIndex, vDuty, aggregatorData)
isAggregator, err := r.processSyncCommitteeSelectionProof(blsSig, metadata.ValidatorSyncCommitteeIndex, vDuty, consensusData)
if err == nil {
if isAggregator {
hasAnyAggregator = true
Expand All @@ -196,27 +196,29 @@ func (r *AggregatorCommitteeRunner) ProcessPreConsensus(signedMsg *types.Partial
}
}

// Early exit if no aggregators selected (conditioned to no error)
// Early exit if no error and no aggregators is selected (really no operator is aggregator or sync committee contributor)
if !hasAnyAggregator && anyErr == nil {
r.BaseRunner.State.Finished = true
return nil
}

// If there was an error, and no aggregators or contributors were selected, return the error
if len(aggregatorData.Aggregators) == 0 &&
len(aggregatorData.Contributors) == 0 &&
if len(consensusData.Aggregators) == 0 &&
len(consensusData.Contributors) == 0 &&
anyErr != nil {
return anyErr
}

if err := aggregatorData.Validate(); err != nil {
// Else, if some aggregators or contributors were selected (even with an error for others), proceed to consensus
if err := consensusData.Validate(); err != nil {
return errors.Wrap(err, "invalid aggregator consensus data")
}

if err := r.BaseRunner.decide(r, r.BaseRunner.State.StartingDuty.DutySlot(), aggregatorData); err != nil {
if err := r.BaseRunner.decide(r, r.BaseRunner.State.StartingDuty.DutySlot(), consensusData); err != nil {
return errors.Wrap(err, "failed to start consensus")
}

// Raise error if any
if anyErr != nil {
return anyErr
}
Expand Down Expand Up @@ -469,6 +471,15 @@ func (r *AggregatorCommitteeRunner) executeDuty(duty types.Duty) error {
return errors.New("invalid duty type for aggregator committee runner")
}

// Validate duty
valIdxs := make(map[phase0.ValidatorIndex]struct{})
for idx := range r.BaseRunner.Share {
valIdxs[idx] = struct{}{}
}
if err := aggCommitteeDuty.Validate(valIdxs); err != nil {
return err
}

msg := &types.PartialSignatureMessages{
Type: types.AggregatorCommitteePartialSig,
Slot: duty.DutySlot(),
Expand Down Expand Up @@ -636,6 +647,21 @@ func (r *AggregatorCommitteeRunner) processAggregatorSelectionProof(
return false, nil
}

// Check if attestation for committee index was already included
for _, idx := range aggregatorData.AggregatorsCommitteeIndexes {
if idx == uint64(vDuty.CommitteeIndex) {
// If so, just add to aggregators and return
aggregatorData.Aggregators = append(aggregatorData.Aggregators, types.AssignedAggregator{
ValidatorIndex: vDuty.ValidatorIndex,
SelectionProof: selectionProof,
CommitteeIndex: uint64(vDuty.CommitteeIndex),
})
return true, nil
}
}

// Else, fetch attestation and include everything (if successful)

attestation, err := r.beacon.GetAggregateAttestation(vDuty.Slot, vDuty.CommitteeIndex)
if err != nil {
return true, errors.Wrap(err, "failed to get aggregate attestation")
Expand All @@ -654,19 +680,19 @@ func (r *AggregatorCommitteeRunner) processAggregatorSelectionProof(
}

aggregatorData.AggregatorsCommitteeIndexes = append(aggregatorData.AggregatorsCommitteeIndexes, uint64(vDuty.CommitteeIndex))
aggregatorData.Attestations = append(aggregatorData.Attestations, attestationBytes)
aggregatorData.AggregatedAttestations = append(aggregatorData.AggregatedAttestations, attestationBytes)

return true, nil
}

// processSyncCommitteeSelectionProof handles sync committee selection proofs with known index
func (r *AggregatorCommitteeRunner) processSyncCommitteeSelectionProof(
selectionProof phase0.BLSSignature,
syncCommitteeIndex uint64,
validatorSyncCommitteeIndex types.ValidatorSyncCommitteeIndex,
vDuty *types.ValidatorDuty,
aggregatorData *types.AggregatorCommitteeConsensusData,
) (bool, error) {
subnetID := r.beacon.SyncCommitteeSubnetID(phase0.CommitteeIndex(syncCommitteeIndex))
subnetID := r.beacon.SyncCommitteeSubnetID(phase0.CommitteeIndex(validatorSyncCommitteeIndex))

isAggregator := r.beacon.IsSyncCommitteeAggregator(selectionProof[:])

Expand All @@ -675,13 +701,20 @@ func (r *AggregatorCommitteeRunner) processSyncCommitteeSelectionProof(
}

// Check if we already have a contribution for this sync committee subnet ID
for _, existingSubnet := range aggregatorData.SyncCommitteeSubnets {
if existingSubnet == subnetID {
// Contribution already exists for this subnet—skip duplicate.
for _, contrib := range aggregatorData.SyncCommitteeContributions {
if contrib.SubcommitteeIndex == subnetID {
// If so, just add to contributors and return
aggregatorData.Contributors = append(aggregatorData.Contributors, types.AssignedAggregator{
ValidatorIndex: vDuty.ValidatorIndex,
SelectionProof: selectionProof,
CommitteeIndex: subnetID,
})
return true, nil
}
}

// Else, fetch contribution and include everything (if successful)

contributions, _, err := r.GetBeaconNode().GetSyncCommitteeContribution(
vDuty.Slot, []phase0.BLSSignature{selectionProof}, []uint64{subnetID})
if err != nil {
Expand All @@ -707,9 +740,9 @@ func (r *AggregatorCommitteeRunner) processSyncCommitteeSelectionProof(
aggregatorData.Contributors = append(aggregatorData.Contributors, types.AssignedAggregator{
ValidatorIndex: vDuty.ValidatorIndex,
SelectionProof: selectionProof,
CommitteeIndex: subnetID,
})

aggregatorData.SyncCommitteeSubnets = append(aggregatorData.SyncCommitteeSubnets, subnetID)
aggregatorData.SyncCommitteeContributions = append(aggregatorData.SyncCommitteeContributions, contrib.Contribution)
}

Expand All @@ -734,9 +767,9 @@ func (r *AggregatorCommitteeRunner) expectedAggregatorSelectionRoot(
func (r *AggregatorCommitteeRunner) expectedSyncCommitteeSelectionRoot(
_ *types.ValidatorDuty,
slot phase0.Slot,
syncCommitteeIndex uint64,
validatorSyncCommitteeIndex types.ValidatorSyncCommitteeIndex,
) ([32]byte, error) {
subnet := r.beacon.SyncCommitteeSubnetID(phase0.CommitteeIndex(syncCommitteeIndex))
subnet := r.beacon.SyncCommitteeSubnetID(phase0.CommitteeIndex(validatorSyncCommitteeIndex))

data := &altair.SyncAggregatorSelectionData{
Slot: slot,
Expand All @@ -756,11 +789,11 @@ func (r *AggregatorCommitteeRunner) expectedSyncCommitteeSelectionRoot(
// It returns the aggregator and sync committee validator to root maps.
func (r *AggregatorCommitteeRunner) expectedPreConsensusRoots() (
aggregatorMap map[phase0.ValidatorIndex][32]byte,
contributionMap map[phase0.ValidatorIndex]map[uint64][32]byte,
contributionMap map[phase0.ValidatorIndex]map[types.ValidatorSyncCommitteeIndex][32]byte,
error error,
) {
aggregatorMap = make(map[phase0.ValidatorIndex][32]byte)
contributionMap = make(map[phase0.ValidatorIndex]map[uint64][32]byte)
contributionMap = make(map[phase0.ValidatorIndex]map[types.ValidatorSyncCommitteeIndex][32]byte)

duty := r.BaseRunner.State.StartingDuty.(*types.AggregatorCommitteeDuty)

Expand Down Expand Up @@ -893,16 +926,16 @@ func (r *AggregatorCommitteeRunner) expectedPostConsensusRootsAndBeaconObjects()
// - findValidatorsForPreConsensusRoot: Returns detailed metadata including sync committee indices (pre-consensus)
// - findValidatorsForPostConsensusRoot: Returns just the role and validator list (post-consensus)
type preConsensusMetadata struct {
ValidatorIndex phase0.ValidatorIndex
Role types.BeaconRole
SyncCommitteeIndex uint64 // only for sync committee role
ValidatorIndex phase0.ValidatorIndex
Role types.BeaconRole
ValidatorSyncCommitteeIndex types.ValidatorSyncCommitteeIndex // only for sync committee role
}

// findValidatorsForPreConsensusRoot finds all validators that have the given root in pre-consensus
func findValidatorsForPreConsensusRoot(
expectedRoot [32]byte,
aggregatorMap map[phase0.ValidatorIndex][32]byte,
contributionMap map[phase0.ValidatorIndex]map[uint64][32]byte,
contributionMap map[phase0.ValidatorIndex]map[types.ValidatorSyncCommitteeIndex][32]byte,
) ([]preConsensusMetadata, bool) {
var metadata []preConsensusMetadata

Expand All @@ -921,9 +954,9 @@ func findValidatorsForPreConsensusRoot(
for index, root := range indexMap {
if root == expectedRoot {
metadata = append(metadata, preConsensusMetadata{
ValidatorIndex: validator,
Role: types.BNRoleSyncCommitteeContribution,
SyncCommitteeIndex: index,
ValidatorIndex: validator,
Role: types.BNRoleSyncCommitteeContribution,
ValidatorSyncCommitteeIndex: index,
})
}
}
Expand Down
Loading