Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: moved non-verification misbehaviour checks to checkForMisbehaviour #3046

Merged
merged 12 commits into from
Jan 30, 2023
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* [\#2434](https://github.com/cosmos/ibc-go/pull/2478) Removed all `TypeMsg` constants
* (modules/core/exported) [#1689] (https://github.com/cosmos/ibc-go/pull/2539) Removing `GetVersions` from `ConnectionI` interface.
* (core/02-connection) [#2419](https://github.com/cosmos/ibc-go/pull/2419) Add optional proof data to proto definitions of `MsgConnectionOpenTry` and `MsgConnectionOpenAck` for host state machines that are unable to introspect their own consensus state.
* (modules/light-clients/07-tendermint)[#2007] Moved non-verification misbehaviour checks to `CheckForMisbehaviour`

### Features

Expand Down
51 changes: 24 additions & 27 deletions modules/light-clients/07-tendermint/misbehaviour_handle.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
)

// CheckForMisbehaviour detects duplicate height misbehaviour and BFT time violation misbehaviour
// in a submitted Header message and verifies the correctness of a submitted Misbehaviour ClientMessage
func (cs ClientState) CheckForMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, msg exported.ClientMessage) bool {
switch msg := msg.(type) {
case *Header:
Expand Down Expand Up @@ -51,9 +52,29 @@ func (cs ClientState) CheckForMisbehaviour(ctx sdk.Context, cdc codec.BinaryCode
return true
}
case *Misbehaviour:
// The correctness of Misbehaviour ClientMessage types is ensured by calling VerifyClientMessage prior to this function
// Thus, here we can return true, as ClientMessage is of type Misbehaviour
return true
// if heights are equal check that this is valid misbehaviour of a fork
// otherwise if heights are unequal check that this is valid misbehavior of BFT time violation
if msg.Header1.GetHeight().EQ(msg.Header2.GetHeight()) {
blockID1, err := tmtypes.BlockIDFromProto(&msg.Header1.SignedHeader.Commit.BlockID)
if err != nil {
return false
}

blockID2, err := tmtypes.BlockIDFromProto(&msg.Header2.SignedHeader.Commit.BlockID)
if err != nil {
return false
}

// Ensure that Commit Hashes are different
if !bytes.Equal(blockID1.Hash, blockID2.Hash) {
return true
}

} else if !msg.Header1.SignedHeader.Header.Time.After(msg.Header2.SignedHeader.Header.Time) {
// Header1 is at greater height than Header2, therefore Header1 time must be less than or equal to
// Header2 time in order to be valid misbehaviour (violation of monotonic time).
return true
}
}

return false
Expand All @@ -68,30 +89,6 @@ func (cs ClientState) CheckForMisbehaviour(ctx sdk.Context, cdc codec.BinaryCode
// to misbehaviour.Header2
// Misbehaviour sets frozen height to {0, 1} since it is only used as a boolean value (zero or non-zero).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there anything we should update in the godoc of this function after this change?

func (cs *ClientState) verifyMisbehaviour(ctx sdk.Context, clientStore sdk.KVStore, cdc codec.BinaryCodec, misbehaviour *Misbehaviour) error {
// if heights are equal check that this is valid misbehaviour of a fork
// otherwise if heights are unequal check that this is valid misbehavior of BFT time violation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also copy over these 2 lines of comments?

if misbehaviour.Header1.GetHeight().EQ(misbehaviour.Header2.GetHeight()) {
blockID1, err := tmtypes.BlockIDFromProto(&misbehaviour.Header1.SignedHeader.Commit.BlockID)
if err != nil {
return sdkerrors.Wrap(err, "invalid block ID from header 1 in misbehaviour")
}

blockID2, err := tmtypes.BlockIDFromProto(&misbehaviour.Header2.SignedHeader.Commit.BlockID)
if err != nil {
return sdkerrors.Wrap(err, "invalid block ID from header 2 in misbehaviour")
}

// Ensure that Commit Hashes are different
if bytes.Equal(blockID1.Hash, blockID2.Hash) {
return sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "headers block hashes are equal")
}

} else if misbehaviour.Header1.SignedHeader.Header.Time.After(misbehaviour.Header2.SignedHeader.Header.Time) {
// Header1 is at greater height than Header2, therefore Header1 time must be less than or equal to
// Header2 time in order to be valid misbehaviour (violation of monotonic time).
return sdkerrors.Wrap(clienttypes.ErrInvalidMisbehaviour, "headers are not at same height and are monotonically increasing")
}

// Regardless of the type of misbehaviour, ensure that both headers are valid and would have been accepted by light-client

// Retrieve trusted consensus states for each Header in misbehaviour
Expand Down
64 changes: 0 additions & 64 deletions modules/light-clients/07-tendermint/misbehaviour_handle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,38 +204,6 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() {
}
}, true,
},
{
"invalid fork misbehaviour: identical headers", func() {
trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height)

trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1)
suite.Require().True(found)

err := path.EndpointA.UpdateClient()
suite.Require().NoError(err)

height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height)

misbehaviourHeader := suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers)
misbehaviour = &ibctm.Misbehaviour{
Header1: misbehaviourHeader,
Header2: misbehaviourHeader,
}
}, false,
},
{
"invalid time misbehaviour: monotonically increasing time", func() {
trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height)

trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1)
suite.Require().True(found)

misbehaviour = &ibctm.Misbehaviour{
Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+3, trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers),
Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers),
}
}, false,
},
{
"invalid misbehaviour: misbehaviour from different chain", func() {
trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height)
Expand Down Expand Up @@ -523,38 +491,6 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() {
}
}, true,
},
{
"invalid fork misbehaviour: identical headers", func() {
trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height)

trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1)
suite.Require().True(found)

err := path.EndpointA.UpdateClient()
suite.Require().NoError(err)

height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height)

misbehaviourHeader := suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers)
misbehaviour = &ibctm.Misbehaviour{
Header1: misbehaviourHeader,
Header2: misbehaviourHeader,
}
}, false,
},
{
"invalid time misbehaviour: monotonically increasing time", func() {
trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height)

trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1)
suite.Require().True(found)

misbehaviour = &ibctm.Misbehaviour{
Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+3, trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers),
Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers),
}
}, false,
},
{
"invalid misbehaviour: misbehaviour from different chain", func() {
trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height)
Expand Down
45 changes: 45 additions & 0 deletions modules/light-clients/07-tendermint/update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,38 @@ func (suite *TendermintTestSuite) TestCheckForMisbehaviour() {
},
false,
},
{
"invalid fork misbehaviour: identical headers", func() {
trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height)

trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1)
suite.Require().True(found)

err := path.EndpointA.UpdateClient()
suite.Require().NoError(err)

height := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height)

misbehaviourHeader := suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers)
clientMessage = &ibctm.Misbehaviour{
Header1: misbehaviourHeader,
Header2: misbehaviourHeader,
}
}, false,
},
{
"invalid time misbehaviour: monotonically increasing time", func() {
trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height)

trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1)
suite.Require().True(found)

clientMessage = &ibctm.Misbehaviour{
Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+3, trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers),
Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers),
}
}, false,
},
{
"consensus state already exists, app hash mismatch",
func() {
Expand Down Expand Up @@ -705,6 +737,19 @@ func (suite *TendermintTestSuite) TestCheckForMisbehaviour() {
},
true,
},
{
"valid time misbehaviour: not monotonically increasing time", func() {
trustedHeight := path.EndpointA.GetClientState().GetLatestHeight().(clienttypes.Height)

trustedVals, found := suite.chainB.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1)
suite.Require().True(found)

clientMessage = &ibctm.Misbehaviour{
Header2: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height+3, trustedHeight, suite.chainB.CurrentHeader.Time.Add(time.Minute), suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers),
Header1: suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, suite.chainB.CurrentHeader.Height, trustedHeight, suite.chainB.CurrentHeader.Time, suite.chainB.Vals, suite.chainB.NextVals, trustedVals, suite.chainB.Signers),
}
}, true,
},
}

for _, tc := range testCases {
Expand Down