Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
  •  
  •  
  •  
137 changes: 81 additions & 56 deletions ssv/committee.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,91 +13,96 @@ import (
type CreateRunnerFn func(shareMap map[phase0.ValidatorIndex]*types.Share) Runner

type Committee struct {
Runners map[phase0.Slot]Runner
CommitteeMember types.CommitteeMember
CreateRunnerFn CreateRunnerFn
Share map[phase0.ValidatorIndex]*types.Share
CommitteeRunners map[phase0.Slot]Runner
AggregatorCommitteeRunners map[phase0.Slot]Runner
CommitteeMember types.CommitteeMember
CreateCommitteeRunnerFn CreateRunnerFn
CreateAggregatorCommitteeRunnerFn CreateRunnerFn
Share map[phase0.ValidatorIndex]*types.Share
}

// NewCommittee creates a new cluster
func NewCommittee(
committeeMember types.CommitteeMember,
share map[phase0.ValidatorIndex]*types.Share,
createRunnerFn CreateRunnerFn,
createAggregatorCommitteeRunnerFn CreateRunnerFn,
) *Committee {
c := &Committee{
Runners: make(map[phase0.Slot]Runner),
CommitteeMember: committeeMember,
CreateRunnerFn: createRunnerFn,
Share: share,
CommitteeRunners: make(map[phase0.Slot]Runner),
AggregatorCommitteeRunners: make(map[phase0.Slot]Runner),
CommitteeMember: committeeMember,
CreateCommitteeRunnerFn: createRunnerFn,
CreateAggregatorCommitteeRunnerFn: createAggregatorCommitteeRunnerFn,
Share: share,
}
return c
}

// StartDuty starts a new duty for the given slot
func (c *Committee) StartDuty(duty types.Duty) error {

slot := duty.DutySlot()
if _, exists := c.Runners[slot]; exists {
return fmt.Errorf("Runner for slot %d already exists", slot)
}

// Handle different duty types
// Get objects according to duty type
var runnerMap *map[phase0.Slot]Runner
var createFn *CreateRunnerFn
var validatorDuties []*types.ValidatorDuty
switch d := duty.(type) {
case *types.CommitteeDuty:
if len(d.ValidatorDuties) == 0 {
return types.NewError(types.NoBeaconDutiesErrorCode, "no beacon duties")
}

// Filter duty and create share map according validators that belong to c.Share
dutyShares := make(map[phase0.ValidatorIndex]*types.Share)
filteredDuty := &types.CommitteeDuty{
Slot: d.Slot,
}
runnerMap = &c.CommitteeRunners
createFn = &c.CreateCommitteeRunnerFn
validatorDuties = d.ValidatorDuties
case *types.AggregatorCommitteeDuty:
runnerMap = &c.AggregatorCommitteeRunners
createFn = &c.CreateAggregatorCommitteeRunnerFn
validatorDuties = d.ValidatorDuties
default:
return errors.Errorf("unsupported duty type: %T", duty)
}

for _, bduty := range d.ValidatorDuties {
if _, exists := c.Share[bduty.ValidatorIndex]; !exists {
continue
}
dutyShares[bduty.ValidatorIndex] = c.Share[bduty.ValidatorIndex]
filteredDuty.ValidatorDuties = append(filteredDuty.ValidatorDuties, bduty)
}
if _, exists := (*runnerMap)[slot]; exists {
return fmt.Errorf("Runner for slot %d already exists", slot)
}

if len(dutyShares) == 0 {
return types.NewError(types.NoValidatorSharesErrorCode, "no shares for duty's validators")
}
if len(validatorDuties) == 0 {
return types.NewError(types.NoBeaconDutiesErrorCode, "no beacon duties")
}

c.Runners[slot] = c.CreateRunnerFn(dutyShares)
return c.Runners[slot].StartNewDuty(filteredDuty, c.CommitteeMember.GetQuorum())
// Filter duty and create share map according validators that belong to c.Share
dutyShares := make(map[phase0.ValidatorIndex]*types.Share)
filteredValidatorDuties := make([]*types.ValidatorDuty, 0)

case *types.AggregatorCommitteeDuty:
if len(d.ValidatorDuties) == 0 {
return types.NewError(types.NoBeaconDutiesErrorCode, "no beacon duties")
for _, bduty := range validatorDuties {
if _, exists := c.Share[bduty.ValidatorIndex]; !exists {
continue
}
dutyShares[bduty.ValidatorIndex] = c.Share[bduty.ValidatorIndex]
filteredValidatorDuties = append(filteredValidatorDuties, bduty)
}

// Filter duty and create share map according to validators that belong to c.Share
dutyShares := make(map[phase0.ValidatorIndex]*types.Share)
filteredDuty := &types.AggregatorCommitteeDuty{
Slot: d.Slot,
}
if len(dutyShares) == 0 {
return types.NewError(types.NoValidatorSharesErrorCode, "no shares for duty's validators")
}

for _, bduty := range d.ValidatorDuties {
if _, exists := c.Share[bduty.ValidatorIndex]; !exists {
continue
}
dutyShares[bduty.ValidatorIndex] = c.Share[bduty.ValidatorIndex]
filteredDuty.ValidatorDuties = append(filteredDuty.ValidatorDuties, bduty)
var filteredDuty types.Duty
switch duty.(type) {
case *types.CommitteeDuty:
filteredDuty = &types.CommitteeDuty{
Slot: slot,
ValidatorDuties: filteredValidatorDuties,
}

if len(dutyShares) == 0 {
return types.NewError(types.NoValidatorSharesErrorCode, "no shares for duty's validators")
case *types.AggregatorCommitteeDuty:
filteredDuty = &types.AggregatorCommitteeDuty{
Slot: slot,
ValidatorDuties: filteredValidatorDuties,
}

c.Runners[slot] = c.CreateRunnerFn(dutyShares)
return c.Runners[slot].StartNewDuty(filteredDuty, c.CommitteeMember.GetQuorum())

default:
return errors.Errorf("unsupported duty type: %T", duty)
}

(*runnerMap)[slot] = (*createFn)(dutyShares)
return (*runnerMap)[slot].StartNewDuty(filteredDuty, c.CommitteeMember.GetQuorum())
}

// ProcessMessage processes Network Message of all types
Expand All @@ -117,6 +122,18 @@ func (c *Committee) ProcessMessage(signedSSVMessage *types.SignedSSVMessage) err
return errors.Wrap(err, "Message invalid")
}

// Get runner map according to message role
var runnerMap *map[phase0.Slot]Runner
role := msg.MsgID.GetRoleType()
switch role {
case types.RoleCommittee:
runnerMap = &c.CommitteeRunners
case types.RoleAggregatorCommittee:
runnerMap = &c.AggregatorCommitteeRunners
default:
return types.NewError(types.CommitteeWrongRoleErrorCode, "msg role is invalid")
}

switch msg.GetType() {
case types.SSVConsensusMsgType:
qbftMsg := &qbft.Message{}
Expand All @@ -128,7 +145,7 @@ func (c *Committee) ProcessMessage(signedSSVMessage *types.SignedSSVMessage) err
return errors.Wrap(err, "invalid qbft Message")
}

runner, exists := c.Runners[phase0.Slot(qbftMsg.Height)]
runner, exists := (*runnerMap)[phase0.Slot(qbftMsg.Height)]
if !exists {
return types.NewError(types.NoRunnerForSlotErrorCode, "no runner found for message's slot")
}
Expand All @@ -148,7 +165,7 @@ func (c *Committee) ProcessMessage(signedSSVMessage *types.SignedSSVMessage) err
return errors.Wrap(err, "invalid PartialSignatureMessages")
}

runner, exists := c.Runners[pSigMessages.Slot]
runner, exists := (*runnerMap)[pSigMessages.Slot]
if !exists {
return types.NewError(types.NoRunnerForSlotErrorCode, "no runner found for message's slot")
}
Expand All @@ -157,6 +174,9 @@ func (c *Committee) ProcessMessage(signedSSVMessage *types.SignedSSVMessage) err
case types.PostConsensusPartialSig:
return runner.ProcessPostConsensus(pSigMessages)
case types.AggregatorCommitteePartialSig:
if role != types.RoleAggregatorCommittee {
return errors.Errorf("invalid aggregator partial sig msg for commmittee role")
}
return runner.ProcessPreConsensus(pSigMessages)
default:
return errors.Errorf("unknown partial signature message type: %v", pSigMessages.Type)
Expand All @@ -171,6 +191,11 @@ func (c *Committee) validateMessage(msg *types.SSVMessage) error {
return types.NewError(types.MessageIDCommitteeIDMismatchErrorCode, "msg ID doesn't match committee ID")
}

role := msg.GetID().GetRoleType()
if role != types.RoleCommittee && role != types.RoleAggregatorCommittee {
return types.NewError(types.CommitteeWrongRoleErrorCode, "msg role is invalid")
}

if len(msg.GetData()) == 0 {
return fmt.Errorf("msg data is invalid")
}
Expand Down
60 changes: 24 additions & 36 deletions ssv/json_testutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,16 +131,18 @@ func (c *Committee) GetRoot() ([32]byte, error) {
func (c *Committee) MarshalJSON() ([]byte, error) {

type CommitteeAlias struct {
Runners map[phase0.Slot]Runner
CommitteeMember types.CommitteeMember
Share map[phase0.ValidatorIndex]*types.Share
CommitteeRunners map[phase0.Slot]Runner
AggregatorCommitteeRunners map[phase0.Slot]Runner
CommitteeMember types.CommitteeMember
Share map[phase0.ValidatorIndex]*types.Share
}

// Create object and marshal
alias := &CommitteeAlias{
Runners: c.Runners,
CommitteeMember: c.CommitteeMember,
Share: c.Share,
CommitteeRunners: c.CommitteeRunners,
AggregatorCommitteeRunners: c.AggregatorCommitteeRunners,
CommitteeMember: c.CommitteeMember,
Share: c.Share,
}

byts, err := json.Marshal(alias)
Expand All @@ -151,9 +153,10 @@ func (c *Committee) MarshalJSON() ([]byte, error) {
func (c *Committee) UnmarshalJSON(data []byte) error {
// First, unmarshal to get the raw JSON for runners
type CommitteeRaw struct {
Runners map[phase0.Slot]json.RawMessage
CommitteeMember types.CommitteeMember
Share map[phase0.ValidatorIndex]*types.Share
CommitteeRunners map[phase0.Slot]json.RawMessage
AggregatorCommitteeRunners map[phase0.Slot]json.RawMessage
CommitteeMember types.CommitteeMember
Share map[phase0.ValidatorIndex]*types.Share
}

raw := &CommitteeRaw{}
Expand All @@ -162,39 +165,24 @@ func (c *Committee) UnmarshalJSON(data []byte) error {
}

// Initialize the committee
c.Runners = make(map[phase0.Slot]Runner)
c.CommitteeRunners = make(map[phase0.Slot]Runner)
c.AggregatorCommitteeRunners = make(map[phase0.Slot]Runner)
c.CommitteeMember = raw.CommitteeMember
c.Share = raw.Share

// For each runner, detect its type and unmarshal accordingly
for slot, runnerData := range raw.Runners {
// Try to detect the runner type by looking for type-specific fields
var typeDetector struct {
BaseRunner struct {
RunnerRoleType types.RunnerRole `json:"RunnerRoleType"`
} `json:"BaseRunner"`
}

if err := json.Unmarshal(runnerData, &typeDetector); err != nil {
for slot, runnerData := range raw.CommitteeRunners {
var runner CommitteeRunner
if err := json.Unmarshal(runnerData, &runner); err != nil {
return err
}

switch typeDetector.BaseRunner.RunnerRoleType {
case types.RoleCommittee:
var runner CommitteeRunner
if err := json.Unmarshal(runnerData, &runner); err != nil {
return err
}
c.Runners[slot] = &runner
case types.RoleAggregatorCommittee:
var runner AggregatorCommitteeRunner
if err := json.Unmarshal(runnerData, &runner); err != nil {
return err
}
c.Runners[slot] = &runner
default:
return errors.Errorf("unknown runner type for slot %d: RunnerRoleType=%v", slot, typeDetector.BaseRunner.RunnerRoleType)
c.CommitteeRunners[slot] = &runner
}
for slot, runnerData := range raw.AggregatorCommitteeRunners {
var runner AggregatorCommitteeRunner
if err := json.Unmarshal(runnerData, &runner); err != nil {
return err
}
c.AggregatorCommitteeRunners[slot] = &runner
}

return nil
Expand Down
9 changes: 9 additions & 0 deletions ssv/spectest/all_tests.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package spectest
import (
"github.com/ssvlabs/ssv-spec/ssv/spectest/tests"
aggregatorcommitteesingleduty "github.com/ssvlabs/ssv-spec/ssv/spectest/tests/aggregatorcommittee/singleduty"
"github.com/ssvlabs/ssv-spec/ssv/spectest/tests/committee"
committeemixedduties "github.com/ssvlabs/ssv-spec/ssv/spectest/tests/committee/mixedduties"
committeemultipleduty "github.com/ssvlabs/ssv-spec/ssv/spectest/tests/committee/multipleduty"
committeesingleduty "github.com/ssvlabs/ssv-spec/ssv/spectest/tests/committee/singleduty"
"github.com/ssvlabs/ssv-spec/ssv/spectest/tests/dutyexe"
Expand Down Expand Up @@ -79,6 +81,10 @@ var AllTests = []tests.TestF{
newduty.DuplicateDutyNotFinished,
newduty.FirstHeight,

committee.InvalidSig,
committee.CommitteeIDMismatch,
committee.WrongRole,

committeesingleduty.StartDuty,
committeesingleduty.StartNoDuty,
committeesingleduty.ValidBeaconVote,
Expand All @@ -104,6 +110,9 @@ var AllTests = []tests.TestF{
aggregatorcommitteesingleduty.DutyWithDifferentSlots,
aggregatorcommitteesingleduty.MaxValidators,

committeemixedduties.SameSlot,
committeemixedduties.MixedMultipleSlots,

consensus.FutureDecidedNoInstance,
consensus.FutureDecided,
consensus.InvalidDecidedValue,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"Runners": {},
"CommitteeRunners": {},
"AggregatorCommitteeRunners": {},
"CommitteeMember": {
"OperatorID": 1,
"CommitteeID": "cf97adeedb59e05bfd73a2b4c2a8885708c4f4f70c84c64b27120e72ab733b72",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"Runners": {},
"CommitteeRunners": {},
"AggregatorCommitteeRunners": {},
"CommitteeMember": {
"OperatorID": 1,
"CommitteeID": "cf97adeedb59e05bfd73a2b4c2a8885708c4f4f70c84c64b27120e72ab733b72",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"Runners": {
"CommitteeRunners": {},
"AggregatorCommitteeRunners": {
"7424012": {
"BaseRunner": {
"State": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"Runners": {
"CommitteeRunners": {},
"AggregatorCommitteeRunners": {
"12": {
"BaseRunner": {
"State": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"Runners": {
"CommitteeRunners": {},
"AggregatorCommitteeRunners": {
"7424012": {
"BaseRunner": {
"State": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"Runners": {
"CommitteeRunners": {},
"AggregatorCommitteeRunners": {
"12": {
"BaseRunner": {
"State": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"Runners": {
"CommitteeRunners": {},
"AggregatorCommitteeRunners": {
"7424012": {
"BaseRunner": {
"State": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"Runners": {
"CommitteeRunners": {},
"AggregatorCommitteeRunners": {
"12": {
"BaseRunner": {
"State": {
Expand Down
Loading