Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: middleware support for ICS20 #533

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ Ref: https://keepachangelog.com/en/1.0.0/
* [\#404](https://github.com/cosmos/ibc-go/pull/404) Bump Go version to 1.17

### API Breaking


* (transfer) [\#533](https://github.com/cosmos/ibc-go/pull/533) Add ICS30 Middleware support to transfer module.
* (core) [\#650](https://github.com/cosmos/ibc-go/pull/650) Modify `OnChanOpenTry` IBC application module callback to return the negotiated app version. The version passed into the `MsgChanOpenTry` has been deprecated and will be ignored by core IBC.
* (core) [\#629](https://github.com/cosmos/ibc-go/pull/629) Removes the `GetProofSpecs` from the ClientState interface. This function was previously unused by core IBC.
* (transfer) [\#517](https://github.com/cosmos/ibc-go/pull/517) Separates the ICS 26 callback functions from `AppModule` into a new type `IBCModule` for ICS 20 transfer.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package keeper

import (
"context"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/controller/types"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ func (k Keeper) createOutgoingPacket(
return 0, sdkerrors.Wrapf(channeltypes.ErrSequenceSendNotFound, "failed to retrieve next sequence send for channel %s on port %s", sourceChannel, sourcePort)
}

// timeoutTimestamp is set to be a max number here so that we never recieve a timeout
// ics-27-1 uses ordered channels which can close upon recieving a timeout, which is an undesired effect
// timeoutTimestamp is set to be a max number here so that we never receive a timeout
// ics-27-1 uses ordered channels which can close upon receiving a timeout, which is an undesired effect
const timeoutTimestamp = ^uint64(0) >> 1 // Shift the unsigned bit to satisfy hermes relayer timestamp conversion

packet := channeltypes.NewPacket(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package keeper

import (
"context"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/ibc-go/v3/modules/apps/27-interchain-accounts/host/types"
)
Expand Down
37 changes: 33 additions & 4 deletions modules/apps/transfer/ibc_module.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
// IBCModule implements the ICS26 interface for transfer given the transfer keeper.
type IBCModule struct {
keeper keeper.Keeper
app porttypes.IBCModule
}

// NewIBCModule creates a new IBCModule given the keeper
Expand Down Expand Up @@ -60,6 +61,14 @@ func ValidateTransferChannelParams(
return nil
}

// SetMiddleware sets ICS30 middleware
func (im *IBCModule) SetMiddleware(app porttypes.IBCModule) {
if im.app != nil {
panic("middleware already set for transfer app")
}
im.app = app
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
}

// OnChanOpenInit implements the IBCModule interface
func (im IBCModule) OnChanOpenInit(
ctx sdk.Context,
Expand All @@ -84,7 +93,13 @@ func (im IBCModule) OnChanOpenInit(
return err
}

return nil
if im.app == nil {
return nil
}

// call underlying app's OnChanOpenInit callback with the appVersion
return im.app.OnChanOpenInit(ctx, order, connectionHops, portID, channelID,
chanCap, counterparty, version)
}

// OnChanOpenTry implements the IBCModule interface.
Expand Down Expand Up @@ -130,7 +145,12 @@ func (im IBCModule) OnChanOpenAck(
if counterpartyVersion != types.Version {
return sdkerrors.Wrapf(types.ErrInvalidVersion, "invalid counterparty version: %s, expected %s", counterpartyVersion, types.Version)
}
return nil

if im.app == nil {
return nil
}
// call underlying app's OnChanOpenAck callback with the counterparty app version.
return im.app.OnChanOpenAck(ctx, portID, channelID, counterpartyVersion)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reasoning for now adding callbacks for OnChanOpenTry and OnChanOpenConfirm?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not much reason beside adding more flexibility, might be useful for the wired app? if not it should return nil anyways?

}

// OnChanOpenConfirm implements the IBCModule interface
Expand Down Expand Up @@ -248,7 +268,12 @@ func (im IBCModule) OnAcknowledgementPacket(
)
}

return nil
if im.app == nil {
return nil
}

// call underlying app's OnAcknowledgementPacket callback.
return im.app.OnAcknowledgementPacket(ctx, packet, acknowledgement, relayer)
}

// OnTimeoutPacket implements the IBCModule interface
Expand Down Expand Up @@ -276,5 +301,9 @@ func (im IBCModule) OnTimeoutPacket(
),
)

return nil
if im.app == nil {
return nil
}

return im.app.OnTimeoutPacket(ctx, packet, relayer)
}
4 changes: 3 additions & 1 deletion modules/apps/transfer/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type Keeper struct {
cdc codec.BinaryCodec
paramSpace paramtypes.Subspace

ics4Wrapper types.ICS4Wrapper
channelKeeper types.ChannelKeeper
portKeeper types.PortKeeper
authKeeper types.AccountKeeper
Expand All @@ -31,7 +32,7 @@ type Keeper struct {
// NewKeeper creates a new IBC transfer Keeper instance
func NewKeeper(
cdc codec.BinaryCodec, key sdk.StoreKey, paramSpace paramtypes.Subspace,
channelKeeper types.ChannelKeeper, portKeeper types.PortKeeper,
ics4Wrapper types.ICS4Wrapper, channelKeeper types.ChannelKeeper, portKeeper types.PortKeeper,
authKeeper types.AccountKeeper, bankKeeper types.BankKeeper, scopedKeeper capabilitykeeper.ScopedKeeper,
) Keeper {

Expand All @@ -49,6 +50,7 @@ func NewKeeper(
cdc: cdc,
storeKey: key,
paramSpace: paramSpace,
ics4Wrapper: ics4Wrapper,
channelKeeper: channelKeeper,
portKeeper: portKeeper,
authKeeper: authKeeper,
Expand Down
31 changes: 21 additions & 10 deletions modules/apps/transfer/keeper/mbt_relay_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"strconv"
"strings"

host "github.com/cosmos/ibc-go/v3/modules/core/24-host"

"github.com/tendermint/tendermint/crypto"

sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -280,7 +282,7 @@ func (suite *KeeperTestSuite) TestModelBasedRelay() {
panic(fmt.Errorf("Failed to read model-based test files: %w", err))
}
for _, file_info := range files {
var tlaTestCases = []TlaOnRecvPacketTestCase{}
tlaTestCases := []TlaOnRecvPacketTestCase{}
if !strings.HasSuffix(file_info.Name(), ".json") {
continue
}
Expand Down Expand Up @@ -337,16 +339,25 @@ func (suite *KeeperTestSuite) TestModelBasedRelay() {
if !ok {
panic("MBT failed to parse amount from string")
}
err = suite.chainB.GetSimApp().TransferKeeper.SendTransfer(
suite.chainB.GetContext(),
tc.packet.SourcePort,
tc.packet.SourceChannel,
sdk.NewCoin(denom, amount),
sender,
tc.packet.Data.Receiver,
clienttypes.NewHeight(0, 110),
0)

channelCap, ok := suite.chainB.GetSimApp().ScopedTransferKeeper.GetCapability(
suite.chainB.GetContext(), host.ChannelCapabilityPath(tc.packet.SourcePort, tc.packet.SourceChannel))
if !ok {
err = sdkerrors.Wrap(channeltypes.ErrChannelCapabilityNotFound, "module does not own channel capability")
} else {
err = suite.chainB.GetSimApp().TransferKeeper.SendTransfer(
suite.chainB.GetContext(),
channelCap,
tc.packet.SourcePort,
tc.packet.SourceChannel,
sdk.NewCoin(denom, amount),
sender,
tc.packet.Data.Receiver,
clienttypes.NewHeight(0, 110),
0)
}
}

case "OnRecvPacket":
err = suite.chainB.GetSimApp().TransferKeeper.OnRecvPacket(suite.chainB.GetContext(), packet, tc.packet.Data)
case "OnTimeoutPacket":
Expand Down
12 changes: 11 additions & 1 deletion modules/apps/transfer/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ package keeper
import (
"context"

sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types"
host "github.com/cosmos/ibc-go/v3/modules/core/24-host"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/ibc-go/v3/modules/apps/transfer/types"
)
Expand All @@ -19,8 +23,14 @@ func (k Keeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types.
if err != nil {
return nil, err
}

channelCap, ok := k.scopedKeeper.GetCapability(ctx, host.ChannelCapabilityPath(msg.SourcePort, msg.SourceChannel))
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@colin-axner I am not totally sure how I should handle the channel cap here. I moved it to the msg_server level. Is there anything else that needs to be done?

Copy link
Contributor

@colin-axner colin-axner Nov 30, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delayed reply! This looks good to me, although I'm not sure it matters if ics20 continues to claim the channel capability in the channel handshake callbacks.

@AdityaSripal Do we want to allow applications using ics20 as middleware to claim the capability?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thomas-nguy Is it desirable for your use case for the connected application to be capable of sending packets? Otherwise I'm thinking maybe we should leave all the capability management as it was. Essentially only allowing the connected application to get information about the ICS20 transfer, but not act upon it

Copy link
Author

@thomas-nguy thomas-nguy Jan 3, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessary, but I did claim here to preserve the original behavior.

Before it was claimed at "SendTransfer" level, but this line needs to be removed since the capability is now passed by parameter

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So in this case, we should have some sort of conditional logic that only claims capability if im.app is nil. This applies to both port and channnel capabilities.

Do you refer to this section @AdityaSripal ?

The capability here is claim by the msg_server which is outside the middleware layer. Not sure to understand which check you want to do

if !ok {
return nil, sdkerrors.Wrap(channeltypes.ErrChannelCapabilityNotFound, "module does not own channel capability")
}

if err := k.SendTransfer(
ctx, msg.SourcePort, msg.SourceChannel, msg.Token, sender, msg.Receiver, msg.TimeoutHeight, msg.TimeoutTimestamp,
ctx, channelCap, msg.SourcePort, msg.SourceChannel, msg.Token, sender, msg.Receiver, msg.TimeoutHeight, msg.TimeoutTimestamp,
); err != nil {
return nil, err
}
Expand Down
15 changes: 4 additions & 11 deletions modules/apps/transfer/keeper/relay.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"fmt"
"strings"

capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"

"github.com/armon/go-metrics"
"github.com/cosmos/cosmos-sdk/telemetry"
sdk "github.com/cosmos/cosmos-sdk/types"
Expand All @@ -12,7 +14,6 @@ import (
"github.com/cosmos/ibc-go/v3/modules/apps/transfer/types"
clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types"
channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types"
host "github.com/cosmos/ibc-go/v3/modules/core/24-host"
coretypes "github.com/cosmos/ibc-go/v3/modules/core/types"
)

Expand Down Expand Up @@ -50,6 +51,7 @@ import (
// 6. B -> A : sender chain is sink zone. Denom upon receiving: 'denom'
func (k Keeper) SendTransfer(
ctx sdk.Context,
chanCap *capabilitytypes.Capability,
sourcePort,
sourceChannel string,
token sdk.Coin,
Expand Down Expand Up @@ -82,11 +84,6 @@ func (k Keeper) SendTransfer(

// begin createOutgoingPacket logic
// See spec for this logic: https://github.com/cosmos/ibc/tree/master/spec/app/ics-020-fungible-token-transfer#packet-relay
channelCap, ok := k.scopedKeeper.GetCapability(ctx, host.ChannelCapabilityPath(sourcePort, sourceChannel))
if !ok {
return sdkerrors.Wrap(channeltypes.ErrChannelCapabilityNotFound, "module does not own channel capability")
}

// NOTE: denomination and hex hash correctness checked during msg.ValidateBasic
fullDenomPath := token.Denom

Expand Down Expand Up @@ -158,10 +155,6 @@ func (k Keeper) SendTransfer(
timeoutTimestamp,
)

if err := k.channelKeeper.SendPacket(ctx, channelCap, packet); err != nil {
return err
}

defer func() {
if token.Amount.IsInt64() {
telemetry.SetGaugeWithLabels(
Expand All @@ -178,7 +171,7 @@ func (k Keeper) SendTransfer(
)
}()

return nil
return k.ics4Wrapper.SendPacket(ctx, chanCap, packet)
}

// OnRecvPacket processes a cross chain fungible token transfer. If the
Expand Down
15 changes: 11 additions & 4 deletions modules/apps/transfer/keeper/relay_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package keeper_test

import (
"fmt"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"

"github.com/cosmos/ibc-go/v3/testing/simapp"

Expand Down Expand Up @@ -116,10 +117,16 @@ func (suite *KeeperTestSuite) TestSendTransfer() {
suite.Require().NoError(err) // message committed
}

err = suite.chainA.GetSimApp().TransferKeeper.SendTransfer(
suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, amount,
suite.chainA.SenderAccount.GetAddress(), suite.chainB.SenderAccount.GetAddress().String(), clienttypes.NewHeight(0, 110), 0,
)
channelCap, ok := suite.chainA.GetSimApp().ScopedTransferKeeper.GetCapability(
suite.chainA.GetContext(), host.ChannelCapabilityPath(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID))
if !ok {
err = sdkerrors.Wrap(channeltypes.ErrChannelCapabilityNotFound, "module does not own channel capability")
} else {
err = suite.chainA.GetSimApp().TransferKeeper.SendTransfer(
suite.chainA.GetContext(), channelCap, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, amount,
suite.chainA.SenderAccount.GetAddress(), suite.chainB.SenderAccount.GetAddress().String(), clienttypes.NewHeight(0, 110), 0,
)
}

if tc.expPass {
suite.Require().NoError(err)
Expand Down
6 changes: 5 additions & 1 deletion modules/apps/transfer/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,15 @@ type BankKeeper interface {
SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
}

// ICS4Wrapper defines the expected ICS4Wrapper for middleware
type ICS4Wrapper interface {
SendPacket(ctx sdk.Context, channelCap *capabilitytypes.Capability, packet ibcexported.PacketI) 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
}

// ClientKeeper defines the expected IBC client keeper
Expand Down
2 changes: 1 addition & 1 deletion modules/apps/transfer/types/msgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const (
)

// NewMsgTransfer creates a new MsgTransfer instance
//nolint:interfacer

func NewMsgTransfer(
sourcePort, sourceChannel string,
token sdk.Coin, sender, receiver string,
Expand Down
2 changes: 1 addition & 1 deletion modules/core/02-client/legacy/v100/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ func MigrateStore(ctx sdk.Context, storeKey sdk.StoreKey, cdc codec.BinaryCodec)
return nil
}

// migrateSolomachine migrates the solomachine from v1 to v2 solo machine protobuf defintion.
// migrateSolomachine migrates the solomachine from v1 to v2 solo machine protobuf definition.
func migrateSolomachine(clientState *ClientState) *smtypes.ClientState {
isFrozen := clientState.FrozenSequence != 0
consensusState := &smtypes.ConsensusState{
Expand Down
8 changes: 4 additions & 4 deletions modules/core/02-client/types/msgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ var (
)

// NewMsgCreateClient creates a new MsgCreateClient instance
//nolint:interfacer

func NewMsgCreateClient(
clientState exported.ClientState, consensusState exported.ConsensusState, signer string,
) (*MsgCreateClient, error) {
Expand Down Expand Up @@ -102,7 +102,7 @@ func (msg MsgCreateClient) UnpackInterfaces(unpacker codectypes.AnyUnpacker) err
}

// NewMsgUpdateClient creates a new MsgUpdateClient instance
//nolint:interfacer

func NewMsgUpdateClient(id string, header exported.Header, signer string) (*MsgUpdateClient, error) {
anyHeader, err := PackHeader(header)
if err != nil {
Expand Down Expand Up @@ -151,7 +151,7 @@ func (msg MsgUpdateClient) UnpackInterfaces(unpacker codectypes.AnyUnpacker) err
}

// NewMsgUpgradeClient creates a new MsgUpgradeClient instance
// nolint: interfacer

func NewMsgUpgradeClient(clientID string, clientState exported.ClientState, consState exported.ConsensusState,
proofUpgradeClient, proofUpgradeConsState []byte, signer string) (*MsgUpgradeClient, error) {
anyClient, err := PackClientState(clientState)
Expand Down Expand Up @@ -227,7 +227,7 @@ func (msg MsgUpgradeClient) UnpackInterfaces(unpacker codectypes.AnyUnpacker) er
}

// NewMsgSubmitMisbehaviour creates a new MsgSubmitMisbehaviour instance.
//nolint:interfacer

func NewMsgSubmitMisbehaviour(clientID string, misbehaviour exported.Misbehaviour, signer string) (*MsgSubmitMisbehaviour, error) {
anyMisbehaviour, err := PackMisbehaviour(misbehaviour)
if err != nil {
Expand Down
Loading