Some checks failed
CodeQL / Analyze (push) Waiting to run
Docker Build & Push Simapp (main) / docker-build (push) Waiting to run
golangci-lint / lint (push) Waiting to run
Tests / Code Coverage / build (amd64) (push) Waiting to run
Tests / Code Coverage / build (arm64) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[additional-args:-tags="test_e2e" name:e2e path:./e2e]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:08-wasm path:./modules/light-clients/08-wasm]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:ibc-go path:.]) (push) Waiting to run
Deploy to GitHub Pages / Deploy to GitHub Pages (push) Has been cancelled
Buf-Push / push (push) Has been cancelled
240 lines
9.7 KiB
Go
240 lines
9.7 KiB
Go
package tendermint_test
|
|
|
|
import (
|
|
"errors"
|
|
"time"
|
|
|
|
errorsmod "cosmossdk.io/errors"
|
|
|
|
"github.com/cometbft/cometbft/crypto/tmhash"
|
|
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
|
|
cmttypes "github.com/cometbft/cometbft/types"
|
|
|
|
clienttypes "github.com/cosmos/ibc-go/v10/modules/core/02-client/types"
|
|
"github.com/cosmos/ibc-go/v10/modules/core/exported"
|
|
ibctm "github.com/cosmos/ibc-go/v10/modules/light-clients/07-tendermint"
|
|
ibctesting "github.com/cosmos/ibc-go/v10/testing"
|
|
)
|
|
|
|
func (suite *TendermintTestSuite) TestMisbehaviour() {
|
|
heightMinus1 := clienttypes.NewHeight(0, height.RevisionHeight-1)
|
|
|
|
misbehaviour := &ibctm.Misbehaviour{
|
|
Header1: suite.header,
|
|
Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, suite.valSet, suite.valSet, suite.valSet, suite.signers),
|
|
ClientId: clientID,
|
|
}
|
|
|
|
suite.Require().Equal(exported.Tendermint, misbehaviour.ClientType())
|
|
}
|
|
|
|
func (suite *TendermintTestSuite) TestMisbehaviourValidateBasic() {
|
|
altPrivVal := cmttypes.NewMockPV()
|
|
altPubKey, err := altPrivVal.GetPubKey()
|
|
suite.Require().NoError(err)
|
|
|
|
revisionHeight := int64(height.RevisionHeight)
|
|
|
|
altVal := cmttypes.NewValidator(altPubKey, revisionHeight)
|
|
|
|
// Create alternative validator set with only altVal
|
|
altValSet := cmttypes.NewValidatorSet([]*cmttypes.Validator{altVal})
|
|
|
|
// Create signer array and ensure it is in same order as bothValSet
|
|
bothValSet, bothSigners := getBothSigners(suite, altVal, altPrivVal)
|
|
|
|
altSignerArr := []cmttypes.PrivValidator{altPrivVal}
|
|
|
|
heightMinus1 := clienttypes.NewHeight(0, height.RevisionHeight-1)
|
|
|
|
testCases := []struct {
|
|
name string
|
|
misbehaviour *ibctm.Misbehaviour
|
|
malleateMisbehaviour func(misbehaviour *ibctm.Misbehaviour) error
|
|
expErr error
|
|
}{
|
|
{
|
|
"valid fork misbehaviour, two headers at same height have different time",
|
|
&ibctm.Misbehaviour{
|
|
Header1: suite.header,
|
|
Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now.Add(time.Minute), suite.valSet, suite.valSet, suite.valSet, suite.signers),
|
|
ClientId: clientID,
|
|
},
|
|
func(misbehaviour *ibctm.Misbehaviour) error { return nil },
|
|
nil,
|
|
},
|
|
{
|
|
"valid time misbehaviour, both headers at different heights are at same time",
|
|
&ibctm.Misbehaviour{
|
|
Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight+5), heightMinus1, suite.now, suite.valSet, suite.valSet, suite.valSet, suite.signers),
|
|
Header2: suite.header,
|
|
ClientId: clientID,
|
|
},
|
|
func(misbehaviour *ibctm.Misbehaviour) error { return nil },
|
|
nil,
|
|
},
|
|
{
|
|
"misbehaviour Header1 is nil",
|
|
ibctm.NewMisbehaviour(clientID, nil, suite.header),
|
|
func(m *ibctm.Misbehaviour) error { return nil },
|
|
errorsmod.Wrap(ibctm.ErrInvalidHeader, "misbehaviour Header1 cannot be nil"),
|
|
},
|
|
{
|
|
"misbehaviour Header2 is nil",
|
|
ibctm.NewMisbehaviour(clientID, suite.header, nil),
|
|
func(m *ibctm.Misbehaviour) error { return nil },
|
|
errorsmod.Wrap(ibctm.ErrInvalidHeader, "misbehaviour Header2 cannot be nil"),
|
|
},
|
|
{
|
|
"valid misbehaviour with different trusted headers",
|
|
&ibctm.Misbehaviour{
|
|
Header1: suite.header,
|
|
Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), clienttypes.NewHeight(0, height.RevisionHeight-3), suite.now.Add(time.Minute), suite.valSet, suite.valSet, bothValSet, suite.signers),
|
|
ClientId: clientID,
|
|
},
|
|
func(misbehaviour *ibctm.Misbehaviour) error { return nil },
|
|
nil,
|
|
},
|
|
{
|
|
"trusted height is 0 in Header1",
|
|
&ibctm.Misbehaviour{
|
|
Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), clienttypes.ZeroHeight(), suite.now.Add(time.Minute), suite.valSet, suite.valSet, suite.valSet, suite.signers),
|
|
Header2: suite.header,
|
|
ClientId: clientID,
|
|
},
|
|
func(misbehaviour *ibctm.Misbehaviour) error { return nil },
|
|
errorsmod.Wrap(ibctm.ErrInvalidHeaderHeight, "misbehaviour Header1 cannot have zero revision height"),
|
|
},
|
|
{
|
|
"trusted height is 0 in Header2",
|
|
&ibctm.Misbehaviour{
|
|
Header1: suite.header,
|
|
Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), clienttypes.ZeroHeight(), suite.now.Add(time.Minute), suite.valSet, suite.valSet, suite.valSet, suite.signers),
|
|
ClientId: clientID,
|
|
},
|
|
func(misbehaviour *ibctm.Misbehaviour) error { return nil },
|
|
errorsmod.Wrap(ibctm.ErrInvalidHeaderHeight, "misbehaviour Header2 cannot have zero revision height"),
|
|
},
|
|
{
|
|
"trusted valset is nil in Header1",
|
|
&ibctm.Misbehaviour{
|
|
Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now.Add(time.Minute), suite.valSet, suite.valSet, nil, suite.signers),
|
|
Header2: suite.header,
|
|
ClientId: clientID,
|
|
},
|
|
func(misbehaviour *ibctm.Misbehaviour) error { return nil },
|
|
errorsmod.Wrap(ibctm.ErrInvalidValidatorSet, "trusted validator set in Header1 cannot be empty"),
|
|
},
|
|
{
|
|
"trusted valset is nil in Header2",
|
|
&ibctm.Misbehaviour{
|
|
Header1: suite.header,
|
|
Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now.Add(time.Minute), suite.valSet, suite.valSet, nil, suite.signers),
|
|
ClientId: clientID,
|
|
},
|
|
func(misbehaviour *ibctm.Misbehaviour) error { return nil },
|
|
errorsmod.Wrap(ibctm.ErrInvalidValidatorSet, "trusted validator set in Header2 cannot be empty"),
|
|
},
|
|
{
|
|
"invalid client ID ",
|
|
&ibctm.Misbehaviour{
|
|
Header1: suite.header,
|
|
Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, suite.valSet, suite.valSet, suite.valSet, suite.signers),
|
|
ClientId: "GAI",
|
|
},
|
|
func(misbehaviour *ibctm.Misbehaviour) error { return nil },
|
|
errors.New("identifier GAI has invalid length"),
|
|
},
|
|
{
|
|
"chainIDs do not match",
|
|
&ibctm.Misbehaviour{
|
|
Header1: suite.header,
|
|
Header2: suite.chainA.CreateTMClientHeader("ethermint", int64(height.RevisionHeight), heightMinus1, suite.now, suite.valSet, suite.valSet, suite.valSet, suite.signers),
|
|
ClientId: clientID,
|
|
},
|
|
func(misbehaviour *ibctm.Misbehaviour) error { return nil },
|
|
errorsmod.Wrap(clienttypes.ErrInvalidMisbehaviour, "headers must have identical chainIDs"),
|
|
},
|
|
{
|
|
"header2 height is greater",
|
|
&ibctm.Misbehaviour{
|
|
Header1: suite.header,
|
|
Header2: suite.chainA.CreateTMClientHeader(chainID, 6, clienttypes.NewHeight(0, height.RevisionHeight+1), suite.now, suite.valSet, suite.valSet, suite.valSet, suite.signers),
|
|
ClientId: clientID,
|
|
},
|
|
func(misbehaviour *ibctm.Misbehaviour) error { return nil },
|
|
errors.New("Header1 height is less than Header2 height"),
|
|
},
|
|
{
|
|
"header 1 doesn't have 2/3 majority",
|
|
&ibctm.Misbehaviour{
|
|
Header1: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, bothValSet, bothValSet, suite.valSet, bothSigners),
|
|
Header2: suite.header,
|
|
ClientId: clientID,
|
|
},
|
|
func(misbehaviour *ibctm.Misbehaviour) error {
|
|
// voteSet contains only altVal which is less than 2/3 of total power (height/1height)
|
|
wrongVoteSet := cmttypes.NewVoteSet(chainID, int64(misbehaviour.Header1.GetHeight().GetRevisionHeight()), 1, cmtproto.PrecommitType, altValSet)
|
|
blockID, err := cmttypes.BlockIDFromProto(&misbehaviour.Header1.Commit.BlockID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
extCommit, err := cmttypes.MakeExtCommit(*blockID, int64(misbehaviour.Header2.GetHeight().GetRevisionHeight()), misbehaviour.Header1.Commit.Round, wrongVoteSet, altSignerArr, suite.now, false)
|
|
misbehaviour.Header1.Commit = extCommit.ToCommit().ToProto()
|
|
return err
|
|
},
|
|
errors.New("validator set did not commit to header"),
|
|
},
|
|
{
|
|
"header 2 doesn't have 2/3 majority",
|
|
&ibctm.Misbehaviour{
|
|
Header1: suite.header,
|
|
Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, bothValSet, bothValSet, suite.valSet, bothSigners),
|
|
ClientId: clientID,
|
|
},
|
|
func(misbehaviour *ibctm.Misbehaviour) error {
|
|
// voteSet contains only altVal which is less than 2/3 of total power (height/1height)
|
|
wrongVoteSet := cmttypes.NewVoteSet(chainID, int64(misbehaviour.Header2.GetHeight().GetRevisionHeight()), 1, cmtproto.PrecommitType, altValSet)
|
|
blockID, err := cmttypes.BlockIDFromProto(&misbehaviour.Header2.Commit.BlockID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
extCommit, err := cmttypes.MakeExtCommit(*blockID, int64(misbehaviour.Header2.GetHeight().GetRevisionHeight()), misbehaviour.Header2.Commit.Round, wrongVoteSet, altSignerArr, suite.now, false)
|
|
misbehaviour.Header2.Commit = extCommit.ToCommit().ToProto()
|
|
return err
|
|
},
|
|
errors.New("validator set did not commit to header"),
|
|
},
|
|
{
|
|
"validators sign off on wrong commit",
|
|
&ibctm.Misbehaviour{
|
|
Header1: suite.header,
|
|
Header2: suite.chainA.CreateTMClientHeader(chainID, int64(height.RevisionHeight), heightMinus1, suite.now, bothValSet, bothValSet, suite.valSet, bothSigners),
|
|
ClientId: clientID,
|
|
},
|
|
func(misbehaviour *ibctm.Misbehaviour) error {
|
|
tmBlockID := ibctesting.MakeBlockID(tmhash.Sum([]byte("other_hash")), 3, tmhash.Sum([]byte("other_partset")))
|
|
misbehaviour.Header2.Commit.BlockID = tmBlockID.ToProto()
|
|
return nil
|
|
},
|
|
errors.New("header 2 failed validation"),
|
|
},
|
|
}
|
|
|
|
for i, tc := range testCases {
|
|
suite.Run(tc.name, func() {
|
|
err := tc.malleateMisbehaviour(tc.misbehaviour)
|
|
suite.Require().NoError(err)
|
|
err = tc.misbehaviour.ValidateBasic()
|
|
|
|
if tc.expErr == nil {
|
|
suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name)
|
|
} else {
|
|
suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name)
|
|
suite.Require().ErrorContains(err, tc.expErr.Error())
|
|
}
|
|
})
|
|
}
|
|
}
|