diff --git a/CHANGELOG.md b/CHANGELOG.md index 9845d1492ee..28c6957b46e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -671,6 +671,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Client Breaking Changes * (02-client/cli) [\#196](https://github.com/cosmos/ibc-go/pull/196) Rename `node-state` cli command to `self-consensus-state`. +* (02-client/cli) [\#897](https://github.com/cosmos/ibc-go/pull/897) Remove `GetClientID()` from `Misbehaviour` interface. Submit client misbehaviour cli command requires an explicit client id now. ## IBC in the Cosmos SDK Repository diff --git a/modules/core/02-client/client/cli/tx.go b/modules/core/02-client/client/cli/tx.go index 794b6f5ca5e..65703fb1f4c 100644 --- a/modules/core/02-client/client/cli/tx.go +++ b/modules/core/02-client/client/cli/tx.go @@ -12,12 +12,12 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" govcli "github.com/cosmos/cosmos-sdk/x/gov/client/cli" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" "github.com/spf13/cobra" - "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v5/modules/core/exported" + "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v3/modules/core/exported" ) // NewCreateClientCmd defines the command to create a new IBC light client. @@ -86,10 +86,10 @@ func NewCreateClientCmd() *cobra.Command { // NewUpdateClientCmd defines the command to update an IBC client. func NewUpdateClientCmd() *cobra.Command { return &cobra.Command{ - Use: "update [client-id] [path/to/client_msg.json]", - Short: "update existing client with a client message", - Long: "update existing client with a client message, for example a header, misbehaviour or batch update", - Example: fmt.Sprintf("%s tx ibc %s update [client-id] [path/to/client_msg.json] --from node0 --home ../node0/cli --chain-id $CID", version.AppName, types.SubModuleName), + Use: "update [client-id] [path/to/header.json]", + Short: "update existing client with a header", + Long: "update existing client with a header", + Example: fmt.Sprintf("%s tx ibc %s update [client-id] [path/to/header.json] --from node0 --home ../node0/cli --chain-id $CID", version.AppName, types.SubModuleName), Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientTxContext(cmd) @@ -100,22 +100,22 @@ func NewUpdateClientCmd() *cobra.Command { cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) - var clientMsg exported.ClientMessage - clientMsgContentOrFileName := args[1] - if err := cdc.UnmarshalInterfaceJSON([]byte(clientMsgContentOrFileName), &clientMsg); err != nil { + var header exported.Header + headerContentOrFileName := args[1] + if err := cdc.UnmarshalInterfaceJSON([]byte(headerContentOrFileName), &header); err != nil { // check for file path if JSON input is not provided - contents, err := ioutil.ReadFile(clientMsgContentOrFileName) + contents, err := ioutil.ReadFile(headerContentOrFileName) if err != nil { return fmt.Errorf("neither JSON input nor path to .json file for header were provided: %w", err) } - if err := cdc.UnmarshalInterfaceJSON(contents, &clientMsg); err != nil { + if err := cdc.UnmarshalInterfaceJSON(contents, &header); err != nil { return fmt.Errorf("error unmarshalling header file: %w", err) } } - msg, err := types.NewMsgUpdateClient(clientID, clientMsg, clientCtx.GetFromAddress().String()) + msg, err := types.NewMsgUpdateClient(clientID, header, clientCtx.GetFromAddress().String()) if err != nil { return err } @@ -127,8 +127,6 @@ func NewUpdateClientCmd() *cobra.Command { // NewSubmitMisbehaviourCmd defines the command to submit a misbehaviour to prevent // future updates. -// Deprecated: NewSubmitMisbehaviourCmd is deprecated and will be removed in a future release. -// Please use NewUpdateClientCmd instead. func NewSubmitMisbehaviourCmd() *cobra.Command { return &cobra.Command{ Use: "misbehaviour [clientID] [path/to/misbehaviour.json]", @@ -143,7 +141,7 @@ func NewSubmitMisbehaviourCmd() *cobra.Command { } cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) - var misbehaviour exported.ClientMessage + var misbehaviour exported.Misbehaviour clientID := args[0] misbehaviourContentOrFileName := args[1] if err := cdc.UnmarshalInterfaceJSON([]byte(misbehaviourContentOrFileName), &misbehaviour); err != nil { @@ -251,12 +249,12 @@ func NewCmdSubmitUpdateClientProposal() *cobra.Command { return err } - title, err := cmd.Flags().GetString(govcli.FlagTitle) //nolint:staticcheck // need this till full govv1 conversion. + title, err := cmd.Flags().GetString(govcli.FlagTitle) if err != nil { return err } - description, err := cmd.Flags().GetString(govcli.FlagDescription) //nolint:staticcheck // need this till full govv1 conversion. + description, err := cmd.Flags().GetString(govcli.FlagDescription) if err != nil { return err } @@ -290,8 +288,8 @@ func NewCmdSubmitUpdateClientProposal() *cobra.Command { }, } - cmd.Flags().String(govcli.FlagTitle, "", "title of proposal") //nolint:staticcheck // need this till full govv1 conversion. - cmd.Flags().String(govcli.FlagDescription, "", "description of proposal") //nolint:staticcheck // need this till full govv1 conversion. + cmd.Flags().String(govcli.FlagTitle, "", "title of proposal") + cmd.Flags().String(govcli.FlagDescription, "", "description of proposal") cmd.Flags().String(govcli.FlagDeposit, "", "deposit of proposal") return cmd @@ -322,12 +320,12 @@ func NewCmdSubmitUpgradeProposal() *cobra.Command { } cdc := codec.NewProtoCodec(clientCtx.InterfaceRegistry) - title, err := cmd.Flags().GetString(govcli.FlagTitle) //nolint:staticcheck // need this till full govv1 conversion. + title, err := cmd.Flags().GetString(govcli.FlagTitle) if err != nil { return err } - description, err := cmd.Flags().GetString(govcli.FlagDescription) //nolint:staticcheck // need this till full govv1 conversion. + description, err := cmd.Flags().GetString(govcli.FlagDescription) if err != nil { return err } @@ -389,8 +387,8 @@ func NewCmdSubmitUpgradeProposal() *cobra.Command { }, } - cmd.Flags().String(govcli.FlagTitle, "", "title of proposal") //nolint:staticcheck // need this till full govv1 conversion. - cmd.Flags().String(govcli.FlagDescription, "", "description of proposal") //nolint:staticcheck // need this till full govv1 conversion. + cmd.Flags().String(govcli.FlagTitle, "", "title of proposal") + cmd.Flags().String(govcli.FlagDescription, "", "description of proposal") cmd.Flags().String(govcli.FlagDeposit, "", "deposit of proposal") return cmd diff --git a/modules/core/02-client/keeper/client.go b/modules/core/02-client/keeper/client.go index 21ffba25e90..600519bf5f4 100644 --- a/modules/core/02-client/keeper/client.go +++ b/modules/core/02-client/keeper/client.go @@ -1,13 +1,15 @@ package keeper import ( - metrics "github.com/armon/go-metrics" + "encoding/hex" + + "github.com/armon/go-metrics" "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" - "github.com/cosmos/ibc-go/v5/modules/core/exported" + "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v3/modules/core/exported" ) // CreateClient creates a new client state and populates it with a given consensus @@ -25,6 +27,7 @@ func (k Keeper) CreateClient( clientID := k.GenerateClientIdentifier(ctx, clientState.ClientType()) + k.SetClientState(ctx, clientID, clientState) k.Logger(ctx).Info("client created at height", "client-id", clientID, "height", clientState.GetLatestHeight().String()) // verifies initial consensus state against client state and initializes client store with any client-specific metadata @@ -33,16 +36,20 @@ func (k Keeper) CreateClient( return "", err } - k.SetClientState(ctx, clientID, clientState) - k.SetClientConsensusState(ctx, clientID, clientState.GetLatestHeight(), consensusState) + // check if consensus state is nil in case the created client is Localhost + if consensusState != nil { + k.SetClientConsensusState(ctx, clientID, clientState.GetLatestHeight(), consensusState) + } k.Logger(ctx).Info("client created at height", "client-id", clientID, "height", clientState.GetLatestHeight().String()) - defer telemetry.IncrCounterWithLabels( - []string{"ibc", "client", "create"}, - 1, - []metrics.Label{telemetry.NewLabel(types.LabelClientType, clientState.ClientType())}, - ) + defer func() { + telemetry.IncrCounterWithLabels( + []string{"ibc", "client", "create"}, + 1, + []metrics.Label{telemetry.NewLabel(types.LabelClientType, clientState.ClientType())}, + ) + }() EmitCreateClientEvent(ctx, clientID, clientState) @@ -50,7 +57,7 @@ func (k Keeper) CreateClient( } // UpdateClient updates the consensus state and the state root from a provided header. -func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, clientMsg exported.ClientMessage) error { +func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.Header) error { clientState, found := k.GetClientState(ctx, clientID) if !found { return sdkerrors.Wrapf(types.ErrClientNotFound, "cannot update client with ID %s", clientID) @@ -62,47 +69,76 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, clientMsg exporte return sdkerrors.Wrapf(types.ErrClientNotActive, "cannot update client (%s) with status %s", clientID, status) } - if err := clientState.VerifyClientMessage(ctx, k.cdc, clientStore, clientMsg); err != nil { - return err + // Any writes made in CheckHeaderAndUpdateState are persisted on both valid updates and misbehaviour updates. + // Light client implementations are responsible for writing the correct metadata (if any) in either case. + newClientState, newConsensusState, err := clientState.CheckHeaderAndUpdateState(ctx, k.cdc, clientStore, header) + if err != nil { + return sdkerrors.Wrapf(err, "cannot update client with ID %s", clientID) } - foundMisbehaviour := clientState.CheckForMisbehaviour(ctx, k.cdc, clientStore, clientMsg) - if foundMisbehaviour { - clientState.UpdateStateOnMisbehaviour(ctx, k.cdc, clientStore, clientMsg) - - k.Logger(ctx).Info("client frozen due to misbehaviour", "client-id", clientID) - - defer telemetry.IncrCounterWithLabels( - []string{"ibc", "client", "misbehaviour"}, - 1, - []metrics.Label{ - telemetry.NewLabel(types.LabelClientType, clientState.ClientType()), - telemetry.NewLabel(types.LabelClientID, clientID), - telemetry.NewLabel(types.LabelMsgType, "update"), - }, - ) - - EmitSubmitMisbehaviourEvent(ctx, clientID, clientState) + // emit the full header in events + var ( + headerStr string + consensusHeight exported.Height + ) + if header != nil { + // Marshal the Header as an Any and encode the resulting bytes to hex. + // This prevents the event value from containing invalid UTF-8 characters + // which may cause data to be lost when JSON encoding/decoding. + headerStr = hex.EncodeToString(types.MustMarshalHeader(k.cdc, header)) + // set default consensus height with header height + consensusHeight = header.GetHeight() - return nil } - consensusHeights := clientState.UpdateState(ctx, k.cdc, clientStore, clientMsg) + // set new client state regardless of if update is valid update or misbehaviour + k.SetClientState(ctx, clientID, newClientState) + // If client state is not frozen after clientState CheckHeaderAndUpdateState, + // then update was valid. Write the update state changes, and set new consensus state. + // Else the update was proof of misbehaviour and we must emit appropriate misbehaviour events. + if status := newClientState.Status(ctx, clientStore, k.cdc); status != exported.Frozen { + // if update is not misbehaviour then update the consensus state + // we don't set consensus state for localhost client + if header != nil && clientID != exported.Localhost { + k.SetClientConsensusState(ctx, clientID, header.GetHeight(), newConsensusState) + } else { + consensusHeight = types.GetSelfHeight(ctx) + } + + k.Logger(ctx).Info("client state updated", "client-id", clientID, "height", consensusHeight.String()) + + defer func() { + telemetry.IncrCounterWithLabels( + []string{"ibc", "client", "update"}, + 1, + []metrics.Label{ + telemetry.NewLabel(types.LabelClientType, clientState.ClientType()), + telemetry.NewLabel(types.LabelClientID, clientID), + telemetry.NewLabel(types.LabelUpdateType, "msg"), + }, + ) + }() + + // emitting events in the keeper emits for both begin block and handler client updates + EmitUpdateClientEvent(ctx, clientID, newClientState, consensusHeight, headerStr) + } else { - k.Logger(ctx).Info("client state updated", "client-id", clientID, "heights", consensusHeights) - - defer telemetry.IncrCounterWithLabels( - []string{"ibc", "client", "update"}, - 1, - []metrics.Label{ - telemetry.NewLabel(types.LabelClientType, clientState.ClientType()), - telemetry.NewLabel(types.LabelClientID, clientID), - telemetry.NewLabel(types.LabelUpdateType, "msg"), - }, - ) + k.Logger(ctx).Info("client frozen due to misbehaviour", "client-id", clientID) - // emitting events in the keeper emits for both begin block and handler client updates - EmitUpdateClientEvent(ctx, clientID, clientState.ClientType(), consensusHeights, k.cdc, clientMsg) + defer func() { + telemetry.IncrCounterWithLabels( + []string{"ibc", "client", "misbehaviour"}, + 1, + []metrics.Label{ + telemetry.NewLabel(types.LabelClientType, clientState.ClientType()), + telemetry.NewLabel(types.LabelClientID, clientID), + telemetry.NewLabel(types.LabelMsgType, "update"), + }, + ) + }() + + EmitSubmitMisbehaviourEventOnUpdate(ctx, clientID, newClientState, consensusHeight, headerStr) + } return nil } @@ -110,8 +146,7 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, clientMsg exporte // UpgradeClient upgrades the client to a new client state if this new client was committed to // by the old client at the specified upgrade height func (k Keeper) UpgradeClient(ctx sdk.Context, clientID string, upgradedClient exported.ClientState, upgradedConsState exported.ConsensusState, - proofUpgradeClient, proofUpgradeConsState []byte, -) error { + proofUpgradeClient, proofUpgradeConsState []byte) error { clientState, found := k.GetClientState(ctx, clientID) if !found { return sdkerrors.Wrapf(types.ErrClientNotFound, "cannot update client with ID %s", clientID) @@ -123,24 +158,72 @@ func (k Keeper) UpgradeClient(ctx sdk.Context, clientID string, upgradedClient e return sdkerrors.Wrapf(types.ErrClientNotActive, "cannot upgrade client (%s) with status %s", clientID, status) } - if err := clientState.VerifyUpgradeAndUpdateState(ctx, k.cdc, clientStore, - upgradedClient, upgradedConsState, proofUpgradeClient, proofUpgradeConsState, - ); err != nil { + updatedClientState, updatedConsState, err := clientState.VerifyUpgradeAndUpdateState(ctx, k.cdc, clientStore, + upgradedClient, upgradedConsState, proofUpgradeClient, proofUpgradeConsState) + if err != nil { return sdkerrors.Wrapf(err, "cannot upgrade client with ID %s", clientID) } - k.Logger(ctx).Info("client state upgraded", "client-id", clientID, "height", upgradedClient.GetLatestHeight().String()) + k.SetClientState(ctx, clientID, updatedClientState) + k.SetClientConsensusState(ctx, clientID, updatedClientState.GetLatestHeight(), updatedConsState) - defer telemetry.IncrCounterWithLabels( - []string{"ibc", "client", "upgrade"}, - 1, - []metrics.Label{ - telemetry.NewLabel(types.LabelClientType, upgradedClient.ClientType()), - telemetry.NewLabel(types.LabelClientID, clientID), - }, - ) + k.Logger(ctx).Info("client state upgraded", "client-id", clientID, "height", updatedClientState.GetLatestHeight().String()) + + defer func() { + telemetry.IncrCounterWithLabels( + []string{"ibc", "client", "upgrade"}, + 1, + []metrics.Label{ + telemetry.NewLabel(types.LabelClientType, updatedClientState.ClientType()), + telemetry.NewLabel(types.LabelClientID, clientID), + }, + ) + }() + + // emitting events in the keeper emits for client upgrades + EmitUpgradeClientEvent(ctx, clientID, updatedClientState) + + return nil +} + +// CheckMisbehaviourAndUpdateState checks for client misbehaviour and freezes the +// client if so. +func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, clientID string, misbehaviour exported.Misbehaviour) error { + clientState, found := k.GetClientState(ctx, clientID) + if !found { + return sdkerrors.Wrapf(types.ErrClientNotFound, "cannot check misbehaviour for client with ID %s", clientID) + } + + clientStore := k.ClientStore(ctx, clientID) + + if status := clientState.Status(ctx, clientStore, k.cdc); status != exported.Active { + return sdkerrors.Wrapf(types.ErrClientNotActive, "cannot process misbehaviour for client (%s) with status %s", clientID, status) + } + + if err := misbehaviour.ValidateBasic(); err != nil { + return err + } + + clientState, err := clientState.CheckMisbehaviourAndUpdateState(ctx, k.cdc, clientStore, misbehaviour) + if err != nil { + return err + } + + k.SetClientState(ctx, clientID, clientState) + k.Logger(ctx).Info("client frozen due to misbehaviour", "client-id", clientID) + + defer func() { + telemetry.IncrCounterWithLabels( + []string{"ibc", "client", "misbehaviour"}, + 1, + []metrics.Label{ + telemetry.NewLabel(types.LabelClientType, misbehaviour.ClientType()), + telemetry.NewLabel(types.LabelClientID, clientID), + }, + ) + }() - EmitUpgradeClientEvent(ctx, clientID, upgradedClient) + EmitSubmitMisbehaviourEvent(ctx, clientID, clientState) return nil } diff --git a/modules/core/02-client/keeper/client_test.go b/modules/core/02-client/keeper/client_test.go index 22b3c3556fd..dad38787c47 100644 --- a/modules/core/02-client/keeper/client_test.go +++ b/modules/core/02-client/keeper/client_test.go @@ -6,14 +6,16 @@ import ( "time" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" - - "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" - clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" - commitmenttypes "github.com/cosmos/ibc-go/v5/modules/core/23-commitment/types" - "github.com/cosmos/ibc-go/v5/modules/core/exported" - solomachinetypes "github.com/cosmos/ibc-go/v5/modules/light-clients/06-solomachine" - ibctm "github.com/cosmos/ibc-go/v5/modules/light-clients/07-tendermint" - ibctesting "github.com/cosmos/ibc-go/v5/testing" + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v3/modules/core/exported" + ibctmtypes "github.com/cosmos/ibc-go/v3/modules/light-clients/07-tendermint/types" + localhosttypes "github.com/cosmos/ibc-go/v3/modules/light-clients/09-localhost/types" + ibctesting "github.com/cosmos/ibc-go/v3/testing" + ibctestingmock "github.com/cosmos/ibc-go/v3/testing/mock" ) func (suite *KeeperTestSuite) TestCreateClient() { @@ -22,8 +24,8 @@ func (suite *KeeperTestSuite) TestCreateClient() { clientState exported.ClientState expPass bool }{ - {"success", ibctm.NewClientState(testChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), true}, - {"client type not supported", solomachinetypes.NewClientState(0, &solomachinetypes.ConsensusState{suite.solomachine.ConsensusState().PublicKey, suite.solomachine.Diversifier, suite.solomachine.Time}, false), false}, + {"success", ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false), true}, + {"client type not supported", localhosttypes.NewClientState(testChainID, clienttypes.NewHeight(0, 1)), false}, } for i, tc := range cases { @@ -42,21 +44,21 @@ func (suite *KeeperTestSuite) TestCreateClient() { func (suite *KeeperTestSuite) TestUpdateClientTendermint() { var ( path *ibctesting.Path - updateHeader *ibctm.Header + updateHeader *ibctmtypes.Header ) // Must create header creation functions since suite.header gets recreated on each test case - createFutureUpdateFn := func(trustedHeight clienttypes.Height) *ibctm.Header { + createFutureUpdateFn := func(trustedHeight clienttypes.Height) *ibctmtypes.Header { header, err := suite.chainA.ConstructUpdateTMClientHeaderWithTrustedHeight(path.EndpointB.Chain, path.EndpointA.ClientID, trustedHeight) suite.Require().NoError(err) return header } - createPastUpdateFn := func(fillHeight, trustedHeight clienttypes.Height) *ibctm.Header { + createPastUpdateFn := func(fillHeight, trustedHeight clienttypes.Height) *ibctmtypes.Header { consState, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientConsensusState(suite.chainA.GetContext(), path.EndpointA.ClientID, trustedHeight) suite.Require().True(found) - return suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(fillHeight.RevisionHeight), trustedHeight, consState.(*ibctm.ConsensusState).Timestamp.Add(time.Second*5), - suite.chainB.Vals, suite.chainB.Vals, suite.chainB.Vals, suite.chainB.Signers) + return suite.chainB.CreateTMClientHeader(suite.chainB.ChainID, int64(fillHeight.RevisionHeight), trustedHeight, consState.(*ibctmtypes.ConsensusState).Timestamp.Add(time.Second*5), + suite.chainB.Vals, suite.chainB.Vals, suite.chainB.Signers) } cases := []struct { @@ -66,12 +68,11 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { expFreeze bool }{ {"valid update", func() { - clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) + clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) trustHeight := clientState.GetLatestHeight().(types.Height) // store intermediate consensus state to check that trustedHeight does not need to be highest consensus state before header height - err := path.EndpointA.UpdateClient() - suite.Require().NoError(err) + path.EndpointA.UpdateClient() updateHeader = createFutureUpdateFn(trustHeight) }, true, false}, @@ -86,8 +87,7 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { suite.coordinator.CommitBlock(suite.chainB) // this height is not filled in yet suite.coordinator.CommitBlock(suite.chainB) // this height is filled in by the update below - err := path.EndpointA.UpdateClient() - suite.Require().NoError(err) + path.EndpointA.UpdateClient() // ensure fill height not set _, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientConsensusState(suite.chainA.GetContext(), path.EndpointA.ClientID, fillHeight) @@ -100,24 +100,24 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { {"valid duplicate update", func() { clientID := path.EndpointA.ClientID - height1 := types.NewHeight(1, 1) + height1 := types.NewHeight(0, 1) // store previous consensus state - prevConsState := &ibctm.ConsensusState{ + prevConsState := &ibctmtypes.ConsensusState{ Timestamp: suite.past, NextValidatorsHash: suite.chainB.Vals.Hash(), } suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), clientID, height1, prevConsState) - height5 := types.NewHeight(1, 5) + height5 := types.NewHeight(0, 5) // store next consensus state to check that trustedHeight does not need to be hightest consensus state before header height - nextConsState := &ibctm.ConsensusState{ + nextConsState := &ibctmtypes.ConsensusState{ Timestamp: suite.past.Add(time.Minute), NextValidatorsHash: suite.chainB.Vals.Hash(), } suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), clientID, height5, nextConsState) - height3 := types.NewHeight(1, 3) + height3 := types.NewHeight(0, 3) // updateHeader will fill in consensus state between prevConsState and suite.consState // clientState should not be updated updateHeader = createPastUpdateFn(height3, height1) @@ -127,23 +127,23 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { {"misbehaviour detection: conflicting header", func() { clientID := path.EndpointA.ClientID - height1 := types.NewHeight(1, 1) + height1 := types.NewHeight(0, 1) // store previous consensus state - prevConsState := &ibctm.ConsensusState{ + prevConsState := &ibctmtypes.ConsensusState{ Timestamp: suite.past, NextValidatorsHash: suite.chainB.Vals.Hash(), } suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), clientID, height1, prevConsState) - height5 := types.NewHeight(1, 5) + height5 := types.NewHeight(0, 5) // store next consensus state to check that trustedHeight does not need to be hightest consensus state before header height - nextConsState := &ibctm.ConsensusState{ + nextConsState := &ibctmtypes.ConsensusState{ Timestamp: suite.past.Add(time.Minute), NextValidatorsHash: suite.chainB.Vals.Hash(), } suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), clientID, height5, nextConsState) - height3 := types.NewHeight(1, 3) + height3 := types.NewHeight(0, 3) // updateHeader will fill in consensus state between prevConsState and suite.consState // clientState should not be updated updateHeader = createPastUpdateFn(height3, height1) @@ -153,21 +153,21 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), clientID, updateHeader.GetHeight(), conflictConsState) }, true, true}, {"misbehaviour detection: monotonic time violation", func() { - clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) + clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) clientID := path.EndpointA.ClientID trustedHeight := clientState.GetLatestHeight().(types.Height) // store intermediate consensus state at a time greater than updateHeader time // this will break time monotonicity incrementedClientHeight := clientState.GetLatestHeight().Increment().(types.Height) - intermediateConsState := &ibctm.ConsensusState{ + intermediateConsState := &ibctmtypes.ConsensusState{ Timestamp: suite.coordinator.CurrentTime.Add(2 * time.Hour), NextValidatorsHash: suite.chainB.Vals.Hash(), } suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientConsensusState(suite.chainA.GetContext(), clientID, incrementedClientHeight, intermediateConsState) // set iteration key clientStore := suite.keeper.ClientStore(suite.ctx, clientID) - ibctm.SetIterationKey(clientStore, incrementedClientHeight) + ibctmtypes.SetIterationKey(clientStore, incrementedClientHeight) clientState.LatestHeight = incrementedClientHeight suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), clientID, clientState) @@ -181,7 +181,7 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { }, false, false}, {"consensus state not found", func() { clientState := path.EndpointA.GetClientState() - tmClient, ok := clientState.(*ibctm.ClientState) + tmClient, ok := clientState.(*ibctmtypes.ClientState) suite.Require().True(ok) tmClient.LatestHeight = tmClient.LatestHeight.Increment().(types.Height) @@ -189,8 +189,8 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { updateHeader = createFutureUpdateFn(clientState.GetLatestHeight().(types.Height)) }, false, false}, {"client is not active", func() { - clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) - clientState.FrozenHeight = types.NewHeight(1, 1) + clientState := path.EndpointA.GetClientState().(*ibctmtypes.ClientState) + clientState.FrozenHeight = types.NewHeight(0, 1) suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID, clientState) updateHeader = createFutureUpdateFn(clientState.GetLatestHeight().(types.Height)) }, false, false}, @@ -222,9 +222,9 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { newClientState := path.EndpointA.GetClientState() if tc.expFreeze { - suite.Require().True(!newClientState.(*ibctm.ClientState).FrozenHeight.IsZero(), "client did not freeze after conflicting header was submitted to UpdateClient") + suite.Require().True(!newClientState.(*ibctmtypes.ClientState).FrozenHeight.IsZero(), "client did not freeze after conflicting header was submitted to UpdateClient") } else { - expConsensusState := &ibctm.ConsensusState{ + expConsensusState := &ibctmtypes.ConsensusState{ Timestamp: updateHeader.GetTime(), Root: commitmenttypes.NewMerkleRoot(updateHeader.Header.GetAppHash()), NextValidatorsHash: updateHeader.Header.NextValidatorsHash, @@ -252,6 +252,19 @@ func (suite *KeeperTestSuite) TestUpdateClientTendermint() { } } +func (suite *KeeperTestSuite) TestUpdateClientLocalhost() { + revision := types.ParseChainID(suite.chainA.ChainID) + var localhostClient exported.ClientState = localhosttypes.NewClientState(suite.chainA.ChainID, types.NewHeight(revision, uint64(suite.chainA.GetContext().BlockHeight()))) + + ctx := suite.chainA.GetContext().WithBlockHeight(suite.chainA.GetContext().BlockHeight() + 1) + err := suite.chainA.App.GetIBCKeeper().ClientKeeper.UpdateClient(ctx, exported.Localhost, nil) + suite.Require().NoError(err) + + clientState, found := suite.chainA.App.GetIBCKeeper().ClientKeeper.GetClientState(ctx, exported.Localhost) + suite.Require().True(found) + suite.Require().Equal(localhostClient.GetLatestHeight().(types.Height).Increment(), clientState.GetLatestHeight()) +} + func (suite *KeeperTestSuite) TestUpgradeClient() { var ( path *ibctesting.Path @@ -260,6 +273,7 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { lastHeight exported.Height proofUpgradedClient, proofUpgradedConsState []byte upgradedClientBz, upgradedConsStateBz []byte + err error ) testCases := []struct { @@ -271,17 +285,16 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { name: "successful upgrade", setup: func() { // last Height is at next block - lastHeight = clienttypes.NewHeight(1, uint64(suite.chainB.GetContext().BlockHeight()+1)) + 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) + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) // commit upgrade store changes and update clients + 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) @@ -296,18 +309,16 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { name: "client state not found", setup: func() { // last Height is at next block - lastHeight = clienttypes.NewHeight(1, uint64(suite.chainB.GetContext().BlockHeight()+1)) + 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) + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) // commit upgrade store changes and update clients 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) @@ -326,18 +337,16 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { // client is frozen // last Height is at next block - lastHeight = clienttypes.NewHeight(1, uint64(suite.chainB.GetContext().BlockHeight()+1)) + 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) + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) // commit upgrade store changes and update clients 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) @@ -347,9 +356,9 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { proofUpgradedConsState, _ = suite.chainB.QueryUpgradeProof(upgradetypes.UpgradedConsStateKey(int64(lastHeight.GetRevisionHeight())), cs.GetLatestHeight().GetRevisionHeight()) // set frozen client in store - tmClient, ok := cs.(*ibctm.ClientState) + tmClient, ok := cs.(*ibctmtypes.ClientState) suite.Require().True(ok) - tmClient.FrozenHeight = types.NewHeight(1, 1) + tmClient.FrozenHeight = types.NewHeight(0, 1) suite.chainA.App.GetIBCKeeper().ClientKeeper.SetClientState(suite.chainA.GetContext(), path.EndpointA.ClientID, tmClient) }, expPass: false, @@ -358,21 +367,17 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { name: "tendermint client VerifyUpgrade fails", setup: func() { // last Height is at next block - lastHeight = clienttypes.NewHeight(1, uint64(suite.chainB.GetContext().BlockHeight()+1)) + 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) + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedClient(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedClientBz) + suite.chainB.GetSimApp().UpgradeKeeper.SetUpgradedConsensusState(suite.chainB.GetContext(), int64(lastHeight.GetRevisionHeight()), upgradedConsStateBz) // change upgradedClient client-specified parameters - tmClient := upgradedClient.(*ibctm.ClientState) - tmClient.ChainId = "wrongchainID" - upgradedClient = tmClient + upgradedClient = ibctmtypes.NewClientState("wrongchainID", ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, true, true) 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) @@ -389,19 +394,12 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { tc := tc path = ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupClients(path) - - clientState := path.EndpointA.GetClientState().(*ibctm.ClientState) - revisionNumber := clienttypes.ParseChainID(clientState.ChainId) - - newChainID, err := clienttypes.SetRevisionNumber(clientState.ChainId, revisionNumber+1) - suite.Require().NoError(err) - - upgradedClient = ibctm.NewClientState(newChainID, ibctm.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, clienttypes.NewHeight(revisionNumber+1, clientState.GetLatestHeight().GetRevisionHeight()+1), commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + upgradedClient = ibctmtypes.NewClientState("newChainId-1", ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod+trustingPeriod, maxClockDrift, newClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) upgradedClient = upgradedClient.ZeroCustomFields() upgradedClientBz, err = types.MarshalClientState(suite.chainA.App.AppCodec(), upgradedClient) suite.Require().NoError(err) - upgradedConsState = &ibctm.ConsensusState{ + upgradedConsState = &ibctmtypes.ConsensusState{ NextValidatorsHash: []byte("nextValsHash"), } upgradedConsStateBz, err = types.MarshalConsensusState(suite.chainA.App.AppCodec(), upgradedConsState) @@ -409,6 +407,9 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { tc.setup() + // Call ZeroCustomFields on upgraded clients to clear any client-chosen parameters in test-case upgradedClient + upgradedClient = upgradedClient.ZeroCustomFields() + err = suite.chainA.App.GetIBCKeeper().ClientKeeper.UpgradeClient(suite.chainA.GetContext(), path.EndpointA.ClientID, upgradedClient, upgradedConsState, proofUpgradedClient, proofUpgradedConsState) if tc.expPass { @@ -417,12 +418,256 @@ func (suite *KeeperTestSuite) TestUpgradeClient() { suite.Require().Error(err, "verify upgrade passed on invalid case: %s", tc.name) } } + +} + +func (suite *KeeperTestSuite) TestCheckMisbehaviourAndUpdateState() { + var ( + clientID string + err error + ) + + altPrivVal := ibctestingmock.NewPV() + altPubKey, err := altPrivVal.GetPubKey() + suite.Require().NoError(err) + altVal := tmtypes.NewValidator(altPubKey, 4) + + // Set valSet here with suite.valSet so it doesn't get reset on each testcase + valSet := suite.valSet + valsHash := valSet.Hash() + + // Create bothValSet with both suite validator and altVal + bothValSet := tmtypes.NewValidatorSet(append(suite.valSet.Validators, altVal)) + bothValsHash := bothValSet.Hash() + // Create alternative validator set with only altVal + altValSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{altVal}) + + // Create signer array and ensure it is in same order as bothValSet + _, suiteVal := suite.valSet.GetByIndex(0) + bothSigners := ibctesting.CreateSortedSignerArray(altPrivVal, suite.privVal, altVal, suiteVal) + + altSigners := []tmtypes.PrivValidator{altPrivVal} + + // Create valid Misbehaviour by making a duplicate header that signs over different block time + altTime := suite.ctx.BlockTime().Add(time.Minute) + + heightPlus3 := types.NewHeight(0, height+3) + heightPlus5 := types.NewHeight(0, height+5) + + testCases := []struct { + name string + misbehaviour *ibctmtypes.Misbehaviour + malleate func() error + expPass bool + }{ + { + "trusting period misbehavior should pass", + &ibctmtypes.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, altTime, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), + ClientId: clientID, + }, + func() error { + suite.consensusState.NextValidatorsHash = bothValsHash + clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) + + return err + }, + true, + }, + { + "time misbehavior should pass", + &ibctmtypes.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+5), testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, altTime, bothValSet, bothValSet, bothSigners), + ClientId: clientID, + }, + func() error { + suite.consensusState.NextValidatorsHash = bothValsHash + clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) + + return err + }, + true, + }, + { + "misbehavior at later height should pass", + &ibctmtypes.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), testClientHeight, altTime, bothValSet, valSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), testClientHeight, suite.ctx.BlockTime(), bothValSet, valSet, bothSigners), + ClientId: clientID, + }, + func() error { + suite.consensusState.NextValidatorsHash = valsHash + clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) + + // store intermediate consensus state to check that trustedHeight does not need to be highest consensus state before header height + intermediateConsState := &ibctmtypes.ConsensusState{ + Timestamp: suite.now.Add(time.Minute), + NextValidatorsHash: suite.chainB.Vals.Hash(), + } + suite.keeper.SetClientConsensusState(suite.ctx, clientID, heightPlus3, intermediateConsState) + + clientState.LatestHeight = heightPlus3 + suite.keeper.SetClientState(suite.ctx, clientID, clientState) + + return err + }, + true, + }, + { + "misbehavior at later height with different trusted heights should pass", + &ibctmtypes.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), testClientHeight, altTime, bothValSet, valSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), heightPlus3, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), + ClientId: clientID, + }, + func() error { + suite.consensusState.NextValidatorsHash = valsHash + clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) + + // store trusted consensus state for Header2 + intermediateConsState := &ibctmtypes.ConsensusState{ + Timestamp: suite.now.Add(time.Minute), + NextValidatorsHash: bothValsHash, + } + suite.keeper.SetClientConsensusState(suite.ctx, clientID, heightPlus3, intermediateConsState) + + clientState.LatestHeight = heightPlus3 + suite.keeper.SetClientState(suite.ctx, clientID, clientState) + + return err + }, + true, + }, + { + "misbehavior ValidateBasic fails: misbehaviour height is at same height as trusted height", + &ibctmtypes.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight), testClientHeight, altTime, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight), testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), + ClientId: clientID, + }, + func() error { + suite.consensusState.NextValidatorsHash = bothValsHash + clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) + + return err + }, + false, + }, + { + "trusted ConsensusState1 not found", + &ibctmtypes.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), heightPlus3, altTime, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), testClientHeight, suite.ctx.BlockTime(), bothValSet, valSet, bothSigners), + ClientId: clientID, + }, + func() error { + suite.consensusState.NextValidatorsHash = valsHash + clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) + // intermediate consensus state at height + 3 is not created + return err + }, + false, + }, + { + "trusted ConsensusState2 not found", + &ibctmtypes.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), testClientHeight, altTime, bothValSet, valSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(heightPlus5.RevisionHeight+1), heightPlus3, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), + ClientId: clientID, + }, + func() error { + suite.consensusState.NextValidatorsHash = valsHash + clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) + // intermediate consensus state at height + 3 is not created + return err + }, + false, + }, + { + "client state not found", + &ibctmtypes.Misbehaviour{}, + func() error { return nil }, + false, + }, + { + "client already is not active - client is frozen", + &ibctmtypes.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, altTime, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, suite.ctx.BlockTime(), bothValSet, bothValSet, bothSigners), + ClientId: clientID, + }, + func() error { + suite.consensusState.NextValidatorsHash = bothValsHash + clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) + + clientState.FrozenHeight = types.NewHeight(0, 1) + suite.keeper.SetClientState(suite.ctx, clientID, clientState) + + return err + }, + false, + }, + { + "misbehaviour check failed", + &ibctmtypes.Misbehaviour{ + Header1: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, altTime, bothValSet, bothValSet, bothSigners), + Header2: suite.chainA.CreateTMClientHeader(testChainID, int64(testClientHeight.RevisionHeight+1), testClientHeight, suite.ctx.BlockTime(), altValSet, bothValSet, altSigners), + ClientId: clientID, + }, + func() error { + clientState := ibctmtypes.NewClientState(testChainID, ibctmtypes.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, testClientHeight, commitmenttypes.GetSDKSpecs(), ibctesting.UpgradePath, false, false) + if err != nil { + return err + } + clientID, err = suite.keeper.CreateClient(suite.ctx, clientState, suite.consensusState) + + return err + }, + false, + }, + } + + for i, tc := range testCases { + tc := tc + i := i + + suite.Run(tc.name, func() { + suite.SetupTest() // reset + clientID = testClientID // must be explicitly changed + + err := tc.malleate() + suite.Require().NoError(err) + + tc.misbehaviour.ClientId = clientID + + err = suite.keeper.CheckMisbehaviourAndUpdateState(suite.ctx, clientID, tc.misbehaviour) + + if tc.expPass { + suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) + + clientState, found := suite.keeper.GetClientState(suite.ctx, clientID) + suite.Require().True(found, "valid test case %d failed: %s", i, tc.name) + suite.Require().True(!clientState.(*ibctmtypes.ClientState).FrozenHeight.IsZero(), "valid test case %d failed: %s", i, tc.name) + } else { + suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) + } + }) + } } func (suite *KeeperTestSuite) TestUpdateClientEventEmission() { path := ibctesting.NewPath(suite.chainA, suite.chainB) suite.coordinator.SetupClients(path) - header, err := suite.chainA.ConstructUpdateTMClientHeader(suite.chainB, path.EndpointA.ClientID) suite.Require().NoError(err) @@ -446,10 +691,12 @@ func (suite *KeeperTestSuite) TestUpdateClientEventEmission() { bz, err := hex.DecodeString(string(attr.Value)) suite.Require().NoError(err) - emittedHeader, err := types.UnmarshalClientMessage(suite.chainA.App.AppCodec(), bz) + emittedHeader, err := types.UnmarshalHeader(suite.chainA.App.AppCodec(), bz) suite.Require().NoError(err) suite.Require().Equal(header, emittedHeader) } + } suite.Require().True(contains) + } diff --git a/modules/core/exported/client.go b/modules/core/exported/client.go index ac76a647dc5..a4839bdbf97 100644 --- a/modules/core/exported/client.go +++ b/modules/core/exported/client.go @@ -19,6 +19,10 @@ const ( // Tendermint is used to indicate that the client uses the Tendermint Consensus Algorithm. Tendermint string = "07-tendermint" + // Localhost is the client type for a localhost client. It is also used as the clientID + // for the localhost client. + Localhost string = "09-localhost" + // Active is a status type of a client. An active client is allowed to be used. Active Status = "Active" @@ -40,93 +44,137 @@ type ClientState interface { GetLatestHeight() Height Validate() error - // Status must return the status of the client. Only Active clients are allowed to process packets. + // Initialization function + // Clients must validate the initial consensus state, and may store any client-specific metadata + // necessary for correct light client operation + Initialize(sdk.Context, codec.BinaryCodec, sdk.KVStore, ConsensusState) error + + // Status function + // Clients must return their status. Only Active clients are allowed to process packets. Status(ctx sdk.Context, clientStore sdk.KVStore, cdc codec.BinaryCodec) Status - // ExportMetadata must export metadata stored within the clientStore for genesis export - ExportMetadata(clientStore sdk.KVStore) []GenesisMetadata + // Genesis function + ExportMetadata(sdk.KVStore) []GenesisMetadata + + // Update and Misbehaviour functions - // ZeroCustomFields zeroes out any client customizable fields in client state + CheckHeaderAndUpdateState(sdk.Context, codec.BinaryCodec, sdk.KVStore, Header) (ClientState, ConsensusState, error) + CheckMisbehaviourAndUpdateState(sdk.Context, codec.BinaryCodec, sdk.KVStore, Misbehaviour) (ClientState, error) + CheckSubstituteAndUpdateState(ctx sdk.Context, cdc codec.BinaryCodec, subjectClientStore, substituteClientStore sdk.KVStore, substituteClient ClientState) (ClientState, error) + + // Upgrade functions + // NOTE: proof heights are not included as upgrade to a new revision is expected to pass only on the last + // height committed by the current revision. Clients are responsible for ensuring that the planned last + // height of the current revision is somehow encoded in the proof verification process. + // This is to ensure that no premature upgrades occur, since upgrade plans committed to by the counterparty + // may be cancelled or modified before the last planned height. + VerifyUpgradeAndUpdateState( + ctx sdk.Context, + cdc codec.BinaryCodec, + store sdk.KVStore, + newClient ClientState, + newConsState ConsensusState, + proofUpgradeClient, + proofUpgradeConsState []byte, + ) (ClientState, ConsensusState, error) + // Utility function that zeroes out any client customizable fields in client state // Ledger enforced fields are maintained while all custom fields are zero values // Used to verify upgrades ZeroCustomFields() ClientState - // GetTimestampAtHeight must return the timestamp for the consensus state associated with the provided height. - GetTimestampAtHeight( - ctx sdk.Context, - clientStore sdk.KVStore, + // State verification functions + + VerifyClientState( + store sdk.KVStore, cdc codec.BinaryCodec, height Height, - ) (uint64, error) - - // Initialization function - // Clients must validate the initial consensus state, and may store any client-specific metadata - // necessary for correct light client operation - Initialize(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, consensusState ConsensusState) error - - // VerifyMembership is a generic proof verification method which verifies a proof of the existence of a value at a given CommitmentPath at the specified height. - // The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). - VerifyMembership( + prefix Prefix, + counterpartyClientIdentifier string, + proof []byte, + clientState ClientState, + ) error + VerifyClientConsensusState( + store sdk.KVStore, + cdc codec.BinaryCodec, + height Height, + counterpartyClientIdentifier string, + consensusHeight Height, + prefix Prefix, + proof []byte, + consensusState ConsensusState, + ) error + VerifyConnectionState( + store sdk.KVStore, + cdc codec.BinaryCodec, + height Height, + prefix Prefix, + proof []byte, + connectionID string, + connectionEnd ConnectionI, + ) error + VerifyChannelState( + store sdk.KVStore, + cdc codec.BinaryCodec, + height Height, + prefix Prefix, + proof []byte, + portID, + channelID string, + channel ChannelI, + ) error + VerifyPacketCommitment( ctx sdk.Context, - clientStore sdk.KVStore, + store sdk.KVStore, cdc codec.BinaryCodec, height Height, delayTimePeriod uint64, delayBlockPeriod uint64, + prefix Prefix, proof []byte, - path []byte, - value []byte, + portID, + channelID string, + sequence uint64, + commitmentBytes []byte, ) error - - // VerifyNonMembership is a generic proof verification method which verifies the absence of a given CommitmentPath at a specified height. - // The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). - VerifyNonMembership( + VerifyPacketAcknowledgement( ctx sdk.Context, - clientStore sdk.KVStore, + store sdk.KVStore, cdc codec.BinaryCodec, height Height, delayTimePeriod uint64, delayBlockPeriod uint64, + prefix Prefix, proof []byte, - path []byte, + portID, + channelID string, + sequence uint64, + acknowledgement []byte, ) error - - // VerifyClientMessage must verify a ClientMessage. A ClientMessage could be a Header, Misbehaviour, or batch update. - // It must handle each type of ClientMessage appropriately. Calls to CheckForMisbehaviour, UpdateState, and UpdateStateOnMisbehaviour - // will assume that the content of the ClientMessage has been verified and can be trusted. An error should be returned - // if the ClientMessage fails to verify. - VerifyClientMessage(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg ClientMessage) error - - // Checks for evidence of a misbehaviour in Header or Misbehaviour type. It assumes the ClientMessage - // has already been verified. - CheckForMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg ClientMessage) bool - - // UpdateStateOnMisbehaviour should perform appropriate state changes on a client state given that misbehaviour has been detected and verified - UpdateStateOnMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg ClientMessage) - - // UpdateState updates and stores as necessary any associated information for an IBC client, such as the ClientState and corresponding ConsensusState. - // Upon successful update, a list of consensus heights is returned. It assumes the ClientMessage has already been verified. - UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, clientMsg ClientMessage) []Height - - // CheckSubstituteAndUpdateState must verify that the provided substitute may be used to update the subject client. - // The light client must set the updated client and consensus states within the clientStore for the subject client. - CheckSubstituteAndUpdateState(ctx sdk.Context, cdc codec.BinaryCodec, subjectClientStore, substituteClientStore sdk.KVStore, substituteClient ClientState) error - - // Upgrade functions - // NOTE: proof heights are not included as upgrade to a new revision is expected to pass only on the last - // height committed by the current revision. Clients are responsible for ensuring that the planned last - // height of the current revision is somehow encoded in the proof verification process. - // This is to ensure that no premature upgrades occur, since upgrade plans committed to by the counterparty - // may be cancelled or modified before the last planned height. - // If the upgrade is verified, the upgraded client and consensus states must be set in the client store. - VerifyUpgradeAndUpdateState( + VerifyPacketReceiptAbsence( ctx sdk.Context, + store sdk.KVStore, cdc codec.BinaryCodec, + height Height, + delayTimePeriod uint64, + delayBlockPeriod uint64, + prefix Prefix, + proof []byte, + portID, + channelID string, + sequence uint64, + ) error + VerifyNextSequenceRecv( + ctx sdk.Context, store sdk.KVStore, - newClient ClientState, - newConsState ConsensusState, - proofUpgradeClient, - proofUpgradeConsState []byte, + cdc codec.BinaryCodec, + height Height, + delayTimePeriod uint64, + delayBlockPeriod uint64, + prefix Prefix, + proof []byte, + portID, + channelID string, + nextSequenceRecv uint64, ) error } @@ -136,19 +184,30 @@ type ConsensusState interface { ClientType() string // Consensus kind + // GetRoot returns the commitment root of the consensus state, + // which is used for key-value pair verification. + GetRoot() Root + // GetTimestamp returns the timestamp (in nanoseconds) of the consensus state GetTimestamp() uint64 ValidateBasic() error } -// ClientMessage is an interface used to update an IBC client. -// The update may be done by a single header, a batch of headers, misbehaviour, or any type which when verified produces -// a change to state of the IBC client -type ClientMessage interface { +// Misbehaviour defines counterparty misbehaviour for a specific consensus type +type Misbehaviour interface { + proto.Message + + ClientType() string + ValidateBasic() error +} + +// Header is the consensus state update information +type Header interface { proto.Message ClientType() string + GetHeight() Height ValidateBasic() error } diff --git a/modules/core/keeper/msg_server.go b/modules/core/keeper/msg_server.go index 5f6c77a3981..a36f064e8ae 100644 --- a/modules/core/keeper/msg_server.go +++ b/modules/core/keeper/msg_server.go @@ -4,22 +4,21 @@ import ( "context" metrics "github.com/armon/go-metrics" + "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" - connectiontypes "github.com/cosmos/ibc-go/v5/modules/core/03-connection/types" - channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" - porttypes "github.com/cosmos/ibc-go/v5/modules/core/05-port/types" - coretypes "github.com/cosmos/ibc-go/v5/modules/core/types" + clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types" + connectiontypes "github.com/cosmos/ibc-go/v3/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types" + porttypes "github.com/cosmos/ibc-go/v3/modules/core/05-port/types" + coretypes "github.com/cosmos/ibc-go/v3/modules/core/types" ) -var ( - _ clienttypes.MsgServer = Keeper{} - _ connectiontypes.MsgServer = Keeper{} - _ channeltypes.MsgServer = Keeper{} -) +var _ clienttypes.MsgServer = Keeper{} +var _ connectiontypes.MsgServer = Keeper{} +var _ channeltypes.MsgServer = Keeper{} // CreateClient defines a rpc handler method for MsgCreateClient. func (k Keeper) CreateClient(goCtx context.Context, msg *clienttypes.MsgCreateClient) (*clienttypes.MsgCreateClientResponse, error) { @@ -46,12 +45,12 @@ func (k Keeper) CreateClient(goCtx context.Context, msg *clienttypes.MsgCreateCl func (k Keeper) UpdateClient(goCtx context.Context, msg *clienttypes.MsgUpdateClient) (*clienttypes.MsgUpdateClientResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - clientMsg, err := clienttypes.UnpackClientMessage(msg.ClientMessage) + header, err := clienttypes.UnpackHeader(msg.Header) if err != nil { return nil, err } - if err = k.ClientKeeper.UpdateClient(ctx, msg.ClientId, clientMsg); err != nil { + if err = k.ClientKeeper.UpdateClient(ctx, msg.ClientId, header); err != nil { return nil, err } @@ -80,18 +79,16 @@ func (k Keeper) UpgradeClient(goCtx context.Context, msg *clienttypes.MsgUpgrade } // SubmitMisbehaviour defines a rpc handler method for MsgSubmitMisbehaviour. -// Warning: DEPRECATED -// This handler is redudant as `MsgUpdateClient` is now capable of handling both a Header and a Misbehaviour func (k Keeper) SubmitMisbehaviour(goCtx context.Context, msg *clienttypes.MsgSubmitMisbehaviour) (*clienttypes.MsgSubmitMisbehaviourResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - misbehaviour, err := clienttypes.UnpackClientMessage(msg.Misbehaviour) + misbehaviour, err := clienttypes.UnpackMisbehaviour(msg.Misbehaviour) if err != nil { return nil, err } - if err = k.ClientKeeper.UpdateClient(ctx, msg.ClientId, misbehaviour); err != nil { - return nil, err + if err := k.ClientKeeper.CheckMisbehaviourAndUpdateState(ctx, msg.ClientId, misbehaviour); err != nil { + return nil, sdkerrors.Wrap(err, "failed to process misbehaviour for IBC client") } return &clienttypes.MsgSubmitMisbehaviourResponse{}, nil @@ -118,7 +115,7 @@ func (k Keeper) ConnectionOpenTry(goCtx context.Context, msg *connectiontypes.Ms } if _, err := k.ConnectionKeeper.ConnOpenTry( - ctx, msg.Counterparty, msg.DelayPeriod, msg.ClientId, targetClient, + ctx, msg.PreviousConnectionId, msg.Counterparty, msg.DelayPeriod, msg.ClientId, targetClient, connectiontypes.ProtoVersionsToExported(msg.CounterpartyVersions), msg.ProofInit, msg.ProofClient, msg.ProofConsensus, msg.ProofHeight, msg.ConsensusHeight, ); err != nil { @@ -188,17 +185,15 @@ func (k Keeper) ChannelOpenInit(goCtx context.Context, msg *channeltypes.MsgChan } // Perform application logic callback - version, err := cbs.OnChanOpenInit(ctx, msg.Channel.Ordering, msg.Channel.ConnectionHops, msg.PortId, channelID, cap, msg.Channel.Counterparty, msg.Channel.Version) - if err != nil { + if err = cbs.OnChanOpenInit(ctx, msg.Channel.Ordering, msg.Channel.ConnectionHops, msg.PortId, channelID, cap, msg.Channel.Counterparty, msg.Channel.Version); err != nil { return nil, sdkerrors.Wrap(err, "channel open init callback failed") } // Write channel into state - k.ChannelKeeper.WriteOpenInitChannel(ctx, msg.PortId, channelID, msg.Channel.Ordering, msg.Channel.ConnectionHops, msg.Channel.Counterparty, version) + k.ChannelKeeper.WriteOpenInitChannel(ctx, msg.PortId, channelID, msg.Channel.Ordering, msg.Channel.ConnectionHops, msg.Channel.Counterparty, msg.Channel.Version) return &channeltypes.MsgChannelOpenInitResponse{ ChannelId: channelID, - Version: version, }, nil } @@ -221,7 +216,7 @@ func (k Keeper) ChannelOpenTry(goCtx context.Context, msg *channeltypes.MsgChann } // Perform 04-channel verification - channelID, cap, err := k.ChannelKeeper.ChanOpenTry(ctx, msg.Channel.Ordering, msg.Channel.ConnectionHops, msg.PortId, + channelID, cap, err := k.ChannelKeeper.ChanOpenTry(ctx, msg.Channel.Ordering, msg.Channel.ConnectionHops, msg.PortId, msg.PreviousChannelId, portCap, msg.Channel.Counterparty, msg.CounterpartyVersion, msg.ProofInit, msg.ProofHeight, ) if err != nil { @@ -237,9 +232,7 @@ func (k Keeper) ChannelOpenTry(goCtx context.Context, msg *channeltypes.MsgChann // Write channel into state k.ChannelKeeper.WriteOpenTryChannel(ctx, msg.PortId, channelID, msg.Channel.Ordering, msg.Channel.ConnectionHops, msg.Channel.Counterparty, version) - return &channeltypes.MsgChannelOpenTryResponse{ - Version: version, - }, nil + return &channeltypes.MsgChannelOpenTryResponse{}, nil } // ChannelOpenAck defines a rpc handler method for MsgChannelOpenAck. @@ -268,7 +261,7 @@ func (k Keeper) ChannelOpenAck(goCtx context.Context, msg *channeltypes.MsgChann } // Perform application logic callback - if err = cbs.OnChanOpenAck(ctx, msg.PortId, msg.ChannelId, msg.CounterpartyChannelId, msg.CounterpartyVersion); err != nil { + if err = cbs.OnChanOpenAck(ctx, msg.PortId, msg.ChannelId, msg.CounterpartyVersion); err != nil { return nil, sdkerrors.Wrap(err, "channel open ack callback failed") } @@ -402,7 +395,7 @@ func (k Keeper) RecvPacket(goCtx context.Context, msg *channeltypes.MsgRecvPacke case nil: writeFn() case channeltypes.ErrNoOpMsg: - return &channeltypes.MsgRecvPacketResponse{Result: channeltypes.NOOP}, nil + return &channeltypes.MsgRecvPacketResponse{}, nil // no-op default: return nil, sdkerrors.Wrap(err, "receive packet verification failed") } @@ -442,7 +435,7 @@ func (k Keeper) RecvPacket(goCtx context.Context, msg *channeltypes.MsgRecvPacke ) }() - return &channeltypes.MsgRecvPacketResponse{Result: channeltypes.SUCCESS}, nil + return &channeltypes.MsgRecvPacketResponse{}, nil } // Timeout defines a rpc handler method for MsgTimeout. @@ -480,7 +473,7 @@ func (k Keeper) Timeout(goCtx context.Context, msg *channeltypes.MsgTimeout) (*c case nil: writeFn() case channeltypes.ErrNoOpMsg: - return &channeltypes.MsgTimeoutResponse{Result: channeltypes.NOOP}, nil + return &channeltypes.MsgTimeoutResponse{}, nil // no-op default: return nil, sdkerrors.Wrap(err, "timeout packet verification failed") } @@ -510,7 +503,7 @@ func (k Keeper) Timeout(goCtx context.Context, msg *channeltypes.MsgTimeout) (*c ) }() - return &channeltypes.MsgTimeoutResponse{Result: channeltypes.SUCCESS}, nil + return &channeltypes.MsgTimeoutResponse{}, nil } // TimeoutOnClose defines a rpc handler method for MsgTimeoutOnClose. @@ -548,7 +541,7 @@ func (k Keeper) TimeoutOnClose(goCtx context.Context, msg *channeltypes.MsgTimeo case nil: writeFn() case channeltypes.ErrNoOpMsg: - return &channeltypes.MsgTimeoutOnCloseResponse{Result: channeltypes.NOOP}, nil + return &channeltypes.MsgTimeoutOnCloseResponse{}, nil // no-op default: return nil, sdkerrors.Wrap(err, "timeout on close packet verification failed") } @@ -581,7 +574,7 @@ func (k Keeper) TimeoutOnClose(goCtx context.Context, msg *channeltypes.MsgTimeo ) }() - return &channeltypes.MsgTimeoutOnCloseResponse{Result: channeltypes.SUCCESS}, nil + return &channeltypes.MsgTimeoutOnCloseResponse{}, nil } // Acknowledgement defines a rpc handler method for MsgAcknowledgement. @@ -619,7 +612,7 @@ func (k Keeper) Acknowledgement(goCtx context.Context, msg *channeltypes.MsgAckn case nil: writeFn() case channeltypes.ErrNoOpMsg: - return &channeltypes.MsgAcknowledgementResponse{Result: channeltypes.NOOP}, nil + return &channeltypes.MsgAcknowledgementResponse{}, nil // no-op default: return nil, sdkerrors.Wrap(err, "acknowledge packet verification failed") } @@ -643,5 +636,5 @@ func (k Keeper) Acknowledgement(goCtx context.Context, msg *channeltypes.MsgAckn ) }() - return &channeltypes.MsgAcknowledgementResponse{Result: channeltypes.SUCCESS}, nil + return &channeltypes.MsgAcknowledgementResponse{}, nil }