diff --git a/CHANGELOG.md b/CHANGELOG.md index 71a0727f32d..0f26b2a9993 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Improvements * (core/ante) [\#6302](https://github.com/cosmos/ibc-go/pull/6302) Performance: Skip app callbacks during RecvPacket execution in checkTx within the redundant relay ante handler. * (core/ante) [\#6280](https://github.com/cosmos/ibc-go/pull/6280) Performance: Skip redundant proof checking in RecvPacket execution in reCheckTx within the redundant relay ante handler. +* (core/ante) [\#6306](https://github.com/cosmos/ibc-go/pull/6306) Performance: Skip misbehaviour checks in UpdateClient flow and skip signature checks in reCheckTx mode. ### Features diff --git a/modules/core/ante/ante.go b/modules/core/ante/ante.go index 96bf9e5bc8d..2736e319168 100644 --- a/modules/core/ante/ante.go +++ b/modules/core/ante/ante.go @@ -7,7 +7,10 @@ import ( clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/v8/modules/core/exported" "github.com/cosmos/ibc-go/v8/modules/core/keeper" + solomachine "github.com/cosmos/ibc-go/v8/modules/light-clients/06-solomachine" + tendermint "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" ) type RedundantRelayDecorator struct { @@ -84,8 +87,7 @@ func (rrd RedundantRelayDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula packetMsgs++ case *clienttypes.MsgUpdateClient: - _, err := rrd.k.UpdateClient(ctx, msg) - if err != nil { + if err := rrd.updateClientCheckTx(ctx, msg); err != nil { return ctx, err } @@ -105,6 +107,46 @@ func (rrd RedundantRelayDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula return next(ctx, tx, simulate) } +// updateClientCheckTx runs a subset of ibc client update logic to be used specifically within the RedundantRelayDecorator AnteHandler. +// The following function performs ibc client message verification for CheckTx only and state updates in both CheckTx and ReCheckTx. +// Note that misbehaviour checks are omitted. +func (rrd RedundantRelayDecorator) updateClientCheckTx(ctx sdk.Context, msg *clienttypes.MsgUpdateClient) error { + clientMsg, err := clienttypes.UnpackClientMessage(msg.ClientMessage) + if err != nil { + return err + } + + clientState, found := rrd.k.ClientKeeper.GetClientState(ctx, msg.ClientId) + if !found { + return errorsmod.Wrapf(clienttypes.ErrClientNotFound, msg.ClientId) + } + + if status := rrd.k.ClientKeeper.GetClientStatus(ctx, clientState, msg.ClientId); status != exported.Active { + return errorsmod.Wrapf(clienttypes.ErrClientNotActive, "cannot update client (%s) with status %s", msg.ClientId, status) + } + + clientStore := rrd.k.ClientKeeper.ClientStore(ctx, msg.ClientId) + + if !ctx.IsReCheckTx() { + if err := clientState.VerifyClientMessage(ctx, rrd.k.Codec(), clientStore, clientMsg); err != nil { + return err + } + } + + // NOTE: the following avoids panics in ante handler client updates for ibc-go v8.3.x + // without state machine breaking changes within light client modules. + switch clientMsg.(type) { + case *solomachine.Misbehaviour: + // ignore solomachine misbehaviour for update state in ante + case *tendermint.Misbehaviour: + // ignore tendermint misbehaviour for update state in ante + default: + heights := clientState.UpdateState(ctx, rrd.k.Codec(), clientStore, clientMsg) + ctx.Logger().With("module", "x/"+exported.ModuleName).Debug("ante ibc client update", "consensusHeights", heights) + } + return nil +} + // recvPacketCheckTx runs a subset of ibc recv packet logic to be used specifically within the RedundantRelayDecorator AnteHandler. // It only performs core IBC receiving logic and skips any application logic. func (rrd RedundantRelayDecorator) recvPacketCheckTx(ctx sdk.Context, msg *channeltypes.MsgRecvPacket) (*channeltypes.MsgRecvPacketResponse, error) { diff --git a/modules/core/ante/ante_test.go b/modules/core/ante/ante_test.go index 3de738dcc89..d89c95fde35 100644 --- a/modules/core/ante/ante_test.go +++ b/modules/core/ante/ante_test.go @@ -3,10 +3,12 @@ package ante_test import ( "fmt" "testing" + "time" "github.com/stretchr/testify/require" testifysuite "github.com/stretchr/testify/suite" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" @@ -14,6 +16,7 @@ import ( host "github.com/cosmos/ibc-go/v8/modules/core/24-host" "github.com/cosmos/ibc-go/v8/modules/core/ante" "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" ) @@ -357,6 +360,64 @@ func (suite *AnteTestSuite) TestAnteDecoratorCheckTx() { }, true, }, + { + "success on new UpdateClient messages: solomachine misbehaviour", + func(suite *AnteTestSuite) []sdk.Msg { + solomachine := ibctesting.NewSolomachine(suite.T(), suite.chainB.Codec, "06-solomachine-0", "testing", 1) + suite.chainB.GetSimApp().GetIBCKeeper().ClientKeeper.SetClientState(suite.chainB.GetContext(), solomachine.ClientID, solomachine.ClientState()) + + msgUpdateClient, err := clienttypes.NewMsgUpdateClient(solomachine.ClientID, solomachine.CreateMisbehaviour(), suite.chainB.SenderAccount.GetAddress().String()) + suite.Require().NoError(err) + + msgs := []sdk.Msg{msgUpdateClient} + + return msgs + }, + true, + }, + { + "success on new UpdateClient messages: solomachine multisig misbehaviour", + func(suite *AnteTestSuite) []sdk.Msg { + solomachine := ibctesting.NewSolomachine(suite.T(), suite.chainA.Codec, "06-solomachine-0", "testing", 4) + suite.chainB.GetSimApp().GetIBCKeeper().ClientKeeper.SetClientState(suite.chainB.GetContext(), solomachine.ClientID, solomachine.ClientState()) + + msgUpdateClient, err := clienttypes.NewMsgUpdateClient(solomachine.ClientID, solomachine.CreateMisbehaviour(), suite.chainB.SenderAccount.GetAddress().String()) + suite.Require().NoError(err) + + msgs := []sdk.Msg{msgUpdateClient} + + return msgs + }, + true, + }, + { + "success on new UpdateClient messages: tendermint misbehaviour", + func(suite *AnteTestSuite) []sdk.Msg { + trustedHeight := suite.path.EndpointB.GetClientState().GetLatestHeight().(clienttypes.Height) + + trustedVals, found := suite.chainA.GetValsAtHeight(int64(trustedHeight.RevisionHeight) + 1) + suite.Require().True(found) + + err := suite.path.EndpointB.UpdateClient() + suite.Require().NoError(err) + + height := suite.path.EndpointB.GetClientState().GetLatestHeight().(clienttypes.Height) + + // construct valid fork misbehaviour: two headers at the same height with different time + misbehaviour := &ibctm.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainA.CurrentHeader.Time.Add(time.Minute), suite.chainA.Vals, suite.chainA.NextVals, trustedVals, suite.chainA.Signers), + Header2: suite.chainA.CreateTMClientHeader(suite.chainA.ChainID, int64(height.RevisionHeight), trustedHeight, suite.chainA.CurrentHeader.Time, suite.chainA.Vals, suite.chainA.NextVals, trustedVals, suite.chainA.Signers), + } + + msgUpdateClient, err := clienttypes.NewMsgUpdateClient(suite.path.EndpointB.ClientID, misbehaviour, suite.chainB.SenderAccount.GetAddress().String()) + suite.Require().NoError(err) + + msgs := []sdk.Msg{msgUpdateClient} + + return msgs + }, + true, + }, { "no success on one redundant RecvPacket message", func(suite *AnteTestSuite) []sdk.Msg { @@ -402,6 +463,39 @@ func (suite *AnteTestSuite) TestAnteDecoratorCheckTx() { }, false, }, + { + "no success on one new UpdateClient message: invalid client identifier", + func(suite *AnteTestSuite) []sdk.Msg { + clientMsg, err := codectypes.NewAnyWithValue(&ibctm.Header{}) + suite.Require().NoError(err) + + msgs := []sdk.Msg{&clienttypes.MsgUpdateClient{ClientId: ibctesting.InvalidID, ClientMessage: clientMsg}} + return msgs + }, + false, + }, + { + "no success on one new UpdateClient message: client module not found", + func(suite *AnteTestSuite) []sdk.Msg { + clientMsg, err := codectypes.NewAnyWithValue(&ibctm.Header{}) + suite.Require().NoError(err) + + msgs := []sdk.Msg{&clienttypes.MsgUpdateClient{ClientId: clienttypes.FormatClientIdentifier("08-wasm", 1), ClientMessage: clientMsg}} + return msgs + }, + false, + }, + { + "no success on one new UpdateClient message: no consensus state for trusted height", + func(suite *AnteTestSuite) []sdk.Msg { + clientMsg, err := codectypes.NewAnyWithValue(&ibctm.Header{TrustedHeight: clienttypes.NewHeight(1, 10000)}) + suite.Require().NoError(err) + + msgs := []sdk.Msg{&clienttypes.MsgUpdateClient{ClientId: suite.path.EndpointA.ClientID, ClientMessage: clientMsg}} + return msgs + }, + false, + }, { "no success on three new UpdateClient messages and three redundant messages of each type", func(suite *AnteTestSuite) []sdk.Msg {