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

ICA Controller Side Middleware #417

Merged
merged 24 commits into from
Nov 5, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
7de03ee
initial draft for ica middleware
colin-axner Sep 16, 2021
9f0f495
add capability handling, fix build and tests
colin-axner Sep 17, 2021
51fe4b2
merge interchain-accounts branch and fix conflicts
colin-axner Sep 29, 2021
0eb681a
split module.go into ibc_module.go
colin-axner Sep 29, 2021
e3a3d8f
merge interchain-accounts branch, fix merge conflicts
colin-axner Sep 29, 2021
4404535
add middleware handshake tests
colin-axner Sep 29, 2021
eb071fb
fix app.go wiring and various tests
colin-axner Oct 5, 2021
607aa8f
merge interchain accounts branch
colin-axner Oct 8, 2021
a6066da
godoc self nits
colin-axner Oct 12, 2021
eccb110
merge interchain account branch
colin-axner Oct 12, 2021
70bf29b
remove unnecessary error
colin-axner Oct 12, 2021
205d6f0
update comment
colin-axner Nov 1, 2021
ee61276
merge interchain-accounts branch
colin-axner Nov 2, 2021
8e279ea
merge interchain accounts branch
colin-axner Nov 4, 2021
7baafe0
fix testing build
colin-axner Nov 4, 2021
b166880
split channel keeper interface
colin-axner Nov 4, 2021
45bee51
fix tests
colin-axner Nov 4, 2021
816293b
remove comments
colin-axner Nov 4, 2021
9a0e7de
add callback for timeouts
colin-axner Nov 4, 2021
b2db520
Apply suggestions from code review
colin-axner Nov 5, 2021
c4a4aeb
fix test and update testing README
colin-axner Nov 5, 2021
46f30de
add OnRecvPacket test
colin-axner Nov 5, 2021
e22684b
Merge branch 'colin/313-ica-middleware' of github.com:cosmos/ibc-go i…
colin-axner Nov 5, 2021
62dd3d3
add failing test case for NegotiateAppVersion
colin-axner Nov 5, 2021
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
2 changes: 1 addition & 1 deletion modules/apps/27-interchain-accounts/keeper/handshake.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func (k Keeper) OnChanOpenInit(

// Claim channel capability passed back by IBC module
if err := k.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil {
return sdkerrors.Wrap(channeltypes.ErrChannelCapabilityNotFound, err.Error())
return sdkerrors.Wrap(err, "interchain accounts moudle could not claim capability passed back by the IBC module")
}

return nil
Expand Down
5 changes: 1 addition & 4 deletions modules/apps/27-interchain-accounts/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ type Keeper struct {
storeKey sdk.StoreKey
cdc codec.BinaryCodec

hook types.IBCAccountHooks

channelKeeper types.ChannelKeeper
portKeeper types.PortKeeper
accountKeeper types.AccountKeeper
Expand All @@ -35,7 +33,7 @@ type Keeper struct {
func NewKeeper(
cdc codec.BinaryCodec, key sdk.StoreKey,
channelKeeper types.ChannelKeeper, portKeeper types.PortKeeper,
accountKeeper types.AccountKeeper, scopedKeeper capabilitykeeper.ScopedKeeper, msgRouter *baseapp.MsgServiceRouter, hook types.IBCAccountHooks,
accountKeeper types.AccountKeeper, scopedKeeper capabilitykeeper.ScopedKeeper, msgRouter *baseapp.MsgServiceRouter,
) Keeper {
return Keeper{
storeKey: key,
Expand All @@ -45,7 +43,6 @@ func NewKeeper(
accountKeeper: accountKeeper,
scopedKeeper: scopedKeeper,
msgRouter: msgRouter,
hook: hook,
}
}

Expand Down
49 changes: 16 additions & 33 deletions modules/apps/27-interchain-accounts/keeper/relay.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@ import (

sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
"github.com/tendermint/tendermint/crypto/tmhash"

"github.com/cosmos/ibc-go/modules/apps/27-interchain-accounts/types"
clienttypes "github.com/cosmos/ibc-go/modules/core/02-client/types"
channeltypes "github.com/cosmos/ibc-go/modules/core/04-channel/types"
host "github.com/cosmos/ibc-go/modules/core/24-host"
"github.com/tendermint/tendermint/crypto/tmhash"
)

// TODO: implement middleware functionality, this will allow us to use capabilities to
// manage helper module access to owner addresses they do not have capabilities for
func (k Keeper) TrySendTx(ctx sdk.Context, portID string, data interface{}) ([]byte, error) {
// TrySendTx takes in a transaction from a base application and attempts to send the packet
colin-axner marked this conversation as resolved.
Show resolved Hide resolved
// if the base application has the capability to send on the provided portID
func (k Keeper) TrySendTx(ctx sdk.Context, appCap *capabilitytypes.Capability, portID string, data interface{}) ([]byte, error) {
// Check for the active channel
activeChannelId, found := k.GetActiveChannel(ctx, portID)
if !found {
Expand All @@ -23,7 +25,11 @@ func (k Keeper) TrySendTx(ctx sdk.Context, portID string, data interface{}) ([]b

sourceChannelEnd, found := k.channelKeeper.GetChannel(ctx, portID, activeChannelId)
if !found {
return []byte{}, sdkerrors.Wrap(channeltypes.ErrChannelNotFound, activeChannelId)
return nil, sdkerrors.Wrap(channeltypes.ErrChannelNotFound, activeChannelId)
}

if !k.AuthenticateCapability(ctx, appCap, types.AppCapabilityName(portID, activeChannelId)) {
return nil, sdkerrors.Wrapf(types.ErrAppCapabilityNotFound, "could not authenticate provided capability for portID %s and channelID %s", portID, activeChannelId)
}

destinationPort := sourceChannelEnd.GetCounterparty().GetPortID()
Expand Down Expand Up @@ -91,7 +97,11 @@ func (k Keeper) createOutgoingPacket(
timeoutTimestamp,
)

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

return k.ComputeVirtualTxHash(packetData.Data, packet.Sequence), nil
}

func (k Keeper) DeserializeTx(_ sdk.Context, txBytes []byte) ([]sdk.Msg, error) {
Expand Down Expand Up @@ -222,30 +232,3 @@ func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet) error
return types.ErrUnknownPacketData
}
}

func (k Keeper) OnAcknowledgementPacket(ctx sdk.Context, packet channeltypes.Packet, data types.IBCAccountPacketData, ack channeltypes.Acknowledgement) error {
switch ack.Response.(type) {
case *channeltypes.Acknowledgement_Error:
if k.hook != nil {
k.hook.OnTxFailed(ctx, packet.SourcePort, packet.SourceChannel, k.ComputeVirtualTxHash(data.Data, packet.Sequence), data.Data)
}
return nil
case *channeltypes.Acknowledgement_Result:
if k.hook != nil {
k.hook.OnTxSucceeded(ctx, packet.SourcePort, packet.SourceChannel, k.ComputeVirtualTxHash(data.Data, packet.Sequence), data.Data)
}
return nil
default:
// the acknowledgement succeeded on the receiving chain so nothing
// needs to be executed and no error needs to be returned
return nil
}
}

func (k Keeper) OnTimeoutPacket(ctx sdk.Context, packet channeltypes.Packet, data types.IBCAccountPacketData) error {
if k.hook != nil {
k.hook.OnTxFailed(ctx, packet.SourcePort, packet.SourceChannel, k.ComputeVirtualTxHash(data.Data, packet.Sequence), data.Data)
}

return nil
}
15 changes: 14 additions & 1 deletion modules/apps/27-interchain-accounts/keeper/relay_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package keeper_test
import (
sdk "github.com/cosmos/cosmos-sdk/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"

"github.com/cosmos/ibc-go/modules/apps/27-interchain-accounts/types"
ibctesting "github.com/cosmos/ibc-go/testing"
)

Expand All @@ -12,6 +14,7 @@ func (suite *KeeperTestSuite) TestTrySendTx() {
path *ibctesting.Path
msg interface{}
portID string
appCap *capabilitytypes.Capability
)

testCases := []struct {
Expand Down Expand Up @@ -56,6 +59,15 @@ func (suite *KeeperTestSuite) TestTrySendTx() {
suite.chainA.GetSimApp().ICAKeeper.SetActiveChannel(suite.chainA.GetContext(), portID, "channel-100")
}, false,
},
{
"could not authenticate app capability", func() {
amount, _ := sdk.ParseCoinsNormalized("100stake")
interchainAccountAddr, _ := suite.chainB.GetSimApp().ICAKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), path.EndpointA.ChannelConfig.PortID)
msg = &banktypes.MsgSend{FromAddress: interchainAccountAddr, ToAddress: suite.chainB.SenderAccount.GetAddress().String(), Amount: amount}
appCap = nil
}, false,
},

{
"data is nil", func() {
msg = nil
Expand All @@ -80,8 +92,9 @@ func (suite *KeeperTestSuite) TestTrySendTx() {
err := suite.SetupICAPath(path, owner)
suite.Require().NoError(err)
portID = path.EndpointA.ChannelConfig.PortID
appCap, _ = suite.chainA.GetSimApp().ScopedICAKeeper.GetCapability(suite.chainA.GetContext(), types.AppCapabilityName(portID, path.EndpointA.ChannelID))
tc.malleate()
_, err = suite.chainA.GetSimApp().ICAKeeper.TrySendTx(suite.chainA.GetContext(), portID, msg)
_, err = suite.chainA.GetSimApp().ICAKeeper.TrySendTx(suite.chainA.GetContext(), appCap, portID, msg)

if tc.expPass {
suite.Require().NoError(err)
Expand Down
80 changes: 55 additions & 25 deletions modules/apps/27-interchain-accounts/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/types/module"
capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper"
capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
abci "github.com/tendermint/tendermint/abci/types"
Expand Down Expand Up @@ -74,12 +75,17 @@ func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *r

type AppModule struct {
AppModuleBasic
keeper keeper.Keeper
keeper keeper.Keeper
scopedKeeper capabilitykeeper.ScopedKeeper
app porttypes.IBCModule
colin-axner marked this conversation as resolved.
Show resolved Hide resolved
}

func NewAppModule(k keeper.Keeper) AppModule {
// NewAppModule creates an interchain accounts app module.
func NewAppModule(k keeper.Keeper, scopedKeeper capabilitykeeper.ScopedKeeper, app porttypes.IBCModule) AppModule {
return AppModule{
keeper: k,
keeper: k,
scopedKeeper: scopedKeeper,
app: app,
}
}

Expand Down Expand Up @@ -136,7 +142,13 @@ func (am AppModule) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.V
return []abci.ValidatorUpdate{}
}

// Implement IBCModule callbacks
// OnChanOpenInit implements the IBCModule callbacks. Interchain Accounts is
// implemented to act as middleware for connected authentication modules on
// the controller side. The connected modules may not change the portID or
// version. They will be allowed to perform custom logic without changing
// the parameters stored within a channel struct.
//
// Controller Chain
func (am AppModule) OnChanOpenInit(
ctx sdk.Context,
order channeltypes.Order,
Expand All @@ -147,9 +159,24 @@ func (am AppModule) OnChanOpenInit(
counterparty channeltypes.Counterparty,
version string,
) error {
return am.keeper.OnChanOpenInit(ctx, order, connectionHops, portID, channelID, chanCap, counterparty, version)
if err := am.keeper.OnChanOpenInit(ctx, order, connectionHops, portID, channelID, chanCap, counterparty, version); err != nil {
return err
}

// issue capability for connected authentication module
appCap, err := am.scopedKeeper.NewCapability(ctx, types.AppCapabilityName(portID, channelID))
if err != nil {
return sdkerrors.Wrap(err, "could not create capability for underlying application")
}

// call underlying app's OnChanOpenInit callback with the appVersion
return am.app.OnChanOpenInit(ctx, order, connectionHops, portID, channelID,
colin-axner marked this conversation as resolved.
Show resolved Hide resolved
appCap, counterparty, version)
}

// OnChanOpenTry implements the IBCModule callbacks.
//
// Host Chain
func (am AppModule) OnChanOpenTry(
ctx sdk.Context,
order channeltypes.Order,
Expand All @@ -164,15 +191,26 @@ func (am AppModule) OnChanOpenTry(
return am.keeper.OnChanOpenTry(ctx, order, connectionHops, portID, channelID, chanCap, counterparty, version, counterpartyVersion)
}

// OnChanOpenAck implements the IBCModule callbacks.
//
// Controller Chain
func (am AppModule) OnChanOpenAck(
ctx sdk.Context,
portID,
channelID string,
counterpartyVersion string,
) error {
return am.keeper.OnChanOpenAck(ctx, portID, channelID, counterpartyVersion)
if err := am.keeper.OnChanOpenAck(ctx, portID, channelID, counterpartyVersion); err != nil {
return err
}

// call underlying app's OnChanOpenAck callback with the counterparty app version.
return am.app.OnChanOpenAck(ctx, portID, channelID, counterpartyVersion)
}

// OnChanOpenAck implements the IBCModule callbacks.
//
// Host Chain
func (am AppModule) OnChanOpenConfirm(
ctx sdk.Context,
portID,
Expand All @@ -181,6 +219,7 @@ func (am AppModule) OnChanOpenConfirm(
return am.keeper.OnChanOpenConfirm(ctx, portID, channelID)
}

// OnChanCloseInit implements the IBCModule callbacks.
func (am AppModule) OnChanCloseInit(
ctx sdk.Context,
portID,
Expand All @@ -190,6 +229,7 @@ func (am AppModule) OnChanCloseInit(
return nil
}

// OnChanCloseConfirm implements the IBCModule callbacks.
func (am AppModule) OnChanCloseConfirm(
ctx sdk.Context,
portID,
Expand All @@ -198,6 +238,7 @@ func (am AppModule) OnChanCloseConfirm(
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "user cannot close channel")
}

// OnRecvPacket implements the IBCModule callbacks.
func (am AppModule) OnRecvPacket(
ctx sdk.Context,
packet channeltypes.Packet,
Expand All @@ -223,36 +264,25 @@ func (am AppModule) OnRecvPacket(
return ack
}

// OnAcknowledgementPacket implements the IBCModule callbacks.
func (am AppModule) OnAcknowledgementPacket(
ctx sdk.Context,
packet channeltypes.Packet,
acknowledgement []byte,
_ sdk.AccAddress,
relayer sdk.AccAddress,
) error {
var ack channeltypes.Acknowledgement

if err := types.ModuleCdc.UnmarshalJSON(acknowledgement, &ack); err != nil {
return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal ICS-27 interchain account packet acknowledgment: %v", err)
}
var data types.IBCAccountPacketData
if err := types.ModuleCdc.UnmarshalJSON(packet.GetData(), &data); err != nil {
return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "cannot unmarshal ICS-27 interchain account packet data: %s", err.Error())
}

if err := am.keeper.OnAcknowledgementPacket(ctx, packet, data, ack); err != nil {
return err
}

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

// OnTimeoutPacket implements the IBCModule callbacks.
func (am AppModule) OnTimeoutPacket(
ctx sdk.Context,
packet channeltypes.Packet,
_ sdk.AccAddress,
relayer sdk.AccAddress,
) error {
// TODO
return nil
// call underlying app's OnTimeoutPacket callback
return am.app.OnTimeoutPacket(ctx, packet, relayer)
}

func (am AppModule) NegotiateAppVersion(ctx sdk.Context, order channeltypes.Order, connectionID, portID string, counterparty channeltypes.Counterparty, proposedVersion string) (string, error) {
Expand Down
1 change: 1 addition & 0 deletions modules/apps/27-interchain-accounts/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ var (
ErrActiveChannelNotFound = sdkerrors.Register(ModuleName, 10, "no active channel for this owner")
ErrInvalidVersion = sdkerrors.Register(ModuleName, 11, "invalid interchain accounts version")
ErrInvalidOwnerAddress = sdkerrors.Register(ModuleName, 12, "invalid owner address")
ErrAppCapabilityNotFound = sdkerrors.Register(ModuleName, 13, "app capability not found")
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "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"
Expand All @@ -26,7 +27,6 @@ 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
ChanOpenInit(ctx sdk.Context, order channeltypes.Order, connectionHops []string, portID string, portCap *capabilitytypes.Capability, counterparty channeltypes.Counterparty, version string) (string, *capabilitytypes.Capability, error)
}

Expand Down
30 changes: 0 additions & 30 deletions modules/apps/27-interchain-accounts/types/hook.go

This file was deleted.

7 changes: 7 additions & 0 deletions modules/apps/27-interchain-accounts/types/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ const (

// MemStoreKey defines the in-memory store key
MemStoreKey = "mem_capability"

KeyAppCapabilityPrefix = "appCapabilities"
)

func KeyActiveChannel(portId string) []byte {
Expand All @@ -39,3 +41,8 @@ var (
func GetIdentifier(portID, channelID string) string {
return fmt.Sprintf("%s/%s/", portID, channelID)
}

// TODO: remove once generic function is added to 24-host
func AppCapabilityName(channelID, portID string) string {
return fmt.Sprintf("%s/%s/%s", KeyAppCapabilityPrefix, channelID, portID)
}
Loading