Skip to content

Commit

Permalink
tmp
Browse files Browse the repository at this point in the history
  • Loading branch information
iurii-ssv committed Oct 27, 2024
1 parent 3de919b commit 9c3ecdf
Show file tree
Hide file tree
Showing 15 changed files with 412 additions and 334 deletions.
138 changes: 103 additions & 35 deletions message/validation/consensus_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,8 @@ import (
"github.com/ssvlabs/ssv-spec-pre-cc/types"
specqbft "github.com/ssvlabs/ssv-spec/qbft"
spectypes "github.com/ssvlabs/ssv-spec/types"

"github.com/ssvlabs/ssv/protocol/v2/message"
"github.com/ssvlabs/ssv/protocol/v2/qbft/roundtimer"
ssvtypes "github.com/ssvlabs/ssv/protocol/v2/types"
"github.com/ssvlabs/ssv/utils/casts"
)

Expand Down Expand Up @@ -82,48 +80,92 @@ func (mv *messageValidator) validateConsensusMessageSemantics(
committee []spectypes.OperatorID,
) error {
signers := signedSSVMessage.OperatorIDs
quorumSize, _ := ssvtypes.ComputeQuorumAndPartialQuorum(uint64(len(committee)))
msgType := consensusMessage.MsgType

if len(signers) > 1 {
// Rule: Decided msg with different type than Commit
if msgType != specqbft.CommitMsgType {
e := ErrNonDecidedWithMultipleSigners
e.got = len(signers)
return e
}

// Rule: Number of signers must be >= quorum size
if uint64(len(signers)) < quorumSize {
e := ErrDecidedNotEnoughSigners
e.want = quorumSize
e.got = len(signers)
return e
}
// Rule: Consensus message type must be valid
if !mv.validConsensusMsgType(msgType) {
return ErrUnknownQBFTMessageType
}

if len(signedSSVMessage.FullData) != 0 {
// Rule: Prepare or commit messages must not have full data
if msgType == specqbft.PrepareMsgType || (msgType == specqbft.CommitMsgType && len(signers) == 1) {
return ErrPrepareOrCommitWithFullData
if msgType == specqbft.ProposalMsgType {
// Rule: Proposal message must have exactly 1 signer
if err := ensureSingleSignerAtMost(signers); err != nil {
return fmt.Errorf("proposal message with > 1 signer: %w", err)
}

hashedFullData, err := specqbft.HashDataRoot(signedSSVMessage.FullData)
if err != nil {
e := ErrFullDataHash
e.innerErr = err
return e
// Rule: Proposal message must have full data
if len(signedSSVMessage.FullData) == 0 {
return ErrProposalWithoutFullData
}

// Rule: Full data hash must match root
if hashedFullData != consensusMessage.Root {
return ErrInvalidHash
if err := verifyRootAgainstFullData(consensusMessage.Root, signedSSVMessage.FullData); err != nil {
return err
}
}

// Rule: Consensus message type must be valid
if !mv.validConsensusMsgType(msgType) {
return ErrUnknownQBFTMessageType
if msgType == specqbft.PrepareMsgType {
// Rule: Prepare message must have exactly 1 signer
if err := ensureSingleSignerAtMost(signers); err != nil {
return fmt.Errorf("prepare message with > 1 signer: %w", err)
}
// Rule: Prepare message must not have full data
if len(signedSSVMessage.FullData) != 0 {
return ErrPrepareWithFullData
}
// Rule: Prepare message must have non-zero root (operator validates it against locally
// stored proposal)
if consensusMessage.Root == [32]byte{} {
return ErrZeroRootHash
}
}
if msgType == specqbft.CommitMsgType && len(signers) == 1 {
// Rule: Commit message (unless it's decided commit message, which is handled below)
// must not have full data
if len(signedSSVMessage.FullData) != 0 {
return ErrCommitWithFullData
}
// Rule: Commit message (unless it's decided commit message, which is handled below)
// must have non-zero root (operator validates it against locally stored proposal)
if consensusMessage.Root == [32]byte{} {
return ErrZeroRootHash
}
}
if msgType == specqbft.CommitMsgType && len(signers) >= 2 {
// Rule: Commit message must have exactly 1 signer
if err := ensureSingleSignerAtMost(signers); err != nil {
return fmt.Errorf("commit message with > 1 signer: %w", err)
}
// TODO in case we want to spread the data QBFT decided upon via QBFT commit phase we need
// to introduce the concept of "decided commit" as well as adjust the rule-set above to the
// change it to the rule-set commented out below.
// Note also, QBFT itself doesn't support this ^ at the moment and hence will not permit
// commit messages with >= 2 signers.
//
//// Rule: Commit message with multiple signers (aka decided commit) must have full data so
//// it can share it with those operator who didn't receive it during proposal stage of QBFT
//if len(signedSSVMessage.FullData) == 0 {
// return ErrDecidedCommitWithoutFullData
//}
//// Rule: Number of signers for decided commit must be >= quorum size
//quorumSize, _ := ssvtypes.ComputeQuorumAndPartialQuorum(uint64(len(committee)))
//if uint64(len(signers)) < quorumSize {
// e := ErrCommitSignersNotEnoughForQuorum
// e.want = quorumSize
// e.got = len(signers)
// return e
//}
//// Rule: Full data hash must match root
//if err := verifyRootAgainstFullData(consensusMessage.Root, signedSSVMessage.FullData); err != nil {
// return err
//}
}
if msgType == specqbft.RoundChangeMsgType {
// Rule: Round change message must have exactly 1 signer
if err := ensureSingleSignerAtMost(signers); err != nil {
return fmt.Errorf("round change message with > 1 signer: %w", err)
}
// Rule: Full data hash must match root
if err := verifyRootAgainstFullData(consensusMessage.Root, signedSSVMessage.FullData); err != nil {
return err
}
}

// Rule: Round must not be zero
Expand Down Expand Up @@ -291,6 +333,32 @@ func (mv *messageValidator) validateQBFTMessageByDutyLogic(
return nil
}

func ensureSingleSignerAtMost(signers []spectypes.OperatorID) error {
if len(signers) > 1 {
e := ErrMultipleSigners
e.got = len(signers)
e.want = 1
return e
}
return nil
}

func verifyRootAgainstFullData(root [32]byte, fullData []byte) error {
hashedFullData, err := specqbft.HashDataRoot(fullData)
if err != nil {
e := ErrComputeFullDataHash
e.innerErr = err
return e
}
if hashedFullData != root {
e := ErrInvalidRootHash
e.want = hashedFullData
e.got = root
return e
}
return nil
}

func (mv *messageValidator) updateConsensusState(signedSSVMessage *spectypes.SignedSSVMessage, consensusMessage *specqbft.Message, consensusState *consensusState) error {
msgSlot := phase0.Slot(consensusMessage.Height)
msgEpoch := mv.netCfg.Beacon.EstimatedEpochAtSlot(msgSlot)
Expand Down
40 changes: 29 additions & 11 deletions message/validation/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,22 @@ import (

pubsub "github.com/libp2p/go-libp2p-pubsub"
"github.com/libp2p/go-libp2p/core/peer"
"go.uber.org/zap"

"github.com/ssvlabs/ssv/logging/fields"
"github.com/ssvlabs/ssv/protocol/v2/ssv/queue"
"go.uber.org/zap"
)

type Error struct {
text string
// text is the main/core factor defining this error
text string
// reject signals if we want to reject incoming message based on this error
reject bool

got any
want any
innerErr error
reject bool
silent bool

silent bool
}

func (e Error) Error() string {
Expand All @@ -39,6 +42,17 @@ func (e Error) Error() string {
return sb.String()
}

// Is defines the equivalency of Error (compared to other errors), 2 Error(s) are same if
// they have matching Error.text and Error.reject values.
func (e Error) Is(target error) bool {
var targetErr Error
ok := errors.As(target, &targetErr)
if !ok {
return false
}
return targetErr.text == e.text && targetErr.reject == e.reject
}

func (e Error) Reject() bool {
return e.reject
}
Expand Down Expand Up @@ -88,16 +102,17 @@ var (
ErrSignerNotLeader = Error{text: "signer is not leader", reject: true}
ErrSignersNotSorted = Error{text: "signers are not sorted", reject: true}
ErrInconsistentSigners = Error{text: "signer is not expected", reject: true}
ErrInvalidHash = Error{text: "root doesn't match full data hash", reject: true}
ErrFullDataHash = Error{text: "couldn't hash root", reject: true}
ErrZeroRootHash = Error{text: "root hash is zero", reject: true}
ErrInvalidRootHash = Error{text: "root doesn't match full data hash", reject: true}
ErrComputeFullDataHash = Error{text: "couldn't hash root", reject: true}
ErrUndecodableMessageData = Error{text: "message data could not be decoded", reject: true}
ErrEventMessage = Error{text: "unexpected event message", reject: true}
ErrUnknownSSVMessageType = Error{text: "unknown SSV message type", reject: true}
ErrUnknownQBFTMessageType = Error{text: "unknown QBFT message type", reject: true}
ErrInvalidPartialSignatureType = Error{text: "unknown partial signature message type", reject: true}
ErrPartialSignatureTypeRoleMismatch = Error{text: "partial signature type and role don't match", reject: true}
ErrNonDecidedWithMultipleSigners = Error{text: "non-decided with multiple signers", reject: true}
ErrDecidedNotEnoughSigners = Error{text: "not enough signers in decided message", reject: true}
ErrMultipleSigners = Error{text: "message with multiple signers", reject: true}
ErrCommitSignersNotEnoughForQuorum = Error{text: "commit message has > 1 signers, but not enough for quorum", reject: true}
ErrDifferentProposalData = Error{text: "different proposal data", reject: true}
ErrMalformedPrepareJustifications = Error{text: "malformed prepare justifications", reject: true}
ErrUnexpectedPrepareJustifications = Error{text: "prepare justifications unexpected for this message type", reject: true}
Expand All @@ -108,8 +123,11 @@ var (
ErrNoSignatures = Error{text: "no signatures", reject: true}
ErrSignersAndSignaturesWithDifferentLength = Error{text: "signature and operator ID length mismatch", reject: true}
ErrPartialSigOneSigner = Error{text: "partial signature message must have only one signer", reject: true}
ErrPrepareOrCommitWithFullData = Error{text: "prepare or commit with full data", reject: true}
ErrFullDataNotInConsensusMessage = Error{text: "full data not in consensus message", reject: true}
ErrProposalWithoutFullData = Error{text: "proposal without full data", reject: true}
ErrPrepareWithFullData = Error{text: "prepare with full data", reject: true}
ErrCommitWithFullData = Error{text: "commit with full data", reject: true}
ErrDecidedCommitWithoutFullData = Error{text: "decided commit without full data", reject: true}
ErrFullDataNotInPartialSignatureMessage = Error{text: "full data not in partial signature message", reject: true}
ErrTripleValidatorIndexInPartialSignatures = Error{text: "triple validator index in partial signatures", reject: true}
ErrZeroRound = Error{text: "zero round", reject: true}
ErrDuplicatedMessage = Error{text: "message is duplicated", reject: true}
Expand Down
2 changes: 1 addition & 1 deletion message/validation/partial_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func (mv *messageValidator) validatePartialSignatureMessageSemantics(

// Rule: Partial signature message must not have full data
if len(signedSSVMessage.FullData) > 0 {
return ErrFullDataNotInConsensusMessage
return ErrFullDataNotInPartialSignatureMessage
}

// Rule: Valid signature type
Expand Down
Loading

0 comments on commit 9c3ecdf

Please sign in to comment.