From b870952a66c4f217b4e8402f93fd8e7e89a6190e Mon Sep 17 00:00:00 2001
From: MatheusFranco99 <48058141+MatheusFranco99@users.noreply.github.com>
Date: Wed, 7 Jan 2026 11:20:13 +0000
Subject: [PATCH 1/6] update rules for agg committee
---
.../ReceivedMessagesEstimation.md | 3 +
p2p/MessageValidation/Rules.md | 177 +++++++++---------
2 files changed, 91 insertions(+), 89 deletions(-)
diff --git a/p2p/MessageValidation/ReceivedMessagesEstimation.md b/p2p/MessageValidation/ReceivedMessagesEstimation.md
index f16349e..c709bc8 100644
--- a/p2p/MessageValidation/ReceivedMessagesEstimation.md
+++ b/p2p/MessageValidation/ReceivedMessagesEstimation.md
@@ -1,5 +1,8 @@
# Estimation of amount of messages received
+> [!WARNING]
+> This document is deprecated due to protocol changes as the Alan and Boole forks.
+
To play with the below formulas, follow this [google sheet link](https://docs.google.com/spreadsheets/d/1TpXnVFzF4eGiQarXuPBrOIU4tJXhzOHav9PkGve3_Qc/edit?usp=sharing).
## Probability of having a duty per slot
diff --git a/p2p/MessageValidation/Rules.md b/p2p/MessageValidation/Rules.md
index 4959479..ba955e2 100644
--- a/p2p/MessageValidation/Rules.md
+++ b/p2p/MessageValidation/Rules.md
@@ -120,7 +120,7 @@ var (
ErrPartialSigOneSigner = Error{text: "partial signature message with len(signers) != 1", reject: true}
ErrTooManyPartialSignatureMessages = Error{text: "too many signatures for cluster in partial signature message"}
- ErrTripleValidatorIndexInPartialSignatures = Error{text: "validator index appear 3 times in partial signature message", reject: true}
+ ErrTooManyEqualValidatorIndexInPartialSignatures = Error{text: "validator index appear 3 times in partial signature message", reject: true}
ErrNoPartialSignatureMessages = Error{text: "no partial signature messages", reject: true}
ErrInconsistentSigners = Error{text: "inconsistent signers", reject: true}
ErrValidatorIndexMismatch = Error{text: "validator index mismatch"}
@@ -140,14 +140,13 @@ The main structure is the `MessageValidation` structure which has a `ValidatePub
```go
const (
- MaxMsgSize = 4945164
- maxConsensusMsgSize = 722412
- maxPartialSignatureMsgSize = 144020
+ MaxMsgSize = 9114816
+ maxConsensusMsgSize = 722480
+ maxPartialSignatureMsgSize = 727000
maxSSVMessageDataSize = max(maxConsensusMsgSize, maxPartialSignatureMsgSize)
PartialSignatureSize = 48
MessageSignatureSize = 256
SyncCommitteeSize = 512
- MaxSignaturesInSyncCommitteeContribution = 13
)
type MessageValidation struct {
@@ -423,19 +422,19 @@ func (mv *MessageValidation) ValidatePubSubMessage(pmsg *pubsub.Message) error {
### Semantics General Rules
-| Verification | Error | Classification | Explanation |
-| -------------------- | ------------------------- | -------------- | --------------------------------------------------------------------- |
-| Signers in committee | ErrSignerNotInCommittee | Reject | Signers must belong to validator's or CommitteeID's committee. |
-| Different Domain | ErrWrongDomain | Ignore | MsgID.Domain is different than self domain. |
-| Invalid Role | ErrInvalidRole | Reject | MsgID.Role is not known. |
-| Validator exists | ErrUnknownValidator | Ignore | If MsgID.SenderID is a validator, it must exist. |
-| Active Validator ID | ErrValidatorNotAttesting | Ignore | If MsgID.SenderID is a validator, it must be active active validator. |
-| Validator Liquidated | ErrValidatorLiquidated | Ignore | If MsgID.SenderID is a validator, it must not be liquidated. |
-| CommitteeID exists | ErrNonExistentCommitteeID | Ignore | If MsgID.SenderID is a committee, it must exist. |
-| Wrong topic | ErrIncorrectTopic | Ignore | The message should be sent in the correct topic |
-| Event Message | ErrEventMessage | Reject | MsgType can't be of event message. |
-| DKG Message | ErrDKGMessage | Reject | MsgType can't be of DKG message. |
-| Unknown MsgType | ErrUnknownSSVMessageType | Reject | MsgType is not known. |
+| Verification | Error | Classification | Explanation |
+| -------------------- | ------------------------- | -------------- |--------------------------------------------------------------------------------------------------------------------------------------------|
+| Signers in committee | ErrSignerNotInCommittee | Reject | Signers must belong to validator's or CommitteeID's committee. |
+| Different Domain | ErrWrongDomain | Ignore | MsgID.Domain is different than self domain. |
+| Invalid Role | ErrInvalidRole | Reject | MsgID.Role is wrong (not `RoleCommittee`, `RoleProposer`, `RoleValidatorRegistration`, `RoleVoluntaryExit`, or `RoleAggregatorCommittee`). |
+| Validator exists | ErrUnknownValidator | Ignore | If MsgID.SenderID is a validator, it must exist. |
+| Active Validator ID | ErrValidatorNotAttesting | Ignore | If MsgID.SenderID is a validator, it must be active active validator. |
+| Validator Liquidated | ErrValidatorLiquidated | Ignore | If MsgID.SenderID is a validator, it must not be liquidated. |
+| CommitteeID exists | ErrNonExistentCommitteeID | Ignore | If MsgID.SenderID is a committee, it must exist. |
+| Wrong topic | ErrIncorrectTopic | Ignore | The message should be sent in the correct topic |
+| Event Message | ErrEventMessage | Reject | MsgType can't be of event message. |
+| DKG Message | ErrDKGMessage | Reject | MsgType can't be of DKG message. |
+| Unknown MsgType | ErrUnknownSSVMessageType | Reject | MsgType is not known. |
```go
@@ -477,7 +476,7 @@ func (mv *MessageValidation) ValidateSemantics(peerID peer.ID, signedSSVMessage
}
senderID := signedSSVMessage.SSVMessage.MsgID.GetSenderID()
- if role != types.RoleCommittee {
+ if !isCommitteeRole(role) {
validatorPK := senderID
// Rule: Validator does not exist
@@ -521,6 +520,10 @@ func (mv *MessageValidation) ValidateSemantics(peerID peer.ID, signedSSVMessage
return ErrUnknownSSVMessageType
}
}
+
+func isCommitteeRole(role) bool {
+ return role == types.RoleCommittee || role == types.RoleAggregatorCommittee
+}
```
### Consensus
@@ -732,14 +735,14 @@ func (mv *MessageValidation) ValidateQBFTLogic(peerID peer.ID, signedSSVMessage
#### Duty Logic
-| Verification | Error | Classification | Explanation |
-| ------------------------- | ----------------------------- | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| Already advanced slot | ErrSlotAlreadyAdvanced | Ignore | (Non-committee roles) Signer already advanced to later slot. |
-| Invalid role for consensus | ErrUnexpectedConsensusMessage | Reject | SSVMessage.MsgID.Role must not be ValidatorRegistration or VoluntaryExit. |
-| No beacon duty | ErrNoDuty | Ignore | If Proposal or Sync committee contribution duty, check if duty exists with beacon node. |
-| Slot not in time for role | ErrEarlySlotMessage or ErrLateSlotMessage | Ignore | Current time must be between duty's starting time and
+34 (committee and aggregator) or +3 (else) slots. |
-| Too many duties per epoch | ErrTooManyDutiesPerEpoch | Ignore | If role is either aggregator, voluntary exit and validator registration,
it's allowed 2 duties per epoch. Else if committee,
2*V (if no validator is doing sync committee).
Else accept. |
-| Valid round for role | ErrRoundTooHigh | Reject | For committee and aggregation, round can go up to 12. Else, it can go up to 6. |
+| Verification | Error | Classification | Explanation |
+| ------------------------- | ----------------------------- | -------------- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Already advanced slot | ErrSlotAlreadyAdvanced | Ignore | (Non-committee roles) Signer already advanced to later slot. |
+| Invalid role for consensus | ErrUnexpectedConsensusMessage | Reject | SSVMessage.MsgID.Role must not be ValidatorRegistration or VoluntaryExit. |
+| No beacon duty | ErrNoDuty | Ignore | If Proposal duty, check if duty exists with beacon node. |
+| Slot not in time for role | ErrEarlySlotMessage or ErrLateSlotMessage | Ignore | Current time must be between duty's starting time and
+34 (committee and aggregator committee) or +3 (else) slots. |
+| Too many duties per epoch | ErrTooManyDutiesPerEpoch | Ignore | If role is either voluntary exit and validator registration,
it's allowed 2 duties per epoch. Else if committee or aggregator committee,
2*V (if no validator is doing sync committee).
Else accept. |
+| Valid round for role | ErrRoundTooHigh | Reject | For committee and aggregator committee, round can go up to 12. Else, it can go up to 6. |
```go
@@ -757,7 +760,7 @@ func (mv *MessageValidation) ValidateQBFTMessageByDutyLogic(peerID peer.ID, sign
}
type SSVMessage struct {
MsgType MsgType
- MsgID MessageID -> Role must have consensus, Must be assigned to duty if role is Proposal or Sync Committee Aggregator
+ MsgID MessageID -> Role must have consensus, Must be assigned to duty if role is Proposal
Data []byte
}
type Message struct {
@@ -777,7 +780,7 @@ func (mv *MessageValidation) ValidateQBFTMessageByDutyLogic(peerID peer.ID, sign
_ = qbftMessage.Decode(signedSSVMessage.SSVMessage.Data)
// Rule: Height must not be "old". I.e., signer must not have already advanced to a later slot.
- if signedSSVMessage.SSVMessage.MsgID.GetRoleType() != types.RoleCommittee { // Rule only for validator runners
+ if !isCommitteeRole(signedSSVMessage.SSVMessage.MsgID.GetRoleType()) { // Rule only for validator runners
if !mv.MessageFromOldSlot(peerID, signedSSVMessage.SSVMessage.MsgID, qbftMessage.Height) {
return ErrSlotAlreadyAdvanced
}
@@ -788,8 +791,8 @@ func (mv *MessageValidation) ValidateQBFTMessageByDutyLogic(peerID peer.ID, sign
return ErrUnexpectedConsensusMessage
}
- // Rule: For proposal and sync committee aggregation duties, we check if the validator is assigned to it
- if err := mv.ValidBeaconDuty(signedSSVMessage.SSVMessage.MsgID.GetSenderID(), signedSSVMessage.SSVMessage.MsgID.GetRoleType(), phase0.Slot(qbftMessage.Height)); err != nil {
+ // Rule: For proposer duty, we check if the validator is assigned to it
+ if err := mv.ValidProposerDuty(signedSSVMessage.SSVMessage.MsgID.GetSenderID(), signedSSVMessage.SSVMessage.MsgID.GetRoleType(), phase0.Slot(qbftMessage.Height)); err != nil {
return err
}
@@ -802,15 +805,15 @@ func (mv *MessageValidation) ValidateQBFTMessageByDutyLogic(peerID peer.ID, sign
}
// Rule: valid number of duties per epoch:
- // - 2 for aggregation, voluntary exit and validator registration
- // - 2*V for Committee duty (where V is the number of validators in the cluster) (if no validator is doing sync committee in this epoch)
+ // - 2 for voluntary exit and validator registration
+ // - 2*V for Committee and Aggrgeator Committee duty (where V is the number of validators in the cluster) (if no validator is doing sync committee in this epoch)
// - else, accept
if !mv.ValidNumberOfDutiesPerEpoch(peerID, signedSSVMessage.SSVMessage.MsgID, phase0.Slot(qbftMessage.Height)) {
return ErrTooManyDutiesPerEpoch
}
// Rule: Round cut-offs for roles:
- // - 12 (committee and aggregation)
+ // - 12 (committee and aggregator committee)
// - 6 (other types)
if !mv.ValidRoundForRole(qbftMessage.Round, signedSSVMessage.SSVMessage.MsgID.GetRoleType()) {
return ErrRoundTooHigh
@@ -819,7 +822,7 @@ func (mv *MessageValidation) ValidateQBFTMessageByDutyLogic(peerID peer.ID, sign
return nil
}
-func (mv *MessageValidation) ValidBeaconDuty() error {
+func (mv *MessageValidation) ValidProposerDuty() error {
// Rule: For a proposal duty message, we check if the validator is assigned to it
if signedSSVMessage.SSVMessage.MsgID.GetRoleType() == types.RoleProposer {
@@ -828,13 +831,6 @@ func (mv *MessageValidation) ValidBeaconDuty() error {
}
}
- // Rule: For a sync committee aggregation duty message, we check if the validator is assigned to it
- if signedSSVMessage.SSVMessage.MsgID.GetRoleType() == types.RoleSyncCommitteeContribution {
- if !mv.HasSyncCommitteeDuty(signedSSVMessage.SSVMessage.MsgID.GetSenderID(), phase0.Slot(qbftMessage.Height)) {
- return ErrNoDuty
- }
- }
-
return nil
}
@@ -844,16 +840,16 @@ func (mv *MessageValidation) ValidBeaconDuty() error {
#### Semantics
-| Verification | Error | Classification | Explanation |
-| -------------------------- | ----------------------------------- | -------------- | ----------- |
-| More than one signer | ErrPartialSigOneSigner | Reject | Must have only 1 signer. |
-| Unexpected FullData | ErrFullDataNotInConsensusMessage | Reject | Must not have FullData. |
-| Unknown type | ErrInvalidPartialSignatureType | Reject | Type not known. |
-| Wrong type for role | ErrPartialSignatureTypeRoleMismatch | Reject | Type must match role:
PostConsensusPartialSig for Committee,
RandaoPartialSig or PostConsensusPartialSig for Proposer,
SelectionProofPartialSig or PostConsensusPartialSig for Aggregator,
SelectionProofPartialSig or PostConsensusPartialSig for Sync committee contribution,
ValidatorRegistrationPartialSig for Validator Registration,
VoluntaryExitPartialSig for Voluntary Exit |
-| No PartialSignatureMessage | ErrNoPartialSignatureMessages | Reject | Message must have at least one PartialSignatureMessage. |
-| Wrong BLS Signature Size | ErrWrongBLSSignatureSize | Reject | $\forall i$ PartialSignatureMessages.Message[i].Signature must have the correct length. |
-| Inconsistent signer | ErrInconsistentSigners | Reject | $\forall i$ PartialSignatureMessages.Message[i].Signer must be the same as the
SignedSSVMessage.OperatorIDs[i]. |
-| Validtor's index mismatch | ErrValidatorIndexMismatch | Ignore | $\forall i$ PartialSignatureMessages.Message[i].ValidatorIndex must belong to SSVMessage.SenderID(). |
+| Verification | Error | Classification | Explanation |
+| -------------------------- | ----------------------------------- | -------------- |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| More than one signer | ErrPartialSigOneSigner | Reject | Must have only 1 signer. |
+| Unexpected FullData | ErrFullDataNotInConsensusMessage | Reject | Must not have FullData. |
+| Unknown type | ErrInvalidPartialSignatureType | Reject | Type not known. |
+| Wrong type for role | ErrPartialSignatureTypeRoleMismatch | Reject | Type must match role:
PostConsensusPartialSig for Committee,
RandaoPartialSig or PostConsensusPartialSig for Proposer,
AggregatorCommitteePartialSig or PostConsensusPartialSig for AggregatorCommittee,
ValidatorRegistrationPartialSig for Validator Registration,
VoluntaryExitPartialSig for Voluntary Exit |
+| No PartialSignatureMessage | ErrNoPartialSignatureMessages | Reject | Message must have at least one PartialSignatureMessage. |
+| Wrong BLS Signature Size | ErrWrongBLSSignatureSize | Reject | $\forall i$ PartialSignatureMessages.Message[i].Signature must have the correct length. |
+| Inconsistent signer | ErrInconsistentSigners | Reject | $\forall i$ PartialSignatureMessages.Message[i].Signer must be the same as the
SignedSSVMessage.OperatorIDs[i]. |
+| Validtor's index mismatch | ErrValidatorIndexMismatch | Ignore | $\forall i$ PartialSignatureMessages.Message[i].ValidatorIndex must belong to SSVMessage.SenderID(). |
```go
@@ -911,8 +907,7 @@ func (mv *MessageValidation) ValidatePartialSignatureMessageSemantics(peerID pee
// Rule: Partial signature type must match expected type:
// - PostConsensusPartialSig, for Committee duty
// - RandaoPartialSig or PostConsensusPartialSig for Proposer
- // - SelectionProofPartialSig or PostConsensusPartialSig for Aggregator
- // - SelectionProofPartialSig or PostConsensusPartialSig for Sync committee contribution
+ // - AggregatorCommitteePartialSig or PostConsensusPartialSig for AggregatorCommittee
// - ValidatorRegistrationPartialSig for Validator Registration
// - VoluntaryExitPartialSig for Voluntary Exit
if !mv.ExpectedPartialSignatureTypeForRole(partialSignatureMessages.Type, signedSSVMessage.SSVMessage.MsgID) {
@@ -945,15 +940,15 @@ func (mv *MessageValidation) ValidatePartialSignatureMessageSemantics(peerID pee
#### Duty Logic
-| Verification | Error | Classification | Explanation |
-| ---------------------------- | ------------------------ | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| Already advanced slot | ErrSlotAlreadyAdvanced | Ignore | (Non-committee roles) Signer already advanced to later slot. |
-| No beacon duty | ErrNoDuty | Ignore | If Proposal or Sync committee contribution duty, check if duty exists with beacon node. |
-| Invalid signature type count | ErrInvalidPartialSignatureTypeCount | Reject | It's allow only:
1 PostConsensusPartialSig, for Committee duty,
1 RandaoPartialSig and 1 PostConsensusPartialSig for Proposer,
1 SelectionProofPartialSig and 1 PostConsensusPartialSig for Aggregator,
1 SelectionProofPartialSig and 1 PostConsensusPartialSig for Sync committee contribution,
1 ValidatorRegistrationPartialSig for Validator Registration,
1 VoluntaryExitPartialSig for Voluntary Exit. |
-| Slot not in time for role | ErrEarlySlotMessage or ErrLateSlotMessage | Ignore | Current time must be between duty's starting time and
+34 (committee and aggregator) or +3 (else) slots. |
-| Too many duties per epoch | ErrTooManyDutiesPerEpoch | Ignore | If role is either aggregator, voluntary exit and validator registration,
it's allowed 2 duties per epoch. Else if committee,
2*V (if no validator is doing sync committee).
Else accept. |
-| Too many partial signatures | ErrTooManyPartialSignatureMessages | Reject | For the committee role, it's allowed $min(2*V, V + $ SYNC_COMMITTEE_SIZE $)$
where $V$ is the number of committee's validatos.
For sync committee contribution, it's allowed 13.
Else, only 1. |
-| Triple validator index | ErrTripleValidatorIndexInPartialSignatures | Reject | A validator index can not be associated to more than 2 signatures. |
+| Verification | Error | Classification | Explanation |
+| ---------------------------- | ------------------------ | -------------- |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Already advanced slot | ErrSlotAlreadyAdvanced | Ignore | (Non-committee roles) Signer already advanced to later slot. |
+| No beacon duty | ErrNoDuty | Ignore | If Proposal duty, check if duty exists with beacon node. |
+| Invalid signature type count | ErrInvalidPartialSignatureTypeCount | Reject | It's allow only:
1 PostConsensusPartialSig, for Committee duty,
1 RandaoPartialSig and 1 PostConsensusPartialSig for Proposer,
1 AggregatorCommitteePartialSig and 1 PostConsensusPartialSig for AggregatorCommittee,
1 ValidatorRegistrationPartialSig for Validator Registration,
1 VoluntaryExitPartialSig for Voluntary Exit. |
+| Slot not in time for role | ErrEarlySlotMessage or ErrLateSlotMessage | Ignore | Current time must be between duty's starting time and
+34 (committee and aggregator committee) or +3 (else) slots. |
+| Too many duties per epoch | ErrTooManyDutiesPerEpoch | Ignore | If role is either aggregator, voluntary exit and validator registration,
it's allowed 2 duties per epoch. Else if committee or aggregator committee,
2*V (if no validator is doing sync committee).
Else accept. |
+| Too many partial signatures | ErrTooManyPartialSignatureMessages | Reject | For the committee role, it's allowed $min(2*V, V + $ SYNC_COMMITTEE_SIZE $)$
where $V$ is the number of committee's validators.
For the aggregator committee role, it's allowed $min((1+4)*V, V + 4\times$ SYNC_COMMITTEE_SIZE $)$
where $V$ is the number of committee's validators.
Else, only 1. |
+| Triple validator index | ErrTooManyEqualValidatorIndexInPartialSignatures | Reject | For the committee (resp. aggregator committee) role, a validator index can not be associated to more than 2 (resp. 5) signatures. |
```go
@@ -971,20 +966,20 @@ func (mv *MessageValidation) ValidatePartialSigMessagesByDutyLogic(peerID peer.I
}
type SSVMessage struct {
MsgType MsgType
- MsgID MessageID -> Must be assigned to duty if role is Proposal or Sync Committee Aggregator
+ MsgID MessageID -> Must be assigned to duty if role is Proposal
Data []byte
}
type PartialSignatureMessages struct {
Type PartialSigMsgType -> Message count rules
Slot phase0.Slot -> Must belong to allowed spread, Satisfies a maximum number of duties per epoch for role, Must not be "old"
- Messages []*PartialSignatureMessage -> Valid number of signatures (3 cases: committee duty, sync committee contribution, others)
+ Messages []*PartialSignatureMessage -> Valid number of signatures (3 cases: committee duty, aggregator committee duty, others)
}
type PartialSignatureMessage struct {
PartialSignature Signature
SigningRoot [32]byte
Signer OperatorID
- ValidatorIndex phase0.ValidatorIndex -> Can't appear more than 2 times for role Committee
+ ValidatorIndex phase0.ValidatorIndex -> Can't appear more than 2 (resp. 5) times for role Committee (resp. AggregatorCommittee)
}
*/
@@ -992,25 +987,25 @@ func (mv *MessageValidation) ValidatePartialSigMessagesByDutyLogic(peerID peer.I
var partialSignatureMessages types.PartialSignatureMessages
_ = partialSignatureMessages.Decode(signedSSVMessage.SSVMessage.Data)
+ msgRole := signedSSVMessage.SSVMessage.MsgID.GetRoleType()
// Rule: Height must not be "old". I.e., signer must not have already advanced to a later slot.
- if signedSSVMessage.SSVMessage.MsgID.GetRoleType() != types.RoleCommittee { // Rule only for validator runners
+ if isCommitteeRole(msgRole) { // Rule only for validator runners
if !mv.MessageFromOldSlot(peerID, signedSSVMessage.SSVMessage.MsgID, partialSignatureMessages.Slot) {
return ErrSlotAlreadyAdvanced
}
}
- // Rule: For proposal and sync committee aggregation duties, we check if the validator is assigned to it
- if err := mv.ValidBeaconDuty(signedSSVMessage.SSVMessage.MsgID.GetSenderID(), signedSSVMessage.SSVMessage.MsgID.GetRoleType(), partialSignatureMessages.Slot); err != nil {
+ // Rule: For proposer duty, we check if the validator is assigned to it
+ if err := mv.ValidProposerDuty(signedSSVMessage.SSVMessage.MsgID.GetSenderID(), signedSSVMessage.SSVMessage.MsgID.GetRoleType(), partialSignatureMessages.Slot); err != nil {
return err
}
// Rule: peer must send only:
// - 1 PostConsensusPartialSig, for Committee duty
// - 1 RandaoPartialSig and 1 PostConsensusPartialSig for Proposer
- // - 1 SelectionProofPartialSig and 1 PostConsensusPartialSig for Aggregator
- // - 1 SelectionProofPartialSig and 1 PostConsensusPartialSig for Sync committee contribution
+ // - 1 AggregatorCommitteePartialSig and 1 PostConsensusPartialSig for AggregatorCommittee
// - 1 ValidatorRegistrationPartialSig for Validator Registration
// - 1 VoluntaryExitPartialSig for Voluntary Exit
if err := mv.ValidPartialSigMessageCount(peerID, signedSSVMessage.SSVMessage.MsgID, &partialSignatureMessages); err != nil {
@@ -1018,7 +1013,7 @@ func (mv *MessageValidation) ValidatePartialSigMessagesByDutyLogic(peerID peer.I
}
// Rule: current slot must be between duty's starting slot and:
- // - duty's starting slot + 34 (committee and aggregation)
+ // - duty's starting slot + 34 (committee and aggregator committee)
// - duty's starting slot + 3 (other duties)
if err := mv.ValidDutySlot(peerID, partialSignatureMessages.Slot, signedSSVMessage.SSVMessage.MsgID.GetRoleType()); err != nil {
// Err should be ErrEarlySlotMessage or ErrLateSlotMessage
@@ -1026,28 +1021,32 @@ func (mv *MessageValidation) ValidatePartialSigMessagesByDutyLogic(peerID peer.I
}
// Rule: valid number of duties per epoch:
- // - 2 for aggregation, voluntary exit and validator registration
- // - 2*V for Committee duty (where V is the number of validators in the cluster) (if no validator is doing sync committee in this epoch)
+ // - 2 for voluntary exit and validator registration
+ // - 2*V for Committee and AggregatorCommittee duty (where V is the number of validators in the cluster) (if no validator is doing sync committee in this epoch)
// - else, accept
if !mv.ValidNumberOfDutiesPerEpoch(peerID, signedSSVMessage.SSVMessage.MsgID, partialSignatureMessages.Slot) {
return ErrTooManyDutiesPerEpoch
}
- if signedSSVMessage.SSVMessage.MsgID.GetRoleType() == types.RoleCommittee {
-
- // Rule: The number of signatures must be <= min(2*V, V + SYNC_COMMITTEE_SIZE) where V is the number of validators assigned to the cluster
- if !mv.ValidNumberOfSignaturesForCommitteeDuty(signedSSVMessage.SSVMessage.MsgID.GetSenderID(), &partialSignatureMessages) {
- return ErrTooManyPartialSignatureMessages
- }
-
- // Rule: a ValidatorIndex can't appear more than 2 times in the []*PartialSignatureMessage list
- if !mv.NoTripleValidatorOccurrence(&partialSignatureMessages) {
- return ErrTripleValidatorIndexInPartialSignatures
- }
- } else if signedSSVMessage.SSVMessage.MsgID.GetRoleType() == types.RoleSyncCommitteeContribution {
- // Rule: The number of signatures must be <= MaxSignaturesInSyncCommitteeContribution for the sync comittee contribution duty
- if len(partialSignatureMessages.Messages) > MaxSignaturesInSyncCommitteeContribution {
- return ErrTooManyPartialSignatureMessages
+ if isCommitteeRole(msgRole) {
+
+ if msgRole == types.RoleCommmittee {
+ // Rule: The number of signatures must be <= min(2*V, V + SYNC_COMMITTEE_SIZE) where V is the number of validators assigned to the cluster
+ if !mv.ValidNumberOfSignaturesForCommitteeDuty(signedSSVMessage.SSVMessage.MsgID.GetSenderID(), &partialSignatureMessages) {
+ return ErrTooManyPartialSignatureMessages
+ }
+ }
+
+ if msgRole == types.RoleAggregatorCommittee {
+ // Rule: The number of signatures must be <= min(5*V, V + 4*SYNC_COMMITTEE_SIZE) where V is the number of validators assigned to the cluster
+ if !mv.ValidNumberOfSignaturesForAggregatorCommitteeDuty(signedSSVMessage.SSVMessage.MsgID.GetSenderID(), &partialSignatureMessages) {
+ return ErrTooManyPartialSignatureMessages
+ }
+ }
+
+ // Rule: a ValidatorIndex can't appear more than 2 (resp. 5) times in the []*PartialSignatureMessage list for role Committee (resp. AggregatorCommittee)
+ if !mv.TooManyEqualValidatorOccurrence(&partialSignatureMessages, msgRole) {
+ return ErrTooManyEqualValidatorIndexInPartialSignatures
}
} else {
// Rule: The number of signatures must be 1 for the other types of duties
From 8d6b65c8dbd14a5702400a9e16a359b67121619c Mon Sep 17 00:00:00 2001
From: MatheusFranco99 <48058141+MatheusFranco99@users.noreply.github.com>
Date: Wed, 7 Jan 2026 11:37:38 +0000
Subject: [PATCH 2/6] apply suggestions
---
p2p/MessageValidation/Rules.md | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/p2p/MessageValidation/Rules.md b/p2p/MessageValidation/Rules.md
index ba955e2..b738678 100644
--- a/p2p/MessageValidation/Rules.md
+++ b/p2p/MessageValidation/Rules.md
@@ -521,7 +521,7 @@ func (mv *MessageValidation) ValidateSemantics(peerID peer.ID, signedSSVMessage
}
}
-func isCommitteeRole(role) bool {
+func isCommitteeRole(role types.RunnerRole) bool {
return role == types.RoleCommittee || role == types.RoleAggregatorCommittee
}
```
@@ -806,7 +806,7 @@ func (mv *MessageValidation) ValidateQBFTMessageByDutyLogic(peerID peer.ID, sign
// Rule: valid number of duties per epoch:
// - 2 for voluntary exit and validator registration
- // - 2*V for Committee and Aggrgeator Committee duty (where V is the number of validators in the cluster) (if no validator is doing sync committee in this epoch)
+ // - 2*V for Committee and Aggregator Committee duty (where V is the number of validators in the cluster) (if no validator is doing sync committee in this epoch)
// - else, accept
if !mv.ValidNumberOfDutiesPerEpoch(peerID, signedSSVMessage.SSVMessage.MsgID, phase0.Slot(qbftMessage.Height)) {
return ErrTooManyDutiesPerEpoch
@@ -840,16 +840,16 @@ func (mv *MessageValidation) ValidProposerDuty() error {
#### Semantics
-| Verification | Error | Classification | Explanation |
-| -------------------------- | ----------------------------------- | -------------- |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Verification | Error | Classification | Explanation |
+|----------------------------| ----------------------------------- | -------------- |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| More than one signer | ErrPartialSigOneSigner | Reject | Must have only 1 signer. |
-| Unexpected FullData | ErrFullDataNotInConsensusMessage | Reject | Must not have FullData. |
+| Unexpected FullData | ErrFullDataNotInConsensusMessage | Reject | Must not have FullData. |
| Unknown type | ErrInvalidPartialSignatureType | Reject | Type not known. |
| Wrong type for role | ErrPartialSignatureTypeRoleMismatch | Reject | Type must match role:
PostConsensusPartialSig for Committee,
RandaoPartialSig or PostConsensusPartialSig for Proposer,
AggregatorCommitteePartialSig or PostConsensusPartialSig for AggregatorCommittee,
ValidatorRegistrationPartialSig for Validator Registration,
VoluntaryExitPartialSig for Voluntary Exit |
-| No PartialSignatureMessage | ErrNoPartialSignatureMessages | Reject | Message must have at least one PartialSignatureMessage. |
-| Wrong BLS Signature Size | ErrWrongBLSSignatureSize | Reject | $\forall i$ PartialSignatureMessages.Message[i].Signature must have the correct length. |
-| Inconsistent signer | ErrInconsistentSigners | Reject | $\forall i$ PartialSignatureMessages.Message[i].Signer must be the same as the
SignedSSVMessage.OperatorIDs[i]. |
-| Validtor's index mismatch | ErrValidatorIndexMismatch | Ignore | $\forall i$ PartialSignatureMessages.Message[i].ValidatorIndex must belong to SSVMessage.SenderID(). |
+| No PartialSignatureMessage | ErrNoPartialSignatureMessages | Reject | Message must have at least one PartialSignatureMessage. |
+| Wrong BLS Signature Size | ErrWrongBLSSignatureSize | Reject | $\forall i$ PartialSignatureMessages.Message[i].Signature must have the correct length. |
+| Inconsistent signer | ErrInconsistentSigners | Reject | $\forall i$ PartialSignatureMessages.Message[i].Signer must be the same as the
SignedSSVMessage.OperatorIDs[i]. |
+| Validator's index mismatch | ErrValidatorIndexMismatch | Ignore | $\forall i$ PartialSignatureMessages.Message[i].ValidatorIndex must belong to SSVMessage.SenderID(). |
```go
@@ -990,7 +990,7 @@ func (mv *MessageValidation) ValidatePartialSigMessagesByDutyLogic(peerID peer.I
msgRole := signedSSVMessage.SSVMessage.MsgID.GetRoleType()
// Rule: Height must not be "old". I.e., signer must not have already advanced to a later slot.
- if isCommitteeRole(msgRole) { // Rule only for validator runners
+ if !isCommitteeRole(msgRole) { // Rule only for validator runners
if !mv.MessageFromOldSlot(peerID, signedSSVMessage.SSVMessage.MsgID, partialSignatureMessages.Slot) {
return ErrSlotAlreadyAdvanced
}
@@ -1030,7 +1030,7 @@ func (mv *MessageValidation) ValidatePartialSigMessagesByDutyLogic(peerID peer.I
if isCommitteeRole(msgRole) {
- if msgRole == types.RoleCommmittee {
+ if msgRole == types.RoleCommittee {
// Rule: The number of signatures must be <= min(2*V, V + SYNC_COMMITTEE_SIZE) where V is the number of validators assigned to the cluster
if !mv.ValidNumberOfSignaturesForCommitteeDuty(signedSSVMessage.SSVMessage.MsgID.GetSenderID(), &partialSignatureMessages) {
return ErrTooManyPartialSignatureMessages
@@ -1045,7 +1045,7 @@ func (mv *MessageValidation) ValidatePartialSigMessagesByDutyLogic(peerID peer.I
}
// Rule: a ValidatorIndex can't appear more than 2 (resp. 5) times in the []*PartialSignatureMessage list for role Committee (resp. AggregatorCommittee)
- if !mv.TooManyEqualValidatorOccurrence(&partialSignatureMessages, msgRole) {
+ if mv.TooManyEqualValidatorOccurrence(&partialSignatureMessages, msgRole) {
return ErrTooManyEqualValidatorIndexInPartialSignatures
}
} else {
From db2eeb4e625dc7b7f2535a54382d10a68e09cd72 Mon Sep 17 00:00:00 2001
From: MatheusFranco99 <48058141+MatheusFranco99@users.noreply.github.com>
Date: Tue, 13 Jan 2026 08:53:50 +0000
Subject: [PATCH 3/6] Update p2p/MessageValidation/Rules.md
Co-authored-by: Nikita Kryuchkov
---
p2p/MessageValidation/Rules.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/p2p/MessageValidation/Rules.md b/p2p/MessageValidation/Rules.md
index b738678..b7d757a 100644
--- a/p2p/MessageValidation/Rules.md
+++ b/p2p/MessageValidation/Rules.md
@@ -948,7 +948,7 @@ func (mv *MessageValidation) ValidatePartialSignatureMessageSemantics(peerID pee
| Slot not in time for role | ErrEarlySlotMessage or ErrLateSlotMessage | Ignore | Current time must be between duty's starting time and
+34 (committee and aggregator committee) or +3 (else) slots. |
| Too many duties per epoch | ErrTooManyDutiesPerEpoch | Ignore | If role is either aggregator, voluntary exit and validator registration,
it's allowed 2 duties per epoch. Else if committee or aggregator committee,
2*V (if no validator is doing sync committee).
Else accept. |
| Too many partial signatures | ErrTooManyPartialSignatureMessages | Reject | For the committee role, it's allowed $min(2*V, V + $ SYNC_COMMITTEE_SIZE $)$
where $V$ is the number of committee's validators.
For the aggregator committee role, it's allowed $min((1+4)*V, V + 4\times$ SYNC_COMMITTEE_SIZE $)$
where $V$ is the number of committee's validators.
Else, only 1. |
-| Triple validator index | ErrTooManyEqualValidatorIndexInPartialSignatures | Reject | For the committee (resp. aggregator committee) role, a validator index can not be associated to more than 2 (resp. 5) signatures. |
+| Too many equal validator indices | ErrTooManyEqualValidatorIndexInPartialSignatures | Reject | A validator index can not be associated with more than 2 signatures for the committee role and more than 5 for the aggregator committee role. |
```go
From a1c68c8cf054f8a506a62f0697cfde3474ffa7fd Mon Sep 17 00:00:00 2001
From: MatheusFranco99 <48058141+MatheusFranco99@users.noreply.github.com>
Date: Tue, 13 Jan 2026 09:17:28 +0000
Subject: [PATCH 4/6] apply suggestions
---
.../ReceivedMessagesEstimation.md | 2 +-
p2p/MessageValidation/Rules.md | 50 +++++++++----------
2 files changed, 26 insertions(+), 26 deletions(-)
diff --git a/p2p/MessageValidation/ReceivedMessagesEstimation.md b/p2p/MessageValidation/ReceivedMessagesEstimation.md
index c709bc8..391eccd 100644
--- a/p2p/MessageValidation/ReceivedMessagesEstimation.md
+++ b/p2p/MessageValidation/ReceivedMessagesEstimation.md
@@ -1,7 +1,7 @@
# Estimation of amount of messages received
> [!WARNING]
-> This document is deprecated due to protocol changes as the Alan and Boole forks.
+> This document is deprecated due to protocol changes as of the Alan and Boole forks.
To play with the below formulas, follow this [google sheet link](https://docs.google.com/spreadsheets/d/1TpXnVFzF4eGiQarXuPBrOIU4tJXhzOHav9PkGve3_Qc/edit?usp=sharing).
diff --git a/p2p/MessageValidation/Rules.md b/p2p/MessageValidation/Rules.md
index b7d757a..b4bc99f 100644
--- a/p2p/MessageValidation/Rules.md
+++ b/p2p/MessageValidation/Rules.md
@@ -118,15 +118,15 @@ var (
ErrUnexpectedPrepareJustifications = Error{text: "message has a prepare justification but it's not a proposal", reject: true}
- ErrPartialSigOneSigner = Error{text: "partial signature message with len(signers) != 1", reject: true}
- ErrTooManyPartialSignatureMessages = Error{text: "too many signatures for cluster in partial signature message"}
- ErrTooManyEqualValidatorIndexInPartialSignatures = Error{text: "validator index appear 3 times in partial signature message", reject: true}
- ErrNoPartialSignatureMessages = Error{text: "no partial signature messages", reject: true}
- ErrInconsistentSigners = Error{text: "inconsistent signers", reject: true}
- ErrValidatorIndexMismatch = Error{text: "validator index mismatch"}
- ErrInvalidPartialSignatureType = Error{text: "invalid partial signature type", reject: true}
- ErrPartialSignatureTypeRoleMismatch = Error{text: "partial signature type and role don't match", reject: true}
- ErrInvalidPartialSignatureTypeCount = Error{text: "sent more partial signature messages of a certain type than allowed", reject: true}
+ ErrPartialSigOneSigner = Error{text: "partial signature message with len(signers) != 1", reject: true}
+ ErrTooManyPartialSignatureMessages = Error{text: "too many signatures for cluster in partial signature message"}
+ ErrTooManyEqualValidatorIndicesInPartialSignatures = Error{text: "validator index appears too many times in partial signature message", reject: true}
+ ErrNoPartialSignatureMessages = Error{text: "no partial signature messages", reject: true}
+ ErrInconsistentSigners = Error{text: "inconsistent signers", reject: true}
+ ErrValidatorIndexMismatch = Error{text: "validator index mismatch"}
+ ErrInvalidPartialSignatureType = Error{text: "invalid partial signature type", reject: true}
+ ErrPartialSignatureTypeRoleMismatch = Error{text: "partial signature type and role don't match", reject: true}
+ ErrInvalidPartialSignatureTypeCount = Error{text: "sent more partial signature messages of a certain type than allowed", reject: true}
ErrTooManyDutiesPerEpoch = Error{text: "too many duties per epoch"}
ErrNoDuty = Error{text: "no duty for this epoch"}
@@ -140,13 +140,13 @@ The main structure is the `MessageValidation` structure which has a `ValidatePub
```go
const (
- MaxMsgSize = 9114816
- maxConsensusMsgSize = 722480
- maxPartialSignatureMsgSize = 727000
+ MaxMsgSize = 9114816 // Source: https://github.com/ssvlabs/ssv-spec/blob/a65134ed45c932588c17d421173923f0216c6c73/types/spectest/tests/maxmsgsize/max_signed_ssv_message.go#L11
+ maxConsensusMsgSize = 722480 // Source: https://github.com/ssvlabs/ssv-spec/blob/a65134ed45c932588c17d421173923f0216c6c73/types/spectest/tests/maxmsgsize/max_ssv_message.go#L9
+ maxPartialSignatureMsgSize = 727000 // Source: https://github.com/ssvlabs/ssv-spec/blob/a65134ed45c932588c17d421173923f0216c6c73/types/spectest/tests/maxmsgsize/max_ssv_message.go#L10
maxSSVMessageDataSize = max(maxConsensusMsgSize, maxPartialSignatureMsgSize)
- PartialSignatureSize = 48
- MessageSignatureSize = 256
- SyncCommitteeSize = 512
+ PartialSignatureSize = 96 // Source: https://github.com/ssvlabs/ssv-spec/blob/a65134ed45c932588c17d421173923f0216c6c73/types/partial_sig_message.go#L80 and https://eth2book.info/latest/part2/building_blocks/signatures/#signing
+ MessageSignatureSize = 256 // Source: https://github.com/ssvlabs/ssv-spec/blob/a65134ed45c932588c17d421173923f0216c6c73/types/messages.go#L128
+ SyncCommitteeSize = 512 // Source: https://github.com/ethereum/consensus-specs/blob/master/specs/altair/beacon-chain.md#sync-committee
)
type MessageValidation struct {
@@ -940,15 +940,15 @@ func (mv *MessageValidation) ValidatePartialSignatureMessageSemantics(peerID pee
#### Duty Logic
-| Verification | Error | Classification | Explanation |
-| ---------------------------- | ------------------------ | -------------- |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| Already advanced slot | ErrSlotAlreadyAdvanced | Ignore | (Non-committee roles) Signer already advanced to later slot. |
-| No beacon duty | ErrNoDuty | Ignore | If Proposal duty, check if duty exists with beacon node. |
-| Invalid signature type count | ErrInvalidPartialSignatureTypeCount | Reject | It's allow only:
1 PostConsensusPartialSig, for Committee duty,
1 RandaoPartialSig and 1 PostConsensusPartialSig for Proposer,
1 AggregatorCommitteePartialSig and 1 PostConsensusPartialSig for AggregatorCommittee,
1 ValidatorRegistrationPartialSig for Validator Registration,
1 VoluntaryExitPartialSig for Voluntary Exit. |
-| Slot not in time for role | ErrEarlySlotMessage or ErrLateSlotMessage | Ignore | Current time must be between duty's starting time and
+34 (committee and aggregator committee) or +3 (else) slots. |
-| Too many duties per epoch | ErrTooManyDutiesPerEpoch | Ignore | If role is either aggregator, voluntary exit and validator registration,
it's allowed 2 duties per epoch. Else if committee or aggregator committee,
2*V (if no validator is doing sync committee).
Else accept. |
-| Too many partial signatures | ErrTooManyPartialSignatureMessages | Reject | For the committee role, it's allowed $min(2*V, V + $ SYNC_COMMITTEE_SIZE $)$
where $V$ is the number of committee's validators.
For the aggregator committee role, it's allowed $min((1+4)*V, V + 4\times$ SYNC_COMMITTEE_SIZE $)$
where $V$ is the number of committee's validators.
Else, only 1. |
-| Too many equal validator indices | ErrTooManyEqualValidatorIndexInPartialSignatures | Reject | A validator index can not be associated with more than 2 signatures for the committee role and more than 5 for the aggregator committee role. |
+| Verification | Error | Classification | Explanation |
+|----------------------------------|----------------------------------------------------| -------------- |-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Already advanced slot | ErrSlotAlreadyAdvanced | Ignore | (Non-committee roles) Signer already advanced to later slot. |
+| No beacon duty | ErrNoDuty | Ignore | If Proposal duty, check if duty exists with beacon node. |
+| Invalid signature type count | ErrInvalidPartialSignatureTypeCount | Reject | It allows only:
1 PostConsensusPartialSig, for Committee duty,
1 RandaoPartialSig and 1 PostConsensusPartialSig for Proposer,
1 AggregatorCommitteePartialSig and 1 PostConsensusPartialSig for AggregatorCommittee,
1 ValidatorRegistrationPartialSig for Validator Registration,
1 VoluntaryExitPartialSig for Voluntary Exit.|
+| Slot not in time for role | ErrEarlySlotMessage or ErrLateSlotMessage | Ignore | Current time must be between duty's starting time and
+34 (committee and aggregator committee) or +3 (else) slots. |
+| Too many duties per epoch | ErrTooManyDutiesPerEpoch | Ignore | If role is either aggregator, voluntary exit and validator registration,
it's allowed 2 duties per epoch. Else if committee or aggregator committee,
2*V (if no validator is doing sync committee).
Else accept. |
+| Too many partial signatures | ErrTooManyPartialSignatureMessages | Reject | For the committee role, it's allowed $min(2*V, V + $ SYNC_COMMITTEE_SIZE $)$
where $V$ is the number of committee's validators.
For the aggregator committee role, it's allowed $min((1+4)*V, V + 4 \times$ SYNC_COMMITTEE_SIZE $)$
where $V$ is the number of committee's validators.
Else, only 1. |
+| Too many equal validator indices | ErrTooManyEqualValidatorIndicesInPartialSignatures | Reject | A validator index can not be associated with more than 2 signatures for the committee role and more than 5 for the aggregator committee role. |
```go
@@ -1046,7 +1046,7 @@ func (mv *MessageValidation) ValidatePartialSigMessagesByDutyLogic(peerID peer.I
// Rule: a ValidatorIndex can't appear more than 2 (resp. 5) times in the []*PartialSignatureMessage list for role Committee (resp. AggregatorCommittee)
if mv.TooManyEqualValidatorOccurrence(&partialSignatureMessages, msgRole) {
- return ErrTooManyEqualValidatorIndexInPartialSignatures
+ return ErrTooManyEqualValidatorIndicesInPartialSignatures
}
} else {
// Rule: The number of signatures must be 1 for the other types of duties
From 41309871f8e40776692a8037d077446436a340a0 Mon Sep 17 00:00:00 2001
From: Gal Rogozinski
Date: Sun, 25 Jan 2026 13:15:14 +0200
Subject: [PATCH 5/6] whitespaces nit
---
p2p/MessageValidation/Rules.md | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/p2p/MessageValidation/Rules.md b/p2p/MessageValidation/Rules.md
index b4bc99f..5bab75b 100644
--- a/p2p/MessageValidation/Rules.md
+++ b/p2p/MessageValidation/Rules.md
@@ -142,9 +142,9 @@ The main structure is the `MessageValidation` structure which has a `ValidatePub
const (
MaxMsgSize = 9114816 // Source: https://github.com/ssvlabs/ssv-spec/blob/a65134ed45c932588c17d421173923f0216c6c73/types/spectest/tests/maxmsgsize/max_signed_ssv_message.go#L11
maxConsensusMsgSize = 722480 // Source: https://github.com/ssvlabs/ssv-spec/blob/a65134ed45c932588c17d421173923f0216c6c73/types/spectest/tests/maxmsgsize/max_ssv_message.go#L9
- maxPartialSignatureMsgSize = 727000 // Source: https://github.com/ssvlabs/ssv-spec/blob/a65134ed45c932588c17d421173923f0216c6c73/types/spectest/tests/maxmsgsize/max_ssv_message.go#L10
+ maxPartialSignatureMsgSize = 727000 // Source: https://github.com/ssvlabs/ssv-spec/blob/a65134ed45c932588c17d421173923f0216c6c73/types/spectest/tests/maxmsgsize/max_ssv_message.go#L10
maxSSVMessageDataSize = max(maxConsensusMsgSize, maxPartialSignatureMsgSize)
- PartialSignatureSize = 96 // Source: https://github.com/ssvlabs/ssv-spec/blob/a65134ed45c932588c17d421173923f0216c6c73/types/partial_sig_message.go#L80 and https://eth2book.info/latest/part2/building_blocks/signatures/#signing
+ PartialSignatureSize = 96 // Source: https://github.com/ssvlabs/ssv-spec/blob/a65134ed45c932588c17d421173923f0216c6c73/types/partial_sig_message.go#L80 and https://eth2book.info/latest/part2/building_blocks/signatures/#signing
MessageSignatureSize = 256 // Source: https://github.com/ssvlabs/ssv-spec/blob/a65134ed45c932588c17d421173923f0216c6c73/types/messages.go#L128
SyncCommitteeSize = 512 // Source: https://github.com/ethereum/consensus-specs/blob/master/specs/altair/beacon-chain.md#sync-committee
)
@@ -1029,14 +1029,14 @@ func (mv *MessageValidation) ValidatePartialSigMessagesByDutyLogic(peerID peer.I
}
if isCommitteeRole(msgRole) {
-
+
if msgRole == types.RoleCommittee {
// Rule: The number of signatures must be <= min(2*V, V + SYNC_COMMITTEE_SIZE) where V is the number of validators assigned to the cluster
if !mv.ValidNumberOfSignaturesForCommitteeDuty(signedSSVMessage.SSVMessage.MsgID.GetSenderID(), &partialSignatureMessages) {
return ErrTooManyPartialSignatureMessages
}
}
-
+
if msgRole == types.RoleAggregatorCommittee {
// Rule: The number of signatures must be <= min(5*V, V + 4*SYNC_COMMITTEE_SIZE) where V is the number of validators assigned to the cluster
if !mv.ValidNumberOfSignaturesForAggregatorCommitteeDuty(signedSSVMessage.SSVMessage.MsgID.GetSenderID(), &partialSignatureMessages) {
@@ -1067,4 +1067,4 @@ func (mv *MessageValidation) ValidatePartialSigMessagesByDutyLogic(peerID peer.I
### Rules suggestions for future
- Priority-based message handling: priority based on type and sender.
-- Message aggregation: aggregate similar messages (e.g. wait for a quorum of prepares, commits, round-changes, partial-sig) before delivering to the app.
\ No newline at end of file
+- Message aggregation: aggregate similar messages (e.g. wait for a quorum of prepares, commits, round-changes, partial-sig) before delivering to the app.
From 49f11a9e5e98a905747a62e0ccf5f99ca2dac49b Mon Sep 17 00:00:00 2001
From: Gal Rogozinski
Date: Sun, 25 Jan 2026 13:17:48 +0200
Subject: [PATCH 6/6] docs: normalize aggregator committee wording
---
p2p/MessageValidation/Rules.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/p2p/MessageValidation/Rules.md b/p2p/MessageValidation/Rules.md
index 5bab75b..a871265 100644
--- a/p2p/MessageValidation/Rules.md
+++ b/p2p/MessageValidation/Rules.md
@@ -797,7 +797,7 @@ func (mv *MessageValidation) ValidateQBFTMessageByDutyLogic(peerID peer.ID, sign
}
// Rule: current slot(height) must be between duty's starting slot and:
- // - duty's starting slot + 34 (committee and aggregation)
+ // - duty's starting slot + 34 (committee and aggregator committee)
// - duty's starting slot + 3 (other types)
if err != mv.ValidDutySlot(peerID, phase0.Slot(qbftMessage.Height), signedSSVMessage.SSVMessage.MsgID.GetRoleType()); err != nil {
// Err should be ErrEarlySlotMessage or ErrLateSlotMessage
@@ -806,7 +806,7 @@ func (mv *MessageValidation) ValidateQBFTMessageByDutyLogic(peerID peer.ID, sign
// Rule: valid number of duties per epoch:
// - 2 for voluntary exit and validator registration
- // - 2*V for Committee and Aggregator Committee duty (where V is the number of validators in the cluster) (if no validator is doing sync committee in this epoch)
+ // - 2*V for committee and aggregator committee duty (where V is the number of validators in the cluster) (if no validator is doing sync committee in this epoch)
// - else, accept
if !mv.ValidNumberOfDutiesPerEpoch(peerID, signedSSVMessage.SSVMessage.MsgID, phase0.Slot(qbftMessage.Height)) {
return ErrTooManyDutiesPerEpoch