From 84c44700c849755a37a8e447651220c6881f8a9d Mon Sep 17 00:00:00 2001 From: Charly Date: Tue, 2 Apr 2024 11:38:26 +0200 Subject: [PATCH 01/19] move tests from client state to lcm, use lcm entry point --- .../07-tendermint/client_state.go | 3 +- .../07-tendermint/client_state_test.go | 690 +---------------- modules/light-clients/07-tendermint/errors.go | 1 + .../07-tendermint/light_client_module.go | 7 +- .../07-tendermint/light_client_module_test.go | 692 ++++++++++++++++++ .../07-tendermint/tendermint_test.go | 7 +- 6 files changed, 733 insertions(+), 667 deletions(-) diff --git a/modules/light-clients/07-tendermint/client_state.go b/modules/light-clients/07-tendermint/client_state.go index d0a1d10e30e..81563fe2917 100644 --- a/modules/light-clients/07-tendermint/client_state.go +++ b/modules/light-clients/07-tendermint/client_state.go @@ -123,7 +123,7 @@ func (cs ClientState) Validate() error { } if err := light.ValidateTrustLevel(cs.TrustLevel.ToTendermint()); err != nil { - return err + return errorsmod.Wrapf(ErrInvalidTrustLevel, "%s", err) } if cs.TrustingPeriod <= 0 { return errorsmod.Wrap(ErrInvalidTrustingPeriod, "trusting period must be greater than zero") @@ -303,7 +303,6 @@ func verifyDelayPeriodPassed(ctx sdk.Context, store storetypes.KVStore, proofHei return errorsmod.Wrapf(ErrDelayPeriodNotPassed, "cannot verify packet until time: %d, current time: %d", validTime, currentTimestamp) } - } if delayBlockPeriod != 0 { diff --git a/modules/light-clients/07-tendermint/client_state_test.go b/modules/light-clients/07-tendermint/client_state_test.go index 0fa2222de6e..49bd513fd77 100644 --- a/modules/light-clients/07-tendermint/client_state_test.go +++ b/modules/light-clients/07-tendermint/client_state_test.go @@ -1,21 +1,11 @@ package tendermint_test import ( - "time" - ics23 "github.com/cosmos/ics23/go" - sdk "github.com/cosmos/cosmos-sdk/types" - - transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" - channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" commitmenttypes "github.com/cosmos/ibc-go/v8/modules/core/23-commitment/types" - host "github.com/cosmos/ibc-go/v8/modules/core/24-host" - "github.com/cosmos/ibc-go/v8/modules/core/exported" ibctm "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v8/testing" - ibcmock "github.com/cosmos/ibc-go/v8/testing/mock" ) const ( @@ -26,741 +16,121 @@ const ( var invalidProof = []byte("invalid proof") -func (suite *TendermintTestSuite) TestStatus() { - var ( - path *ibctesting.Path - clientState *ibctm.ClientState - ) - - testCases := []struct { - name string - malleate func() - expStatus exported.Status - }{ - {"client is active", func() {}, exported.Active}, - {"client is frozen", func() { - clientState.FrozenHeight = clienttypes.NewHeight(0, 1) - path.EndpointA.SetClientState(clientState) - }, exported.Frozen}, - {"client status without consensus state", func() { - clientState.LatestHeight = clientState.LatestHeight.Increment().(clienttypes.Height) - path.EndpointA.SetClientState(clientState) - }, exported.Expired}, - {"client status is expired", func() { - suite.coordinator.IncrementTimeBy(clientState.TrustingPeriod) - }, exported.Expired}, - } - - for _, tc := range testCases { - tc := tc - suite.Run(tc.name, func() { - suite.SetupTest() - - path = ibctesting.NewPath(suite.chainA, suite.chainB) - path.SetupClients() - - clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - clientState = path.EndpointA.GetClientState().(*ibctm.ClientState) - - tc.malleate() - - status := clientState.Status(suite.chainA.GetContext(), clientStore, suite.chainA.App.AppCodec()) - suite.Require().Equal(tc.expStatus, status) - }) - - } -} - -func (suite *TendermintTestSuite) TestGetTimestampAtHeight() { - var ( - path *ibctesting.Path - height exported.Height - ) - expectedTimestamp := time.Unix(1, 0) - - testCases := []struct { - name string - malleate func() - expErr error - }{ - { - "success", - func() {}, - nil, - }, - { - "failure: consensus state not found for height", - func() { - clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) - height = clientState.LatestHeight.Increment() - }, - clienttypes.ErrConsensusStateNotFound, - }, - } - - for _, tc := range testCases { - tc := tc - suite.Run(tc.name, func() { - suite.SetupTest() - - path = ibctesting.NewPath(suite.chainA, suite.chainB) - path.SetupClients() - - clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) - height = clientState.LatestHeight - - store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - - // grab consensusState from store and update with a predefined timestamp - consensusState := path.EndpointA.GetConsensusState(height) - tmConsensusState, ok := consensusState.(*ibctm.ConsensusState) - suite.Require().True(ok) - - tmConsensusState.Timestamp = expectedTimestamp - path.EndpointA.SetConsensusState(tmConsensusState, height) - - tc.malleate() - - timestamp, err := clientState.GetTimestampAtHeight(suite.chainA.GetContext(), store, suite.chainA.Codec, height) - - expPass := tc.expErr == nil - if expPass { - suite.Require().NoError(err) - - expectedTimestamp := uint64(expectedTimestamp.UnixNano()) - suite.Require().Equal(expectedTimestamp, timestamp) - } else { - suite.Require().ErrorIs(err, tc.expErr) - } - }) - } -} - func (suite *TendermintTestSuite) TestValidate() { testCases := []struct { name string clientState *ibctm.ClientState + expErr error expPass bool }{ { name: "valid client", clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath), - expPass: true, + expErr: nil, }, { name: "valid client with nil upgrade path", clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), nil), - expPass: true, + expErr: nil, }, { name: "invalid chainID", clientState: ibctm.NewClientState(" ", ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath), - expPass: false, + expErr: ibctm.ErrInvalidChainID, }, { // NOTE: if this test fails, the code must account for the change in chainID length across tendermint versions! // Do not only fix the test, fix the code! // https://github.com/cosmos/ibc-go/issues/177 - name: "valid chainID - chainID validation failed for chainID of length 50! ", + name: "valid chainID - chainID validation did not fail for chainID of length 50! ", clientState: ibctm.NewClientState(fiftyCharChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath), - expPass: true, + expErr: nil, }, { // NOTE: if this test fails, the code must account for the change in chainID length across tendermint versions! // Do not only fix the test, fix the code! // https://github.com/cosmos/ibc-go/issues/177 - name: "invalid chainID - chainID validation did not fail for chainID of length 51! ", + name: "invalid chainID - chainID validation failed for chainID of length 51! ", clientState: ibctm.NewClientState(fiftyOneCharChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath), - expPass: false, + expErr: ibctm.ErrInvalidChainID, }, { name: "invalid trust level", clientState: ibctm.NewClientState(chainID, ibctm.Fraction{Numerator: 0, Denominator: 1}, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath), - expPass: false, + expErr: ibctm.ErrInvalidTrustLevel, }, { name: "invalid zero trusting period", clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, 0, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath), - expPass: false, + expErr: ibctm.ErrInvalidTrustingPeriod, }, { name: "invalid negative trusting period", clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, -1, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath), - expPass: false, + expErr: ibctm.ErrInvalidTrustingPeriod, }, { name: "invalid zero unbonding period", clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, trustingPeriod, 0, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath), - expPass: false, + expErr: ibctm.ErrInvalidUnbondingPeriod, }, { name: "invalid negative unbonding period", clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, trustingPeriod, -1, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath), - expPass: false, + expErr: ibctm.ErrInvalidUnbondingPeriod, }, { name: "invalid zero max clock drift", clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, 0, height, commitmenttypes.GetSDKSpecs(), upgradePath), - expPass: false, + expErr: ibctm.ErrInvalidMaxClockDrift, }, { name: "invalid negative max clock drift", clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, -1, height, commitmenttypes.GetSDKSpecs(), upgradePath), - expPass: false, + expErr: ibctm.ErrInvalidMaxClockDrift, }, { name: "invalid revision number", clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.NewHeight(1, 1), commitmenttypes.GetSDKSpecs(), upgradePath), - expPass: false, + expErr: ibctm.ErrInvalidHeaderHeight, }, { name: "invalid revision height", clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, clienttypes.ZeroHeight(), commitmenttypes.GetSDKSpecs(), upgradePath), - expPass: false, + expErr: ibctm.ErrInvalidHeaderHeight, }, { name: "trusting period not less than unbonding period", clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), upgradePath), - expPass: false, + expErr: ibctm.ErrInvalidTrustingPeriod, }, { name: "proof specs is nil", - clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, nil, upgradePath), - expPass: false, + clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, nil, upgradePath), + expErr: ibctm.ErrInvalidProofSpecs, }, { name: "proof specs contains nil", - clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, ubdPeriod, ubdPeriod, maxClockDrift, height, []*ics23.ProofSpec{ics23.TendermintSpec, nil}, upgradePath), - expPass: false, - }, - } - - for _, tc := range testCases { - tc := tc - suite.Run(tc.name, func() { - err := tc.clientState.Validate() - if tc.expPass { - suite.Require().NoError(err, tc.name) - } else { - suite.Require().Error(err, tc.name) - } - }) - } -} - -func (suite *TendermintTestSuite) TestInitialize() { - testCases := []struct { - name string - consensusState exported.ConsensusState - expPass bool - }{ - { - name: "valid consensus", - consensusState: &ibctm.ConsensusState{}, - expPass: true, - }, - { - name: "invalid consensus: consensus state is solomachine consensus", - consensusState: ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2).ConsensusState(), - expPass: false, - }, - } - - for _, tc := range testCases { - tc := tc - suite.Run(tc.name, func() { - suite.SetupTest() - path := ibctesting.NewPath(suite.chainA, suite.chainB) - - tmConfig, ok := path.EndpointB.ClientConfig.(*ibctesting.TendermintConfig) - suite.Require().True(ok) - - clientState := ibctm.NewClientState( - path.EndpointB.Chain.ChainID, - tmConfig.TrustLevel, tmConfig.TrustingPeriod, tmConfig.UnbondingPeriod, tmConfig.MaxClockDrift, - suite.chainB.LatestCommittedHeader.GetTrustedHeight(), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, - ) - - store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - err := clientState.Initialize(suite.chainA.GetContext(), suite.chainA.Codec, store, tc.consensusState) - - if tc.expPass { - suite.Require().NoError(err, "valid case returned an error") - suite.Require().True(store.Has(host.ClientStateKey())) - suite.Require().True(store.Has(host.ConsensusStateKey(suite.chainB.LatestCommittedHeader.GetTrustedHeight()))) - } else { - suite.Require().Error(err, "invalid case didn't return an error") - suite.Require().False(store.Has(host.ClientStateKey())) - suite.Require().False(store.Has(host.ConsensusStateKey(suite.chainB.LatestCommittedHeader.GetTrustedHeight()))) - } - }) - } -} - -func (suite *TendermintTestSuite) TestVerifyMembership() { - var ( - testingpath *ibctesting.Path - delayTimePeriod uint64 - delayBlockPeriod uint64 - err error - proofHeight exported.Height - proof []byte - path exported.Path - value []byte - ) - - testCases := []struct { - name string - malleate func() - expPass bool - }{ - { - "successful ClientState verification", - func() { - // default proof construction uses ClientState - }, - true, - }, - { - "successful ConsensusState verification", func() { - latestHeight := testingpath.EndpointB.GetClientLatestHeight() - - key := host.FullConsensusStateKey(testingpath.EndpointB.ClientID, latestHeight) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = suite.chainB.QueryProof(key) - - consensusState := testingpath.EndpointB.GetConsensusState(latestHeight).(*ibctm.ConsensusState) - value, err = suite.chainB.Codec.MarshalInterface(consensusState) - suite.Require().NoError(err) - }, - true, - }, - { - "successful Connection verification", func() { - key := host.ConnectionKey(testingpath.EndpointB.ConnectionID) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = suite.chainB.QueryProof(key) - - connection := testingpath.EndpointB.GetConnection() - value, err = suite.chainB.Codec.Marshal(&connection) - suite.Require().NoError(err) - }, - true, - }, - { - "successful Channel verification", func() { - key := host.ChannelKey(testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = suite.chainB.QueryProof(key) - - channel := testingpath.EndpointB.GetChannel() - value, err = suite.chainB.Codec.Marshal(&channel) - suite.Require().NoError(err) - }, - true, - }, - { - "successful PacketCommitment verification", func() { - // send from chainB to chainA since we are proving chainB sent a packet - sequence, err := testingpath.EndpointB.SendPacket(clienttypes.NewHeight(1, 100), 0, ibctesting.MockPacketData) - suite.Require().NoError(err) - - // make packet commitment proof - packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequence, testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID, testingpath.EndpointA.ChannelConfig.PortID, testingpath.EndpointA.ChannelID, clienttypes.NewHeight(1, 100), 0) - key := host.PacketCommitmentKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = testingpath.EndpointB.QueryProof(key) - - value = channeltypes.CommitPacket(suite.chainA.App.GetIBCKeeper().Codec(), packet) - }, true, - }, - { - "successful Acknowledgement verification", func() { - // send from chainA to chainB since we are proving chainB wrote an acknowledgement - sequence, err := testingpath.EndpointA.SendPacket(clienttypes.NewHeight(1, 100), 0, ibctesting.MockPacketData) - suite.Require().NoError(err) - - // write receipt and ack - packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequence, testingpath.EndpointA.ChannelConfig.PortID, testingpath.EndpointA.ChannelID, testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) - err = testingpath.EndpointB.RecvPacket(packet) - suite.Require().NoError(err) - - key := host.PacketAcknowledgementKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = testingpath.EndpointB.QueryProof(key) - - value = channeltypes.CommitAcknowledgement(ibcmock.MockAcknowledgement.Acknowledgement()) - }, - true, - }, - { - "successful NextSequenceRecv verification", func() { - // send from chainA to chainB since we are proving chainB incremented the sequence recv - - // send packet - sequence, err := testingpath.EndpointA.SendPacket(clienttypes.NewHeight(1, 100), 0, ibctesting.MockPacketData) - suite.Require().NoError(err) - - // next seq recv incremented - packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequence, testingpath.EndpointA.ChannelConfig.PortID, testingpath.EndpointA.ChannelID, testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) - err = testingpath.EndpointB.RecvPacket(packet) - suite.Require().NoError(err) - - key := host.NextSequenceRecvKey(packet.GetSourcePort(), packet.GetSourceChannel()) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = testingpath.EndpointB.QueryProof(key) - - value = sdk.Uint64ToBigEndian(packet.GetSequence() + 1) - }, - true, - }, - { - "successful verification outside IBC store", func() { - key := transfertypes.PortKey - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(commitmenttypes.NewMerklePrefix([]byte(transfertypes.StoreKey)), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = suite.chainB.QueryProofForStore(transfertypes.StoreKey, key, int64(testingpath.EndpointA.GetClientLatestHeight().GetRevisionHeight())) - - value = []byte(suite.chainB.GetSimApp().TransferKeeper.GetPort(suite.chainB.GetContext())) - suite.Require().NoError(err) - }, - true, - }, - { - "delay time period has passed", func() { - delayTimePeriod = uint64(time.Second.Nanoseconds()) - }, - true, - }, - { - "delay time period has not passed", func() { - delayTimePeriod = uint64(time.Hour.Nanoseconds()) - }, - false, - }, - { - "delay block period has passed", func() { - delayBlockPeriod = 1 - }, - true, - }, - { - "delay block period has not passed", func() { - delayBlockPeriod = 1000 - }, - false, - }, - { - "latest client height < height", func() { - proofHeight = testingpath.EndpointA.GetClientLatestHeight().Increment() - }, false, - }, - { - "invalid path type", - func() { - path = ibcmock.KeyPath{} - }, - false, - }, - { - "failed to unmarshal merkle proof", func() { - proof = invalidProof - }, false, - }, - { - "consensus state not found", func() { - proofHeight = clienttypes.ZeroHeight() - }, false, + clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, []*ics23.ProofSpec{ics23.TendermintSpec, nil}, upgradePath), + expErr: ibctm.ErrInvalidProofSpecs, }, { - "proof verification failed", func() { - // change the value being proved - value = []byte("invalid value") - }, false, - }, - { - "proof is empty", func() { - // change the inserted proof - proof = []byte{} - }, false, + name: "invalid upgrade path", + clientState: ibctm.NewClientState(chainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs(), invalidUpgradePath), + expErr: clienttypes.ErrInvalidClient, }, } for _, tc := range testCases { tc := tc - suite.Run(tc.name, func() { - suite.SetupTest() // reset - testingpath = ibctesting.NewPath(suite.chainA, suite.chainB) - testingpath.SetChannelOrdered() - testingpath.Setup() - - // reset time and block delays to 0, malleate may change to a specific non-zero value. - delayTimePeriod = 0 - delayBlockPeriod = 0 - - // create default proof, merklePath, and value which passes - // may be overwritten by malleate() - key := host.FullClientStateKey(testingpath.EndpointB.ClientID) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = suite.chainB.QueryProof(key) - - clientState := testingpath.EndpointB.GetClientState().(*ibctm.ClientState) - value, err = suite.chainB.Codec.MarshalInterface(clientState) - suite.Require().NoError(err) - - tc.malleate() // make changes as necessary - - clientState = testingpath.EndpointA.GetClientState().(*ibctm.ClientState) - - ctx := suite.chainA.GetContext() - store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, testingpath.EndpointA.ClientID) - - err = clientState.VerifyMembership( - ctx, store, suite.chainA.Codec, proofHeight, delayTimePeriod, delayBlockPeriod, - proof, path, value, - ) - - if tc.expPass { - suite.Require().NoError(err) - } else { - suite.Require().Error(err) - } - }) - } -} - -func (suite *TendermintTestSuite) TestVerifyNonMembership() { - var ( - testingpath *ibctesting.Path - delayTimePeriod uint64 - delayBlockPeriod uint64 - err error - proofHeight exported.Height - path exported.Path - proof []byte - invalidClientID = "09-tendermint" - invalidConnectionID = "connection-100" - invalidChannelID = "channel-800" - invalidPortID = "invalid-port" - ) - - testCases := []struct { - name string - malleate func() - expPass bool - }{ - { - "successful ClientState verification of non membership", - func() { - // default proof construction uses ClientState - }, - true, - }, - { - "successful ConsensusState verification of non membership", func() { - key := host.FullConsensusStateKey(invalidClientID, testingpath.EndpointB.GetClientLatestHeight()) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = suite.chainB.QueryProof(key) - }, - true, - }, - { - "successful Connection verification of non membership", func() { - key := host.ConnectionKey(invalidConnectionID) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = suite.chainB.QueryProof(key) - }, - true, - }, - { - "successful Channel verification of non membership", func() { - key := host.ChannelKey(testingpath.EndpointB.ChannelConfig.PortID, invalidChannelID) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = suite.chainB.QueryProof(key) - }, - true, - }, - { - "successful PacketCommitment verification of non membership", func() { - // make packet commitment proof - key := host.PacketCommitmentKey(invalidPortID, invalidChannelID, 1) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = testingpath.EndpointB.QueryProof(key) - }, true, - }, - { - "successful Acknowledgement verification of non membership", func() { - key := host.PacketAcknowledgementKey(invalidPortID, invalidChannelID, 1) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = testingpath.EndpointB.QueryProof(key) - }, - true, - }, - { - "successful NextSequenceRecv verification of non membership", func() { - key := host.NextSequenceRecvKey(invalidPortID, invalidChannelID) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = testingpath.EndpointB.QueryProof(key) - }, - true, - }, - { - "successful verification of non membership outside IBC store", func() { - key := []byte{0x08} - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(commitmenttypes.NewMerklePrefix([]byte(transfertypes.StoreKey)), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = suite.chainB.QueryProofForStore(transfertypes.StoreKey, key, int64(testingpath.EndpointA.GetClientLatestHeight().GetRevisionHeight())) - }, - true, - }, - { - "delay time period has passed", func() { - delayTimePeriod = uint64(time.Second.Nanoseconds()) - }, - true, - }, - { - "delay time period has not passed", func() { - delayTimePeriod = uint64(time.Hour.Nanoseconds()) - }, - false, - }, - { - "delay block period has passed", func() { - delayBlockPeriod = 1 - }, - true, - }, - { - "delay block period has not passed", func() { - delayBlockPeriod = 1000 - }, - false, - }, - { - "latest client height < height", func() { - proofHeight = testingpath.EndpointA.GetClientLatestHeight().Increment() - }, false, - }, - { - "invalid path type", - func() { - path = ibcmock.KeyPath{} - }, - false, - }, - { - "failed to unmarshal merkle proof", func() { - proof = invalidProof - }, false, - }, - { - "consensus state not found", func() { - proofHeight = clienttypes.ZeroHeight() - }, false, - }, - { - "verify non membership fails as path exists", func() { - // change the value being proved - key := host.FullClientStateKey(testingpath.EndpointB.ClientID) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = suite.chainB.QueryProof(key) - }, false, - }, - { - "proof is empty", func() { - // change the inserted proof - proof = []byte{} - }, false, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() // reset - testingpath = ibctesting.NewPath(suite.chainA, suite.chainB) - testingpath.SetChannelOrdered() - testingpath.Setup() - - // reset time and block delays to 0, malleate may change to a specific non-zero value. - delayTimePeriod = 0 - delayBlockPeriod = 0 - - // create default proof, merklePath, and value which passes - // may be overwritten by malleate() - key := host.FullClientStateKey("invalid-client-id") - - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = suite.chainB.QueryProof(key) - - tc.malleate() // make changes as necessary - - clientState := testingpath.EndpointA.GetClientState().(*ibctm.ClientState) - - ctx := suite.chainA.GetContext() - store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, testingpath.EndpointA.ClientID) - - err = clientState.VerifyNonMembership( - ctx, store, suite.chainA.Codec, proofHeight, delayTimePeriod, delayBlockPeriod, - proof, path, - ) + err := tc.clientState.Validate() - if tc.expPass { - suite.Require().NoError(err) + expPass := tc.expErr == nil + if expPass { + suite.Require().NoError(err, tc.name) } else { - suite.Require().Error(err) + suite.Require().ErrorContains(err, tc.expErr.Error()) } }) } diff --git a/modules/light-clients/07-tendermint/errors.go b/modules/light-clients/07-tendermint/errors.go index 8bbb58ec33a..11c2cc0c66d 100644 --- a/modules/light-clients/07-tendermint/errors.go +++ b/modules/light-clients/07-tendermint/errors.go @@ -19,4 +19,5 @@ var ( ErrUnbondingPeriodExpired = errorsmod.Register(ModuleName, 12, "time since latest trusted state has passed the unbonding period") ErrInvalidProofSpecs = errorsmod.Register(ModuleName, 13, "invalid proof specs") ErrInvalidValidatorSet = errorsmod.Register(ModuleName, 14, "invalid validator set") + ErrInvalidTrustLevel = errorsmod.Register(ModuleName, 15, "invalid trust level") ) diff --git a/modules/light-clients/07-tendermint/light_client_module.go b/modules/light-clients/07-tendermint/light_client_module.go index 46e52eec621..1919adca887 100644 --- a/modules/light-clients/07-tendermint/light_client_module.go +++ b/modules/light-clients/07-tendermint/light_client_module.go @@ -1,6 +1,8 @@ package tendermint import ( + fmt "fmt" + errorsmod "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/codec" @@ -41,7 +43,7 @@ func (l *LightClientModule) RegisterStoreProvider(storeProvider exported.ClientS func (l LightClientModule) Initialize(ctx sdk.Context, clientID string, clientStateBz, consensusStateBz []byte) error { var clientState ClientState if err := l.keeper.Codec().Unmarshal(clientStateBz, &clientState); err != nil { - return err + return fmt.Errorf("failed to unmarshal client state bytes into client state: %w", err) } if err := clientState.Validate(); err != nil { @@ -50,7 +52,8 @@ func (l LightClientModule) Initialize(ctx sdk.Context, clientID string, clientSt var consensusState ConsensusState if err := l.keeper.Codec().Unmarshal(consensusStateBz, &consensusState); err != nil { - return err + return fmt.Errorf("failed to unmarshal consensus state bytes into consensus state: %w", err) + } if err := consensusState.ValidateBasic(); err != nil { diff --git a/modules/light-clients/07-tendermint/light_client_module_test.go b/modules/light-clients/07-tendermint/light_client_module_test.go index fef8763a836..5ec5aeba02c 100644 --- a/modules/light-clients/07-tendermint/light_client_module_test.go +++ b/modules/light-clients/07-tendermint/light_client_module_test.go @@ -7,14 +7,18 @@ import ( upgradetypes "cosmossdk.io/x/upgrade/types" codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" + channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" commitmenttypes "github.com/cosmos/ibc-go/v8/modules/core/23-commitment/types" host "github.com/cosmos/ibc-go/v8/modules/core/24-host" ibcerrors "github.com/cosmos/ibc-go/v8/modules/core/errors" "github.com/cosmos/ibc-go/v8/modules/core/exported" ibctm "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v8/testing" + ibcmock "github.com/cosmos/ibc-go/v8/testing/mock" ) var ( @@ -22,6 +26,201 @@ var ( solomachineClientID = clienttypes.FormatClientIdentifier(exported.Solomachine, 0) ) +func (suite *TendermintTestSuite) TestStatus() { + var ( + path *ibctesting.Path + clientState *ibctm.ClientState + ) + + testCases := []struct { + name string + malleate func() + expStatus exported.Status + }{ + {"client is active", func() {}, exported.Active}, + {"client is frozen", func() { + clientState.FrozenHeight = clienttypes.NewHeight(0, 1) + path.EndpointA.SetClientState(clientState) + }, exported.Frozen}, + {"client status without consensus state", func() { + clientState.LatestHeight = clientState.LatestHeight.Increment().(clienttypes.Height) + path.EndpointA.SetClientState(clientState) + }, exported.Expired}, + {"client status is expired", func() { + suite.coordinator.IncrementTimeBy(clientState.TrustingPeriod) + }, exported.Expired}, + { + "client state not found for height", + func() { + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + store.Delete(host.ClientStateKey()) + }, + exported.Unknown, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + suite.SetupTest() + + path = ibctesting.NewPath(suite.chainA, suite.chainB) + path.SetupClients() + + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(path.EndpointA.ClientID) + suite.Require().True(found) + + clientState = path.EndpointA.GetClientState().(*ibctm.ClientState) + + tc.malleate() + + status := lightClientModule.Status(suite.chainA.GetContext(), path.EndpointA.ClientID) + suite.Require().Equal(tc.expStatus, status) + }) + + } +} + +func (suite *TendermintTestSuite) TestGetTimestampAtHeight() { + var ( + path *ibctesting.Path + height exported.Height + ) + expectedTimestamp := time.Unix(1, 0) + + testCases := []struct { + name string + malleate func() + expErr error + }{ + { + "success", + func() {}, + nil, + }, + { + "failure: client state not found for height", + func() { + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + store.Delete(host.ClientStateKey()) + }, + clienttypes.ErrClientNotFound, + }, + { + "failure: consensus state not found for height", + func() { + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) + height = clientState.LatestHeight.Increment() + }, + clienttypes.ErrConsensusStateNotFound, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + suite.SetupTest() + + path = ibctesting.NewPath(suite.chainA, suite.chainB) + path.SetupClients() + + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) + height = clientState.LatestHeight + + // grab consensusState from store and update with a predefined timestamp + consensusState := path.EndpointA.GetConsensusState(height) + tmConsensusState, ok := consensusState.(*ibctm.ConsensusState) + suite.Require().True(ok) + + tmConsensusState.Timestamp = expectedTimestamp + path.EndpointA.SetConsensusState(tmConsensusState, height) + + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(path.EndpointA.ClientID) + suite.Require().True(found) + + tc.malleate() + + timestamp, err := lightClientModule.TimestampAtHeight(suite.chainA.GetContext(), path.EndpointA.ClientID, height) + + expPass := tc.expErr == nil + if expPass { + suite.Require().NoError(err) + + expectedTimestamp := uint64(expectedTimestamp.UnixNano()) + suite.Require().Equal(expectedTimestamp, timestamp) + } else { + suite.Require().ErrorIs(err, tc.expErr) + } + }) + } +} + +// TODO: fix this test +// func (suite *TendermintTestSuite) TestInitialize() { + +// var consensusState exported.ConsensusState +// var clientState *ibctm.ClientState + +// testCases := []struct { +// name string +// malleate func() +// expErr error +// }{ +// { +// "valid consensus & client states", +// func() {}, +// nil, +// }, +// { +// "invalid client state", +// func() { +// clientState.ChainId = "" +// }, +// ibctm.ErrInvalidChainID, +// }, +// { +// "invalid consensus: consensus state is solomachine consensus", +// func() { +// consensusState = ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2).ConsensusState() +// }, +// clienttypes.ErrInvalidConsensus, +// }, +// } + +// for _, tc := range testCases { +// tc := tc +// suite.Run(tc.name, func() { +// suite.SetupTest() +// path := ibctesting.NewPath(suite.chainA, suite.chainB) + +// clientID := suite.chainA.App.GetIBCKeeper().ClientKeeper.GenerateClientIdentifier(suite.chainA.GetContext(), clientState.ClientType()) + +// lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(clientID) +// suite.Require().True(found) + +// tc.malleate() + +// clientStateBz := suite.chainB.Codec.MustMarshal(clientState) +// consStateBz := suite.chainB.Codec.MustMarshal(consensusState) + +// err := lightClientModule.Initialize(suite.chainB.GetContext(), path.EndpointB.ClientID, clientStateBz, consStateBz) + +// store := suite.chainB.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainB.GetContext(), path.EndpointB.ClientID) + +// expPass := tc.expErr == nil +// if expPass { +// suite.Require().NoError(err, "valid case returned an error") +// suite.Require().True(store.Has(host.ClientStateKey())) +// suite.Require().True(store.Has(host.ConsensusStateKey(height))) +// } else { +// suite.Require().ErrorContains(err, tc.expErr.Error()) +// suite.Require().False(store.Has(host.ClientStateKey())) +// suite.Require().False(store.Has(host.ConsensusStateKey(height))) +// } +// }) +// } +// } + func (suite *TendermintTestSuite) TestRecoverClient() { var ( subjectClientID, substituteClientID string @@ -276,3 +475,496 @@ func (suite *TendermintTestSuite) TestVerifyUpgradeAndUpdateState() { }) } } + +func (suite *TendermintTestSuite) TestVerifyMembership() { + var ( + testingpath *ibctesting.Path + delayTimePeriod uint64 + delayBlockPeriod uint64 + err error + proofHeight exported.Height + proof []byte + path exported.Path + value []byte + ) + + testCases := []struct { + name string + malleate func() + expErr error + }{ + { + "successful ClientState verification", + func() { + // default proof construction uses ClientState + }, + nil, + }, + { + "successful ConsensusState verification", func() { + latestHeight := testingpath.EndpointB.GetClientLatestHeight() + + key := host.FullConsensusStateKey(testingpath.EndpointB.ClientID, latestHeight) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + + consensusState := testingpath.EndpointB.GetConsensusState(latestHeight).(*ibctm.ConsensusState) + value, err = suite.chainB.Codec.MarshalInterface(consensusState) + suite.Require().NoError(err) + }, + nil, + }, + { + "successful Connection verification", func() { + key := host.ConnectionKey(testingpath.EndpointB.ConnectionID) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + + connection := testingpath.EndpointB.GetConnection() + value, err = suite.chainB.Codec.Marshal(&connection) + suite.Require().NoError(err) + }, + nil, + }, + { + "successful Channel verification", func() { + key := host.ChannelKey(testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + + channel := testingpath.EndpointB.GetChannel() + value, err = suite.chainB.Codec.Marshal(&channel) + suite.Require().NoError(err) + }, + nil, + }, + { + "successful PacketCommitment verification", func() { + // send from chainB to chainA since we are proving chainB sent a packet + sequence, err := testingpath.EndpointB.SendPacket(clienttypes.NewHeight(1, 100), 0, ibctesting.MockPacketData) + suite.Require().NoError(err) + + // make packet commitment proof + packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequence, testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID, testingpath.EndpointA.ChannelConfig.PortID, testingpath.EndpointA.ChannelID, clienttypes.NewHeight(1, 100), 0) + key := host.PacketCommitmentKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = testingpath.EndpointB.QueryProof(key) + + value = channeltypes.CommitPacket(suite.chainA.App.GetIBCKeeper().Codec(), packet) + }, nil, + }, + { + "successful Acknowledgement verification", func() { + // send from chainA to chainB since we are proving chainB wrote an acknowledgement + sequence, err := testingpath.EndpointA.SendPacket(clienttypes.NewHeight(1, 100), 0, ibctesting.MockPacketData) + suite.Require().NoError(err) + + // write receipt and ack + packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequence, testingpath.EndpointA.ChannelConfig.PortID, testingpath.EndpointA.ChannelID, testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) + err = testingpath.EndpointB.RecvPacket(packet) + suite.Require().NoError(err) + + key := host.PacketAcknowledgementKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = testingpath.EndpointB.QueryProof(key) + + value = channeltypes.CommitAcknowledgement(ibcmock.MockAcknowledgement.Acknowledgement()) + }, + nil, + }, + { + "successful NextSequenceRecv verification", func() { + // send from chainA to chainB since we are proving chainB incremented the sequence recv + + // send packet + sequence, err := testingpath.EndpointA.SendPacket(clienttypes.NewHeight(1, 100), 0, ibctesting.MockPacketData) + suite.Require().NoError(err) + + // next seq recv incremented + packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequence, testingpath.EndpointA.ChannelConfig.PortID, testingpath.EndpointA.ChannelID, testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) + err = testingpath.EndpointB.RecvPacket(packet) + suite.Require().NoError(err) + + key := host.NextSequenceRecvKey(packet.GetSourcePort(), packet.GetSourceChannel()) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = testingpath.EndpointB.QueryProof(key) + + value = sdk.Uint64ToBigEndian(packet.GetSequence() + 1) + }, + nil, + }, + { + "successful verification outside IBC store", func() { + key := transfertypes.PortKey + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(commitmenttypes.NewMerklePrefix([]byte(transfertypes.StoreKey)), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProofForStore(transfertypes.StoreKey, key, int64(testingpath.EndpointA.GetClientLatestHeight().GetRevisionHeight())) + + value = []byte(suite.chainB.GetSimApp().TransferKeeper.GetPort(suite.chainB.GetContext())) + suite.Require().NoError(err) + }, + nil, + }, + { + "delay time period has passed", func() { + delayTimePeriod = uint64(time.Second.Nanoseconds()) + }, + nil, + }, + { + "delay time period has not passed", func() { + delayTimePeriod = uint64(time.Hour.Nanoseconds()) + }, + ibctm.ErrDelayPeriodNotPassed, + }, + { + "delay block period has passed", func() { + delayBlockPeriod = 1 + }, + nil, + }, + { + "delay block period has not passed", func() { + delayBlockPeriod = 1000 + }, + ibctm.ErrDelayPeriodNotPassed, + }, + { + "latest client height < height", func() { + proofHeight = testingpath.EndpointA.GetClientLatestHeight().Increment() + }, + ibcerrors.ErrInvalidHeight, + }, + { + "invalid path type", + func() { + path = ibcmock.KeyPath{} + }, + ibcerrors.ErrInvalidType, + }, + { + "failed to unmarshal merkle proof", func() { + proof = invalidProof + }, + commitmenttypes.ErrInvalidProof, + }, + { + "consensus state not found", func() { + proofHeight = clienttypes.ZeroHeight() + }, + clienttypes.ErrConsensusStateNotFound, + }, + { + "proof verification failed", func() { + // change the value being proved + value = []byte("invalid value") + }, + commitmenttypes.ErrInvalidProof, + }, + { + "proof is empty", func() { + // change the inserted proof + proof = []byte{} + }, + commitmenttypes.ErrInvalidMerkleProof, + }, + { + "client state not found for height", + func() { + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), testingpath.EndpointA.ClientID) + store.Delete(host.ClientStateKey()) + }, + clienttypes.ErrClientNotFound, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() // reset + testingpath = ibctesting.NewPath(suite.chainA, suite.chainB) + testingpath.SetChannelOrdered() + testingpath.Setup() + + // reset time and block delays to 0, malleate may change to a specific non-zero value. + delayTimePeriod = 0 + delayBlockPeriod = 0 + + // create default proof, merklePath, and value which passes + // may be overwritten by malleate() + key := host.FullClientStateKey(testingpath.EndpointB.ClientID) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + + clientState := testingpath.EndpointB.GetClientState().(*ibctm.ClientState) + value, err = suite.chainB.Codec.MarshalInterface(clientState) + suite.Require().NoError(err) + + tc.malleate() // make changes as necessary + + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(testingpath.EndpointA.ClientID) + suite.Require().True(found) + + err = lightClientModule.VerifyMembership( + suite.chainA.GetContext(), testingpath.EndpointA.ClientID, proofHeight, delayTimePeriod, delayBlockPeriod, + proof, path, value, + ) + expPass := tc.expErr == nil + if expPass { + suite.Require().NoError(err) + } else { + suite.Require().ErrorContains(err, tc.expErr.Error()) + } + }) + } +} + +func (suite *TendermintTestSuite) TestVerifyNonMembership() { + var ( + testingpath *ibctesting.Path + delayTimePeriod uint64 + delayBlockPeriod uint64 + err error + proofHeight exported.Height + path exported.Path + proof []byte + invalidClientID = "09-tendermint" + invalidConnectionID = "connection-100" + invalidChannelID = "channel-800" + invalidPortID = "invalid-port" + ) + + testCases := []struct { + name string + malleate func() + expErr error + }{ + { + "successful ClientState verification of non membership", + func() { + // default proof construction uses ClientState + }, + nil, + }, + { + "successful ConsensusState verification of non membership", func() { + key := host.FullConsensusStateKey(invalidClientID, testingpath.EndpointB.GetClientLatestHeight()) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + }, + nil, + }, + { + "successful Connection verification of non membership", func() { + key := host.ConnectionKey(invalidConnectionID) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + }, + nil, + }, + { + "successful Channel verification of non membership", func() { + key := host.ChannelKey(testingpath.EndpointB.ChannelConfig.PortID, invalidChannelID) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + }, + nil, + }, + { + "successful PacketCommitment verification of non membership", func() { + // make packet commitment proof + key := host.PacketCommitmentKey(invalidPortID, invalidChannelID, 1) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = testingpath.EndpointB.QueryProof(key) + }, + nil, + }, + { + "successful Acknowledgement verification of non membership", func() { + key := host.PacketAcknowledgementKey(invalidPortID, invalidChannelID, 1) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = testingpath.EndpointB.QueryProof(key) + }, + nil, + }, + { + "successful NextSequenceRecv verification of non membership", func() { + key := host.NextSequenceRecvKey(invalidPortID, invalidChannelID) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = testingpath.EndpointB.QueryProof(key) + }, + nil, + }, + { + "successful verification of non membership outside IBC store", func() { + key := []byte{0x08} + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(commitmenttypes.NewMerklePrefix([]byte(transfertypes.StoreKey)), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProofForStore(transfertypes.StoreKey, key, int64(testingpath.EndpointA.GetClientLatestHeight().GetRevisionHeight())) + }, + nil, + }, + { + "delay time period has passed", func() { + delayTimePeriod = uint64(time.Second.Nanoseconds()) + }, + nil, + }, + { + "delay time period has not passed", func() { + delayTimePeriod = uint64(time.Hour.Nanoseconds()) + }, + ibctm.ErrDelayPeriodNotPassed, + }, + { + "delay block period has passed", func() { + delayBlockPeriod = 1 + }, + nil, + }, + { + "delay block period has not passed", func() { + delayBlockPeriod = 1000 + }, + ibctm.ErrDelayPeriodNotPassed, + }, + { + "latest client height < height", func() { + proofHeight = testingpath.EndpointA.GetClientLatestHeight().Increment() + }, + ibcerrors.ErrInvalidHeight, + }, + { + "invalid path type", + func() { + path = ibcmock.KeyPath{} + }, + ibcerrors.ErrInvalidType, + }, + { + "failed to unmarshal merkle proof", func() { + proof = invalidProof + }, + commitmenttypes.ErrInvalidProof, + }, + { + "consensus state not found", func() { + proofHeight = clienttypes.ZeroHeight() + }, + clienttypes.ErrConsensusStateNotFound, + }, + { + "verify non membership fails as path exists", func() { + // change the value being proved + key := host.FullClientStateKey(testingpath.EndpointB.ClientID) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + }, + commitmenttypes.ErrInvalidProof, + }, + { + "proof is empty", func() { + // change the inserted proof + proof = []byte{} + }, + commitmenttypes.ErrInvalidMerkleProof, + }, + { + "client state not found for height", + func() { + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), testingpath.EndpointA.ClientID) + store.Delete(host.ClientStateKey()) + }, + clienttypes.ErrClientNotFound, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() // reset + testingpath = ibctesting.NewPath(suite.chainA, suite.chainB) + testingpath.SetChannelOrdered() + testingpath.Setup() + + // reset time and block delays to 0, malleate may change to a specific non-zero value. + delayTimePeriod = 0 + delayBlockPeriod = 0 + + // create default proof, merklePath, and value which passes + // may be overwritten by malleate() + key := host.FullClientStateKey("invalid-client-id") + + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + + tc.malleate() // make changes as necessary + + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(testingpath.EndpointA.ClientID) + suite.Require().True(found) + + err = lightClientModule.VerifyNonMembership( + suite.chainA.GetContext(), testingpath.EndpointA.ClientID, proofHeight, delayTimePeriod, delayBlockPeriod, + proof, path, + ) + + expPass := tc.expErr == nil + if expPass { + suite.Require().NoError(err) + } else { + suite.Require().ErrorContains(err, tc.expErr.Error()) + } + }) + } +} diff --git a/modules/light-clients/07-tendermint/tendermint_test.go b/modules/light-clients/07-tendermint/tendermint_test.go index 5dc7ec722a2..b39162b930d 100644 --- a/modules/light-clients/07-tendermint/tendermint_test.go +++ b/modules/light-clients/07-tendermint/tendermint_test.go @@ -29,9 +29,10 @@ const ( ) var ( - height = clienttypes.NewHeight(0, 4) - newClientHeight = clienttypes.NewHeight(1, 1) - upgradePath = []string{"upgrade", "upgradedIBCState"} + height = clienttypes.NewHeight(0, 4) + newClientHeight = clienttypes.NewHeight(1, 1) + upgradePath = []string{"upgrade", "upgradedIBCState"} + invalidUpgradePath = []string{"upgrade", ""} ) type TendermintTestSuite struct { From 2e8d7db0cd313c872a3c203bb090ebd3cfc1b1d2 Mon Sep 17 00:00:00 2001 From: Charly Date: Tue, 2 Apr 2024 11:51:37 +0200 Subject: [PATCH 02/19] linter --- modules/light-clients/07-tendermint/light_client_module.go | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/light-clients/07-tendermint/light_client_module.go b/modules/light-clients/07-tendermint/light_client_module.go index 1919adca887..8f688576988 100644 --- a/modules/light-clients/07-tendermint/light_client_module.go +++ b/modules/light-clients/07-tendermint/light_client_module.go @@ -53,7 +53,6 @@ func (l LightClientModule) Initialize(ctx sdk.Context, clientID string, clientSt var consensusState ConsensusState if err := l.keeper.Codec().Unmarshal(consensusStateBz, &consensusState); err != nil { return fmt.Errorf("failed to unmarshal consensus state bytes into consensus state: %w", err) - } if err := consensusState.ValidateBasic(); err != nil { From 903d4d6264d7a5ee9ad3f502302ca37a89c99404 Mon Sep 17 00:00:00 2001 From: Charly Date: Tue, 2 Apr 2024 13:40:37 +0200 Subject: [PATCH 03/19] update Initialise test + coverage --- .../07-tendermint/light_client_module_test.go | 192 +++++++++++------- 1 file changed, 115 insertions(+), 77 deletions(-) diff --git a/modules/light-clients/07-tendermint/light_client_module_test.go b/modules/light-clients/07-tendermint/light_client_module_test.go index 5ec5aeba02c..df7b0898864 100644 --- a/modules/light-clients/07-tendermint/light_client_module_test.go +++ b/modules/light-clients/07-tendermint/light_client_module_test.go @@ -2,6 +2,7 @@ package tendermint_test import ( "crypto/sha256" + fmt "fmt" "time" upgradetypes "cosmossdk.io/x/upgrade/types" @@ -37,18 +38,30 @@ func (suite *TendermintTestSuite) TestStatus() { malleate func() expStatus exported.Status }{ - {"client is active", func() {}, exported.Active}, - {"client is frozen", func() { - clientState.FrozenHeight = clienttypes.NewHeight(0, 1) - path.EndpointA.SetClientState(clientState) - }, exported.Frozen}, - {"client status without consensus state", func() { - clientState.LatestHeight = clientState.LatestHeight.Increment().(clienttypes.Height) - path.EndpointA.SetClientState(clientState) - }, exported.Expired}, - {"client status is expired", func() { - suite.coordinator.IncrementTimeBy(clientState.TrustingPeriod) - }, exported.Expired}, + {"client is active", + func() {}, + exported.Active, + }, + {"client is frozen", + func() { + clientState.FrozenHeight = clienttypes.NewHeight(0, 1) + path.EndpointA.SetClientState(clientState) + }, + exported.Frozen, + }, + {"client status without consensus state", + func() { + clientState.LatestHeight = clientState.LatestHeight.Increment().(clienttypes.Height) + path.EndpointA.SetClientState(clientState) + }, + exported.Expired, + }, + {"client status is expired", + func() { + suite.coordinator.IncrementTimeBy(clientState.TrustingPeriod) + }, + exported.Expired, + }, { "client state not found for height", func() { @@ -155,71 +168,96 @@ func (suite *TendermintTestSuite) TestGetTimestampAtHeight() { } } -// TODO: fix this test -// func (suite *TendermintTestSuite) TestInitialize() { - -// var consensusState exported.ConsensusState -// var clientState *ibctm.ClientState - -// testCases := []struct { -// name string -// malleate func() -// expErr error -// }{ -// { -// "valid consensus & client states", -// func() {}, -// nil, -// }, -// { -// "invalid client state", -// func() { -// clientState.ChainId = "" -// }, -// ibctm.ErrInvalidChainID, -// }, -// { -// "invalid consensus: consensus state is solomachine consensus", -// func() { -// consensusState = ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2).ConsensusState() -// }, -// clienttypes.ErrInvalidConsensus, -// }, -// } - -// for _, tc := range testCases { -// tc := tc -// suite.Run(tc.name, func() { -// suite.SetupTest() -// path := ibctesting.NewPath(suite.chainA, suite.chainB) - -// clientID := suite.chainA.App.GetIBCKeeper().ClientKeeper.GenerateClientIdentifier(suite.chainA.GetContext(), clientState.ClientType()) - -// lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(clientID) -// suite.Require().True(found) - -// tc.malleate() - -// clientStateBz := suite.chainB.Codec.MustMarshal(clientState) -// consStateBz := suite.chainB.Codec.MustMarshal(consensusState) - -// err := lightClientModule.Initialize(suite.chainB.GetContext(), path.EndpointB.ClientID, clientStateBz, consStateBz) - -// store := suite.chainB.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainB.GetContext(), path.EndpointB.ClientID) - -// expPass := tc.expErr == nil -// if expPass { -// suite.Require().NoError(err, "valid case returned an error") -// suite.Require().True(store.Has(host.ClientStateKey())) -// suite.Require().True(store.Has(host.ConsensusStateKey(height))) -// } else { -// suite.Require().ErrorContains(err, tc.expErr.Error()) -// suite.Require().False(store.Has(host.ClientStateKey())) -// suite.Require().False(store.Has(host.ConsensusStateKey(height))) -// } -// }) -// } -// } +func (suite *TendermintTestSuite) TestInitialize() { + + var consensusState exported.ConsensusState + var clientState exported.ClientState + + testCases := []struct { + name string + malleate func() + expErr error + }{ + { + "valid consensus & client states", + func() {}, + nil, + }, + { + "invalid client state", + func() { + clientState.(*ibctm.ClientState).ChainId = "" + }, + ibctm.ErrInvalidChainID, + }, + { + "invalid client state: solomachine client state", + func() { + clientState = ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2).ClientState() + + }, + fmt.Errorf("failed to unmarshal client state bytes into client state"), + }, + { + "invalid consensus: consensus state is solomachine consensus", + func() { + consensusState = ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2).ConsensusState() + }, + fmt.Errorf("failed to unmarshal consensus state bytes into consensus state"), + }, + { + "invalid consensus state", + func() { + consensusState = ibctm.NewConsensusState(time.Now(), commitmenttypes.NewMerkleRoot([]byte(ibctm.SentinelRoot)), []byte("invalidNextValsHash")) + }, + fmt.Errorf("next validators hash is invalid"), + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + suite.SetupTest() + path := ibctesting.NewPath(suite.chainA, suite.chainB) + + tmConfig, ok := path.EndpointA.ClientConfig.(*ibctesting.TendermintConfig) + suite.Require().True(ok) + + clientState = ibctm.NewClientState( + path.EndpointA.Chain.ChainID, + tmConfig.TrustLevel, tmConfig.TrustingPeriod, tmConfig.UnbondingPeriod, tmConfig.MaxClockDrift, + suite.chainA.LatestCommittedHeader.GetHeight().(clienttypes.Height), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, + ) + + consensusState = ibctm.NewConsensusState(time.Now(), commitmenttypes.NewMerkleRoot([]byte(ibctm.SentinelRoot)), suite.chainA.ProposedHeader.ValidatorsHash) + + clientID := suite.chainA.App.GetIBCKeeper().ClientKeeper.GenerateClientIdentifier(suite.chainA.GetContext(), clientState.ClientType()) + + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(clientID) + suite.Require().True(found) + + tc.malleate() + + clientStateBz := suite.chainA.Codec.MustMarshal(clientState) + consStateBz := suite.chainA.Codec.MustMarshal(consensusState) + + err := lightClientModule.Initialize(suite.chainA.GetContext(), path.EndpointA.ClientID, clientStateBz, consStateBz) + + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + + expPass := tc.expErr == nil + if expPass { + suite.Require().NoError(err, "valid case returned an error") + suite.Require().True(store.Has(host.ClientStateKey())) + suite.Require().True(store.Has(host.ConsensusStateKey(suite.chainB.LatestCommittedHeader.GetHeight()))) + } else { + suite.Require().ErrorContains(err, tc.expErr.Error()) + suite.Require().False(store.Has(host.ClientStateKey())) + suite.Require().False(store.Has(host.ConsensusStateKey(suite.chainB.LatestCommittedHeader.GetHeight()))) + } + }) + } +} func (suite *TendermintTestSuite) TestRecoverClient() { var ( From d500532f7fbf30abcd277c46763a8613052f95cf Mon Sep 17 00:00:00 2001 From: Charly Date: Tue, 2 Apr 2024 13:43:00 +0200 Subject: [PATCH 04/19] linter --- .../07-tendermint/light_client_module_test.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/modules/light-clients/07-tendermint/light_client_module_test.go b/modules/light-clients/07-tendermint/light_client_module_test.go index df7b0898864..32b42e8ad36 100644 --- a/modules/light-clients/07-tendermint/light_client_module_test.go +++ b/modules/light-clients/07-tendermint/light_client_module_test.go @@ -38,25 +38,29 @@ func (suite *TendermintTestSuite) TestStatus() { malleate func() expStatus exported.Status }{ - {"client is active", + { + "client is active", func() {}, exported.Active, }, - {"client is frozen", + { + "client is frozen", func() { clientState.FrozenHeight = clienttypes.NewHeight(0, 1) path.EndpointA.SetClientState(clientState) }, exported.Frozen, }, - {"client status without consensus state", + { + "client status without consensus state", func() { clientState.LatestHeight = clientState.LatestHeight.Increment().(clienttypes.Height) path.EndpointA.SetClientState(clientState) }, exported.Expired, }, - {"client status is expired", + { + "client status is expired", func() { suite.coordinator.IncrementTimeBy(clientState.TrustingPeriod) }, @@ -169,7 +173,6 @@ func (suite *TendermintTestSuite) TestGetTimestampAtHeight() { } func (suite *TendermintTestSuite) TestInitialize() { - var consensusState exported.ConsensusState var clientState exported.ClientState @@ -194,7 +197,6 @@ func (suite *TendermintTestSuite) TestInitialize() { "invalid client state: solomachine client state", func() { clientState = ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "solomachine", "", 2).ClientState() - }, fmt.Errorf("failed to unmarshal client state bytes into client state"), }, From edf7a6821cf5ac779fbd40de283841dd797635dd Mon Sep 17 00:00:00 2001 From: Charly Date: Tue, 2 Apr 2024 16:41:16 +0200 Subject: [PATCH 05/19] import naming --- modules/light-clients/07-tendermint/light_client_module_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/light-clients/07-tendermint/light_client_module_test.go b/modules/light-clients/07-tendermint/light_client_module_test.go index 32b42e8ad36..c6b3940decd 100644 --- a/modules/light-clients/07-tendermint/light_client_module_test.go +++ b/modules/light-clients/07-tendermint/light_client_module_test.go @@ -2,7 +2,7 @@ package tendermint_test import ( "crypto/sha256" - fmt "fmt" + "fmt" "time" upgradetypes "cosmossdk.io/x/upgrade/types" From f3d57648c290a04d037f1661ea0822cd086c48cd Mon Sep 17 00:00:00 2001 From: Charly Date: Tue, 9 Apr 2024 14:30:13 +0200 Subject: [PATCH 06/19] update for pr review --- modules/light-clients/07-tendermint/light_client_module.go | 2 +- modules/light-clients/07-tendermint/light_client_module_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/light-clients/07-tendermint/light_client_module.go b/modules/light-clients/07-tendermint/light_client_module.go index 8f688576988..9a66b8d6e75 100644 --- a/modules/light-clients/07-tendermint/light_client_module.go +++ b/modules/light-clients/07-tendermint/light_client_module.go @@ -1,7 +1,7 @@ package tendermint import ( - fmt "fmt" + "fmt" errorsmod "cosmossdk.io/errors" diff --git a/modules/light-clients/07-tendermint/light_client_module_test.go b/modules/light-clients/07-tendermint/light_client_module_test.go index c6b3940decd..db1a9ea90d3 100644 --- a/modules/light-clients/07-tendermint/light_client_module_test.go +++ b/modules/light-clients/07-tendermint/light_client_module_test.go @@ -67,7 +67,7 @@ func (suite *TendermintTestSuite) TestStatus() { exported.Expired, }, { - "client state not found for height", + "client state not found", func() { store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) store.Delete(host.ClientStateKey()) From 05a07d2578c6b69e16bbf66e083979448be70901 Mon Sep 17 00:00:00 2001 From: Charly Date: Wed, 10 Apr 2024 13:02:55 +0200 Subject: [PATCH 07/19] followup to lcm testing pr --- .../07-tendermint/light_client_module_test.go | 279 +++++++++++++++++- .../07-tendermint/upgrade_test.go | 165 +++++++---- 2 files changed, 387 insertions(+), 57 deletions(-) diff --git a/modules/light-clients/07-tendermint/light_client_module_test.go b/modules/light-clients/07-tendermint/light_client_module_test.go index db1a9ea90d3..95c916fea13 100644 --- a/modules/light-clients/07-tendermint/light_client_module_test.go +++ b/modules/light-clients/07-tendermint/light_client_module_test.go @@ -5,6 +5,7 @@ import ( "fmt" "time" + errorsmod "cosmossdk.io/errors" upgradetypes "cosmossdk.io/x/upgrade/types" codectypes "github.com/cosmos/cosmos-sdk/codec/types" @@ -12,6 +13,7 @@ import ( transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" + types "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" commitmenttypes "github.com/cosmos/ibc-go/v8/modules/core/23-commitment/types" host "github.com/cosmos/ibc-go/v8/modules/core/24-host" @@ -98,6 +100,51 @@ func (suite *TendermintTestSuite) TestStatus() { } } +func (suite *TendermintTestSuite) TestLatestHeight() { + var ( + path *ibctesting.Path + height exported.Height + ) + + testCases := []struct { + name string + malleate func() + expHeight exported.Height + }{ + { + "success", + func() {}, + types.Height(types.Height{RevisionNumber: 0x1, RevisionHeight: 0x4}), + }, + { + "client state not found", + func() { + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + store.Delete(host.ClientStateKey()) + }, + clienttypes.ZeroHeight(), + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + suite.SetupTest() + + path = ibctesting.NewPath(suite.chainA, suite.chainB) + path.SetupClients() + + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(path.EndpointA.ClientID) + suite.Require().True(found) + + tc.malleate() + + height = lightClientModule.LatestHeight(suite.chainA.GetContext(), path.EndpointA.ClientID) + suite.Require().Equal(tc.expHeight, height) + }) + } +} + func (suite *TendermintTestSuite) TestGetTimestampAtHeight() { var ( path *ibctesting.Path @@ -116,7 +163,7 @@ func (suite *TendermintTestSuite) TestGetTimestampAtHeight() { nil, }, { - "failure: client state not found for height", + "failure: client state not found", func() { store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) store.Delete(host.ClientStateKey()) @@ -261,6 +308,232 @@ func (suite *TendermintTestSuite) TestInitialize() { } } +func (suite *TendermintTestSuite) TestVerifyClientMessage() { + var path *ibctesting.Path + + testCases := []struct { + name string + malleate func() + expErr error + }{ + { + "success", + func() {}, + nil, + }, + { + "failure: client state not found", + func() { + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + store.Delete(host.ClientStateKey()) + }, + clienttypes.ErrClientNotFound, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + suite.SetupTest() + + path = ibctesting.NewPath(suite.chainA, suite.chainB) + path.SetupClients() + + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(path.EndpointA.ClientID) + suite.Require().True(found) + + // ensure counterparty state is committed + suite.coordinator.CommitBlock(suite.chainB) + trustedHeight := path.EndpointA.GetClientLatestHeight().(clienttypes.Height) + header, err := path.EndpointA.Counterparty.Chain.IBCClientHeader(path.EndpointA.Counterparty.Chain.LatestCommittedHeader, trustedHeight) + suite.Require().NoError(err) + + tc.malleate() + + err = lightClientModule.VerifyClientMessage(suite.chainA.GetContext(), path.EndpointA.ClientID, header) + + expPass := tc.expErr == nil + if expPass { + suite.Require().NoError(err) + } else { + suite.Require().ErrorIs(err, tc.expErr) + } + }) + } +} + +func (suite *TendermintTestSuite) TestLCMCheckForMisbehaviour() { + var path *ibctesting.Path + + testCases := []struct { + name string + malleate func() + expPanic bool + }{ + { + "success", + func() {}, + false, + }, + { + "failure: client state not found", + func() { + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + store.Delete(host.ClientStateKey()) + }, + true, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + suite.SetupTest() + + path = ibctesting.NewPath(suite.chainA, suite.chainB) + path.SetupClients() + + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(path.EndpointA.ClientID) + suite.Require().True(found) + + // ensure counterparty state is committed + suite.coordinator.CommitBlock(suite.chainB) + trustedHeight := path.EndpointA.GetClientLatestHeight().(clienttypes.Height) + header, err := path.EndpointA.Counterparty.Chain.IBCClientHeader(path.EndpointA.Counterparty.Chain.LatestCommittedHeader, trustedHeight) + suite.Require().NoError(err) + + tc.malleate() + + if !tc.expPanic { + misbehaviour := lightClientModule.CheckForMisbehaviour(suite.chainA.GetContext(), path.EndpointA.ClientID, header) + suite.Require().False(misbehaviour) + } else { + suite.Require().PanicsWithError(errorsmod.Wrap(clienttypes.ErrClientNotFound, path.EndpointA.ClientID).Error(), + func() { + lightClientModule.CheckForMisbehaviour(suite.chainA.GetContext(), path.EndpointA.ClientID, header) + }) + } + }) + } +} + +func (suite *TendermintTestSuite) TestLCMUpdateStateOnMisbehaviour() { + var path *ibctesting.Path + + testCases := []struct { + name string + malleate func() + expPanic bool + }{ + { + "success", + func() {}, + false, + }, + { + "failure: client state not found", + func() { + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + store.Delete(host.ClientStateKey()) + }, + true, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + suite.SetupTest() + + path = ibctesting.NewPath(suite.chainA, suite.chainB) + path.SetupClients() + + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(path.EndpointA.ClientID) + suite.Require().True(found) + + // ensure counterparty state is committed + suite.coordinator.CommitBlock(suite.chainB) + trustedHeight := path.EndpointA.GetClientLatestHeight().(clienttypes.Height) + header, err := path.EndpointA.Counterparty.Chain.IBCClientHeader(path.EndpointA.Counterparty.Chain.LatestCommittedHeader, trustedHeight) + suite.Require().NoError(err) + + tc.malleate() + + if !tc.expPanic { + suite.Require().NotPanics( + func() { + lightClientModule.UpdateStateOnMisbehaviour(suite.chainA.GetContext(), path.EndpointA.ClientID, header) + }) + } else { + suite.Require().PanicsWithError( + errorsmod.Wrap(clienttypes.ErrClientNotFound, path.EndpointA.ClientID).Error(), + func() { + lightClientModule.UpdateStateOnMisbehaviour(suite.chainA.GetContext(), path.EndpointA.ClientID, header) + }, + ) + } + }) + } +} + +func (suite *TendermintTestSuite) TestLCMUpdateState() { + var path *ibctesting.Path + + testCases := []struct { + name string + malleate func() + expPanic bool + }{ + { + "success", + func() {}, + false, + }, + { + "failure: client state not found", + func() { + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + store.Delete(host.ClientStateKey()) + }, + true, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + suite.SetupTest() + + path = ibctesting.NewPath(suite.chainA, suite.chainB) + path.SetupClients() + + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(path.EndpointA.ClientID) + suite.Require().True(found) + + // ensure counterparty state is committed + suite.coordinator.CommitBlock(suite.chainB) + trustedHeight := path.EndpointA.GetClientLatestHeight().(clienttypes.Height) + header, err := path.EndpointA.Counterparty.Chain.IBCClientHeader(path.EndpointA.Counterparty.Chain.LatestCommittedHeader, trustedHeight) + suite.Require().NoError(err) + + tc.malleate() + + if !tc.expPanic { + height := lightClientModule.UpdateState(suite.chainA.GetContext(), path.EndpointA.ClientID, header) + h := header.GetHeight().(clienttypes.Height) + suite.Require().Equal([]exported.Height{h}, height) + } else { + suite.Require().PanicsWithError( + errorsmod.Wrap(clienttypes.ErrClientNotFound, path.EndpointA.ClientID).Error(), + func() { + lightClientModule.UpdateState(suite.chainA.GetContext(), path.EndpointA.ClientID, header) + }, + ) + } + }) + } +} + func (suite *TendermintTestSuite) TestRecoverClient() { var ( subjectClientID, substituteClientID string @@ -729,7 +1002,7 @@ func (suite *TendermintTestSuite) TestVerifyMembership() { commitmenttypes.ErrInvalidMerkleProof, }, { - "client state not found for height", + "client state not found", func() { store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), testingpath.EndpointA.ClientID) store.Delete(host.ClientStateKey()) @@ -957,7 +1230,7 @@ func (suite *TendermintTestSuite) TestVerifyNonMembership() { commitmenttypes.ErrInvalidMerkleProof, }, { - "client state not found for height", + "client state not found", func() { store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), testingpath.EndpointA.ClientID) store.Delete(host.ClientStateKey()) diff --git a/modules/light-clients/07-tendermint/upgrade_test.go b/modules/light-clients/07-tendermint/upgrade_test.go index 4715aad3961..9225168d719 100644 --- a/modules/light-clients/07-tendermint/upgrade_test.go +++ b/modules/light-clients/07-tendermint/upgrade_test.go @@ -1,11 +1,14 @@ package tendermint_test import ( + "fmt" + upgradetypes "cosmossdk.io/x/upgrade/types" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" commitmenttypes "github.com/cosmos/ibc-go/v8/modules/core/23-commitment/types" "github.com/cosmos/ibc-go/v8/modules/core/exported" + solomachine "github.com/cosmos/ibc-go/v8/modules/light-clients/06-solomachine" ibctm "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" ibctesting "github.com/cosmos/ibc-go/v8/testing" ) @@ -13,7 +16,7 @@ import ( func (suite *TendermintTestSuite) TestVerifyUpgrade() { var ( newChainID string - upgradedClient *ibctm.ClientState + upgradedClient exported.ClientState upgradedConsState exported.ConsensusState lastHeight clienttypes.Height path *ibctesting.Path @@ -23,9 +26,10 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { ) testCases := []struct { - name string - setup func() - expPass bool + name string + setup func() + // expPass bool + expErr error }{ { name: "successful upgrade", @@ -51,13 +55,13 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { upgradedClientProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) upgradedConsensusStateProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) }, - expPass: true, + expErr: nil, }, { name: "successful upgrade to same revision", setup: func() { - upgradedClient = ibctm.NewClientState(suite.chainB.ChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, clienttypes.NewHeight(clienttypes.ParseChainID(suite.chainB.ChainID), upgradedClient.LatestHeight.GetRevisionHeight()+10), commitmenttypes.GetSDKSpecs(), upgradePath) - upgradedClient = upgradedClient.ZeroCustomFields() + upgradedClient = ibctm.NewClientState(suite.chainB.ChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, clienttypes.NewHeight(clienttypes.ParseChainID(suite.chainB.ChainID), upgradedClient.(*ibctm.ClientState).LatestHeight.GetRevisionHeight()+10), commitmenttypes.GetSDKSpecs(), upgradePath) + upgradedClient = upgradedClient.(*ibctm.ClientState).ZeroCustomFields() upgradedClientBz, err = clienttypes.MarshalClientState(suite.chainA.App.AppCodec(), upgradedClient) suite.Require().NoError(err) @@ -82,9 +86,48 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { upgradedClientProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) upgradedConsensusStateProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) }, - expPass: true, + expErr: nil, }, + { + name: "unsuccessful upgrade: upgrade path not set", + setup: func() { + suite.coordinator.CommitBlock(suite.chainB) + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID) + suite.Require().True(found) + tmCs, ok := cs.(*ibctm.ClientState) + suite.Require().True(ok) + + // set upgrade path to empty + tmCs.UpgradePath = []string{} + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID, tmCs) + + upgradedClientProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) + upgradedConsensusStateProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) + }, + expErr: clienttypes.ErrInvalidUpgradeClient, + }, + { + name: "unsuccessful upgrade: upgrade consensus state must be tendermint consensus state", + setup: func() { + upgradedConsState = &solomachine.ConsensusState{} + + suite.coordinator.CommitBlock(suite.chainB) + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID) + suite.Require().True(found) + tmCs, ok := cs.(*ibctm.ClientState) + suite.Require().True(ok) + + upgradedClientProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) + upgradedConsensusStateProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) + }, + expErr: clienttypes.ErrInvalidConsensus, + }, { name: "unsuccessful upgrade: upgrade height revision height is more than the current client revision height", setup: func() { @@ -109,7 +152,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { upgradedClientProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) upgradedConsensusStateProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) }, - expPass: false, + expErr: commitmenttypes.ErrInvalidProof, }, { name: "unsuccessful upgrade: committed client does not have zeroed custom fields", @@ -140,18 +183,11 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { upgradedClientProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) upgradedConsensusStateProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) }, - expPass: false, + expErr: commitmenttypes.ErrInvalidProof, }, { name: "unsuccessful upgrade: chain-specified parameters do not match committed client", setup: func() { - // upgrade Height is at next block - lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) - - // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) //nolint:errcheck // ignore error for test - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) //nolint:errcheck // ignore error for test - // change upgradedClient client-specified parameters upgradedClient = ibctm.NewClientState("wrongchainID", ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath) @@ -167,15 +203,11 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { upgradedClientProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) upgradedConsensusStateProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) }, - expPass: false, + expErr: commitmenttypes.ErrInvalidProof, }, { name: "unsuccessful upgrade: client-specified parameters do not match previous client", setup: func() { - // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) //nolint:errcheck // ignore error for test - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) //nolint:errcheck // ignore error for test - // change upgradedClient client-specified parameters upgradedClient = ibctm.NewClientState(newChainID, ibctm.DefaultTrustLevel, ubdPeriod, ubdPeriod+trustingPeriod, maxClockDrift+5, lastHeight, commitmenttypes.GetSDKSpecs(), upgradePath) @@ -191,25 +223,24 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { upgradedClientProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) upgradedConsensusStateProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) }, - expPass: false, + expErr: commitmenttypes.ErrInvalidProof, + }, + { + name: "unsuccessful upgrade: upgrade client is not tendermint", + setup: func() { + upgradedClient = &solomachine.ClientState{} + }, + expErr: clienttypes.ErrInvalidClientType, }, { name: "unsuccessful upgrade: relayer-submitted consensus state does not match counterparty-committed consensus state", setup: func() { - // upgrade Height is at next block - lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) - - // zero custom fields and store in upgrade store - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) //nolint:errcheck // ignore error for test - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) //nolint:errcheck // ignore error for test - // change submitted upgradedConsensusState upgradedConsState = &ibctm.ConsensusState{ NextValidatorsHash: []byte("maliciousValidators"), } // commit upgrade store changes and update clients - suite.coordinator.CommitBlock(suite.chainB) err := path.EndpointA.UpdateClient() suite.Require().NoError(err) @@ -222,13 +253,11 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { upgradedClientProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) upgradedConsensusStateProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) }, - expPass: false, + expErr: commitmenttypes.ErrInvalidProof, }, { name: "unsuccessful upgrade: client proof unmarshal failed", setup: func() { - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) //nolint:errcheck // ignore error for test - cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID) suite.Require().True(found) tmCs, ok := cs.(*ibctm.ClientState) @@ -238,13 +267,11 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { upgradedClientProof = []byte("proof") }, - expPass: false, + expErr: fmt.Errorf("could not unmarshal client merkle proof"), }, { name: "unsuccessful upgrade: consensus state proof unmarshal failed", setup: func() { - suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) //nolint:errcheck // ignore error for test - cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID) suite.Require().True(found) tmCs, ok := cs.(*ibctm.ClientState) @@ -254,7 +281,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { upgradedConsensusStateProof = []byte("proof") }, - expPass: false, + expErr: fmt.Errorf("could not unmarshal consensus state merkle proof"), }, { name: "unsuccessful upgrade: client proof verification failed", @@ -274,7 +301,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { upgradedClientProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) upgradedConsensusStateProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) }, - expPass: false, + expErr: commitmenttypes.ErrInvalidProof, }, { name: "unsuccessful upgrade: consensus state proof verification failed", @@ -294,10 +321,10 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { upgradedClientProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) upgradedConsensusStateProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) }, - expPass: false, + expErr: commitmenttypes.ErrInvalidProof, }, { - name: "unsuccessful upgrade: upgrade path is empty", + name: "unsuccessful upgrade: client state merkle path is empty", setup: func() { // upgrade Height is at next block lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) @@ -319,12 +346,12 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { upgradedClientProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) upgradedConsensusStateProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) - // SetClientState with empty upgrade path + // SetClientState with empty string upgrade path tmClient, _ := cs.(*ibctm.ClientState) tmClient.UpgradePath = []string{""} suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID, tmClient) }, - expPass: false, + expErr: fmt.Errorf("client state proof failed"), }, { name: "unsuccessful upgrade: upgraded height is not greater than current height", @@ -349,7 +376,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { upgradedClientProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) upgradedConsensusStateProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) }, - expPass: false, + expErr: fmt.Errorf("consensus state proof failed"), }, { name: "unsuccessful upgrade: consensus state for upgrade height cannot be found", @@ -374,7 +401,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { upgradedClientProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) upgradedConsensusStateProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) }, - expPass: false, + expErr: commitmenttypes.ErrInvalidProof, }, { name: "unsuccessful upgrade: client is expired", @@ -399,7 +426,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { upgradedClientProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) upgradedConsensusStateProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) }, - expPass: false, + expErr: commitmenttypes.ErrInvalidProof, }, { name: "unsuccessful upgrade: updated unbonding period is equal to trusting period", @@ -424,7 +451,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { upgradedClientProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) upgradedConsensusStateProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) }, - expPass: false, + expErr: commitmenttypes.ErrInvalidProof, }, { name: "unsuccessful upgrade: final client is not valid", @@ -455,7 +482,39 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { upgradedClientProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) upgradedConsensusStateProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) }, - expPass: false, + expErr: commitmenttypes.ErrInvalidProof, + }, + { + name: "unsuccessful upgrade: consensus state not found for latest height", + setup: func() { + // upgrade Height is at next block + lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) + + // zero custom fields and store in upgrade store + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) //nolint:errcheck // ignore error for test + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) //nolint:errcheck // ignore error for test + + // commit upgrade store changes and update clients + + suite.coordinator.CommitBlock(suite.chainB) + err := path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID) + suite.Require().True(found) + tmCs, ok := cs.(*ibctm.ClientState) + suite.Require().True(ok) + + revisionHeight := tmCs.LatestHeight.GetRevisionHeight() + + // set latest height to a height where consensus state does not exist + tmCs.LatestHeight = clienttypes.NewHeight(tmCs.LatestHeight.GetRevisionNumber(), tmCs.LatestHeight.GetRevisionHeight()+5) + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID, tmCs) + + upgradedClientProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), revisionHeight) + upgradedConsensusStateProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), revisionHeight) + }, + expErr: clienttypes.ErrConsensusStateNotFound, }, } @@ -477,7 +536,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { suite.Require().NoError(err) upgradedClient = ibctm.NewClientState(newChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, clienttypes.NewHeight(revisionNumber+1, clientState.LatestHeight.GetRevisionHeight()+1), commitmenttypes.GetSDKSpecs(), upgradePath) - upgradedClient = upgradedClient.ZeroCustomFields() + upgradedClient = upgradedClient.(*ibctm.ClientState).ZeroCustomFields() upgradedClientBz, err = clienttypes.MarshalClientState(suite.chainA.App.AppCodec(), upgradedClient) suite.Require().NoError(err) @@ -492,9 +551,6 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { cs := suite.chainA.GetClientState(path.EndpointA.ClientID).(*ibctm.ClientState) clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - // Call ZeroCustomFields on upgraded clients to clear any client-chosen parameters in test-case upgradedClient - upgradedClient = upgradedClient.ZeroCustomFields() - err = cs.VerifyUpgradeAndUpdateState( suite.chainA.GetContext(), suite.cdc, @@ -505,7 +561,8 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { upgradedConsensusStateProof, ) - if tc.expPass { + expPass := tc.expErr == nil + if expPass { suite.Require().NoError(err, "verify upgrade failed on valid case: %s", tc.name) clientState := suite.chainA.GetClientState(path.EndpointA.ClientID).(*ibctm.ClientState) @@ -515,7 +572,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { suite.Require().NotNil(consensusState, "verify upgrade failed on valid case: %s", tc.name) suite.Require().True(found) } else { - suite.Require().Error(err, "verify upgrade passed on invalid case: %s", tc.name) + suite.Require().ErrorContains(err, tc.expErr.Error(), "verify upgrade passed on invalid case: %s", tc.name) } }) } From 627153eb0c81be2fc019d713eb5abe9917204ba4 Mon Sep 17 00:00:00 2001 From: Charly Date: Wed, 10 Apr 2024 14:43:09 +0200 Subject: [PATCH 08/19] linter --- .../light-clients/07-tendermint/light_client_module_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/light-clients/07-tendermint/light_client_module_test.go b/modules/light-clients/07-tendermint/light_client_module_test.go index 95c916fea13..b7733285575 100644 --- a/modules/light-clients/07-tendermint/light_client_module_test.go +++ b/modules/light-clients/07-tendermint/light_client_module_test.go @@ -13,7 +13,6 @@ import ( transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" - types "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" commitmenttypes "github.com/cosmos/ibc-go/v8/modules/core/23-commitment/types" host "github.com/cosmos/ibc-go/v8/modules/core/24-host" @@ -114,7 +113,7 @@ func (suite *TendermintTestSuite) TestLatestHeight() { { "success", func() {}, - types.Height(types.Height{RevisionNumber: 0x1, RevisionHeight: 0x4}), + clienttypes.Height{RevisionNumber: 0x1, RevisionHeight: 0x4}, }, { "client state not found", From 115ab278fdce9f9cf0bc1b16db886848dc41e67e Mon Sep 17 00:00:00 2001 From: Charly Date: Wed, 10 Apr 2024 15:16:55 +0200 Subject: [PATCH 09/19] update: use lcm entrypoint --- .../07-tendermint/light_client_module_test.go | 1425 +++++++++-------- .../07-tendermint/misbehaviour_handle_test.go | 16 +- .../07-tendermint/upgrade_test.go | 5 + 3 files changed, 726 insertions(+), 720 deletions(-) diff --git a/modules/light-clients/07-tendermint/light_client_module_test.go b/modules/light-clients/07-tendermint/light_client_module_test.go index b7733285575..d23658cf988 100644 --- a/modules/light-clients/07-tendermint/light_client_module_test.go +++ b/modules/light-clients/07-tendermint/light_client_module_test.go @@ -28,196 +28,6 @@ var ( solomachineClientID = clienttypes.FormatClientIdentifier(exported.Solomachine, 0) ) -func (suite *TendermintTestSuite) TestStatus() { - var ( - path *ibctesting.Path - clientState *ibctm.ClientState - ) - - testCases := []struct { - name string - malleate func() - expStatus exported.Status - }{ - { - "client is active", - func() {}, - exported.Active, - }, - { - "client is frozen", - func() { - clientState.FrozenHeight = clienttypes.NewHeight(0, 1) - path.EndpointA.SetClientState(clientState) - }, - exported.Frozen, - }, - { - "client status without consensus state", - func() { - clientState.LatestHeight = clientState.LatestHeight.Increment().(clienttypes.Height) - path.EndpointA.SetClientState(clientState) - }, - exported.Expired, - }, - { - "client status is expired", - func() { - suite.coordinator.IncrementTimeBy(clientState.TrustingPeriod) - }, - exported.Expired, - }, - { - "client state not found", - func() { - store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - store.Delete(host.ClientStateKey()) - }, - exported.Unknown, - }, - } - - for _, tc := range testCases { - tc := tc - suite.Run(tc.name, func() { - suite.SetupTest() - - path = ibctesting.NewPath(suite.chainA, suite.chainB) - path.SetupClients() - - lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(path.EndpointA.ClientID) - suite.Require().True(found) - - clientState = path.EndpointA.GetClientState().(*ibctm.ClientState) - - tc.malleate() - - status := lightClientModule.Status(suite.chainA.GetContext(), path.EndpointA.ClientID) - suite.Require().Equal(tc.expStatus, status) - }) - - } -} - -func (suite *TendermintTestSuite) TestLatestHeight() { - var ( - path *ibctesting.Path - height exported.Height - ) - - testCases := []struct { - name string - malleate func() - expHeight exported.Height - }{ - { - "success", - func() {}, - clienttypes.Height{RevisionNumber: 0x1, RevisionHeight: 0x4}, - }, - { - "client state not found", - func() { - store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - store.Delete(host.ClientStateKey()) - }, - clienttypes.ZeroHeight(), - }, - } - - for _, tc := range testCases { - tc := tc - suite.Run(tc.name, func() { - suite.SetupTest() - - path = ibctesting.NewPath(suite.chainA, suite.chainB) - path.SetupClients() - - lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(path.EndpointA.ClientID) - suite.Require().True(found) - - tc.malleate() - - height = lightClientModule.LatestHeight(suite.chainA.GetContext(), path.EndpointA.ClientID) - suite.Require().Equal(tc.expHeight, height) - }) - } -} - -func (suite *TendermintTestSuite) TestGetTimestampAtHeight() { - var ( - path *ibctesting.Path - height exported.Height - ) - expectedTimestamp := time.Unix(1, 0) - - testCases := []struct { - name string - malleate func() - expErr error - }{ - { - "success", - func() {}, - nil, - }, - { - "failure: client state not found", - func() { - store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - store.Delete(host.ClientStateKey()) - }, - clienttypes.ErrClientNotFound, - }, - { - "failure: consensus state not found for height", - func() { - clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) - height = clientState.LatestHeight.Increment() - }, - clienttypes.ErrConsensusStateNotFound, - }, - } - - for _, tc := range testCases { - tc := tc - suite.Run(tc.name, func() { - suite.SetupTest() - - path = ibctesting.NewPath(suite.chainA, suite.chainB) - path.SetupClients() - - clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) - height = clientState.LatestHeight - - // grab consensusState from store and update with a predefined timestamp - consensusState := path.EndpointA.GetConsensusState(height) - tmConsensusState, ok := consensusState.(*ibctm.ConsensusState) - suite.Require().True(ok) - - tmConsensusState.Timestamp = expectedTimestamp - path.EndpointA.SetConsensusState(tmConsensusState, height) - - lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(path.EndpointA.ClientID) - suite.Require().True(found) - - tc.malleate() - - timestamp, err := lightClientModule.TimestampAtHeight(suite.chainA.GetContext(), path.EndpointA.ClientID, height) - - expPass := tc.expErr == nil - if expPass { - suite.Require().NoError(err) - - expectedTimestamp := uint64(expectedTimestamp.UnixNano()) - suite.Require().Equal(expectedTimestamp, timestamp) - } else { - suite.Require().ErrorIs(err, tc.expErr) - } - }) - } -} - func (suite *TendermintTestSuite) TestInitialize() { var consensusState exported.ConsensusState var clientState exported.ClientState @@ -533,10 +343,16 @@ func (suite *TendermintTestSuite) TestLCMUpdateState() { } } -func (suite *TendermintTestSuite) TestRecoverClient() { +func (suite *TendermintTestSuite) TestVerifyMembership() { var ( - subjectClientID, substituteClientID string - subjectClientState exported.ClientState + testingpath *ibctesting.Path + delayTimePeriod uint64 + delayBlockPeriod uint64 + err error + proofHeight exported.Height + proof []byte + path exported.Path + value []byte ) testCases := []struct { @@ -545,36 +361,205 @@ func (suite *TendermintTestSuite) TestRecoverClient() { expErr error }{ { - "success", + "successful ClientState verification", func() { + // default proof construction uses ClientState }, nil, }, { - "cannot parse malformed substitute client ID", - func() { - substituteClientID = ibctesting.InvalidID + "successful ConsensusState verification", func() { + latestHeight := testingpath.EndpointB.GetClientLatestHeight() + + key := host.FullConsensusStateKey(testingpath.EndpointB.ClientID, latestHeight) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + + consensusState := testingpath.EndpointB.GetConsensusState(latestHeight).(*ibctm.ConsensusState) + value, err = suite.chainB.Codec.MarshalInterface(consensusState) + suite.Require().NoError(err) }, - host.ErrInvalidID, + nil, }, { - "substitute client ID does not contain 07-tendermint prefix", - func() { - substituteClientID = solomachineClientID + "successful Connection verification", func() { + key := host.ConnectionKey(testingpath.EndpointB.ConnectionID) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + + connection := testingpath.EndpointB.GetConnection() + value, err = suite.chainB.Codec.Marshal(&connection) + suite.Require().NoError(err) }, - clienttypes.ErrInvalidClientType, + nil, }, { - "cannot find subject client state", - func() { - subjectClientID = tmClientID + "successful Channel verification", func() { + key := host.ChannelKey(testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + + channel := testingpath.EndpointB.GetChannel() + value, err = suite.chainB.Codec.Marshal(&channel) + suite.Require().NoError(err) }, - clienttypes.ErrClientNotFound, + nil, }, { - "cannot find substitute client state", + "successful PacketCommitment verification", func() { + // send from chainB to chainA since we are proving chainB sent a packet + sequence, err := testingpath.EndpointB.SendPacket(clienttypes.NewHeight(1, 100), 0, ibctesting.MockPacketData) + suite.Require().NoError(err) + + // make packet commitment proof + packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequence, testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID, testingpath.EndpointA.ChannelConfig.PortID, testingpath.EndpointA.ChannelID, clienttypes.NewHeight(1, 100), 0) + key := host.PacketCommitmentKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = testingpath.EndpointB.QueryProof(key) + + value = channeltypes.CommitPacket(suite.chainA.App.GetIBCKeeper().Codec(), packet) + }, nil, + }, + { + "successful Acknowledgement verification", func() { + // send from chainA to chainB since we are proving chainB wrote an acknowledgement + sequence, err := testingpath.EndpointA.SendPacket(clienttypes.NewHeight(1, 100), 0, ibctesting.MockPacketData) + suite.Require().NoError(err) + + // write receipt and ack + packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequence, testingpath.EndpointA.ChannelConfig.PortID, testingpath.EndpointA.ChannelID, testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) + err = testingpath.EndpointB.RecvPacket(packet) + suite.Require().NoError(err) + + key := host.PacketAcknowledgementKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = testingpath.EndpointB.QueryProof(key) + + value = channeltypes.CommitAcknowledgement(ibcmock.MockAcknowledgement.Acknowledgement()) + }, + nil, + }, + { + "successful NextSequenceRecv verification", func() { + // send from chainA to chainB since we are proving chainB incremented the sequence recv + + // send packet + sequence, err := testingpath.EndpointA.SendPacket(clienttypes.NewHeight(1, 100), 0, ibctesting.MockPacketData) + suite.Require().NoError(err) + + // next seq recv incremented + packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequence, testingpath.EndpointA.ChannelConfig.PortID, testingpath.EndpointA.ChannelID, testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) + err = testingpath.EndpointB.RecvPacket(packet) + suite.Require().NoError(err) + + key := host.NextSequenceRecvKey(packet.GetSourcePort(), packet.GetSourceChannel()) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = testingpath.EndpointB.QueryProof(key) + + value = sdk.Uint64ToBigEndian(packet.GetSequence() + 1) + }, + nil, + }, + { + "successful verification outside IBC store", func() { + key := transfertypes.PortKey + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(commitmenttypes.NewMerklePrefix([]byte(transfertypes.StoreKey)), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProofForStore(transfertypes.StoreKey, key, int64(testingpath.EndpointA.GetClientLatestHeight().GetRevisionHeight())) + + value = []byte(suite.chainB.GetSimApp().TransferKeeper.GetPort(suite.chainB.GetContext())) + suite.Require().NoError(err) + }, + nil, + }, + { + "delay time period has passed", func() { + delayTimePeriod = uint64(time.Second.Nanoseconds()) + }, + nil, + }, + { + "delay time period has not passed", func() { + delayTimePeriod = uint64(time.Hour.Nanoseconds()) + }, + ibctm.ErrDelayPeriodNotPassed, + }, + { + "delay block period has passed", func() { + delayBlockPeriod = 1 + }, + nil, + }, + { + "delay block period has not passed", func() { + delayBlockPeriod = 1000 + }, + ibctm.ErrDelayPeriodNotPassed, + }, + { + "latest client height < height", func() { + proofHeight = testingpath.EndpointA.GetClientLatestHeight().Increment() + }, + ibcerrors.ErrInvalidHeight, + }, + { + "invalid path type", func() { - substituteClientID = tmClientID + path = ibcmock.KeyPath{} + }, + ibcerrors.ErrInvalidType, + }, + { + "failed to unmarshal merkle proof", func() { + proof = invalidProof + }, + commitmenttypes.ErrInvalidProof, + }, + { + "consensus state not found", func() { + proofHeight = clienttypes.ZeroHeight() + }, + clienttypes.ErrConsensusStateNotFound, + }, + { + "proof verification failed", func() { + // change the value being proved + value = []byte("invalid value") + }, + commitmenttypes.ErrInvalidProof, + }, + { + "proof is empty", func() { + // change the inserted proof + proof = []byte{} + }, + commitmenttypes.ErrInvalidMerkleProof, + }, + { + "client state not found", + func() { + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), testingpath.EndpointA.ClientID) + store.Delete(host.ClientStateKey()) }, clienttypes.ErrClientNotFound, }, @@ -582,223 +567,397 @@ func (suite *TendermintTestSuite) TestRecoverClient() { for _, tc := range testCases { tc := tc + suite.Run(tc.name, func() { suite.SetupTest() // reset - ctx := suite.chainA.GetContext() + testingpath = ibctesting.NewPath(suite.chainA, suite.chainB) + testingpath.SetChannelOrdered() + testingpath.Setup() - subjectPath := ibctesting.NewPath(suite.chainA, suite.chainB) - subjectPath.SetupClients() - subjectClientID = subjectPath.EndpointA.ClientID - subjectClientState = suite.chainA.GetClientState(subjectClientID) + // reset time and block delays to 0, malleate may change to a specific non-zero value. + delayTimePeriod = 0 + delayBlockPeriod = 0 - substitutePath := ibctesting.NewPath(suite.chainA, suite.chainB) - substitutePath.SetupClients() - substituteClientID = substitutePath.EndpointA.ClientID + // create default proof, merklePath, and value which passes + // may be overwritten by malleate() + key := host.FullClientStateKey(testingpath.EndpointB.ClientID) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) - tmClientState, ok := subjectClientState.(*ibctm.ClientState) - suite.Require().True(ok) - tmClientState.FrozenHeight = tmClientState.LatestHeight - suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(ctx, subjectPath.EndpointA.ClientID, tmClientState) + proof, proofHeight = suite.chainB.QueryProof(key) + + clientState := testingpath.EndpointB.GetClientState().(*ibctm.ClientState) + value, err = suite.chainB.Codec.MarshalInterface(clientState) + suite.Require().NoError(err) + + tc.malleate() // make changes as necessary + + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(testingpath.EndpointA.ClientID) + suite.Require().True(found) + + err = lightClientModule.VerifyMembership( + suite.chainA.GetContext(), testingpath.EndpointA.ClientID, proofHeight, delayTimePeriod, delayBlockPeriod, + proof, path, value, + ) + expPass := tc.expErr == nil + if expPass { + suite.Require().NoError(err) + } else { + suite.Require().ErrorContains(err, tc.expErr.Error()) + } + }) + } +} + +func (suite *TendermintTestSuite) TestVerifyNonMembership() { + var ( + testingpath *ibctesting.Path + delayTimePeriod uint64 + delayBlockPeriod uint64 + err error + proofHeight exported.Height + path exported.Path + proof []byte + invalidClientID = "09-tendermint" + invalidConnectionID = "connection-100" + invalidChannelID = "channel-800" + invalidPortID = "invalid-port" + ) + + testCases := []struct { + name string + malleate func() + expErr error + }{ + { + "successful ClientState verification of non membership", + func() { + // default proof construction uses ClientState + }, + nil, + }, + { + "successful ConsensusState verification of non membership", func() { + key := host.FullConsensusStateKey(invalidClientID, testingpath.EndpointB.GetClientLatestHeight()) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + }, + nil, + }, + { + "successful Connection verification of non membership", func() { + key := host.ConnectionKey(invalidConnectionID) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + }, + nil, + }, + { + "successful Channel verification of non membership", func() { + key := host.ChannelKey(testingpath.EndpointB.ChannelConfig.PortID, invalidChannelID) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + }, + nil, + }, + { + "successful PacketCommitment verification of non membership", func() { + // make packet commitment proof + key := host.PacketCommitmentKey(invalidPortID, invalidChannelID, 1) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = testingpath.EndpointB.QueryProof(key) + }, + nil, + }, + { + "successful Acknowledgement verification of non membership", func() { + key := host.PacketAcknowledgementKey(invalidPortID, invalidChannelID, 1) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = testingpath.EndpointB.QueryProof(key) + }, + nil, + }, + { + "successful NextSequenceRecv verification of non membership", func() { + key := host.NextSequenceRecvKey(invalidPortID, invalidChannelID) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = testingpath.EndpointB.QueryProof(key) + }, + nil, + }, + { + "successful verification of non membership outside IBC store", func() { + key := []byte{0x08} + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(commitmenttypes.NewMerklePrefix([]byte(transfertypes.StoreKey)), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProofForStore(transfertypes.StoreKey, key, int64(testingpath.EndpointA.GetClientLatestHeight().GetRevisionHeight())) + }, + nil, + }, + { + "delay time period has passed", func() { + delayTimePeriod = uint64(time.Second.Nanoseconds()) + }, + nil, + }, + { + "delay time period has not passed", func() { + delayTimePeriod = uint64(time.Hour.Nanoseconds()) + }, + ibctm.ErrDelayPeriodNotPassed, + }, + { + "delay block period has passed", func() { + delayBlockPeriod = 1 + }, + nil, + }, + { + "delay block period has not passed", func() { + delayBlockPeriod = 1000 + }, + ibctm.ErrDelayPeriodNotPassed, + }, + { + "latest client height < height", func() { + proofHeight = testingpath.EndpointA.GetClientLatestHeight().Increment() + }, + ibcerrors.ErrInvalidHeight, + }, + { + "invalid path type", + func() { + path = ibcmock.KeyPath{} + }, + ibcerrors.ErrInvalidType, + }, + { + "failed to unmarshal merkle proof", func() { + proof = invalidProof + }, + commitmenttypes.ErrInvalidProof, + }, + { + "consensus state not found", func() { + proofHeight = clienttypes.ZeroHeight() + }, + clienttypes.ErrConsensusStateNotFound, + }, + { + "verify non membership fails as path exists", func() { + // change the value being proved + key := host.FullClientStateKey(testingpath.EndpointB.ClientID) + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + }, + commitmenttypes.ErrInvalidProof, + }, + { + "proof is empty", func() { + // change the inserted proof + proof = []byte{} + }, + commitmenttypes.ErrInvalidMerkleProof, + }, + { + "client state not found", + func() { + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), testingpath.EndpointA.ClientID) + store.Delete(host.ClientStateKey()) + }, + clienttypes.ErrClientNotFound, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() // reset + testingpath = ibctesting.NewPath(suite.chainA, suite.chainB) + testingpath.SetChannelOrdered() + testingpath.Setup() + + // reset time and block delays to 0, malleate may change to a specific non-zero value. + delayTimePeriod = 0 + delayBlockPeriod = 0 + + // create default proof, merklePath, and value which passes + // may be overwritten by malleate() + key := host.FullClientStateKey("invalid-client-id") + + merklePath := commitmenttypes.NewMerklePath(string(key)) + path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + suite.Require().NoError(err) + + proof, proofHeight = suite.chainB.QueryProof(key) + + tc.malleate() // make changes as necessary - lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(subjectClientID) + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(testingpath.EndpointA.ClientID) suite.Require().True(found) - tc.malleate() - - err := lightClientModule.RecoverClient(ctx, subjectClientID, substituteClientID) + err = lightClientModule.VerifyNonMembership( + suite.chainA.GetContext(), testingpath.EndpointA.ClientID, proofHeight, delayTimePeriod, delayBlockPeriod, + proof, path, + ) expPass := tc.expErr == nil if expPass { suite.Require().NoError(err) - - // assert that status of subject client is now Active - clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, subjectClientID) - tmClientState := subjectPath.EndpointA.GetClientState().(*ibctm.ClientState) - suite.Require().Equal(exported.Active, tmClientState.Status(ctx, clientStore, suite.chainA.App.AppCodec())) } else { - suite.Require().Error(err) - suite.Require().ErrorIs(err, tc.expErr) + suite.Require().ErrorContains(err, tc.expErr.Error()) } }) } } -func (suite *TendermintTestSuite) TestVerifyUpgradeAndUpdateState() { +func (suite *TendermintTestSuite) TestStatus() { var ( - clientID string - path *ibctesting.Path - upgradedClientState exported.ClientState - upgradedClientStateAny, upgradedConsensusStateAny *codectypes.Any - upgradedClientStateProof, upgradedConsensusStateProof []byte + path *ibctesting.Path + clientState *ibctm.ClientState ) testCases := []struct { - name string - malleate func() - expErr error + name string + malleate func() + expStatus exported.Status }{ { - "success", - func() { - // upgrade height is at next block - upgradeHeight := clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) - - // zero custom fields and store in upgrade store - zeroedUpgradedClient := upgradedClientState.(*ibctm.ClientState).ZeroCustomFields() - zeroedUpgradedClientAny, err := codectypes.NewAnyWithValue(zeroedUpgradedClient) - suite.Require().NoError(err) - - err = suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(upgradeHeight.GetRevisionHeight()), suite.chainB.Codec.MustMarshal(zeroedUpgradedClientAny)) - suite.Require().NoError(err) - - err = suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(upgradeHeight.GetRevisionHeight()), suite.chainB.Codec.MustMarshal(upgradedConsensusStateAny)) - suite.Require().NoError(err) - - // commit upgrade store changes and update clients - suite.coordinator.CommitBlock(suite.chainB) - err = path.EndpointA.UpdateClient() - suite.Require().NoError(err) - - upgradedClientStateProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(upgradeHeight.GetRevisionHeight())), path.EndpointA.GetClientLatestHeight().GetRevisionHeight()) - upgradedConsensusStateProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(upgradeHeight.GetRevisionHeight())), path.EndpointA.GetClientLatestHeight().GetRevisionHeight()) - }, - nil, + "client is active", + func() {}, + exported.Active, }, { - "cannot find client state", + "client is frozen", func() { - clientID = tmClientID + clientState.FrozenHeight = clienttypes.NewHeight(0, 1) + path.EndpointA.SetClientState(clientState) }, - clienttypes.ErrClientNotFound, + exported.Frozen, }, { - "upgraded client state is not for tendermint client state", + "client status without consensus state", func() { - upgradedClientStateAny = &codectypes.Any{ - Value: []byte("invalid client state bytes"), - } + clientState.LatestHeight = clientState.LatestHeight.Increment().(clienttypes.Height) + path.EndpointA.SetClientState(clientState) }, - clienttypes.ErrInvalidClient, + exported.Expired, }, { - "upgraded consensus state is not tendermint consensus state", + "client status is expired", func() { - upgradedConsensusStateAny = &codectypes.Any{ - Value: []byte("invalid consensus state bytes"), - } + suite.coordinator.IncrementTimeBy(clientState.TrustingPeriod) }, - clienttypes.ErrInvalidConsensus, + exported.Expired, }, { - "upgraded client state height is not greater than current height", + "client state not found", func() { - // upgrade height is at next block - upgradeHeight := clienttypes.NewHeight(1, uint64(suite.chainB.GetContext().BlockHeight()+1)) - - // zero custom fields and store in upgrade store - zeroedUpgradedClient := upgradedClientState.(*ibctm.ClientState).ZeroCustomFields() - zeroedUpgradedClientAny, err := codectypes.NewAnyWithValue(zeroedUpgradedClient) - suite.Require().NoError(err) - - err = suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(upgradeHeight.GetRevisionHeight()), suite.chainB.Codec.MustMarshal(zeroedUpgradedClientAny)) - suite.Require().NoError(err) - - err = suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(upgradeHeight.GetRevisionHeight()), suite.chainB.Codec.MustMarshal(upgradedConsensusStateAny)) - suite.Require().NoError(err) - - // change upgraded client state height to be lower than current client state height - tmClient := upgradedClientState.(*ibctm.ClientState) - - newLatestheight, ok := path.EndpointA.GetClientLatestHeight().Decrement() - suite.Require().True(ok) - - tmClient.LatestHeight = newLatestheight.(clienttypes.Height) - upgradedClientStateAny, err = codectypes.NewAnyWithValue(tmClient) - suite.Require().NoError(err) - - suite.coordinator.CommitBlock(suite.chainB) - err = path.EndpointA.UpdateClient() - suite.Require().NoError(err) - - upgradedClientStateProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(upgradeHeight.GetRevisionHeight())), path.EndpointA.GetClientLatestHeight().GetRevisionHeight()) - upgradedConsensusStateProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(upgradeHeight.GetRevisionHeight())), path.EndpointA.GetClientLatestHeight().GetRevisionHeight()) + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + store.Delete(host.ClientStateKey()) }, - ibcerrors.ErrInvalidHeight, + exported.Unknown, }, } for _, tc := range testCases { tc := tc suite.Run(tc.name, func() { - suite.SetupTest() // reset + suite.SetupTest() path = ibctesting.NewPath(suite.chainA, suite.chainB) path.SetupClients() - clientID = path.EndpointA.ClientID - clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) - revisionNumber := clienttypes.ParseChainID(clientState.ChainId) + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(path.EndpointA.ClientID) + suite.Require().True(found) - newUnbondindPeriod := ubdPeriod + trustingPeriod - newChainID, err := clienttypes.SetRevisionNumber(clientState.ChainId, revisionNumber+1) - suite.Require().NoError(err) + clientState = path.EndpointA.GetClientState().(*ibctm.ClientState) - upgradedClientState = ibctm.NewClientState(newChainID, ibctm.DefaultTrustLevel, trustingPeriod, newUnbondindPeriod, maxClockDrift, clienttypes.NewHeight(revisionNumber+1, clientState.LatestHeight.GetRevisionHeight()+1), commitmenttypes.GetSDKSpecs(), upgradePath) - upgradedClientStateAny, err = codectypes.NewAnyWithValue(upgradedClientState) - suite.Require().NoError(err) + tc.malleate() - nextValsHash := sha256.Sum256([]byte("new-nextValsHash")) - upgradedConsensusState := ibctm.NewConsensusState(time.Now(), commitmenttypes.NewMerkleRoot([]byte("new-hash")), nextValsHash[:]) + status := lightClientModule.Status(suite.chainA.GetContext(), path.EndpointA.ClientID) + suite.Require().Equal(tc.expStatus, status) + }) - upgradedConsensusStateAny, err = codectypes.NewAnyWithValue(upgradedConsensusState) - suite.Require().NoError(err) + } +} - tc.malleate() +func (suite *TendermintTestSuite) TestLatestHeight() { + var ( + path *ibctesting.Path + height exported.Height + ) - lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(clientID) - suite.Require().True(found) + testCases := []struct { + name string + malleate func() + expHeight exported.Height + }{ + { + "success", + func() {}, + clienttypes.Height{RevisionNumber: 0x1, RevisionHeight: 0x4}, + }, + { + "client state not found", + func() { + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + store.Delete(host.ClientStateKey()) + }, + clienttypes.ZeroHeight(), + }, + } - err = lightClientModule.VerifyUpgradeAndUpdateState( - suite.chainA.GetContext(), - clientID, - upgradedClientStateAny.Value, - upgradedConsensusStateAny.Value, - upgradedClientStateProof, - upgradedConsensusStateProof, - ) + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + suite.SetupTest() - expPass := tc.expErr == nil - if expPass { - suite.Require().NoError(err) + path = ibctesting.NewPath(suite.chainA, suite.chainB) + path.SetupClients() - expClientState := path.EndpointA.GetClientState() - expClientStateBz := suite.chainA.Codec.MustMarshal(expClientState) - suite.Require().Equal(upgradedClientStateAny.Value, expClientStateBz) + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(path.EndpointA.ClientID) + suite.Require().True(found) - expConsensusState := ibctm.NewConsensusState(upgradedConsensusState.Timestamp, commitmenttypes.NewMerkleRoot([]byte(ibctm.SentinelRoot)), upgradedConsensusState.NextValidatorsHash) - expConsensusStateBz := suite.chainA.Codec.MustMarshal(expConsensusState) + tc.malleate() - consensusStateBz := suite.chainA.Codec.MustMarshal(path.EndpointA.GetConsensusState(path.EndpointA.GetClientLatestHeight())) - suite.Require().Equal(expConsensusStateBz, consensusStateBz) - } else { - suite.Require().Error(err) - suite.Require().ErrorIs(err, tc.expErr) - } + height = lightClientModule.LatestHeight(suite.chainA.GetContext(), path.EndpointA.ClientID) + suite.Require().Equal(tc.expHeight, height) }) } } -func (suite *TendermintTestSuite) TestVerifyMembership() { +func (suite *TendermintTestSuite) TestGetTimestampAtHeight() { var ( - testingpath *ibctesting.Path - delayTimePeriod uint64 - delayBlockPeriod uint64 - err error - proofHeight exported.Height - proof []byte - path exported.Path - value []byte + path *ibctesting.Path + height exported.Height ) + expectedTimestamp := time.Unix(1, 0) testCases := []struct { name string @@ -806,205 +965,110 @@ func (suite *TendermintTestSuite) TestVerifyMembership() { expErr error }{ { - "successful ClientState verification", - func() { - // default proof construction uses ClientState - }, - nil, - }, - { - "successful ConsensusState verification", func() { - latestHeight := testingpath.EndpointB.GetClientLatestHeight() - - key := host.FullConsensusStateKey(testingpath.EndpointB.ClientID, latestHeight) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = suite.chainB.QueryProof(key) - - consensusState := testingpath.EndpointB.GetConsensusState(latestHeight).(*ibctm.ConsensusState) - value, err = suite.chainB.Codec.MarshalInterface(consensusState) - suite.Require().NoError(err) - }, - nil, - }, - { - "successful Connection verification", func() { - key := host.ConnectionKey(testingpath.EndpointB.ConnectionID) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = suite.chainB.QueryProof(key) - - connection := testingpath.EndpointB.GetConnection() - value, err = suite.chainB.Codec.Marshal(&connection) - suite.Require().NoError(err) - }, + "success", + func() {}, nil, }, { - "successful Channel verification", func() { - key := host.ChannelKey(testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = suite.chainB.QueryProof(key) - - channel := testingpath.EndpointB.GetChannel() - value, err = suite.chainB.Codec.Marshal(&channel) - suite.Require().NoError(err) + "failure: client state not found", + func() { + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + store.Delete(host.ClientStateKey()) }, - nil, + clienttypes.ErrClientNotFound, }, { - "successful PacketCommitment verification", func() { - // send from chainB to chainA since we are proving chainB sent a packet - sequence, err := testingpath.EndpointB.SendPacket(clienttypes.NewHeight(1, 100), 0, ibctesting.MockPacketData) - suite.Require().NoError(err) - - // make packet commitment proof - packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequence, testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID, testingpath.EndpointA.ChannelConfig.PortID, testingpath.EndpointA.ChannelID, clienttypes.NewHeight(1, 100), 0) - key := host.PacketCommitmentKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) + "failure: consensus state not found for height", + func() { + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) + height = clientState.LatestHeight.Increment() + }, + clienttypes.ErrConsensusStateNotFound, + }, + } - proof, proofHeight = testingpath.EndpointB.QueryProof(key) + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + suite.SetupTest() - value = channeltypes.CommitPacket(suite.chainA.App.GetIBCKeeper().Codec(), packet) - }, nil, - }, - { - "successful Acknowledgement verification", func() { - // send from chainA to chainB since we are proving chainB wrote an acknowledgement - sequence, err := testingpath.EndpointA.SendPacket(clienttypes.NewHeight(1, 100), 0, ibctesting.MockPacketData) - suite.Require().NoError(err) + path = ibctesting.NewPath(suite.chainA, suite.chainB) + path.SetupClients() - // write receipt and ack - packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequence, testingpath.EndpointA.ChannelConfig.PortID, testingpath.EndpointA.ChannelID, testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) - err = testingpath.EndpointB.RecvPacket(packet) - suite.Require().NoError(err) + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) + height = clientState.LatestHeight - key := host.PacketAcknowledgementKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) + // grab consensusState from store and update with a predefined timestamp + consensusState := path.EndpointA.GetConsensusState(height) + tmConsensusState, ok := consensusState.(*ibctm.ConsensusState) + suite.Require().True(ok) - proof, proofHeight = testingpath.EndpointB.QueryProof(key) + tmConsensusState.Timestamp = expectedTimestamp + path.EndpointA.SetConsensusState(tmConsensusState, height) - value = channeltypes.CommitAcknowledgement(ibcmock.MockAcknowledgement.Acknowledgement()) - }, - nil, - }, - { - "successful NextSequenceRecv verification", func() { - // send from chainA to chainB since we are proving chainB incremented the sequence recv + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(path.EndpointA.ClientID) + suite.Require().True(found) - // send packet - sequence, err := testingpath.EndpointA.SendPacket(clienttypes.NewHeight(1, 100), 0, ibctesting.MockPacketData) - suite.Require().NoError(err) + tc.malleate() - // next seq recv incremented - packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequence, testingpath.EndpointA.ChannelConfig.PortID, testingpath.EndpointA.ChannelID, testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) - err = testingpath.EndpointB.RecvPacket(packet) - suite.Require().NoError(err) + timestamp, err := lightClientModule.TimestampAtHeight(suite.chainA.GetContext(), path.EndpointA.ClientID, height) - key := host.NextSequenceRecvKey(packet.GetSourcePort(), packet.GetSourceChannel()) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + expPass := tc.expErr == nil + if expPass { suite.Require().NoError(err) - proof, proofHeight = testingpath.EndpointB.QueryProof(key) + expectedTimestamp := uint64(expectedTimestamp.UnixNano()) + suite.Require().Equal(expectedTimestamp, timestamp) + } else { + suite.Require().ErrorIs(err, tc.expErr) + } + }) + } +} - value = sdk.Uint64ToBigEndian(packet.GetSequence() + 1) - }, - nil, - }, - { - "successful verification outside IBC store", func() { - key := transfertypes.PortKey - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(commitmenttypes.NewMerklePrefix([]byte(transfertypes.StoreKey)), merklePath) - suite.Require().NoError(err) - proof, proofHeight = suite.chainB.QueryProofForStore(transfertypes.StoreKey, key, int64(testingpath.EndpointA.GetClientLatestHeight().GetRevisionHeight())) +func (suite *TendermintTestSuite) TestRecoverClient() { + var ( + subjectClientID, substituteClientID string + subjectClientState exported.ClientState + ) - value = []byte(suite.chainB.GetSimApp().TransferKeeper.GetPort(suite.chainB.GetContext())) - suite.Require().NoError(err) - }, - nil, - }, - { - "delay time period has passed", func() { - delayTimePeriod = uint64(time.Second.Nanoseconds()) - }, - nil, - }, - { - "delay time period has not passed", func() { - delayTimePeriod = uint64(time.Hour.Nanoseconds()) - }, - ibctm.ErrDelayPeriodNotPassed, - }, + testCases := []struct { + name string + malleate func() + expErr error + }{ { - "delay block period has passed", func() { - delayBlockPeriod = 1 + "success", + func() { }, nil, }, { - "delay block period has not passed", func() { - delayBlockPeriod = 1000 - }, - ibctm.ErrDelayPeriodNotPassed, - }, - { - "latest client height < height", func() { - proofHeight = testingpath.EndpointA.GetClientLatestHeight().Increment() - }, - ibcerrors.ErrInvalidHeight, - }, - { - "invalid path type", + "cannot parse malformed substitute client ID", func() { - path = ibcmock.KeyPath{} - }, - ibcerrors.ErrInvalidType, - }, - { - "failed to unmarshal merkle proof", func() { - proof = invalidProof - }, - commitmenttypes.ErrInvalidProof, - }, - { - "consensus state not found", func() { - proofHeight = clienttypes.ZeroHeight() + substituteClientID = ibctesting.InvalidID }, - clienttypes.ErrConsensusStateNotFound, + host.ErrInvalidID, }, { - "proof verification failed", func() { - // change the value being proved - value = []byte("invalid value") + "substitute client ID does not contain 07-tendermint prefix", + func() { + substituteClientID = solomachineClientID }, - commitmenttypes.ErrInvalidProof, + clienttypes.ErrInvalidClientType, }, { - "proof is empty", func() { - // change the inserted proof - proof = []byte{} + "cannot find subject client state", + func() { + subjectClientID = tmClientID }, - commitmenttypes.ErrInvalidMerkleProof, + clienttypes.ErrClientNotFound, }, { - "client state not found", + "cannot find substitute client state", func() { - store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), testingpath.EndpointA.ClientID) - store.Delete(host.ClientStateKey()) + substituteClientID = tmClientID }, clienttypes.ErrClientNotFound, }, @@ -1012,62 +1076,54 @@ func (suite *TendermintTestSuite) TestVerifyMembership() { for _, tc := range testCases { tc := tc - suite.Run(tc.name, func() { suite.SetupTest() // reset - testingpath = ibctesting.NewPath(suite.chainA, suite.chainB) - testingpath.SetChannelOrdered() - testingpath.Setup() + ctx := suite.chainA.GetContext() - // reset time and block delays to 0, malleate may change to a specific non-zero value. - delayTimePeriod = 0 - delayBlockPeriod = 0 + subjectPath := ibctesting.NewPath(suite.chainA, suite.chainB) + subjectPath.SetupClients() + subjectClientID = subjectPath.EndpointA.ClientID + subjectClientState = suite.chainA.GetClientState(subjectClientID) - // create default proof, merklePath, and value which passes - // may be overwritten by malleate() - key := host.FullClientStateKey(testingpath.EndpointB.ClientID) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) + substitutePath := ibctesting.NewPath(suite.chainA, suite.chainB) + substitutePath.SetupClients() + substituteClientID = substitutePath.EndpointA.ClientID - proof, proofHeight = suite.chainB.QueryProof(key) + tmClientState, ok := subjectClientState.(*ibctm.ClientState) + suite.Require().True(ok) + tmClientState.FrozenHeight = tmClientState.LatestHeight + suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(ctx, subjectPath.EndpointA.ClientID, tmClientState) - clientState := testingpath.EndpointB.GetClientState().(*ibctm.ClientState) - value, err = suite.chainB.Codec.MarshalInterface(clientState) - suite.Require().NoError(err) + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(subjectClientID) + suite.Require().True(found) - tc.malleate() // make changes as necessary + tc.malleate() - lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(testingpath.EndpointA.ClientID) - suite.Require().True(found) + err := lightClientModule.RecoverClient(ctx, subjectClientID, substituteClientID) - err = lightClientModule.VerifyMembership( - suite.chainA.GetContext(), testingpath.EndpointA.ClientID, proofHeight, delayTimePeriod, delayBlockPeriod, - proof, path, value, - ) expPass := tc.expErr == nil if expPass { suite.Require().NoError(err) + + // assert that status of subject client is now Active + clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(ctx, subjectClientID) + tmClientState := subjectPath.EndpointA.GetClientState().(*ibctm.ClientState) + suite.Require().Equal(exported.Active, tmClientState.Status(ctx, clientStore, suite.chainA.App.AppCodec())) } else { - suite.Require().ErrorContains(err, tc.expErr.Error()) + suite.Require().Error(err) + suite.Require().ErrorIs(err, tc.expErr) } }) } -} - -func (suite *TendermintTestSuite) TestVerifyNonMembership() { - var ( - testingpath *ibctesting.Path - delayTimePeriod uint64 - delayBlockPeriod uint64 - err error - proofHeight exported.Height - path exported.Path - proof []byte - invalidClientID = "09-tendermint" - invalidConnectionID = "connection-100" - invalidChannelID = "channel-800" - invalidPortID = "invalid-port" +} + +func (suite *TendermintTestSuite) TestVerifyUpgradeAndUpdateState() { + var ( + clientID string + path *ibctesting.Path + upgradedClientState exported.ClientState + upgradedClientStateAny, upgradedConsensusStateAny *codectypes.Any + upgradedClientStateProof, upgradedConsensusStateProof []byte ) testCases := []struct { @@ -1076,206 +1132,151 @@ func (suite *TendermintTestSuite) TestVerifyNonMembership() { expErr error }{ { - "successful ClientState verification of non membership", + "success", func() { - // default proof construction uses ClientState - }, - nil, - }, - { - "successful ConsensusState verification of non membership", func() { - key := host.FullConsensusStateKey(invalidClientID, testingpath.EndpointB.GetClientLatestHeight()) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = suite.chainB.QueryProof(key) - }, - nil, - }, - { - "successful Connection verification of non membership", func() { - key := host.ConnectionKey(invalidConnectionID) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = suite.chainB.QueryProof(key) - }, - nil, - }, - { - "successful Channel verification of non membership", func() { - key := host.ChannelKey(testingpath.EndpointB.ChannelConfig.PortID, invalidChannelID) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) + // upgrade height is at next block + upgradeHeight := clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) - proof, proofHeight = suite.chainB.QueryProof(key) - }, - nil, - }, - { - "successful PacketCommitment verification of non membership", func() { - // make packet commitment proof - key := host.PacketCommitmentKey(invalidPortID, invalidChannelID, 1) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + // zero custom fields and store in upgrade store + zeroedUpgradedClient := upgradedClientState.(*ibctm.ClientState).ZeroCustomFields() + zeroedUpgradedClientAny, err := codectypes.NewAnyWithValue(zeroedUpgradedClient) suite.Require().NoError(err) - proof, proofHeight = testingpath.EndpointB.QueryProof(key) - }, - nil, - }, - { - "successful Acknowledgement verification of non membership", func() { - key := host.PacketAcknowledgementKey(invalidPortID, invalidChannelID, 1) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + err = suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(upgradeHeight.GetRevisionHeight()), suite.chainB.Codec.MustMarshal(zeroedUpgradedClientAny)) suite.Require().NoError(err) - proof, proofHeight = testingpath.EndpointB.QueryProof(key) - }, - nil, - }, - { - "successful NextSequenceRecv verification of non membership", func() { - key := host.NextSequenceRecvKey(invalidPortID, invalidChannelID) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + err = suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(upgradeHeight.GetRevisionHeight()), suite.chainB.Codec.MustMarshal(upgradedConsensusStateAny)) suite.Require().NoError(err) - proof, proofHeight = testingpath.EndpointB.QueryProof(key) - }, - nil, - }, - { - "successful verification of non membership outside IBC store", func() { - key := []byte{0x08} - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(commitmenttypes.NewMerklePrefix([]byte(transfertypes.StoreKey)), merklePath) + // commit upgrade store changes and update clients + suite.coordinator.CommitBlock(suite.chainB) + err = path.EndpointA.UpdateClient() suite.Require().NoError(err) - proof, proofHeight = suite.chainB.QueryProofForStore(transfertypes.StoreKey, key, int64(testingpath.EndpointA.GetClientLatestHeight().GetRevisionHeight())) - }, - nil, - }, - { - "delay time period has passed", func() { - delayTimePeriod = uint64(time.Second.Nanoseconds()) - }, - nil, - }, - { - "delay time period has not passed", func() { - delayTimePeriod = uint64(time.Hour.Nanoseconds()) - }, - ibctm.ErrDelayPeriodNotPassed, - }, - { - "delay block period has passed", func() { - delayBlockPeriod = 1 + upgradedClientStateProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(upgradeHeight.GetRevisionHeight())), path.EndpointA.GetClientLatestHeight().GetRevisionHeight()) + upgradedConsensusStateProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(upgradeHeight.GetRevisionHeight())), path.EndpointA.GetClientLatestHeight().GetRevisionHeight()) }, nil, }, { - "delay block period has not passed", func() { - delayBlockPeriod = 1000 - }, - ibctm.ErrDelayPeriodNotPassed, - }, - { - "latest client height < height", func() { - proofHeight = testingpath.EndpointA.GetClientLatestHeight().Increment() - }, - ibcerrors.ErrInvalidHeight, - }, - { - "invalid path type", + "cannot find client state", func() { - path = ibcmock.KeyPath{} + clientID = tmClientID }, - ibcerrors.ErrInvalidType, + clienttypes.ErrClientNotFound, }, { - "failed to unmarshal merkle proof", func() { - proof = invalidProof + "upgraded client state is not for tendermint client state", + func() { + upgradedClientStateAny = &codectypes.Any{ + Value: []byte("invalid client state bytes"), + } }, - commitmenttypes.ErrInvalidProof, + clienttypes.ErrInvalidClient, }, { - "consensus state not found", func() { - proofHeight = clienttypes.ZeroHeight() + "upgraded consensus state is not tendermint consensus state", + func() { + upgradedConsensusStateAny = &codectypes.Any{ + Value: []byte("invalid consensus state bytes"), + } }, - clienttypes.ErrConsensusStateNotFound, + clienttypes.ErrInvalidConsensus, }, { - "verify non membership fails as path exists", func() { - // change the value being proved - key := host.FullClientStateKey(testingpath.EndpointB.ClientID) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + "upgraded client state height is not greater than current height", + func() { + // upgrade height is at next block + upgradeHeight := clienttypes.NewHeight(1, uint64(suite.chainB.GetContext().BlockHeight()+1)) + + // zero custom fields and store in upgrade store + zeroedUpgradedClient := upgradedClientState.(*ibctm.ClientState).ZeroCustomFields() + zeroedUpgradedClientAny, err := codectypes.NewAnyWithValue(zeroedUpgradedClient) suite.Require().NoError(err) - proof, proofHeight = suite.chainB.QueryProof(key) - }, - commitmenttypes.ErrInvalidProof, - }, - { - "proof is empty", func() { - // change the inserted proof - proof = []byte{} - }, - commitmenttypes.ErrInvalidMerkleProof, - }, - { - "client state not found", - func() { - store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), testingpath.EndpointA.ClientID) - store.Delete(host.ClientStateKey()) + err = suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(upgradeHeight.GetRevisionHeight()), suite.chainB.Codec.MustMarshal(zeroedUpgradedClientAny)) + suite.Require().NoError(err) + + err = suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(upgradeHeight.GetRevisionHeight()), suite.chainB.Codec.MustMarshal(upgradedConsensusStateAny)) + suite.Require().NoError(err) + + // change upgraded client state height to be lower than current client state height + tmClient := upgradedClientState.(*ibctm.ClientState) + + newLatestheight, ok := path.EndpointA.GetClientLatestHeight().Decrement() + suite.Require().True(ok) + + tmClient.LatestHeight = newLatestheight.(clienttypes.Height) + upgradedClientStateAny, err = codectypes.NewAnyWithValue(tmClient) + suite.Require().NoError(err) + + suite.coordinator.CommitBlock(suite.chainB) + err = path.EndpointA.UpdateClient() + suite.Require().NoError(err) + + upgradedClientStateProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(upgradeHeight.GetRevisionHeight())), path.EndpointA.GetClientLatestHeight().GetRevisionHeight()) + upgradedConsensusStateProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(upgradeHeight.GetRevisionHeight())), path.EndpointA.GetClientLatestHeight().GetRevisionHeight()) }, - clienttypes.ErrClientNotFound, + ibcerrors.ErrInvalidHeight, }, } for _, tc := range testCases { tc := tc - suite.Run(tc.name, func() { suite.SetupTest() // reset - testingpath = ibctesting.NewPath(suite.chainA, suite.chainB) - testingpath.SetChannelOrdered() - testingpath.Setup() - // reset time and block delays to 0, malleate may change to a specific non-zero value. - delayTimePeriod = 0 - delayBlockPeriod = 0 + path = ibctesting.NewPath(suite.chainA, suite.chainB) + path.SetupClients() - // create default proof, merklePath, and value which passes - // may be overwritten by malleate() - key := host.FullClientStateKey("invalid-client-id") + clientID = path.EndpointA.ClientID + clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) + revisionNumber := clienttypes.ParseChainID(clientState.ChainId) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) + newUnbondindPeriod := ubdPeriod + trustingPeriod + newChainID, err := clienttypes.SetRevisionNumber(clientState.ChainId, revisionNumber+1) suite.Require().NoError(err) - proof, proofHeight = suite.chainB.QueryProof(key) + upgradedClientState = ibctm.NewClientState(newChainID, ibctm.DefaultTrustLevel, trustingPeriod, newUnbondindPeriod, maxClockDrift, clienttypes.NewHeight(revisionNumber+1, clientState.LatestHeight.GetRevisionHeight()+1), commitmenttypes.GetSDKSpecs(), upgradePath) + upgradedClientStateAny, err = codectypes.NewAnyWithValue(upgradedClientState) + suite.Require().NoError(err) - tc.malleate() // make changes as necessary + nextValsHash := sha256.Sum256([]byte("new-nextValsHash")) + upgradedConsensusState := ibctm.NewConsensusState(time.Now(), commitmenttypes.NewMerkleRoot([]byte("new-hash")), nextValsHash[:]) - lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(testingpath.EndpointA.ClientID) + upgradedConsensusStateAny, err = codectypes.NewAnyWithValue(upgradedConsensusState) + suite.Require().NoError(err) + + tc.malleate() + + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(clientID) suite.Require().True(found) - err = lightClientModule.VerifyNonMembership( - suite.chainA.GetContext(), testingpath.EndpointA.ClientID, proofHeight, delayTimePeriod, delayBlockPeriod, - proof, path, + err = lightClientModule.VerifyUpgradeAndUpdateState( + suite.chainA.GetContext(), + clientID, + upgradedClientStateAny.Value, + upgradedConsensusStateAny.Value, + upgradedClientStateProof, + upgradedConsensusStateProof, ) expPass := tc.expErr == nil if expPass { suite.Require().NoError(err) + + expClientState := path.EndpointA.GetClientState() + expClientStateBz := suite.chainA.Codec.MustMarshal(expClientState) + suite.Require().Equal(upgradedClientStateAny.Value, expClientStateBz) + + expConsensusState := ibctm.NewConsensusState(upgradedConsensusState.Timestamp, commitmenttypes.NewMerkleRoot([]byte(ibctm.SentinelRoot)), upgradedConsensusState.NextValidatorsHash) + expConsensusStateBz := suite.chainA.Codec.MustMarshal(expConsensusState) + + consensusStateBz := suite.chainA.Codec.MustMarshal(path.EndpointA.GetConsensusState(path.EndpointA.GetClientLatestHeight())) + suite.Require().Equal(expConsensusStateBz, consensusStateBz) } else { - suite.Require().ErrorContains(err, tc.expErr.Error()) + suite.Require().Error(err) + suite.Require().ErrorIs(err, tc.expErr) } }) } diff --git a/modules/light-clients/07-tendermint/misbehaviour_handle_test.go b/modules/light-clients/07-tendermint/misbehaviour_handle_test.go index 835f586f7d5..4d61cf322cb 100644 --- a/modules/light-clients/07-tendermint/misbehaviour_handle_test.go +++ b/modules/light-clients/07-tendermint/misbehaviour_handle_test.go @@ -340,12 +340,12 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviour() { err := path.EndpointA.CreateClient() suite.Require().NoError(err) - tc.malleate() + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(path.EndpointA.ClientID) + suite.Require().True(found) - clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) - clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + tc.malleate() - err = clientState.VerifyClientMessage(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, misbehaviour) + err = lightClientModule.VerifyClientMessage(suite.chainA.GetContext(), path.EndpointA.ClientID, misbehaviour) if tc.expPass { suite.Require().NoError(err) @@ -627,12 +627,12 @@ func (suite *TendermintTestSuite) TestVerifyMisbehaviourNonRevisionChainID() { err := path.EndpointA.CreateClient() suite.Require().NoError(err) - tc.malleate() + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(path.EndpointA.ClientID) + suite.Require().True(found) - clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) - clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + tc.malleate() - err = clientState.VerifyClientMessage(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, misbehaviour) + err = lightClientModule.VerifyClientMessage(suite.chainA.GetContext(), path.EndpointA.ClientID, misbehaviour) if tc.expPass { suite.Require().NoError(err) diff --git a/modules/light-clients/07-tendermint/upgrade_test.go b/modules/light-clients/07-tendermint/upgrade_test.go index 9225168d719..27b09a92edd 100644 --- a/modules/light-clients/07-tendermint/upgrade_test.go +++ b/modules/light-clients/07-tendermint/upgrade_test.go @@ -551,6 +551,11 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { cs := suite.chainA.GetClientState(path.EndpointA.ClientID).(*ibctm.ClientState) clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + // Call ZeroCustomFields on upgraded clients to clear any client-chosen parameters in test-case upgradedClient + if upgraded, ok := upgradedClient.(*ibctm.ClientState); ok { + upgradedClient = upgraded.ZeroCustomFields() + } + err = cs.VerifyUpgradeAndUpdateState( suite.chainA.GetContext(), suite.cdc, From ec87578c5c6ff0d559a1e80e534368ee7e506054 Mon Sep 17 00:00:00 2001 From: Charly Date: Wed, 10 Apr 2024 15:30:41 +0200 Subject: [PATCH 10/19] update: use lcm entrypoint --- .../07-tendermint/update_test.go | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/modules/light-clients/07-tendermint/update_test.go b/modules/light-clients/07-tendermint/update_test.go index 7b6a6032248..97de70c22ab 100644 --- a/modules/light-clients/07-tendermint/update_test.go +++ b/modules/light-clients/07-tendermint/update_test.go @@ -289,13 +289,12 @@ func (suite *TendermintTestSuite) TestVerifyHeader() { header, err = path.EndpointA.Counterparty.Chain.IBCClientHeader(path.EndpointA.Counterparty.Chain.LatestCommittedHeader, trustedHeight) suite.Require().NoError(err) - tc.malleate() - - clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(path.EndpointA.ClientID) + suite.Require().True(found) - clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + tc.malleate() - err = clientState.VerifyClientMessage(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, header) + err = lightClientModule.VerifyClientMessage(suite.chainA.GetContext(), path.EndpointA.ClientID, header) if tc.expPass { suite.Require().NoError(err, tc.name) @@ -492,13 +491,15 @@ func (suite *TendermintTestSuite) TestUpdateState() { clientMessage, err = path.EndpointA.Counterparty.Chain.IBCClientHeader(path.EndpointA.Counterparty.Chain.LatestCommittedHeader, trustedHeight) suite.Require().NoError(err) + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(path.EndpointA.ClientID) + suite.Require().True(found) + tc.malleate() - clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) clientStore = suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) if tc.expPass { - consensusHeights = clientState.UpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, clientMessage) + consensusHeights = lightClientModule.UpdateState(suite.chainA.GetContext(), path.EndpointA.ClientID, clientMessage) header := clientMessage.(*ibctm.Header) expConsensusState := &ibctm.ConsensusState{ @@ -514,7 +515,7 @@ func (suite *TendermintTestSuite) TestUpdateState() { } else { suite.Require().Panics(func() { - clientState.UpdateState(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, clientMessage) + lightClientModule.UpdateState(suite.chainA.GetContext(), path.EndpointA.ClientID, clientMessage) }) } @@ -777,15 +778,14 @@ func (suite *TendermintTestSuite) TestCheckForMisbehaviour() { clientMessage, err = path.EndpointA.Counterparty.Chain.IBCClientHeader(path.EndpointA.Counterparty.Chain.LatestCommittedHeader, trustedHeight) suite.Require().NoError(err) - tc.malleate() + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(path.EndpointA.ClientID) + suite.Require().True(found) - clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) - clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + tc.malleate() - foundMisbehaviour := clientState.CheckForMisbehaviour( + foundMisbehaviour := lightClientModule.CheckForMisbehaviour( suite.chainA.GetContext(), - suite.chainA.App.AppCodec(), - clientStore, // pass in clientID prefixed clientStore + path.EndpointA.ClientID, clientMessage, ) @@ -824,12 +824,14 @@ func (suite *TendermintTestSuite) TestUpdateStateOnMisbehaviour() { err := path.EndpointA.CreateClient() suite.Require().NoError(err) + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(path.EndpointA.ClientID) + suite.Require().True(found) + tc.malleate() - clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) clientStore := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - clientState.UpdateStateOnMisbehaviour(suite.chainA.GetContext(), suite.chainA.App.AppCodec(), clientStore, nil) + lightClientModule.UpdateStateOnMisbehaviour(suite.chainA.GetContext(), path.EndpointA.ClientID, nil) if tc.expPass { clientStateBz := clientStore.Get(host.ClientStateKey()) From 006f59e9b399465933ef14aecfe09f58c7524adc Mon Sep 17 00:00:00 2001 From: Charly Date: Wed, 10 Apr 2024 15:33:51 +0200 Subject: [PATCH 11/19] linter --- modules/light-clients/07-tendermint/light_client_module_test.go | 1 - modules/light-clients/07-tendermint/update_test.go | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/light-clients/07-tendermint/light_client_module_test.go b/modules/light-clients/07-tendermint/light_client_module_test.go index d23658cf988..e39b246c3fb 100644 --- a/modules/light-clients/07-tendermint/light_client_module_test.go +++ b/modules/light-clients/07-tendermint/light_client_module_test.go @@ -1026,7 +1026,6 @@ func (suite *TendermintTestSuite) TestGetTimestampAtHeight() { } } - func (suite *TendermintTestSuite) TestRecoverClient() { var ( subjectClientID, substituteClientID string diff --git a/modules/light-clients/07-tendermint/update_test.go b/modules/light-clients/07-tendermint/update_test.go index 97de70c22ab..46a9f2ed8ba 100644 --- a/modules/light-clients/07-tendermint/update_test.go +++ b/modules/light-clients/07-tendermint/update_test.go @@ -825,7 +825,7 @@ func (suite *TendermintTestSuite) TestUpdateStateOnMisbehaviour() { suite.Require().NoError(err) lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(path.EndpointA.ClientID) - suite.Require().True(found) + suite.Require().True(found) tc.malleate() From 844768b70ac6af04373072221a6c6eabe4b09e87 Mon Sep 17 00:00:00 2001 From: Charly Date: Thu, 11 Apr 2024 14:24:47 +0200 Subject: [PATCH 12/19] linter --- modules/light-clients/07-tendermint/upgrade_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/light-clients/07-tendermint/upgrade_test.go b/modules/light-clients/07-tendermint/upgrade_test.go index 27b09a92edd..b08ec6ca404 100644 --- a/modules/light-clients/07-tendermint/upgrade_test.go +++ b/modules/light-clients/07-tendermint/upgrade_test.go @@ -536,7 +536,11 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { suite.Require().NoError(err) upgradedClient = ibctm.NewClientState(newChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, clienttypes.NewHeight(revisionNumber+1, clientState.LatestHeight.GetRevisionHeight()+1), commitmenttypes.GetSDKSpecs(), upgradePath) - upgradedClient = upgradedClient.(*ibctm.ClientState).ZeroCustomFields() + + if upgraded, ok := upgradedClient.(*ibctm.ClientState); ok { + upgradedClient = upgraded.ZeroCustomFields() + } + upgradedClientBz, err = clienttypes.MarshalClientState(suite.chainA.App.AppCodec(), upgradedClient) suite.Require().NoError(err) From 58fd73fec5562cfcb41b55e4978d5ee35c1209a9 Mon Sep 17 00:00:00 2001 From: Charly Date: Thu, 11 Apr 2024 16:02:32 +0200 Subject: [PATCH 13/19] merge artifact --- .../07-tendermint/light_client_module_test.go | 493 ------------------ 1 file changed, 493 deletions(-) diff --git a/modules/light-clients/07-tendermint/light_client_module_test.go b/modules/light-clients/07-tendermint/light_client_module_test.go index 76363beec61..e39b246c3fb 100644 --- a/modules/light-clients/07-tendermint/light_client_module_test.go +++ b/modules/light-clients/07-tendermint/light_client_module_test.go @@ -1280,496 +1280,3 @@ func (suite *TendermintTestSuite) TestVerifyUpgradeAndUpdateState() { }) } } - -func (suite *TendermintTestSuite) TestVerifyMembership() { - var ( - testingpath *ibctesting.Path - delayTimePeriod uint64 - delayBlockPeriod uint64 - err error - proofHeight exported.Height - proof []byte - path exported.Path - value []byte - ) - - testCases := []struct { - name string - malleate func() - expErr error - }{ - { - "successful ClientState verification", - func() { - // default proof construction uses ClientState - }, - nil, - }, - { - "successful ConsensusState verification", func() { - latestHeight := testingpath.EndpointB.GetClientLatestHeight() - - key := host.FullConsensusStateKey(testingpath.EndpointB.ClientID, latestHeight) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = suite.chainB.QueryProof(key) - - consensusState := testingpath.EndpointB.GetConsensusState(latestHeight).(*ibctm.ConsensusState) - value, err = suite.chainB.Codec.MarshalInterface(consensusState) - suite.Require().NoError(err) - }, - nil, - }, - { - "successful Connection verification", func() { - key := host.ConnectionKey(testingpath.EndpointB.ConnectionID) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = suite.chainB.QueryProof(key) - - connection := testingpath.EndpointB.GetConnection() - value, err = suite.chainB.Codec.Marshal(&connection) - suite.Require().NoError(err) - }, - nil, - }, - { - "successful Channel verification", func() { - key := host.ChannelKey(testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = suite.chainB.QueryProof(key) - - channel := testingpath.EndpointB.GetChannel() - value, err = suite.chainB.Codec.Marshal(&channel) - suite.Require().NoError(err) - }, - nil, - }, - { - "successful PacketCommitment verification", func() { - // send from chainB to chainA since we are proving chainB sent a packet - sequence, err := testingpath.EndpointB.SendPacket(clienttypes.NewHeight(1, 100), 0, ibctesting.MockPacketData) - suite.Require().NoError(err) - - // make packet commitment proof - packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequence, testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID, testingpath.EndpointA.ChannelConfig.PortID, testingpath.EndpointA.ChannelID, clienttypes.NewHeight(1, 100), 0) - key := host.PacketCommitmentKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = testingpath.EndpointB.QueryProof(key) - - value = channeltypes.CommitPacket(suite.chainA.App.GetIBCKeeper().Codec(), packet) - }, nil, - }, - { - "successful Acknowledgement verification", func() { - // send from chainA to chainB since we are proving chainB wrote an acknowledgement - sequence, err := testingpath.EndpointA.SendPacket(clienttypes.NewHeight(1, 100), 0, ibctesting.MockPacketData) - suite.Require().NoError(err) - - // write receipt and ack - packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequence, testingpath.EndpointA.ChannelConfig.PortID, testingpath.EndpointA.ChannelID, testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) - err = testingpath.EndpointB.RecvPacket(packet) - suite.Require().NoError(err) - - key := host.PacketAcknowledgementKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = testingpath.EndpointB.QueryProof(key) - - value = channeltypes.CommitAcknowledgement(ibcmock.MockAcknowledgement.Acknowledgement()) - }, - nil, - }, - { - "successful NextSequenceRecv verification", func() { - // send from chainA to chainB since we are proving chainB incremented the sequence recv - - // send packet - sequence, err := testingpath.EndpointA.SendPacket(clienttypes.NewHeight(1, 100), 0, ibctesting.MockPacketData) - suite.Require().NoError(err) - - // next seq recv incremented - packet := channeltypes.NewPacket(ibctesting.MockPacketData, sequence, testingpath.EndpointA.ChannelConfig.PortID, testingpath.EndpointA.ChannelID, testingpath.EndpointB.ChannelConfig.PortID, testingpath.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) - err = testingpath.EndpointB.RecvPacket(packet) - suite.Require().NoError(err) - - key := host.NextSequenceRecvKey(packet.GetSourcePort(), packet.GetSourceChannel()) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = testingpath.EndpointB.QueryProof(key) - - value = sdk.Uint64ToBigEndian(packet.GetSequence() + 1) - }, - nil, - }, - { - "successful verification outside IBC store", func() { - key := transfertypes.PortKey - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(commitmenttypes.NewMerklePrefix([]byte(transfertypes.StoreKey)), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = suite.chainB.QueryProofForStore(transfertypes.StoreKey, key, int64(testingpath.EndpointA.GetClientLatestHeight().GetRevisionHeight())) - - value = []byte(suite.chainB.GetSimApp().TransferKeeper.GetPort(suite.chainB.GetContext())) - suite.Require().NoError(err) - }, - nil, - }, - { - "delay time period has passed", func() { - delayTimePeriod = uint64(time.Second.Nanoseconds()) - }, - nil, - }, - { - "delay time period has not passed", func() { - delayTimePeriod = uint64(time.Hour.Nanoseconds()) - }, - ibctm.ErrDelayPeriodNotPassed, - }, - { - "delay block period has passed", func() { - delayBlockPeriod = 1 - }, - nil, - }, - { - "delay block period has not passed", func() { - delayBlockPeriod = 1000 - }, - ibctm.ErrDelayPeriodNotPassed, - }, - { - "latest client height < height", func() { - proofHeight = testingpath.EndpointA.GetClientLatestHeight().Increment() - }, - ibcerrors.ErrInvalidHeight, - }, - { - "invalid path type", - func() { - path = ibcmock.KeyPath{} - }, - ibcerrors.ErrInvalidType, - }, - { - "failed to unmarshal merkle proof", func() { - proof = invalidProof - }, - commitmenttypes.ErrInvalidProof, - }, - { - "consensus state not found", func() { - proofHeight = clienttypes.ZeroHeight() - }, - clienttypes.ErrConsensusStateNotFound, - }, - { - "proof verification failed", func() { - // change the value being proved - value = []byte("invalid value") - }, - commitmenttypes.ErrInvalidProof, - }, - { - "proof is empty", func() { - // change the inserted proof - proof = []byte{} - }, - commitmenttypes.ErrInvalidMerkleProof, - }, - { - "client state not found for height", - func() { - store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), testingpath.EndpointA.ClientID) - store.Delete(host.ClientStateKey()) - }, - clienttypes.ErrClientNotFound, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() // reset - testingpath = ibctesting.NewPath(suite.chainA, suite.chainB) - testingpath.SetChannelOrdered() - testingpath.Setup() - - // reset time and block delays to 0, malleate may change to a specific non-zero value. - delayTimePeriod = 0 - delayBlockPeriod = 0 - - // create default proof, merklePath, and value which passes - // may be overwritten by malleate() - key := host.FullClientStateKey(testingpath.EndpointB.ClientID) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = suite.chainB.QueryProof(key) - - clientState := testingpath.EndpointB.GetClientState().(*ibctm.ClientState) - value, err = suite.chainB.Codec.MarshalInterface(clientState) - suite.Require().NoError(err) - - tc.malleate() // make changes as necessary - - lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(testingpath.EndpointA.ClientID) - suite.Require().True(found) - - err = lightClientModule.VerifyMembership( - suite.chainA.GetContext(), testingpath.EndpointA.ClientID, proofHeight, delayTimePeriod, delayBlockPeriod, - proof, path, value, - ) - expPass := tc.expErr == nil - if expPass { - suite.Require().NoError(err) - } else { - suite.Require().ErrorContains(err, tc.expErr.Error()) - } - }) - } -} - -func (suite *TendermintTestSuite) TestVerifyNonMembership() { - var ( - testingpath *ibctesting.Path - delayTimePeriod uint64 - delayBlockPeriod uint64 - err error - proofHeight exported.Height - path exported.Path - proof []byte - invalidClientID = "09-tendermint" - invalidConnectionID = "connection-100" - invalidChannelID = "channel-800" - invalidPortID = "invalid-port" - ) - - testCases := []struct { - name string - malleate func() - expErr error - }{ - { - "successful ClientState verification of non membership", - func() { - // default proof construction uses ClientState - }, - nil, - }, - { - "successful ConsensusState verification of non membership", func() { - key := host.FullConsensusStateKey(invalidClientID, testingpath.EndpointB.GetClientLatestHeight()) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = suite.chainB.QueryProof(key) - }, - nil, - }, - { - "successful Connection verification of non membership", func() { - key := host.ConnectionKey(invalidConnectionID) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = suite.chainB.QueryProof(key) - }, - nil, - }, - { - "successful Channel verification of non membership", func() { - key := host.ChannelKey(testingpath.EndpointB.ChannelConfig.PortID, invalidChannelID) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = suite.chainB.QueryProof(key) - }, - nil, - }, - { - "successful PacketCommitment verification of non membership", func() { - // make packet commitment proof - key := host.PacketCommitmentKey(invalidPortID, invalidChannelID, 1) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = testingpath.EndpointB.QueryProof(key) - }, - nil, - }, - { - "successful Acknowledgement verification of non membership", func() { - key := host.PacketAcknowledgementKey(invalidPortID, invalidChannelID, 1) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = testingpath.EndpointB.QueryProof(key) - }, - nil, - }, - { - "successful NextSequenceRecv verification of non membership", func() { - key := host.NextSequenceRecvKey(invalidPortID, invalidChannelID) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = testingpath.EndpointB.QueryProof(key) - }, - nil, - }, - { - "successful verification of non membership outside IBC store", func() { - key := []byte{0x08} - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(commitmenttypes.NewMerklePrefix([]byte(transfertypes.StoreKey)), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = suite.chainB.QueryProofForStore(transfertypes.StoreKey, key, int64(testingpath.EndpointA.GetClientLatestHeight().GetRevisionHeight())) - }, - nil, - }, - { - "delay time period has passed", func() { - delayTimePeriod = uint64(time.Second.Nanoseconds()) - }, - nil, - }, - { - "delay time period has not passed", func() { - delayTimePeriod = uint64(time.Hour.Nanoseconds()) - }, - ibctm.ErrDelayPeriodNotPassed, - }, - { - "delay block period has passed", func() { - delayBlockPeriod = 1 - }, - nil, - }, - { - "delay block period has not passed", func() { - delayBlockPeriod = 1000 - }, - ibctm.ErrDelayPeriodNotPassed, - }, - { - "latest client height < height", func() { - proofHeight = testingpath.EndpointA.GetClientLatestHeight().Increment() - }, - ibcerrors.ErrInvalidHeight, - }, - { - "invalid path type", - func() { - path = ibcmock.KeyPath{} - }, - ibcerrors.ErrInvalidType, - }, - { - "failed to unmarshal merkle proof", func() { - proof = invalidProof - }, - commitmenttypes.ErrInvalidProof, - }, - { - "consensus state not found", func() { - proofHeight = clienttypes.ZeroHeight() - }, - clienttypes.ErrConsensusStateNotFound, - }, - { - "verify non membership fails as path exists", func() { - // change the value being proved - key := host.FullClientStateKey(testingpath.EndpointB.ClientID) - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = suite.chainB.QueryProof(key) - }, - commitmenttypes.ErrInvalidProof, - }, - { - "proof is empty", func() { - // change the inserted proof - proof = []byte{} - }, - commitmenttypes.ErrInvalidMerkleProof, - }, - { - "client state not found for height", - func() { - store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), testingpath.EndpointA.ClientID) - store.Delete(host.ClientStateKey()) - }, - clienttypes.ErrClientNotFound, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - suite.SetupTest() // reset - testingpath = ibctesting.NewPath(suite.chainA, suite.chainB) - testingpath.SetChannelOrdered() - testingpath.Setup() - - // reset time and block delays to 0, malleate may change to a specific non-zero value. - delayTimePeriod = 0 - delayBlockPeriod = 0 - - // create default proof, merklePath, and value which passes - // may be overwritten by malleate() - key := host.FullClientStateKey("invalid-client-id") - - merklePath := commitmenttypes.NewMerklePath(string(key)) - path, err = commitmenttypes.ApplyPrefix(suite.chainB.GetPrefix(), merklePath) - suite.Require().NoError(err) - - proof, proofHeight = suite.chainB.QueryProof(key) - - tc.malleate() // make changes as necessary - - lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(testingpath.EndpointA.ClientID) - suite.Require().True(found) - - err = lightClientModule.VerifyNonMembership( - suite.chainA.GetContext(), testingpath.EndpointA.ClientID, proofHeight, delayTimePeriod, delayBlockPeriod, - proof, path, - ) - - expPass := tc.expErr == nil - if expPass { - suite.Require().NoError(err) - } else { - suite.Require().ErrorContains(err, tc.expErr.Error()) - } - }) - } -} From 1bf064796e4a4facf5b3170e25fed10e92fa6b4a Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Mon, 22 Apr 2024 10:01:01 +0200 Subject: [PATCH 14/19] add single test for panics cases --- .../07-tendermint/light_client_module_test.go | 210 +++++------------- .../07-tendermint/upgrade_test.go | 5 +- 2 files changed, 61 insertions(+), 154 deletions(-) diff --git a/modules/light-clients/07-tendermint/light_client_module_test.go b/modules/light-clients/07-tendermint/light_client_module_test.go index 1cf902177e5..617a1e24c90 100644 --- a/modules/light-clients/07-tendermint/light_client_module_test.go +++ b/modules/light-clients/07-tendermint/light_client_module_test.go @@ -171,176 +171,84 @@ func (suite *TendermintTestSuite) TestVerifyClientMessage() { } } -func (suite *TendermintTestSuite) TestLCMCheckForMisbehaviour() { - var path *ibctesting.Path +func (suite *TendermintTestSuite) TestCheckForMisbehaviourPanicsOnClientStateNotFound() { + suite.SetupTest() - testCases := []struct { - name string - malleate func() - expPanic bool - }{ - { - "success", - func() {}, - false, - }, - { - "failure: client state not found", - func() { - store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - store.Delete(host.ClientStateKey()) - }, - true, - }, - } + path := ibctesting.NewPath(suite.chainA, suite.chainB) + path.SetupClients() - for _, tc := range testCases { - tc := tc - suite.Run(tc.name, func() { - suite.SetupTest() + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(path.EndpointA.ClientID) + suite.Require().True(found) - path = ibctesting.NewPath(suite.chainA, suite.chainB) - path.SetupClients() + // ensure counterparty state is committed + suite.coordinator.CommitBlock(suite.chainB) + trustedHeight := path.EndpointA.GetClientLatestHeight().(clienttypes.Height) + header, err := path.EndpointA.Counterparty.Chain.IBCClientHeader(path.EndpointA.Counterparty.Chain.LatestCommittedHeader, trustedHeight) + suite.Require().NoError(err) - lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(path.EndpointA.ClientID) - suite.Require().True(found) + // delete client state + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + store.Delete(host.ClientStateKey()) - // ensure counterparty state is committed - suite.coordinator.CommitBlock(suite.chainB) - trustedHeight := path.EndpointA.GetClientLatestHeight().(clienttypes.Height) - header, err := path.EndpointA.Counterparty.Chain.IBCClientHeader(path.EndpointA.Counterparty.Chain.LatestCommittedHeader, trustedHeight) - suite.Require().NoError(err) - - tc.malleate() - - if !tc.expPanic { - misbehaviour := lightClientModule.CheckForMisbehaviour(suite.chainA.GetContext(), path.EndpointA.ClientID, header) - suite.Require().False(misbehaviour) - } else { - suite.Require().PanicsWithError(errorsmod.Wrap(clienttypes.ErrClientNotFound, path.EndpointA.ClientID).Error(), - func() { - lightClientModule.CheckForMisbehaviour(suite.chainA.GetContext(), path.EndpointA.ClientID, header) - }) - } - }) - } -} - -func (suite *TendermintTestSuite) TestLCMUpdateStateOnMisbehaviour() { - var path *ibctesting.Path - - testCases := []struct { - name string - malleate func() - expPanic bool - }{ - { - "success", - func() {}, - false, - }, - { - "failure: client state not found", - func() { - store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - store.Delete(host.ClientStateKey()) - }, - true, + suite.Require().PanicsWithError(errorsmod.Wrap(clienttypes.ErrClientNotFound, path.EndpointA.ClientID).Error(), + func() { + lightClientModule.CheckForMisbehaviour(suite.chainA.GetContext(), path.EndpointA.ClientID, header) }, - } - - for _, tc := range testCases { - tc := tc - suite.Run(tc.name, func() { - suite.SetupTest() + ) +} - path = ibctesting.NewPath(suite.chainA, suite.chainB) - path.SetupClients() +func (suite *TendermintTestSuite) TestUpdateStateOnMisbehaviourPanicsOnClientStateNotFound() { + suite.SetupTest() - lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(path.EndpointA.ClientID) - suite.Require().True(found) + path := ibctesting.NewPath(suite.chainA, suite.chainB) + path.SetupClients() - // ensure counterparty state is committed - suite.coordinator.CommitBlock(suite.chainB) - trustedHeight := path.EndpointA.GetClientLatestHeight().(clienttypes.Height) - header, err := path.EndpointA.Counterparty.Chain.IBCClientHeader(path.EndpointA.Counterparty.Chain.LatestCommittedHeader, trustedHeight) - suite.Require().NoError(err) + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(path.EndpointA.ClientID) + suite.Require().True(found) - tc.malleate() + // ensure counterparty state is committed + suite.coordinator.CommitBlock(suite.chainB) + trustedHeight := path.EndpointA.GetClientLatestHeight().(clienttypes.Height) + header, err := path.EndpointA.Counterparty.Chain.IBCClientHeader(path.EndpointA.Counterparty.Chain.LatestCommittedHeader, trustedHeight) + suite.Require().NoError(err) - if !tc.expPanic { - suite.Require().NotPanics( - func() { - lightClientModule.UpdateStateOnMisbehaviour(suite.chainA.GetContext(), path.EndpointA.ClientID, header) - }) - } else { - suite.Require().PanicsWithError( - errorsmod.Wrap(clienttypes.ErrClientNotFound, path.EndpointA.ClientID).Error(), - func() { - lightClientModule.UpdateStateOnMisbehaviour(suite.chainA.GetContext(), path.EndpointA.ClientID, header) - }, - ) - } - }) - } -} + // delete client state + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + store.Delete(host.ClientStateKey()) -func (suite *TendermintTestSuite) TestLCMUpdateState() { - var path *ibctesting.Path - - testCases := []struct { - name string - malleate func() - expPanic bool - }{ - { - "success", - func() {}, - false, - }, - { - "failure: client state not found", - func() { - store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) - store.Delete(host.ClientStateKey()) - }, - true, + suite.Require().PanicsWithError( + errorsmod.Wrap(clienttypes.ErrClientNotFound, path.EndpointA.ClientID).Error(), + func() { + lightClientModule.UpdateStateOnMisbehaviour(suite.chainA.GetContext(), path.EndpointA.ClientID, header) }, - } + ) +} - for _, tc := range testCases { - tc := tc - suite.Run(tc.name, func() { - suite.SetupTest() +func (suite *TendermintTestSuite) TestUpdateStatePanicsOnClientStateNotFound() { + suite.SetupTest() - path = ibctesting.NewPath(suite.chainA, suite.chainB) - path.SetupClients() + path := ibctesting.NewPath(suite.chainA, suite.chainB) + path.SetupClients() - lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(path.EndpointA.ClientID) - suite.Require().True(found) + lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(path.EndpointA.ClientID) + suite.Require().True(found) - // ensure counterparty state is committed - suite.coordinator.CommitBlock(suite.chainB) - trustedHeight := path.EndpointA.GetClientLatestHeight().(clienttypes.Height) - header, err := path.EndpointA.Counterparty.Chain.IBCClientHeader(path.EndpointA.Counterparty.Chain.LatestCommittedHeader, trustedHeight) - suite.Require().NoError(err) + // ensure counterparty state is committed + suite.coordinator.CommitBlock(suite.chainB) + trustedHeight := path.EndpointA.GetClientLatestHeight().(clienttypes.Height) + header, err := path.EndpointA.Counterparty.Chain.IBCClientHeader(path.EndpointA.Counterparty.Chain.LatestCommittedHeader, trustedHeight) + suite.Require().NoError(err) - tc.malleate() + // delete client state + store := suite.chainA.App.GetIBCKeeper().ClientKeeper.ClientStore(suite.chainA.GetContext(), path.EndpointA.ClientID) + store.Delete(host.ClientStateKey()) - if !tc.expPanic { - height := lightClientModule.UpdateState(suite.chainA.GetContext(), path.EndpointA.ClientID, header) - h := header.GetHeight().(clienttypes.Height) - suite.Require().Equal([]exported.Height{h}, height) - } else { - suite.Require().PanicsWithError( - errorsmod.Wrap(clienttypes.ErrClientNotFound, path.EndpointA.ClientID).Error(), - func() { - lightClientModule.UpdateState(suite.chainA.GetContext(), path.EndpointA.ClientID, header) - }, - ) - } - }) - } + suite.Require().PanicsWithError( + errorsmod.Wrap(clienttypes.ErrClientNotFound, path.EndpointA.ClientID).Error(), + func() { + lightClientModule.UpdateState(suite.chainA.GetContext(), path.EndpointA.ClientID, header) + }, + ) } func (suite *TendermintTestSuite) TestVerifyMembership() { diff --git a/modules/light-clients/07-tendermint/upgrade_test.go b/modules/light-clients/07-tendermint/upgrade_test.go index fd47d9360ee..bc7ddcba05e 100644 --- a/modules/light-clients/07-tendermint/upgrade_test.go +++ b/modules/light-clients/07-tendermint/upgrade_test.go @@ -26,9 +26,8 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { ) testCases := []struct { - name string - setup func() - // expPass bool + name string + setup func() expErr error }{ { From d8db359e878f84b796c60b0b6f72c4e99d3d6b1d Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Tue, 23 Apr 2024 18:09:19 +0200 Subject: [PATCH 15/19] a bit of linting a day keeps the doctor away --- .../07-tendermint/light_client_module_test.go | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/modules/light-clients/07-tendermint/light_client_module_test.go b/modules/light-clients/07-tendermint/light_client_module_test.go index d210700afc2..756252b2b13 100644 --- a/modules/light-clients/07-tendermint/light_client_module_test.go +++ b/modules/light-clients/07-tendermint/light_client_module_test.go @@ -153,7 +153,8 @@ func (suite *TendermintTestSuite) TestVerifyClientMessage() { // ensure counterparty state is committed suite.coordinator.CommitBlock(suite.chainB) - trustedHeight := path.EndpointA.GetClientLatestHeight().(clienttypes.Height) + trustedHeight, ok := path.EndpointA.GetClientLatestHeight().(clienttypes.Height) + suite.Require().True(ok) header, err := path.EndpointA.Counterparty.Chain.IBCClientHeader(path.EndpointA.Counterparty.Chain.LatestCommittedHeader, trustedHeight) suite.Require().NoError(err) @@ -182,7 +183,8 @@ func (suite *TendermintTestSuite) TestCheckForMisbehaviourPanicsOnClientStateNot // ensure counterparty state is committed suite.coordinator.CommitBlock(suite.chainB) - trustedHeight := path.EndpointA.GetClientLatestHeight().(clienttypes.Height) + trustedHeight, ok := path.EndpointA.GetClientLatestHeight().(clienttypes.Height) + suite.Require().True(ok) header, err := path.EndpointA.Counterparty.Chain.IBCClientHeader(path.EndpointA.Counterparty.Chain.LatestCommittedHeader, trustedHeight) suite.Require().NoError(err) @@ -208,7 +210,8 @@ func (suite *TendermintTestSuite) TestUpdateStateOnMisbehaviourPanicsOnClientSta // ensure counterparty state is committed suite.coordinator.CommitBlock(suite.chainB) - trustedHeight := path.EndpointA.GetClientLatestHeight().(clienttypes.Height) + trustedHeight, ok := path.EndpointA.GetClientLatestHeight().(clienttypes.Height) + suite.Require().True(ok) header, err := path.EndpointA.Counterparty.Chain.IBCClientHeader(path.EndpointA.Counterparty.Chain.LatestCommittedHeader, trustedHeight) suite.Require().NoError(err) @@ -235,7 +238,8 @@ func (suite *TendermintTestSuite) TestUpdateStatePanicsOnClientStateNotFound() { // ensure counterparty state is committed suite.coordinator.CommitBlock(suite.chainB) - trustedHeight := path.EndpointA.GetClientLatestHeight().(clienttypes.Height) + trustedHeight, ok := path.EndpointA.GetClientLatestHeight().(clienttypes.Height) + suite.Require().True(ok) header, err := path.EndpointA.Counterparty.Chain.IBCClientHeader(path.EndpointA.Counterparty.Chain.LatestCommittedHeader, trustedHeight) suite.Require().NoError(err) @@ -773,7 +777,9 @@ func (suite *TendermintTestSuite) TestStatus() { { "client status without consensus state", func() { - clientState.LatestHeight = clientState.LatestHeight.Increment().(clienttypes.Height) + newLatestHeight, ok := clientState.LatestHeight.Increment().(clienttypes.Height) + suite.Require().True(ok) + clientState.LatestHeight = newLatestHeight path.EndpointA.SetClientState(clientState) }, exported.Expired, @@ -890,7 +896,8 @@ func (suite *TendermintTestSuite) TestGetTimestampAtHeight() { { "failure: consensus state not found for height", func() { - clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) + clientState, ok := path.EndpointA.GetClientState().(*ibctm.ClientState) + suite.Require().True(ok) height = clientState.LatestHeight.Increment() }, clienttypes.ErrConsensusStateNotFound, @@ -905,7 +912,8 @@ func (suite *TendermintTestSuite) TestGetTimestampAtHeight() { path = ibctesting.NewPath(suite.chainA, suite.chainB) path.SetupClients() - clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) + clientState, ok := path.EndpointA.GetClientState().(*ibctm.ClientState) + suite.Require().True(ok) height = clientState.LatestHeight // grab consensusState from store and update with a predefined timestamp From 5108292b56b871dca0c21f7b06cef769d5270457 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Tue, 23 Apr 2024 21:04:51 +0200 Subject: [PATCH 16/19] more linting --- modules/core/02-client/keeper/client_test.go | 4 +++- modules/core/02-client/keeper/keeper_test.go | 4 +++- .../light-clients/07-tendermint/light_client_module_test.go | 4 +++- modules/light-clients/07-tendermint/upgrade_test.go | 3 ++- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/modules/core/02-client/keeper/client_test.go b/modules/core/02-client/keeper/client_test.go index 847bdee6968..51939164938 100644 --- a/modules/core/02-client/keeper/client_test.go +++ b/modules/core/02-client/keeper/client_test.go @@ -508,7 +508,9 @@ func (suite *KeeperTestSuite) TestUpdateClientEventEmission() { path := ibctesting.NewPath(suite.chainA, suite.chainB) path.SetupClients() - trustedHeight := path.EndpointA.GetClientState().(*ibctm.ClientState).LatestHeight + tmClientState, ok := path.EndpointA.GetClientState().(*ibctm.ClientState) + suite.Require().True(ok) + trustedHeight := tmClientState.LatestHeight header, err := path.EndpointA.Counterparty.Chain.IBCClientHeader(path.EndpointA.Counterparty.Chain.LatestCommittedHeader, trustedHeight) suite.Require().NoError(err) diff --git a/modules/core/02-client/keeper/keeper_test.go b/modules/core/02-client/keeper/keeper_test.go index fd120c12b54..9de8700fd7f 100644 --- a/modules/core/02-client/keeper/keeper_test.go +++ b/modules/core/02-client/keeper/keeper_test.go @@ -570,7 +570,9 @@ func (suite *KeeperTestSuite) TestIBCSoftwareUpgrade() { path := ibctesting.NewPath(suite.chainA, suite.chainB) path.SetupClients() - upgradedClientState = path.EndpointA.GetClientState().(*ibctm.ClientState).ZeroCustomFields() + tmClientState, ok := path.EndpointA.GetClientState().(*ibctm.ClientState) + suite.Require().True(ok) + upgradedClientState := tmClientState.ZeroCustomFields() // use height 1000 to distinguish from old plan plan = upgradetypes.Plan{ diff --git a/modules/light-clients/07-tendermint/light_client_module_test.go b/modules/light-clients/07-tendermint/light_client_module_test.go index 756252b2b13..77c446e5a70 100644 --- a/modules/light-clients/07-tendermint/light_client_module_test.go +++ b/modules/light-clients/07-tendermint/light_client_module_test.go @@ -812,7 +812,9 @@ func (suite *TendermintTestSuite) TestStatus() { lightClientModule, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.Route(path.EndpointA.ClientID) suite.Require().True(found) - clientState = path.EndpointA.GetClientState().(*ibctm.ClientState) + var ok bool + clientState, ok = path.EndpointA.GetClientState().(*ibctm.ClientState) + suite.Require().True(ok) tc.malleate() diff --git a/modules/light-clients/07-tendermint/upgrade_test.go b/modules/light-clients/07-tendermint/upgrade_test.go index bc7ddcba05e..e3368e7e481 100644 --- a/modules/light-clients/07-tendermint/upgrade_test.go +++ b/modules/light-clients/07-tendermint/upgrade_test.go @@ -346,7 +346,8 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { upgradedConsensusStateProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) // SetClientState with empty string upgrade path - tmClient, _ := cs.(*ibctm.ClientState) + tmClient, ok := cs.(*ibctm.ClientState) + suite.Require().True(ok) tmClient.UpgradePath = []string{""} suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID, tmClient) }, From e40423796317c4ecb16f11d31588b017dc699b6e Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Wed, 24 Apr 2024 09:40:47 +0200 Subject: [PATCH 17/19] fix test --- modules/core/02-client/keeper/keeper_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/core/02-client/keeper/keeper_test.go b/modules/core/02-client/keeper/keeper_test.go index 9de8700fd7f..739d735ea54 100644 --- a/modules/core/02-client/keeper/keeper_test.go +++ b/modules/core/02-client/keeper/keeper_test.go @@ -572,7 +572,7 @@ func (suite *KeeperTestSuite) TestIBCSoftwareUpgrade() { path.SetupClients() tmClientState, ok := path.EndpointA.GetClientState().(*ibctm.ClientState) suite.Require().True(ok) - upgradedClientState := tmClientState.ZeroCustomFields() + upgradedClientState = tmClientState.ZeroCustomFields() // use height 1000 to distinguish from old plan plan = upgradetypes.Plan{ From dedc682dc6b5c6dc391e32431e0a8b456db91019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Colin=20Axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Tue, 21 May 2024 16:40:08 +0200 Subject: [PATCH 18/19] test: fixup testing logic --- .../07-tendermint/upgrade_test.go | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/modules/light-clients/07-tendermint/upgrade_test.go b/modules/light-clients/07-tendermint/upgrade_test.go index e3368e7e481..5d9502aa281 100644 --- a/modules/light-clients/07-tendermint/upgrade_test.go +++ b/modules/light-clients/07-tendermint/upgrade_test.go @@ -41,7 +41,6 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) //nolint:errcheck // ignore error for test // commit upgrade store changes and update clients - suite.coordinator.CommitBlock(suite.chainB) err := path.EndpointA.UpdateClient() suite.Require().NoError(err) @@ -187,11 +186,20 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { { name: "unsuccessful upgrade: chain-specified parameters do not match committed client", setup: func() { + // upgrade Height is at next block + lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) + + // zero custom fields and store in upgrade store + err := suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.Require().NoError(err) + err = suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + suite.Require().NoError(err) + // change upgradedClient client-specified parameters upgradedClient = ibctm.NewClientState("wrongchainID", ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), upgradePath) suite.coordinator.CommitBlock(suite.chainB) - err := path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() suite.Require().NoError(err) cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID) @@ -207,11 +215,20 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { { name: "unsuccessful upgrade: client-specified parameters do not match previous client", setup: func() { + // upgrade Height is at next block + lastHeight = clienttypes.NewHeight(0, uint64(suite.chainB.GetContext().BlockHeight()+1)) + + // zero custom fields and store in upgrade store + err := suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.Require().NoError(err) + err = suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) + suite.Require().NoError(err) + // change upgradedClient client-specified parameters upgradedClient = ibctm.NewClientState(newChainID, ibctm.DefaultTrustLevel, ubdPeriod, ubdPeriod+trustingPeriod, maxClockDrift+5, lastHeight, commitmenttypes.GetSDKSpecs(), upgradePath) suite.coordinator.CommitBlock(suite.chainB) - err := path.EndpointA.UpdateClient() + err = path.EndpointA.UpdateClient() suite.Require().NoError(err) cs, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID) @@ -332,7 +349,6 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) //nolint:errcheck // ignore error for test // commit upgrade store changes and update clients - suite.coordinator.CommitBlock(suite.chainB) err := path.EndpointA.UpdateClient() suite.Require().NoError(err) @@ -363,7 +379,6 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) //nolint:errcheck // ignore error for test // commit upgrade store changes and update clients - suite.coordinator.CommitBlock(suite.chainB) err := path.EndpointA.UpdateClient() suite.Require().NoError(err) @@ -388,7 +403,6 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) //nolint:errcheck // ignore error for // commit upgrade store changes and update clients - suite.coordinator.CommitBlock(suite.chainB) err := path.EndpointA.UpdateClient() suite.Require().NoError(err) @@ -410,7 +424,6 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) //nolint:errcheck // ignore error for test // commit upgrade store changes and update clients - suite.coordinator.CommitBlock(suite.chainB) err := path.EndpointA.UpdateClient() suite.Require().NoError(err) @@ -438,7 +451,6 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) //nolint:errcheck // ignore error for test // commit upgrade store changes and update clients - suite.coordinator.CommitBlock(suite.chainB) err := path.EndpointA.UpdateClient() suite.Require().NoError(err) @@ -469,7 +481,6 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) //nolint:errcheck // ignore error for testing // commit upgrade store changes and update clients - suite.coordinator.CommitBlock(suite.chainB) err := path.EndpointA.UpdateClient() suite.Require().NoError(err) From fd8ba4eea4aab22f4c5adec5dc039d7f642a59c4 Mon Sep 17 00:00:00 2001 From: Damian Nolan Date: Tue, 21 May 2024 17:00:37 +0200 Subject: [PATCH 19/19] nit: use errors.New() where no args are present for formatting --- modules/light-clients/07-tendermint/upgrade_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/light-clients/07-tendermint/upgrade_test.go b/modules/light-clients/07-tendermint/upgrade_test.go index 5d9502aa281..2f71d0ee29f 100644 --- a/modules/light-clients/07-tendermint/upgrade_test.go +++ b/modules/light-clients/07-tendermint/upgrade_test.go @@ -1,7 +1,7 @@ package tendermint_test import ( - "fmt" + "errors" upgradetypes "cosmossdk.io/x/upgrade/types" @@ -283,7 +283,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { upgradedClientProof = []byte("proof") }, - expErr: fmt.Errorf("could not unmarshal client merkle proof"), + expErr: errors.New("could not unmarshal client merkle proof"), }, { name: "unsuccessful upgrade: consensus state proof unmarshal failed", @@ -297,7 +297,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { upgradedConsensusStateProof = []byte("proof") }, - expErr: fmt.Errorf("could not unmarshal consensus state merkle proof"), + expErr: errors.New("could not unmarshal consensus state merkle proof"), }, { name: "unsuccessful upgrade: client proof verification failed", @@ -367,7 +367,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { tmClient.UpgradePath = []string{""} suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID, tmClient) }, - expErr: fmt.Errorf("client state proof failed"), + expErr: errors.New("client state proof failed"), }, { name: "unsuccessful upgrade: upgraded height is not greater than current height", @@ -391,7 +391,7 @@ func (suite *TendermintTestSuite) TestVerifyUpgrade() { upgradedClientProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedClientKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) upgradedConsensusStateProof, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), tmCs.LatestHeight.GetRevisionHeight()) }, - expErr: fmt.Errorf("consensus state proof failed"), + expErr: errors.New("consensus state proof failed"), }, { name: "unsuccessful upgrade: consensus state for upgrade height cannot be found",