Skip to content

Commit

Permalink
Add verify signatures flag to isProposalJustification. Add validSigne…
Browse files Browse the repository at this point in the history
…dRoundChangeForData function. Propagate name changes.
  • Loading branch information
MatheusFranco99 committed Feb 1, 2024
1 parent 5dbc3c4 commit b20078c
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 12 deletions.
2 changes: 1 addition & 1 deletion qbft/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ func (i *Instance) BaseMsgValidation(msg *SignedMessage) error {
i.State.Share.Committee,
)
case RoundChangeMsgType:
return validRoundChangeForData(i.State, i.config, msg, i.State.Height, msg.Message.Round, msg.FullData)
return validSignedRoundChangeForData(i.State, i.config, msg, i.State.Height, msg.Message.Round, msg.FullData)
default:
return errors.New("signed message type not supported")
}
Expand Down
45 changes: 34 additions & 11 deletions qbft/proposal.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package qbft

import (
"bytes"

"github.com/bloxapp/ssv-spec/types"
"github.com/pkg/errors"
)
Expand Down Expand Up @@ -93,6 +94,7 @@ func isValidProposal(
signedProposal.Message.Round,
signedProposal.FullData,
valCheck,
true,
); err != nil {
return errors.Wrap(err, "proposal not justified")
}
Expand All @@ -114,6 +116,7 @@ func isProposalJustification(
round Round,
fullData []byte,
valCheck ProposedValueCheckF,
verifySignatures bool,
) error {
if err := valCheck(fullData); err != nil {
return errors.Wrap(err, "proposal fullData invalid")
Expand All @@ -126,9 +129,16 @@ func isProposalJustification(
// no quorum, duplicate signers, invalid still has quorum, invalid no quorum
// prepared
for _, rc := range roundChangeMsgs {
if err := validRoundChangeForData(state, config, rc, height, round, fullData); err != nil {
return errors.Wrap(err, "change round msg not valid")
if verifySignatures {
if err := validSignedRoundChangeForData(state, config, rc, height, round, fullData); err != nil {
return errors.Wrap(err, "change round msg not valid")
}
} else {
if err := validRoundChangeForData(state, config, rc, height, round, fullData); err != nil {
return errors.Wrap(err, "change round msg not valid")
}
}

}

// check there is a quorum
Expand Down Expand Up @@ -178,15 +188,28 @@ func isProposalJustification(

// validate each prepare message against the highest previously prepared fullData and round
for _, pm := range prepareMsgs {
if err := validSignedPrepareForHeightRoundAndRoot(
config,
pm,
height,
rcm.Message.DataRound,
rcm.Message.Root,
state.Share.Committee,
); err != nil {
return errors.New("signed prepare not valid")
if verifySignatures {
if err := validSignedPrepareForHeightRoundAndRoot(
config,
pm,
height,
rcm.Message.DataRound,
rcm.Message.Root,
state.Share.Committee,
); err != nil {
return errors.New("signed prepare not valid")
}
} else {
if err := validPrepareForHeightRoundAndRoot(
config,
pm,
height,
rcm.Message.DataRound,
rcm.Message.Root,
state.Share.Committee,
); err != nil {
return errors.New("signed prepare not valid")
}
}
}
return nil
Expand Down
70 changes: 70 additions & 0 deletions qbft/round_change.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package qbft

import (
"bytes"

"github.com/bloxapp/ssv-spec/types"
"github.com/pkg/errors"
)
Expand Down Expand Up @@ -200,12 +201,15 @@ func isReceivedProposalJustification(
newRound,
value,
valCheck,
false,
); err != nil {
return errors.Wrap(err, "proposal not justified")
}
return nil
}

// validRoundChangeForData is similar to the validSignedRoundChangeForData function
// However, it does not verify signatures
func validRoundChangeForData(
state *State,
config IConfig,
Expand All @@ -227,6 +231,72 @@ func validRoundChangeForData(
return errors.New("msg allows 1 signer")
}

if err := signedMsg.Message.Validate(); err != nil {
return errors.Wrap(err, "roundChange invalid")
}

// Addition to formal spec
// We add this extra tests on the msg itself to filter round change msgs with invalid justifications, before they are inserted into msg containers
if signedMsg.Message.RoundChangePrepared() {
r, err := HashDataRoot(fullData)
if err != nil {
return errors.Wrap(err, "could not hash input data")
}

// validate prepare message justifications
prepareMsgs, _ := signedMsg.Message.GetRoundChangeJustifications() // no need to check error, checked on signedMsg.Message.Validate()
for _, pm := range prepareMsgs {
if err := validPrepareForHeightRoundAndRoot(
config,
pm,
state.Height,
signedMsg.Message.DataRound,
signedMsg.Message.Root,
state.Share.Committee); err != nil {
return errors.Wrap(err, "round change justification invalid")
}
}

if !bytes.Equal(r[:], signedMsg.Message.Root[:]) {
return errors.New("H(data) != root")
}

if !HasQuorum(state.Share, prepareMsgs) {
return errors.New("no justifications quorum")
}

if signedMsg.Message.DataRound > round {
return errors.New("prepared round > round")
}

return nil
}
return nil
}

// validSignedRoundChangeForData is based on dafny's validRoundChange function
// with the addition that nested Prepare messages are also validated
func validSignedRoundChangeForData(
state *State,
config IConfig,
signedMsg *SignedMessage,
height Height,
round Round,
fullData []byte,
) error {
if signedMsg.Message.MsgType != RoundChangeMsgType {
return errors.New("round change msg type is wrong")
}
if signedMsg.Message.Height != height {
return errors.New("wrong msg height")
}
if signedMsg.Message.Round != round {
return errors.New("wrong msg round")
}
if len(signedMsg.GetSigners()) != 1 {
return errors.New("msg allows 1 signer")
}

if err := signedMsg.Signature.VerifyByOperators(signedMsg, config.GetSignatureDomainType(), types.QBFTSignatureType, state.Share.Committee); err != nil {
return errors.Wrap(err, "msg signature invalid")
}
Expand Down

0 comments on commit b20078c

Please sign in to comment.