diff --git a/CHANGELOG.md b/CHANGELOG.md index 2daf3e4c38f..471703349bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ## [Unreleased] * (core) [\#227](https://github.com/cosmos/ibc-go/pull/227) Remove sdk.Result from application callbacks +* (core) [\#268](https://github.com/cosmos/ibc-go/pull/268) Perform a no-op on redundant relay messages. Previous behaviour returned an error. Now no state change will occur and no error will be returned. ## [v1.0.0-rc0](https://github.com/cosmos/ibc-go/releases/tag/v1.0.0-rc0) - 2021-07-07 diff --git a/modules/apps/29-fee/client/cli/cli.go b/modules/apps/29-fee/client/cli/cli.go new file mode 100644 index 00000000000..fb8b9db8320 --- /dev/null +++ b/modules/apps/29-fee/client/cli/cli.go @@ -0,0 +1,40 @@ +package cli + +import ( + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" +) + +// GetQueryCmd returns the query commands for 29-fee +func GetQueryCmd() *cobra.Command { + queryCmd := &cobra.Command{ + Use: "ibc-fee", + Short: "", // TODO + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + } + + queryCmd.AddCommand( + // TODO + ) + + return queryCmd +} + +// NewTxCmd returns the transaction commands for 29-fee +func NewTxCmd() *cobra.Command { + txCmd := &cobra.Command{ + Use: "ibc-fee", + Short: "", // TODO + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + txCmd.AddCommand( + // TODO + ) + + return txCmd +} diff --git a/modules/apps/29-fee/client/cli/query.go b/modules/apps/29-fee/client/cli/query.go new file mode 100644 index 00000000000..e8878f3e042 --- /dev/null +++ b/modules/apps/29-fee/client/cli/query.go @@ -0,0 +1,3 @@ +package cli + +// TODO diff --git a/modules/apps/29-fee/client/cli/tx.go b/modules/apps/29-fee/client/cli/tx.go new file mode 100644 index 00000000000..e8878f3e042 --- /dev/null +++ b/modules/apps/29-fee/client/cli/tx.go @@ -0,0 +1,3 @@ +package cli + +// TODO diff --git a/modules/apps/29-fee/keeper/genesis.go b/modules/apps/29-fee/keeper/genesis.go new file mode 100644 index 00000000000..cb500443231 --- /dev/null +++ b/modules/apps/29-fee/keeper/genesis.go @@ -0,0 +1,16 @@ +package keeper + +/* +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// InitGenesis +func (k Keeper) InitGenesis(ctx sdk.Context, state types.GenesisState) { +} + +// ExportGenesis +func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { + return &types.GenesisState{} +} +*/ diff --git a/modules/apps/29-fee/keeper/grpc_query.go b/modules/apps/29-fee/keeper/grpc_query.go new file mode 100644 index 00000000000..185ad775c93 --- /dev/null +++ b/modules/apps/29-fee/keeper/grpc_query.go @@ -0,0 +1,5 @@ +package keeper + +// TODO + +//var _ types.QueryServer = Keeper{} diff --git a/modules/apps/29-fee/keeper/keeper.go b/modules/apps/29-fee/keeper/keeper.go new file mode 100644 index 00000000000..db304dd4cf9 --- /dev/null +++ b/modules/apps/29-fee/keeper/keeper.go @@ -0,0 +1,87 @@ +package keeper + +/* +import ( + "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + "github.com/cosmos/ibc-go/modules/apps/transfer/types" + host "github.com/cosmos/ibc-go/modules/core/24-host" +) + +// Keeper defines the IBC fungible transfer keeper +type Keeper struct { + storeKey sdk.StoreKey + cdc codec.BinaryCodec + + channelKeeper types.ChannelKeeper + portKeeper types.PortKeeper + authKeeper types.AccountKeeper + bankKeeper types.BankKeeper + scopedKeeper capabilitykeeper.ScopedKeeper +} + + +// NewKeeper creates a new 29-fee Keeper instance +func NewKeeper( + cdc codec.BinaryCodec, key sdk.StoreKey, paramSpace paramtypes.Subspace, + channelKeeper types.ChannelKeeper, portKeeper types.PortKeeper, + authKeeper types.AccountKeeper, bankKeeper types.BankKeeper, scopedKeeper capabilitykeeper.ScopedKeeper, +) Keeper { + + return Keeper{ + cdc: cdc, + storeKey: key, + channelKeeper: channelKeeper, + portKeeper: portKeeper, + authKeeper: authKeeper, + bankKeeper: bankKeeper, + scopedKeeper: scopedKeeper, + } +} + +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", "x/"+host.ModuleName+"-"+types.ModuleName) +} + +// IsBound checks if the transfer module is already bound to the desired port +func (k Keeper) IsBound(ctx sdk.Context, portID string) bool { + _, ok := k.scopedKeeper.GetCapability(ctx, host.PortPath(portID)) + return ok +} + +// BindPort defines a wrapper function for the ort Keeper's function in +// order to expose it to module's InitGenesis function +func (k Keeper) BindPort(ctx sdk.Context, portID string) error { + cap := k.portKeeper.BindPort(ctx, portID) + return k.ClaimCapability(ctx, cap, host.PortPath(portID)) +} + +// GetPort returns the portID for the transfer module. Used in ExportGenesis +func (k Keeper) GetPort(ctx sdk.Context) string { + store := ctx.KVStore(k.storeKey) + return string(store.Get(types.PortKey)) +} + +// SetPort sets the portID for the transfer module. Used in InitGenesis +func (k Keeper) SetPort(ctx sdk.Context, portID string) { + store := ctx.KVStore(k.storeKey) + store.Set(types.PortKey, []byte(portID)) +} + +// AuthenticateCapability wraps the scopedKeeper's AuthenticateCapability function +func (k Keeper) AuthenticateCapability(ctx sdk.Context, cap *capabilitytypes.Capability, name string) bool { + return k.scopedKeeper.AuthenticateCapability(ctx, cap, name) +} + +// ClaimCapability allows the transfer module that can claim a capability that IBC module +// passes to it +func (k Keeper) ClaimCapability(ctx sdk.Context, cap *capabilitytypes.Capability, name string) error { + return k.scopedKeeper.ClaimCapability(ctx, cap, name) +} +*/ diff --git a/modules/apps/29-fee/keeper/keeper_test.go b/modules/apps/29-fee/keeper/keeper_test.go new file mode 100644 index 00000000000..b355ccfe847 --- /dev/null +++ b/modules/apps/29-fee/keeper/keeper_test.go @@ -0,0 +1,45 @@ +package keeper_test + +/* +import ( + "testing" + + "github.com/stretchr/testify/suite" + "github.com/tendermint/tendermint/crypto" + + "github.com/cosmos/cosmos-sdk/baseapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/ibc-go/modules/apps/transfer/types" + ibctesting "github.com/cosmos/ibc-go/testing" +) + +type KeeperTestSuite struct { + suite.Suite + + coordinator *ibctesting.Coordinator + + // testing chains used for convenience and readability + chainA *ibctesting.TestChain + chainB *ibctesting.TestChain + chainC *ibctesting.TestChain +} + +func (suite *KeeperTestSuite) SetupTest() { + suite.coordinator = ibctesting.NewCoordinator(suite.T(), 3) + suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(0)) + suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(1)) + suite.chainC = suite.coordinator.GetChain(ibctesting.GetChainID(2)) +} + +func NewFeePath(chainA, chainB *ibctesting.TestChain) *ibctesting.Path { + path := ibctesting.NewPath(chainA, chainB) + path.EndpointA.ChannelConfig.PortID = ibctesting.FeePort + path.EndpointB.ChannelConfig.PortID = ibctesting.FeePort + + return path +} + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(KeeperTestSuite)) +} +*/ diff --git a/modules/apps/29-fee/keeper/msg_server.go b/modules/apps/29-fee/keeper/msg_server.go new file mode 100644 index 00000000000..2194eed8a68 --- /dev/null +++ b/modules/apps/29-fee/keeper/msg_server.go @@ -0,0 +1,4 @@ +package keeper + +// TODO +//var _ types.MsgServer = Keeper{} diff --git a/modules/apps/29-fee/module.go b/modules/apps/29-fee/module.go new file mode 100644 index 00000000000..c0645e3db78 --- /dev/null +++ b/modules/apps/29-fee/module.go @@ -0,0 +1,277 @@ +package fee + +/* +import ( + "context" + "encoding/json" + "fmt" + "math/rand" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + "github.com/gorilla/mux" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/spf13/cobra" + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/ibc-go/modules/apps/transfer/client/cli" + "github.com/cosmos/ibc-go/modules/apps/transfer/keeper" + "github.com/cosmos/ibc-go/modules/apps/transfer/simulation" + "github.com/cosmos/ibc-go/modules/apps/transfer/types" + channeltypes "github.com/cosmos/ibc-go/modules/core/04-channel/types" + porttypes "github.com/cosmos/ibc-go/modules/core/05-port/types" + ibcexported "github.com/cosmos/ibc-go/modules/core/exported" +) + +var ( + _ module.AppModule = AppModule{} + _ porttypes.IBCModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +// AppModuleBasic is the 29-fee AppModuleBasic +type AppModuleBasic struct{} + +// Name implements AppModuleBasic interface +func (AppModuleBasic) Name() string { + return types.ModuleName +} + +// RegisterLegacyAminoCodec implements AppModuleBasic interface +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {} + +// RegisterInterfaces registers module concrete types into protobuf Any. +func (AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) { + types.RegisterInterfaces(registry) +} + +// DefaultGenesis returns default genesis state as raw bytes for the ibc +// transfer module. +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { + return cdc.MustMarshalJSON(types.DefaultGenesisState()) +} + +// ValidateGenesis performs genesis state validation for the 29-fee module. +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncodingConfig, bz json.RawMessage) error { + var gs types.GenesisState + if err := cdc.UnmarshalJSON(bz, &gs); err != nil { + return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) + } + + return gs.Validate() +} + +// RegisterRESTRoutes implements AppModuleBasic interface +func (AppModuleBasic) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Router) { +} + +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the ibc-transfer module. +func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { + types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) +} + +// GetTxCmd implements AppModuleBasic interface +func (AppModuleBasic) GetTxCmd() *cobra.Command { + return cli.NewTxCmd() +} + +// GetQueryCmd implements AppModuleBasic interface +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd() +} + +// AppModule represents the AppModule for this module +type AppModule struct { + AppModuleBasic + keeper keeper.Keeper +} + +// NewAppModule creates a new 29-fee module +func NewAppModule(k keeper.Keeper) AppModule { + return AppModule{ + keeper: k, + } +} + +// RegisterInvariants implements the AppModule interface +func (AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { + // TODO +} + +// Route implements the AppModule interface +func (am AppModule) Route() sdk.Route { + return sdk.Route{} +} + +// QuerierRoute implements the AppModule interface +func (AppModule) QuerierRoute() string { + return types.QuerierRoute +} + +// LegacyQuerierHandler implements the AppModule interface +func (am AppModule) LegacyQuerierHandler(*codec.LegacyAmino) sdk.Querier { + return nil +} + +// RegisterServices registers module services. +func (am AppModule) RegisterServices(cfg module.Configurator) { + types.RegisterMsgServer(cfg.MsgServer(), am.keeper) + types.RegisterQueryServer(cfg.QueryServer(), am.keeper) +} + +// InitGenesis performs genesis initialization for the ibc-transfer module. It returns +// no validator updates. +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate { + var genesisState types.GenesisState + cdc.MustUnmarshalJSON(data, &genesisState) + am.keeper.InitGenesis(ctx, genesisState) + return []abci.ValidatorUpdate{} +} + +// ExportGenesis returns the exported genesis state as raw bytes for the ibc-transfer +// module. +func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage { + gs := am.keeper.ExportGenesis(ctx) + return cdc.MustMarshalJSON(gs) +} + +// ConsensusVersion implements AppModule/ConsensusVersion. +func (AppModule) ConsensusVersion() uint64 { return 1 } + +// BeginBlock implements the AppModule interface +func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { +} + +// EndBlock implements the AppModule interface +func (am AppModule) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} + +// AppModuleSimulation functions + +// GenerateGenesisState creates a randomized GenState of the transfer module. +func (AppModule) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState) +} + +// ProposalContents doesn't return any content functions for governance proposals. +func (AppModule) ProposalContents(_ module.SimulationState) []simtypes.WeightedProposalContent { + return nil +} + +// RandomizedParams creates randomized ibc-transfer param changes for the simulator. +func (AppModule) RandomizedParams(r *rand.Rand) []simtypes.ParamChange { + return simulation.ParamChanges(r) +} + +// RegisterStoreDecoder registers a decoder for transfer module's types +func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { + sdr[types.StoreKey] = simulation.NewDecodeStore(am.keeper) +} + +// WeightedOperations returns the all the transfer module operations with their respective weights. +func (am AppModule) WeightedOperations(_ module.SimulationState) []simtypes.WeightedOperation { + return nil +} + +// OnChanOpenInit implements the IBCModule interface +func (am AppModule) OnChanOpenInit( + ctx sdk.Context, + order channeltypes.Order, + connectionHops []string, + portID string, + channelID string, + chanCap *capabilitytypes.Capability, + counterparty channeltypes.Counterparty, + version string, +) error { + return nil +} + +// OnChanOpenTry implements the IBCModule interface +func (am AppModule) OnChanOpenTry( + ctx sdk.Context, + order channeltypes.Order, + connectionHops []string, + portID, + channelID string, + chanCap *capabilitytypes.Capability, + counterparty channeltypes.Counterparty, + version, + counterpartyVersion string, +) error { + return nil +} + +// OnChanOpenAck implements the IBCModule interface +func (am AppModule) OnChanOpenAck( + ctx sdk.Context, + portID, + channelID string, + counterpartyVersion string, +) error { + return nil +} + +// OnChanOpenConfirm implements the IBCModule interface +func (am AppModule) OnChanOpenConfirm( + ctx sdk.Context, + portID, + channelID string, +) error { + return nil +} + +// OnChanCloseInit implements the IBCModule interface +func (am AppModule) OnChanCloseInit( + ctx sdk.Context, + portID, + channelID string, +) error { + // Disallow user-initiated channel closing for 29-fee channels + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "user cannot close channel") +} + +// OnChanCloseConfirm implements the IBCModule interface +func (am AppModule) OnChanCloseConfirm( + ctx sdk.Context, + portID, + channelID string, +) error { + return nil +} + +// OnRecvPacket implements the IBCModule interface. +func (am AppModule) OnRecvPacket( + ctx sdk.Context, + packet channeltypes.Packet, + relayer sdk.AccAddress, +) ibcexported.Acknowledgement { + return nil +} + +// OnAcknowledgementPacket implements the IBCModule interface +func (am AppModule) OnAcknowledgementPacket( + ctx sdk.Context, + packet channeltypes.Packet, + acknowledgement []byte, + relayer sdk.AccAddress, +) error { + return nil +} + +// OnTimeoutPacket implements the IBCModule interface +func (am AppModule) OnTimeoutPacket( + ctx sdk.Context, + packet channeltypes.Packet, + relayer sdk.AccAddress, +) error { + return nil +} +*/ diff --git a/modules/apps/29-fee/spec/01_concepts.md b/modules/apps/29-fee/spec/01_concepts.md new file mode 100644 index 00000000000..119c857b0cc --- /dev/null +++ b/modules/apps/29-fee/spec/01_concepts.md @@ -0,0 +1,6 @@ + + +# Concepts + diff --git a/modules/apps/29-fee/spec/02_state.md b/modules/apps/29-fee/spec/02_state.md new file mode 100644 index 00000000000..336ed0f5b65 --- /dev/null +++ b/modules/apps/29-fee/spec/02_state.md @@ -0,0 +1,6 @@ + + +# State + diff --git a/modules/apps/29-fee/spec/03_state_transitions.md b/modules/apps/29-fee/spec/03_state_transitions.md new file mode 100644 index 00000000000..dc70ea002c6 --- /dev/null +++ b/modules/apps/29-fee/spec/03_state_transitions.md @@ -0,0 +1,6 @@ + + +# State Transitions + diff --git a/modules/apps/29-fee/spec/04_messages.md b/modules/apps/29-fee/spec/04_messages.md new file mode 100644 index 00000000000..0a97f2d6b9d --- /dev/null +++ b/modules/apps/29-fee/spec/04_messages.md @@ -0,0 +1,6 @@ + + +# Messages + diff --git a/modules/apps/29-fee/spec/05_events.md b/modules/apps/29-fee/spec/05_events.md new file mode 100644 index 00000000000..eb988be063a --- /dev/null +++ b/modules/apps/29-fee/spec/05_events.md @@ -0,0 +1,6 @@ + + +# Events + diff --git a/modules/apps/29-fee/spec/06_metrics.md b/modules/apps/29-fee/spec/06_metrics.md new file mode 100644 index 00000000000..24996d36b3d --- /dev/null +++ b/modules/apps/29-fee/spec/06_metrics.md @@ -0,0 +1,6 @@ + + +# Metrics + diff --git a/modules/apps/29-fee/spec/07_params.md b/modules/apps/29-fee/spec/07_params.md new file mode 100644 index 00000000000..77d7696b559 --- /dev/null +++ b/modules/apps/29-fee/spec/07_params.md @@ -0,0 +1,6 @@ + + +# Parameters + diff --git a/modules/apps/29-fee/spec/README.md b/modules/apps/29-fee/spec/README.md new file mode 100644 index 00000000000..c5c926f7b9f --- /dev/null +++ b/modules/apps/29-fee/spec/README.md @@ -0,0 +1,11 @@ + + +# `29-fee` + +## Abstract + diff --git a/modules/apps/29-fee/types/codec.go b/modules/apps/29-fee/types/codec.go new file mode 100644 index 00000000000..c9dac580ea5 --- /dev/null +++ b/modules/apps/29-fee/types/codec.go @@ -0,0 +1,16 @@ +package types + +/* +import ( + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" +) + +// RegisterInterfaces register the 29-fee module interfaces to protobuf +// Any. +func RegisterInterfaces(registry codectypes.InterfaceRegistry) { + // registry.RegisterImplementations((*sdk.Msg)(nil), &Msg{}) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} +*/ diff --git a/modules/apps/29-fee/types/errors.go b/modules/apps/29-fee/types/errors.go new file mode 100644 index 00000000000..9ed5755a9a0 --- /dev/null +++ b/modules/apps/29-fee/types/errors.go @@ -0,0 +1,8 @@ +package types + +import ( +// sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// 29-fee sentinel errors +var () diff --git a/modules/apps/29-fee/types/events.go b/modules/apps/29-fee/types/events.go new file mode 100644 index 00000000000..4b8d5ee8c95 --- /dev/null +++ b/modules/apps/29-fee/types/events.go @@ -0,0 +1,4 @@ +package types + +// 29-fee events +const () diff --git a/modules/apps/29-fee/types/expected_keepers.go b/modules/apps/29-fee/types/expected_keepers.go new file mode 100644 index 00000000000..68513b476a5 --- /dev/null +++ b/modules/apps/29-fee/types/expected_keepers.go @@ -0,0 +1,50 @@ +package types + +/* +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/types" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + connectiontypes "github.com/cosmos/ibc-go/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/modules/core/04-channel/types" + ibcexported "github.com/cosmos/ibc-go/modules/core/exported" +) + +// AccountKeeper defines the contract required for account APIs. +type AccountKeeper interface { + GetModuleAddress(name string) sdk.AccAddress + GetModuleAccount(ctx sdk.Context, name string) types.ModuleAccountI +} + +// BankKeeper defines the expected bank keeper +type BankKeeper interface { + SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error + MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error + BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error + SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error + SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error +} + +// ChannelKeeper defines the expected IBC channel keeper +type ChannelKeeper interface { + GetChannel(ctx sdk.Context, srcPort, srcChan string) (channel channeltypes.Channel, found bool) + GetNextSequenceSend(ctx sdk.Context, portID, channelID string) (uint64, bool) + SendPacket(ctx sdk.Context, channelCap *capabilitytypes.Capability, packet ibcexported.PacketI) error + ChanCloseInit(ctx sdk.Context, portID, channelID string, chanCap *capabilitytypes.Capability) error +} + +// ClientKeeper defines the expected IBC client keeper +type ClientKeeper interface { + GetClientConsensusState(ctx sdk.Context, clientID string) (connection ibcexported.ConsensusState, found bool) +} + +// ConnectionKeeper defines the expected IBC connection keeper +type ConnectionKeeper interface { + GetConnection(ctx sdk.Context, connectionID string) (connection connectiontypes.ConnectionEnd, found bool) +} + +// PortKeeper defines the expected IBC port keeper +type PortKeeper interface { + BindPort(ctx sdk.Context, portID string) *capabilitytypes.Capability +} +*/ diff --git a/modules/apps/29-fee/types/genesis.go b/modules/apps/29-fee/types/genesis.go new file mode 100644 index 00000000000..4086325bebb --- /dev/null +++ b/modules/apps/29-fee/types/genesis.go @@ -0,0 +1,29 @@ +package types + +/* +import ( + host "github.com/cosmos/ibc-go/modules/core/24-host" +) + +// NewGenesisState creates a 29-fee GenesisState instance. +func NewGenesisState(portID string, denomTraces Traces, params Params) *GenesisState { + return &GenesisState{ + Params: params, + } +} + +// DefaultGenesisState returns a GenesisState with "transfer" as the default PortID. +func DefaultGenesisState() *GenesisState { + return &GenesisState{ + } +} + +// Validate performs basic genesis state validation returning an error upon any +// failure. +func (gs GenesisState) Validate() error { + if err := host.PortIdentifierValidator(gs.PortId); err != nil { + return err + } + return nil +} +*/ diff --git a/modules/apps/29-fee/types/genesis_test.go b/modules/apps/29-fee/types/genesis_test.go new file mode 100644 index 00000000000..30516612cce --- /dev/null +++ b/modules/apps/29-fee/types/genesis_test.go @@ -0,0 +1,49 @@ +package types_test + +/* +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/ibc-go/modules/apps/transfer/types" +) + +func TestValidateGenesis(t *testing.T) { + testCases := []struct { + name string + genState *types.GenesisState + expPass bool + }{ + { + name: "default", + genState: types.DefaultGenesisState(), + expPass: true, + }, + { + "valid genesis", + &types.GenesisState{ + PortId: "portidone", + }, + true, + }, + { + "invalid client", + &types.GenesisState{ + PortId: "(INVALIDPORT)", + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + err := tc.genState.Validate() + if tc.expPass { + require.NoError(t, err, tc.name) + } else { + require.Error(t, err, tc.name) + } + } +} +*/ diff --git a/modules/apps/29-fee/types/keys.go b/modules/apps/29-fee/types/keys.go new file mode 100644 index 00000000000..769113f504a --- /dev/null +++ b/modules/apps/29-fee/types/keys.go @@ -0,0 +1,15 @@ +package types + +const ( + // ModuleName defines the 29-fee name + ModuleName = "ibcfee" + + // StoreKey is the store key string for IBC transfer + StoreKey = ModuleName + + // RouterKey is the message route for IBC transfer + RouterKey = ModuleName + + // QuerierRoute is the querier route for IBC transfer + QuerierRoute = ModuleName +) diff --git a/modules/apps/29-fee/types/msgs.go b/modules/apps/29-fee/types/msgs.go new file mode 100644 index 00000000000..4df0e84ff1a --- /dev/null +++ b/modules/apps/29-fee/types/msgs.go @@ -0,0 +1,32 @@ +package types + +/* +import ( + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clienttypes "github.com/cosmos/ibc-go/modules/core/02-client/types" + host "github.com/cosmos/ibc-go/modules/core/24-host" +) + +// NewMsg +func NewMsg() *Msg { + return &Msg{ + } +} + +// ValidateBasic performs a basic check of the Msg fields. +func (msg Msg) ValidateBasic() error { + return nil +} + +// GetSigners implements sdk.Msg +func (msg MsgTransfer) GetSigners() []sdk.AccAddress { + signer, err := sdk.AccAddressFromBech32(msg.Sender) + if err != nil { + panic(err) + } + return []sdk.AccAddress{signer} +} +*/ diff --git a/modules/apps/29-fee/types/msgs_test.go b/modules/apps/29-fee/types/msgs_test.go new file mode 100644 index 00000000000..132bde75752 --- /dev/null +++ b/modules/apps/29-fee/types/msgs_test.go @@ -0,0 +1,36 @@ +package types + +/* +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func (suite *TypesTestSuite) TestMsgValidateBasic() { + testCases := []struct { + name string + msg *types.Msg + expPass bool + }{ + {"", types.NewMsg(), true}, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + err := tc.msg.ValidateBasic() + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} +*/ diff --git a/modules/core/04-channel/keeper/events.go b/modules/core/04-channel/keeper/events.go new file mode 100644 index 00000000000..13c92246f02 --- /dev/null +++ b/modules/core/04-channel/keeper/events.go @@ -0,0 +1,85 @@ +package keeper + +import ( + "encoding/hex" + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/ibc-go/modules/core/04-channel/types" + "github.com/cosmos/ibc-go/modules/core/exported" +) + +// EmitRecvPacketEvent emits a receive packet event. It will be emitted both the first time a packet +// is received for a certain sequence and for all duplicate receives. +func EmitRecvPacketEvent(ctx sdk.Context, packet exported.PacketI, channel types.Channel) { + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeRecvPacket, + sdk.NewAttribute(types.AttributeKeyData, string(packet.GetData())), // DEPRECATED + sdk.NewAttribute(types.AttributeKeyDataHex, hex.EncodeToString(packet.GetData())), + sdk.NewAttribute(types.AttributeKeyTimeoutHeight, packet.GetTimeoutHeight().String()), + sdk.NewAttribute(types.AttributeKeyTimeoutTimestamp, fmt.Sprintf("%d", packet.GetTimeoutTimestamp())), + sdk.NewAttribute(types.AttributeKeySequence, fmt.Sprintf("%d", packet.GetSequence())), + sdk.NewAttribute(types.AttributeKeySrcPort, packet.GetSourcePort()), + sdk.NewAttribute(types.AttributeKeySrcChannel, packet.GetSourceChannel()), + sdk.NewAttribute(types.AttributeKeyDstPort, packet.GetDestPort()), + sdk.NewAttribute(types.AttributeKeyDstChannel, packet.GetDestChannel()), + sdk.NewAttribute(types.AttributeKeyChannelOrdering, channel.Ordering.String()), + // we only support 1-hop packets now, and that is the most important hop for a relayer + // (is it going to a chain I am connected to) + sdk.NewAttribute(types.AttributeKeyConnection, channel.ConnectionHops[0]), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + ), + }) +} + +// EmitAcknowledgePacketEvent emits an acknowledge packet event. It will be emitted both the first time +// a packet is acknowledged for a certain sequence and for all duplicate acknowledgements. +func EmitAcknowledgePacketEvent(ctx sdk.Context, packet exported.PacketI, channel types.Channel) { + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeAcknowledgePacket, + sdk.NewAttribute(types.AttributeKeyTimeoutHeight, packet.GetTimeoutHeight().String()), + sdk.NewAttribute(types.AttributeKeyTimeoutTimestamp, fmt.Sprintf("%d", packet.GetTimeoutTimestamp())), + sdk.NewAttribute(types.AttributeKeySequence, fmt.Sprintf("%d", packet.GetSequence())), + sdk.NewAttribute(types.AttributeKeySrcPort, packet.GetSourcePort()), + sdk.NewAttribute(types.AttributeKeySrcChannel, packet.GetSourceChannel()), + sdk.NewAttribute(types.AttributeKeyDstPort, packet.GetDestPort()), + sdk.NewAttribute(types.AttributeKeyDstChannel, packet.GetDestChannel()), + sdk.NewAttribute(types.AttributeKeyChannelOrdering, channel.Ordering.String()), + // we only support 1-hop packets now, and that is the most important hop for a relayer + // (is it going to a chain I am connected to) + sdk.NewAttribute(types.AttributeKeyConnection, channel.ConnectionHops[0]), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + ), + }) +} + +// EmitTimeoutPacketEvent emits a timeout packet event. It will be emitted both the first time a packet +// is timed out for a certain sequence and for all duplicate timeouts. +func EmitTimeoutPacketEvent(ctx sdk.Context, packet exported.PacketI, channel types.Channel) { + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeTimeoutPacket, + sdk.NewAttribute(types.AttributeKeyTimeoutHeight, packet.GetTimeoutHeight().String()), + sdk.NewAttribute(types.AttributeKeyTimeoutTimestamp, fmt.Sprintf("%d", packet.GetTimeoutTimestamp())), + sdk.NewAttribute(types.AttributeKeySequence, fmt.Sprintf("%d", packet.GetSequence())), + sdk.NewAttribute(types.AttributeKeySrcPort, packet.GetSourcePort()), + sdk.NewAttribute(types.AttributeKeySrcChannel, packet.GetSourceChannel()), + sdk.NewAttribute(types.AttributeKeyDstPort, packet.GetDestPort()), + sdk.NewAttribute(types.AttributeKeyDstChannel, packet.GetDestChannel()), + sdk.NewAttribute(types.AttributeKeyChannelOrdering, channel.Ordering.String()), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + ), + }) +} diff --git a/modules/core/04-channel/keeper/packet.go b/modules/core/04-channel/keeper/packet.go index 571b7ae3b53..5cfc7e3c14f 100644 --- a/modules/core/04-channel/keeper/packet.go +++ b/modules/core/04-channel/keeper/packet.go @@ -249,10 +249,11 @@ func (k Keeper) RecvPacket( // check if the packet receipt has been received already for unordered channels _, found := k.GetPacketReceipt(ctx, packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) if found { - return sdkerrors.Wrapf( - types.ErrPacketReceived, - "packet sequence (%d)", packet.GetSequence(), - ) + EmitRecvPacketEvent(ctx, packet, channel) + // This error indicates that the packet has already been relayed. Core IBC will + // treat this error as a no-op in order to prevent an entire relay transaction + // from failing and consuming unnecessary fees. + return types.ErrNoOpMsg } // All verification complete, update state @@ -271,12 +272,12 @@ func (k Keeper) RecvPacket( ) } - // helpful error message for relayers if packet.GetSequence() < nextSequenceRecv { - return sdkerrors.Wrapf( - types.ErrPacketReceived, - "packet sequence (%d), next sequence receive (%d)", packet.GetSequence(), nextSequenceRecv, - ) + EmitRecvPacketEvent(ctx, packet, channel) + // This error indicates that the packet has already been relayed. Core IBC will + // treat this error as a no-op in order to prevent an entire relay transaction + // from failing and consuming unnecessary fees. + return types.ErrNoOpMsg } if packet.GetSequence() != nextSequenceRecv { @@ -300,28 +301,7 @@ func (k Keeper) RecvPacket( k.Logger(ctx).Info("packet received", "packet", fmt.Sprintf("%v", packet)) // emit an event that the relayer can query for - ctx.EventManager().EmitEvents(sdk.Events{ - sdk.NewEvent( - types.EventTypeRecvPacket, - sdk.NewAttribute(types.AttributeKeyData, string(packet.GetData())), // DEPRECATED - sdk.NewAttribute(types.AttributeKeyDataHex, hex.EncodeToString(packet.GetData())), - sdk.NewAttribute(types.AttributeKeyTimeoutHeight, packet.GetTimeoutHeight().String()), - sdk.NewAttribute(types.AttributeKeyTimeoutTimestamp, fmt.Sprintf("%d", packet.GetTimeoutTimestamp())), - sdk.NewAttribute(types.AttributeKeySequence, fmt.Sprintf("%d", packet.GetSequence())), - sdk.NewAttribute(types.AttributeKeySrcPort, packet.GetSourcePort()), - sdk.NewAttribute(types.AttributeKeySrcChannel, packet.GetSourceChannel()), - sdk.NewAttribute(types.AttributeKeyDstPort, packet.GetDestPort()), - sdk.NewAttribute(types.AttributeKeyDstChannel, packet.GetDestChannel()), - sdk.NewAttribute(types.AttributeKeyChannelOrdering, channel.Ordering.String()), - // we only support 1-hop packets now, and that is the most important hop for a relayer - // (is it going to a chain I am connected to) - sdk.NewAttribute(types.AttributeKeyConnection, channel.ConnectionHops[0]), - ), - sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), - ), - }) + EmitRecvPacketEvent(ctx, packet, channel) return nil } @@ -480,7 +460,12 @@ func (k Keeper) AcknowledgePacket( commitment := k.GetPacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) if len(commitment) == 0 { - return sdkerrors.Wrapf(types.ErrPacketCommitmentNotFound, "packet with sequence (%d) has been acknowledged, or timed out. In rare cases, the packet referenced was never sent, likely due to the relayer being misconfigured", packet.GetSequence()) + EmitAcknowledgePacketEvent(ctx, packet, channel) + // This error indicates that the acknowledgement has already been relayed + // or there is a misconfigured relayer attempting to prove an acknowledgement + // for a packet never sent. Core IBC will treat this error as a no-op in order to + // prevent an entire relay transaction from failing and consuming unnecessary fees. + return types.ErrNoOpMsg } packetCommitment := types.CommitPacket(k.cdc, packet) @@ -530,26 +515,7 @@ func (k Keeper) AcknowledgePacket( k.Logger(ctx).Info("packet acknowledged", "packet", fmt.Sprintf("%v", packet)) // emit an event marking that we have processed the acknowledgement - ctx.EventManager().EmitEvents(sdk.Events{ - sdk.NewEvent( - types.EventTypeAcknowledgePacket, - sdk.NewAttribute(types.AttributeKeyTimeoutHeight, packet.GetTimeoutHeight().String()), - sdk.NewAttribute(types.AttributeKeyTimeoutTimestamp, fmt.Sprintf("%d", packet.GetTimeoutTimestamp())), - sdk.NewAttribute(types.AttributeKeySequence, fmt.Sprintf("%d", packet.GetSequence())), - sdk.NewAttribute(types.AttributeKeySrcPort, packet.GetSourcePort()), - sdk.NewAttribute(types.AttributeKeySrcChannel, packet.GetSourceChannel()), - sdk.NewAttribute(types.AttributeKeyDstPort, packet.GetDestPort()), - sdk.NewAttribute(types.AttributeKeyDstChannel, packet.GetDestChannel()), - sdk.NewAttribute(types.AttributeKeyChannelOrdering, channel.Ordering.String()), - // we only support 1-hop packets now, and that is the most important hop for a relayer - // (is it going to a chain I am connected to) - sdk.NewAttribute(types.AttributeKeyConnection, channel.ConnectionHops[0]), - ), - sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), - ), - }) + EmitAcknowledgePacketEvent(ctx, packet, channel) return nil } diff --git a/modules/core/04-channel/keeper/packet_test.go b/modules/core/04-channel/keeper/packet_test.go index d1cb11370c3..9ef7798140d 100644 --- a/modules/core/04-channel/keeper/packet_test.go +++ b/modules/core/04-channel/keeper/packet_test.go @@ -267,8 +267,8 @@ func (suite *KeeperTestSuite) TestRecvPacket() { // attempts to receive packet 2 without receiving packet 1 channelCap = suite.chainB.GetChannelCapability(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID) }, true}, - {"packet already relayed ORDERED channel", func() { - expError = types.ErrPacketReceived + {"packet already relayed ORDERED channel (no-op)", func() { + expError = types.ErrNoOpMsg path.SetChannelOrdered() suite.coordinator.Setup(path) @@ -281,8 +281,8 @@ func (suite *KeeperTestSuite) TestRecvPacket() { err = path.EndpointB.RecvPacket(packet.(types.Packet)) suite.Require().NoError(err) }, false}, - {"packet already relayed UNORDERED channel", func() { - expError = types.ErrPacketReceived + {"packet already relayed UNORDERED channel (no-op)", func() { + expError = types.ErrNoOpMsg // setup uses an UNORDERED channel suite.coordinator.Setup(path) @@ -428,7 +428,7 @@ func (suite *KeeperTestSuite) TestRecvPacket() { path.EndpointB.UpdateClient() }, false}, {"receipt already stored", func() { - expError = types.ErrPacketReceived + expError = types.ErrNoOpMsg suite.coordinator.Setup(path) packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) @@ -617,8 +617,8 @@ func (suite *KeeperTestSuite) TestAcknowledgePacket() { channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) }, true}, - {"packet already acknowledged ordered channel", func() { - expError = types.ErrPacketCommitmentNotFound + {"packet already acknowledged ordered channel (no-op)", func() { + expError = types.ErrNoOpMsg path.SetChannelOrdered() suite.coordinator.Setup(path) @@ -636,8 +636,8 @@ func (suite *KeeperTestSuite) TestAcknowledgePacket() { err = path.EndpointA.AcknowledgePacket(packet, ack.Acknowledgement()) suite.Require().NoError(err) }, false}, - {"packet already acknowledged unordered channel", func() { - expError = types.ErrPacketCommitmentNotFound + {"packet already acknowledged unordered channel (no-op)", func() { + expError = types.ErrNoOpMsg // setup uses an UNORDERED channel suite.coordinator.Setup(path) @@ -738,7 +738,7 @@ func (suite *KeeperTestSuite) TestAcknowledgePacket() { channelCap = suite.chainA.GetChannelCapability(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID) }, false}, {"packet hasn't been sent", func() { - expError = types.ErrPacketCommitmentNotFound + expError = types.ErrNoOpMsg // packet commitment never written suite.coordinator.Setup(path) diff --git a/modules/core/04-channel/keeper/timeout.go b/modules/core/04-channel/keeper/timeout.go index caf4e037cf8..93fa9eca4fe 100644 --- a/modules/core/04-channel/keeper/timeout.go +++ b/modules/core/04-channel/keeper/timeout.go @@ -34,13 +34,6 @@ func (k Keeper) TimeoutPacket( ) } - if channel.State != types.OPEN { - return sdkerrors.Wrapf( - types.ErrInvalidChannelState, - "channel state is not OPEN (got %s)", channel.State.String(), - ) - } - // NOTE: TimeoutPacket is called by the AnteHandler which acts upon the packet.Route(), // so the capability authentication can be omitted here @@ -81,7 +74,19 @@ func (k Keeper) TimeoutPacket( commitment := k.GetPacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) if len(commitment) == 0 { - return sdkerrors.Wrapf(types.ErrPacketCommitmentNotFound, "packet with sequence (%d) has been acknowledged or timed out. In rare cases, the packet referenced was never sent, likely due to the relayer being misconfigured", packet.GetSequence()) + EmitTimeoutPacketEvent(ctx, packet, channel) + // This error indicates that the timeout has already been relayed + // or there is a misconfigured relayer attempting to prove a timeout + // for a packet never sent. Core IBC will treat this error as a no-op in order to + // prevent an entire relay transaction from failing and consuming unnecessary fees. + return types.ErrNoOpMsg + } + + if channel.State != types.OPEN { + return sdkerrors.Wrapf( + types.ErrInvalidChannelState, + "channel state is not OPEN (got %s)", channel.State.String(), + ) } packetCommitment := types.CommitPacket(k.cdc, packet) @@ -155,23 +160,7 @@ func (k Keeper) TimeoutExecuted( k.Logger(ctx).Info("packet timed-out", "packet", fmt.Sprintf("%v", packet)) // emit an event marking that we have processed the timeout - ctx.EventManager().EmitEvents(sdk.Events{ - sdk.NewEvent( - types.EventTypeTimeoutPacket, - sdk.NewAttribute(types.AttributeKeyTimeoutHeight, packet.GetTimeoutHeight().String()), - sdk.NewAttribute(types.AttributeKeyTimeoutTimestamp, fmt.Sprintf("%d", packet.GetTimeoutTimestamp())), - sdk.NewAttribute(types.AttributeKeySequence, fmt.Sprintf("%d", packet.GetSequence())), - sdk.NewAttribute(types.AttributeKeySrcPort, packet.GetSourcePort()), - sdk.NewAttribute(types.AttributeKeySrcChannel, packet.GetSourceChannel()), - sdk.NewAttribute(types.AttributeKeyDstPort, packet.GetDestPort()), - sdk.NewAttribute(types.AttributeKeyDstChannel, packet.GetDestChannel()), - sdk.NewAttribute(types.AttributeKeyChannelOrdering, channel.Ordering.String()), - ), - sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), - ), - }) + EmitTimeoutPacketEvent(ctx, packet, channel) return nil } @@ -222,6 +211,15 @@ func (k Keeper) TimeoutOnClose( commitment := k.GetPacketCommitment(ctx, packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + if len(commitment) == 0 { + EmitTimeoutPacketEvent(ctx, packet, channel) + // This error indicates that the timeout has already been relayed + // or there is a misconfigured relayer attempting to prove a timeout + // for a packet never sent. Core IBC will treat this error as a no-op in order to + // prevent an entire relay transaction from failing and consuming unnecessary fees. + return types.ErrNoOpMsg + } + packetCommitment := types.CommitPacket(k.cdc, packet) // verify we sent the packet and haven't cleared it out yet diff --git a/modules/core/04-channel/keeper/timeout_test.go b/modules/core/04-channel/keeper/timeout_test.go index 460e6097632..7cdb88cdc05 100644 --- a/modules/core/04-channel/keeper/timeout_test.go +++ b/modules/core/04-channel/keeper/timeout_test.go @@ -48,7 +48,7 @@ func (suite *KeeperTestSuite) TestTimeoutPacket() { path.EndpointA.UpdateClient() }, true}, {"packet already timed out: ORDERED", func() { - expError = types.ErrInvalidChannelState + expError = types.ErrNoOpMsg ordered = true path.SetChannelOrdered() @@ -62,7 +62,7 @@ func (suite *KeeperTestSuite) TestTimeoutPacket() { suite.Require().NoError(err) }, false}, {"packet already timed out: UNORDERED", func() { - expError = types.ErrPacketCommitmentNotFound + expError = types.ErrNoOpMsg ordered = false suite.coordinator.Setup(path) @@ -83,9 +83,13 @@ func (suite *KeeperTestSuite) TestTimeoutPacket() { {"channel not open", func() { expError = types.ErrInvalidChannelState suite.coordinator.Setup(path) - packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, disabledTimeoutTimestamp) + packet = types.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, path.EndpointA.GetClientState().GetLatestHeight().Increment().(clienttypes.Height), disabledTimeoutTimestamp) + err := path.EndpointA.SendPacket(packet) + suite.Require().NoError(err) + // need to update chainA's client representing chainB to prove missing ack + path.EndpointA.UpdateClient() - err := path.EndpointA.SetChannelClosed() + err = path.EndpointA.SetChannelClosed() suite.Require().NoError(err) }, false}, {"packet destination port ≠ channel counterparty port", func() { @@ -130,7 +134,7 @@ func (suite *KeeperTestSuite) TestTimeoutPacket() { path.EndpointA.UpdateClient() }, false}, {"packet hasn't been sent", func() { - expError = types.ErrPacketCommitmentNotFound + expError = types.ErrNoOpMsg ordered = true path.SetChannelOrdered() @@ -182,10 +186,12 @@ func (suite *KeeperTestSuite) TestTimeoutPacket() { orderedPacketKey := host.NextSequenceRecvKey(packet.GetDestPort(), packet.GetDestChannel()) unorderedPacketKey := host.PacketReceiptKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) - if ordered { - proof, proofHeight = suite.chainB.QueryProof(orderedPacketKey) - } else { - proof, proofHeight = suite.chainB.QueryProof(unorderedPacketKey) + if path.EndpointB.ConnectionID != "" { + if ordered { + proof, proofHeight = path.EndpointB.QueryProof(orderedPacketKey) + } else { + proof, proofHeight = path.EndpointB.QueryProof(unorderedPacketKey) + } } err := suite.chainA.App.GetIBCKeeper().ChannelKeeper.TimeoutPacket(suite.chainA.GetContext(), packet, proof, proofHeight, nextSeqRecv) diff --git a/modules/core/04-channel/types/errors.go b/modules/core/04-channel/types/errors.go index 74043730bf0..8149136ed56 100644 --- a/modules/core/04-channel/types/errors.go +++ b/modules/core/04-channel/types/errors.go @@ -33,4 +33,7 @@ var ( // Antehandler error ErrRedundantTx = sdkerrors.Register(SubModuleName, 22, "packet messages are redundant") + + // Perform a no-op on the current Msg + ErrNoOpMsg = sdkerrors.Register(SubModuleName, 23, "message is redundant, no-op will be performed") ) diff --git a/modules/core/keeper/msg_server.go b/modules/core/keeper/msg_server.go index ab9e583385c..69bde98b687 100644 --- a/modules/core/keeper/msg_server.go +++ b/modules/core/keeper/msg_server.go @@ -492,15 +492,29 @@ func (k Keeper) RecvPacket(goCtx context.Context, msg *channeltypes.MsgRecvPacke } // Perform TAO verification - if err := k.ChannelKeeper.RecvPacket(ctx, cap, msg.Packet, msg.ProofCommitment, msg.ProofHeight); err != nil { + // + // If the packet was already received, perform a no-op + // Use a cached context to prevent accidental state changes + cacheCtx, writeFn := ctx.CacheContext() + err = k.ChannelKeeper.RecvPacket(cacheCtx, cap, msg.Packet, msg.ProofCommitment, msg.ProofHeight) + + // NOTE: The context returned by CacheContext() refers to a new EventManager, so it needs to explicitly set events to the original context. + ctx.EventManager().EmitEvents(cacheCtx.EventManager().Events()) + + switch err { + case nil: + writeFn() + case channeltypes.ErrNoOpMsg: + return &channeltypes.MsgRecvPacketResponse{}, nil // no-op + default: return nil, sdkerrors.Wrap(err, "receive packet verification failed") } // Perform application logic callback + // // Cache context so that we may discard state changes from callback if the acknowledgement is unsuccessful. - cacheCtx, writeFn := ctx.CacheContext() + cacheCtx, writeFn = ctx.CacheContext() ack := cbs.OnRecvPacket(cacheCtx, msg.Packet, relayer) - // This doesn't cause duplicate events to be emitted. // NOTE: The context returned by CacheContext() refers to a new EventManager, so it needs to explicitly set events to the original context. // Events from callback are emitted regardless of acknowledgement success ctx.EventManager().EmitEvents(cacheCtx.EventManager().Events()) @@ -556,7 +570,21 @@ func (k Keeper) Timeout(goCtx context.Context, msg *channeltypes.MsgTimeout) (*c } // Perform TAO verification - if err := k.ChannelKeeper.TimeoutPacket(ctx, msg.Packet, msg.ProofUnreceived, msg.ProofHeight, msg.NextSequenceRecv); err != nil { + // + // If the timeout was already received, perform a no-op + // Use a cached context to prevent accidental state changes + cacheCtx, writeFn := ctx.CacheContext() + err = k.ChannelKeeper.TimeoutPacket(cacheCtx, msg.Packet, msg.ProofUnreceived, msg.ProofHeight, msg.NextSequenceRecv) + + // NOTE: The context returned by CacheContext() refers to a new EventManager, so it needs to explicitly set events to the original context. + ctx.EventManager().EmitEvents(cacheCtx.EventManager().Events()) + + switch err { + case nil: + writeFn() + case channeltypes.ErrNoOpMsg: + return &channeltypes.MsgTimeoutResponse{}, nil // no-op + default: return nil, sdkerrors.Wrap(err, "timeout packet verification failed") } @@ -610,11 +638,26 @@ func (k Keeper) TimeoutOnClose(goCtx context.Context, msg *channeltypes.MsgTimeo } // Perform TAO verification - if err := k.ChannelKeeper.TimeoutOnClose(ctx, cap, msg.Packet, msg.ProofUnreceived, msg.ProofClose, msg.ProofHeight, msg.NextSequenceRecv); err != nil { + // + // If the timeout was already received, perform a no-op + // Use a cached context to prevent accidental state changes + cacheCtx, writeFn := ctx.CacheContext() + err = k.ChannelKeeper.TimeoutOnClose(cacheCtx, cap, msg.Packet, msg.ProofUnreceived, msg.ProofClose, msg.ProofHeight, msg.NextSequenceRecv) + + // NOTE: The context returned by CacheContext() refers to a new EventManager, so it needs to explicitly set events to the original context. + ctx.EventManager().EmitEvents(cacheCtx.EventManager().Events()) + + switch err { + case nil: + writeFn() + case channeltypes.ErrNoOpMsg: + return &channeltypes.MsgTimeoutOnCloseResponse{}, nil // no-op + default: return nil, sdkerrors.Wrap(err, "timeout on close packet verification failed") } // Perform application logic callback + // // NOTE: MsgTimeout and MsgTimeoutOnClose use the same "OnTimeoutPacket" // application logic callback. err = cbs.OnTimeoutPacket(ctx, msg.Packet, relayer) @@ -666,7 +709,21 @@ func (k Keeper) Acknowledgement(goCtx context.Context, msg *channeltypes.MsgAckn } // Perform TAO verification - if err := k.ChannelKeeper.AcknowledgePacket(ctx, cap, msg.Packet, msg.Acknowledgement, msg.ProofAcked, msg.ProofHeight); err != nil { + // + // If the acknowledgement was already received, perform a no-op + // Use a cached context to prevent accidental state changes + cacheCtx, writeFn := ctx.CacheContext() + err = k.ChannelKeeper.AcknowledgePacket(cacheCtx, cap, msg.Packet, msg.Acknowledgement, msg.ProofAcked, msg.ProofHeight) + + // NOTE: The context returned by CacheContext() refers to a new EventManager, so it needs to explicitly set events to the original context. + ctx.EventManager().EmitEvents(cacheCtx.EventManager().Events()) + + switch err { + case nil: + writeFn() + case channeltypes.ErrNoOpMsg: + return &channeltypes.MsgAcknowledgementResponse{}, nil // no-op + default: return nil, sdkerrors.Wrap(err, "acknowledge packet verification failed") } diff --git a/modules/core/keeper/msg_server_test.go b/modules/core/keeper/msg_server_test.go index a337c1fb323..b28dc263689 100644 --- a/modules/core/keeper/msg_server_test.go +++ b/modules/core/keeper/msg_server_test.go @@ -141,7 +141,8 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { suite.coordinator.Setup(path) packet = channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) }, false, false}, - {"ORDERED: packet already received (replay)", func() { + {"successful no-op: ORDERED - packet already received (replay)", func() { + // mock will panic if application callback is called twice on the same packet path.SetChannelOrdered() suite.coordinator.Setup(path) packet = channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) @@ -151,8 +152,9 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { err = path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) - }, false, false}, - {"UNORDERED: packet already received (replay)", func() { + }, true, false}, + {"successful no-op: UNORDERED - packet already received (replay)", func() { + // mock will panic if application callback is called twice on the same packet suite.coordinator.Setup(path) packet = channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) @@ -161,7 +163,7 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { err = path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) - }, false, false}, + }, true, false}, } for _, tc := range testCases { @@ -174,9 +176,16 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { tc.malleate() + var ( + proof []byte + proofHeight clienttypes.Height + ) + // get proof of packet commitment from chainA packetKey := host.PacketCommitmentKey(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) - proof, proofHeight := suite.chainA.QueryProof(packetKey) + if path.EndpointA.ChannelID != "" { + proof, proofHeight = path.EndpointA.QueryProof(packetKey) + } msg := channeltypes.NewMsgRecvPacket(packet, proof, proofHeight, suite.chainB.SenderAccount.GetAddress().String()) @@ -185,12 +194,12 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { if tc.expPass { suite.Require().NoError(err) - // replay should fail since state changes occur + // replay should not fail since it will be treated as a no-op _, err := keeper.Keeper.RecvPacket(*suite.chainB.App.GetIBCKeeper(), sdk.WrapSDKContext(suite.chainB.GetContext()), msg) - suite.Require().Error(err) + suite.Require().NoError(err) // check that callback state was handled correctly - _, exists := suite.chainB.GetSimApp().ScopedIBCMockKeeper.GetCapability(suite.chainB.GetContext(), ibctesting.MockCanaryCapabilityName) + _, exists := suite.chainB.GetSimApp().ScopedIBCMockKeeper.GetCapability(suite.chainB.GetContext(), ibctesting.GetMockRecvCanaryCapabilityName(packet)) if tc.expRevert { suite.Require().False(exists, "capability exists in store even after callback reverted") } else { @@ -293,7 +302,7 @@ func (suite *KeeperTestSuite) TestHandleAcknowledgePacket() { err := path.EndpointA.SendPacket(packet) suite.Require().NoError(err) }, false}, - {"ORDERED: packet already acknowledged (replay)", func() { + {"successful no-op: ORDERED - packet already acknowledged (replay)", func() { suite.coordinator.Setup(path) packet = channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) @@ -305,8 +314,8 @@ func (suite *KeeperTestSuite) TestHandleAcknowledgePacket() { err = path.EndpointA.AcknowledgePacket(packet, ibctesting.MockAcknowledgement) suite.Require().NoError(err) - }, false}, - {"UNORDERED: packet already acknowledged (replay)", func() { + }, true}, + {"successful no-op: UNORDERED - packet already acknowledged (replay)", func() { suite.coordinator.Setup(path) packet = channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) @@ -319,7 +328,7 @@ func (suite *KeeperTestSuite) TestHandleAcknowledgePacket() { err = path.EndpointA.AcknowledgePacket(packet, ibctesting.MockAcknowledgement) suite.Require().NoError(err) - }, false}, + }, true}, } for _, tc := range testCases { @@ -331,8 +340,14 @@ func (suite *KeeperTestSuite) TestHandleAcknowledgePacket() { tc.malleate() + var ( + proof []byte + proofHeight clienttypes.Height + ) packetKey := host.PacketAcknowledgementKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) - proof, proofHeight := suite.chainB.QueryProof(packetKey) + if path.EndpointB.ChannelID != "" { + proof, proofHeight = path.EndpointB.QueryProof(packetKey) + } msg := channeltypes.NewMsgAcknowledgement(packet, ibcmock.MockAcknowledgement.Acknowledgement(), proof, proofHeight, suite.chainA.SenderAccount.GetAddress().String()) @@ -341,14 +356,13 @@ func (suite *KeeperTestSuite) TestHandleAcknowledgePacket() { if tc.expPass { suite.Require().NoError(err) - // replay should an error - _, err := keeper.Keeper.Acknowledgement(*suite.chainA.App.GetIBCKeeper(), sdk.WrapSDKContext(suite.chainA.GetContext()), msg) - suite.Require().Error(err) - // verify packet commitment was deleted on source chain has := suite.chainA.App.GetIBCKeeper().ChannelKeeper.HasPacketCommitment(suite.chainA.GetContext(), packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) suite.Require().False(has) + // replay should not error as it is treated as a no-op + _, err := keeper.Keeper.Acknowledgement(*suite.chainA.App.GetIBCKeeper(), sdk.WrapSDKContext(suite.chainA.GetContext()), msg) + suite.Require().NoError(err) } else { suite.Require().Error(err) } @@ -441,11 +455,11 @@ func (suite *KeeperTestSuite) TestHandleTimeoutPacket() { packetKey = host.NextSequenceRecvKey(packet.GetDestPort(), packet.GetDestChannel()) }, false}, - {"UNORDERED: packet not sent", func() { + {"successful no-op: UNORDERED - packet not sent", func() { suite.coordinator.Setup(path) - packet = channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) + packet = channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(0, 1), 0) packetKey = host.PacketReceiptKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) - }, false}, + }, true}, } for _, tc := range testCases { @@ -457,7 +471,13 @@ func (suite *KeeperTestSuite) TestHandleTimeoutPacket() { tc.malleate() - proof, proofHeight := suite.chainB.QueryProof(packetKey) + var ( + proof []byte + proofHeight clienttypes.Height + ) + if path.EndpointB.ChannelID != "" { + proof, proofHeight = path.EndpointB.QueryProof(packetKey) + } msg := channeltypes.NewMsgTimeout(packet, 1, proof, proofHeight, suite.chainA.SenderAccount.GetAddress().String()) @@ -466,9 +486,9 @@ func (suite *KeeperTestSuite) TestHandleTimeoutPacket() { if tc.expPass { suite.Require().NoError(err) - // replay should return an error + // replay should not return an error as it is treated as a no-op _, err := keeper.Keeper.Timeout(*suite.chainA.App.GetIBCKeeper(), sdk.WrapSDKContext(suite.chainA.GetContext()), msg) - suite.Require().Error(err) + suite.Require().NoError(err) // verify packet commitment was deleted on source chain has := suite.chainA.App.GetIBCKeeper().ChannelKeeper.HasPacketCommitment(suite.chainA.GetContext(), packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) @@ -577,14 +597,14 @@ func (suite *KeeperTestSuite) TestHandleTimeoutOnClosePacket() { packetKey = host.NextSequenceRecvKey(packet.GetDestPort(), packet.GetDestChannel()) }, false}, - {"UNORDERED: packet not sent", func() { + {"successful no-op: UNORDERED - packet not sent", func() { suite.coordinator.Setup(path) - packet = channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) + packet = channeltypes.NewPacket(ibctesting.MockPacketData, 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(0, 1), 0) packetKey = host.PacketAcknowledgementKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) // close counterparty channel path.EndpointB.SetChannelClosed() - }, false}, + }, true}, {"ORDERED: channel not closed", func() { path.SetChannelOrdered() suite.coordinator.Setup(path) @@ -622,9 +642,9 @@ func (suite *KeeperTestSuite) TestHandleTimeoutOnClosePacket() { if tc.expPass { suite.Require().NoError(err) - // replay should return an error + // replay should not return an error as it will be treated as a no-op _, err := keeper.Keeper.TimeoutOnClose(*suite.chainA.App.GetIBCKeeper(), sdk.WrapSDKContext(suite.chainA.GetContext()), msg) - suite.Require().Error(err) + suite.Require().NoError(err) // verify packet commitment was deleted on source chain has := suite.chainA.App.GetIBCKeeper().ChannelKeeper.HasPacketCommitment(suite.chainA.GetContext(), packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) diff --git a/testing/mock/mock.go b/testing/mock/mock.go index 9edf5d3536a..fe527e1c22d 100644 --- a/testing/mock/mock.go +++ b/testing/mock/mock.go @@ -3,6 +3,7 @@ package mock import ( "bytes" "encoding/json" + "strconv" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" @@ -27,12 +28,14 @@ const ( ) var ( - MockAcknowledgement = channeltypes.NewResultAcknowledgement([]byte("mock acknowledgement")) - MockFailAcknowledgement = channeltypes.NewErrorAcknowledgement("mock failed acknowledgement") - MockPacketData = []byte("mock packet data") - MockFailPacketData = []byte("mock failed packet data") - MockAsyncPacketData = []byte("mock async packet data") - MockCanaryCapabilityName = "mock canary capability name" + MockAcknowledgement = channeltypes.NewResultAcknowledgement([]byte("mock acknowledgement")) + MockFailAcknowledgement = channeltypes.NewErrorAcknowledgement("mock failed acknowledgement") + MockPacketData = []byte("mock packet data") + MockFailPacketData = []byte("mock failed packet data") + MockAsyncPacketData = []byte("mock async packet data") + MockRecvCanaryCapabilityName = "mock receive canary capability name" + MockAckCanaryCapabilityName = "mock acknowledgement canary capability name" + MockTimeoutCanaryCapabilityName = "mock timeout canary capability name" ) var _ porttypes.IBCModule = AppModule{} @@ -194,7 +197,12 @@ func (am AppModule) OnChanCloseConfirm(sdk.Context, string, string) error { // OnRecvPacket implements the IBCModule interface. func (am AppModule) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet, relayer sdk.AccAddress) exported.Acknowledgement { // set state by claiming capability to check if revert happens return - am.scopedKeeper.NewCapability(ctx, MockCanaryCapabilityName) + _, err := am.scopedKeeper.NewCapability(ctx, MockRecvCanaryCapabilityName+strconv.Itoa(int(packet.GetSequence()))) + if err != nil { + // application callback called twice on same packet sequence + // must never occur + panic(err) + } if bytes.Equal(MockPacketData, packet.GetData()) { return MockAcknowledgement } else if bytes.Equal(MockAsyncPacketData, packet.GetData()) { @@ -205,11 +213,25 @@ func (am AppModule) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet, re } // OnAcknowledgementPacket implements the IBCModule interface. -func (am AppModule) OnAcknowledgementPacket(sdk.Context, channeltypes.Packet, []byte, sdk.AccAddress) error { +func (am AppModule) OnAcknowledgementPacket(ctx sdk.Context, packet channeltypes.Packet, _ []byte, _ sdk.AccAddress) error { + _, err := am.scopedKeeper.NewCapability(ctx, MockAckCanaryCapabilityName+strconv.Itoa(int(packet.GetSequence()))) + if err != nil { + // application callback called twice on same packet sequence + // must never occur + panic(err) + } + return nil } // OnTimeoutPacket implements the IBCModule interface. -func (am AppModule) OnTimeoutPacket(sdk.Context, channeltypes.Packet, sdk.AccAddress) error { +func (am AppModule) OnTimeoutPacket(ctx sdk.Context, packet channeltypes.Packet, _ sdk.AccAddress) error { + _, err := am.scopedKeeper.NewCapability(ctx, MockTimeoutCanaryCapabilityName+strconv.Itoa(int(packet.GetSequence()))) + if err != nil { + // application callback called twice on same packet sequence + // must never occur + panic(err) + } + return nil } diff --git a/testing/values.go b/testing/values.go index 5ba3ab1dd29..7a66b683826 100644 --- a/testing/values.go +++ b/testing/values.go @@ -5,12 +5,14 @@ package ibctesting import ( + "strconv" "time" sdk "github.com/cosmos/cosmos-sdk/types" ibctransfertypes "github.com/cosmos/ibc-go/modules/apps/transfer/types" connectiontypes "github.com/cosmos/ibc-go/modules/core/03-connection/types" + channeltypes "github.com/cosmos/ibc-go/modules/core/04-channel/types" commitmenttypes "github.com/cosmos/ibc-go/modules/core/23-commitment/types" ibctmtypes "github.com/cosmos/ibc-go/modules/light-clients/07-tendermint/types" "github.com/cosmos/ibc-go/testing/mock" @@ -50,10 +52,14 @@ var ( ConnectionVersion = connectiontypes.ExportedVersionsToProto(connectiontypes.GetCompatibleVersions())[0] - MockAcknowledgement = mock.MockAcknowledgement.Acknowledgement() - MockPacketData = mock.MockPacketData - MockFailPacketData = mock.MockFailPacketData - MockCanaryCapabilityName = mock.MockCanaryCapabilityName + MockAcknowledgement = mock.MockAcknowledgement.Acknowledgement() + MockPacketData = mock.MockPacketData + MockFailPacketData = mock.MockFailPacketData + MockRecvCanaryCapabilityName = mock.MockRecvCanaryCapabilityName prefix = commitmenttypes.NewMerklePrefix([]byte("ibc")) ) + +func GetMockRecvCanaryCapabilityName(packet channeltypes.Packet) string { + return MockRecvCanaryCapabilityName + strconv.Itoa(int(packet.GetSequence())) +}