diff --git a/app/app.go b/app/app.go index 7f1e9193..03eb4680 100644 --- a/app/app.go +++ b/app/app.go @@ -591,6 +591,7 @@ func NewWasmApp( app.BankKeeper, app.StakingKeeper, distrkeeper.NewQuerier(app.DistrKeeper), + app.IBCFeeKeeper, // ISC4 Wrapper: fee IBC middleware app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper, scopedWasmKeeper, diff --git a/x/wasm/ioutils/ioutil_test.go b/x/wasm/ioutils/ioutil_test.go index 668c94ae..3cd1b5c2 100644 --- a/x/wasm/ioutils/ioutil_test.go +++ b/x/wasm/ioutils/ioutil_test.go @@ -4,11 +4,12 @@ import ( "bytes" "compress/gzip" "errors" - "github.com/cometbft/cometbft/libs/rand" "io" "os" "testing" + "github.com/cometbft/cometbft/libs/rand" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/x/wasm/keeper/genesis_test.go b/x/wasm/keeper/genesis_test.go index d5722fd8..f312af05 100644 --- a/x/wasm/keeper/genesis_test.go +++ b/x/wasm/keeper/genesis_test.go @@ -664,6 +664,7 @@ func setupKeeper(t *testing.T) (*Keeper, sdk.Context) { nil, nil, nil, + nil, tempDir, wasmConfig, AvailableCapabilities, diff --git a/x/wasm/keeper/handler_plugin.go b/x/wasm/keeper/handler_plugin.go index be608ed5..3e38e09f 100644 --- a/x/wasm/keeper/handler_plugin.go +++ b/x/wasm/keeper/handler_plugin.go @@ -34,8 +34,10 @@ type SDKMessageHandler struct { encoders msgEncoder } +// NewDefaultMessageHandler constructor func NewDefaultMessageHandler( router MessageRouter, + ics4Wrapper types.ICS4Wrapper, channelKeeper types.ChannelKeeper, capabilityKeeper types.CapabilityKeeper, bankKeeper types.Burner, @@ -49,7 +51,7 @@ func NewDefaultMessageHandler( } return NewMessageHandlerChain( NewSDKMessageHandler(router, encoders), - NewIBCRawPacketHandler(channelKeeper, capabilityKeeper), + NewIBCRawPacketHandler(ics4Wrapper, channelKeeper, capabilityKeeper), NewBurnCoinMessageHandler(bankKeeper), ) } @@ -142,14 +144,20 @@ func (m MessageHandlerChain) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAd return nil, nil, errorsmod.Wrap(types.ErrUnknownMsg, "no handler found") } -// IBCRawPacketHandler handels IBC.SendPacket messages which are published to an IBC channel. +// IBCRawPacketHandler handles IBC.SendPacket messages which are published to an IBC channel. type IBCRawPacketHandler struct { + ics4Wrapper types.ICS4Wrapper channelKeeper types.ChannelKeeper capabilityKeeper types.CapabilityKeeper } -func NewIBCRawPacketHandler(chk types.ChannelKeeper, cak types.CapabilityKeeper) IBCRawPacketHandler { - return IBCRawPacketHandler{channelKeeper: chk, capabilityKeeper: cak} +// NewIBCRawPacketHandler constructor +func NewIBCRawPacketHandler(ics4Wrapper types.ICS4Wrapper, channelKeeper types.ChannelKeeper, capabilityKeeper types.CapabilityKeeper) IBCRawPacketHandler { + return IBCRawPacketHandler{ + ics4Wrapper: ics4Wrapper, + channelKeeper: channelKeeper, + capabilityKeeper: capabilityKeeper, + } } // DispatchMsg publishes a raw IBC packet onto the channel. @@ -169,7 +177,7 @@ func (h IBCRawPacketHandler) DispatchMsg(ctx sdk.Context, _ sdk.AccAddress, cont if !ok { return nil, nil, errorsmod.Wrap(channeltypes.ErrChannelCapabilityNotFound, "module does not own channel capability") } - seq, err := h.channelKeeper.SendPacket(ctx, channelCap, contractIBCPortID, contractIBCChannelID, ConvertWasmIBCTimeoutHeightToCosmosHeight(msg.IBC.SendPacket.Timeout.Block), msg.IBC.SendPacket.Timeout.Timestamp, msg.IBC.SendPacket.Data) + seq, err := h.ics4Wrapper.SendPacket(ctx, channelCap, contractIBCPortID, contractIBCChannelID, ConvertWasmIBCTimeoutHeightToCosmosHeight(msg.IBC.SendPacket.Timeout.Block), msg.IBC.SendPacket.Timeout.Timestamp, msg.IBC.SendPacket.Data) if err != nil { return nil, nil, errorsmod.Wrap(err, "channel") } diff --git a/x/wasm/keeper/handler_plugin_test.go b/x/wasm/keeper/handler_plugin_test.go index fbfb13a5..beea6daf 100644 --- a/x/wasm/keeper/handler_plugin_test.go +++ b/x/wasm/keeper/handler_plugin_test.go @@ -5,10 +5,10 @@ import ( "testing" errorsmod "cosmossdk.io/errors" - "github.com/cometbft/cometbft/libs/log" wasmvm "github.com/CosmWasm/wasmvm" wasmvmtypes "github.com/CosmWasm/wasmvm/types" + "github.com/cometbft/cometbft/libs/log" "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -236,15 +236,7 @@ func TestIBCRawPacketHandler(t *testing.T) { } var capturedPacket *CapturedPacket - chanKeeper := &wasmtesting.MockChannelKeeper{ - GetChannelFn: func(ctx sdk.Context, srcPort, srcChan string) (channeltypes.Channel, bool) { - return channeltypes.Channel{ - Counterparty: channeltypes.NewCounterparty( - "other-port", - "other-channel-1", - ), - }, true - }, + capturePacketsSenderMock := &wasmtesting.MockIBCPacketSender{ SendPacketFn: func(ctx sdk.Context, channelCap *capabilitytypes.Capability, sourcePort string, sourceChannel string, timeoutHeight clienttypes.Height, timeoutTimestamp uint64, data []byte) (uint64, error) { capturedPacket = &CapturedPacket{ sourcePort: sourcePort, @@ -256,6 +248,16 @@ func TestIBCRawPacketHandler(t *testing.T) { return 1, nil }, } + chanKeeper := &wasmtesting.MockChannelKeeper{ + GetChannelFn: func(ctx sdk.Context, srcPort, srcChan string) (channeltypes.Channel, bool) { + return channeltypes.Channel{ + Counterparty: channeltypes.NewCounterparty( + "other-port", + "other-channel-1", + ), + }, true + }, + } capKeeper := &wasmtesting.MockCapabilityKeeper{ GetCapabilityFn: func(ctx sdk.Context, name string) (*capabilitytypes.Capability, bool) { return &capabilitytypes.Capability{}, true @@ -303,7 +305,7 @@ func TestIBCRawPacketHandler(t *testing.T) { t.Run(name, func(t *testing.T) { capturedPacket = nil // when - h := NewIBCRawPacketHandler(spec.chanKeeper, spec.capKeeper) + h := NewIBCRawPacketHandler(capturePacketsSenderMock, spec.chanKeeper, spec.capKeeper) evts, data, gotErr := h.DispatchMsg(ctx, RandomAccountAddress(t), ibcPort, wasmvmtypes.CosmosMsg{IBC: &wasmvmtypes.IBCMsg{SendPacket: &spec.srcMsg}}) // then require.True(t, spec.expErr.Is(gotErr), "exp %v but got %#+v", spec.expErr, gotErr) diff --git a/x/wasm/keeper/keeper_cgo.go b/x/wasm/keeper/keeper_cgo.go index dd402bc1..78d9edde 100644 --- a/x/wasm/keeper/keeper_cgo.go +++ b/x/wasm/keeper/keeper_cgo.go @@ -22,6 +22,7 @@ func NewKeeper( bankKeeper types.BankKeeper, stakingKeeper types.StakingKeeper, distrKeeper types.DistributionKeeper, + ics4Wrapper types.ICS4Wrapper, channelKeeper types.ChannelKeeper, portKeeper types.PortKeeper, capabilityKeeper types.CapabilityKeeper, @@ -48,7 +49,7 @@ func NewKeeper( accountPruner: NewVestingCoinBurner(bankKeeper), portKeeper: portKeeper, capabilityKeeper: capabilityKeeper, - messenger: NewDefaultMessageHandler(router, channelKeeper, capabilityKeeper, bankKeeper, cdc, portSource), + messenger: NewDefaultMessageHandler(router, ics4Wrapper, channelKeeper, capabilityKeeper, bankKeeper, cdc, portSource), queryGasLimit: wasmConfig.SmartQueryGasLimit, gasRegister: NewDefaultWasmGasRegister(), maxQueryStackSize: types.DefaultMaxQueryStackSize, diff --git a/x/wasm/keeper/keeper_no_cgo.go b/x/wasm/keeper/keeper_no_cgo.go index 30d06c01..4a5db265 100644 --- a/x/wasm/keeper/keeper_no_cgo.go +++ b/x/wasm/keeper/keeper_no_cgo.go @@ -18,6 +18,7 @@ func NewKeeper( bankKeeper types.BankKeeper, stakingKeeper types.StakingKeeper, distrKeeper types.DistributionKeeper, + ics4Wrapper types.ICS4Wrapper, channelKeeper types.ChannelKeeper, portKeeper types.PortKeeper, capabilityKeeper types.CapabilityKeeper, diff --git a/x/wasm/keeper/keeper_test.go b/x/wasm/keeper/keeper_test.go index 9211e74c..c0825d38 100644 --- a/x/wasm/keeper/keeper_test.go +++ b/x/wasm/keeper/keeper_test.go @@ -752,7 +752,7 @@ func TestInstantiateWithContractFactoryChildQueriesParent(t *testing.T) { router := baseapp.NewMsgServiceRouter() router.SetInterfaceRegistry(keepers.EncodingConfig.InterfaceRegistry) types.RegisterMsgServer(router, NewMsgServerImpl(keeper)) - keeper.messenger = NewDefaultMessageHandler(router, nil, nil, nil, keepers.EncodingConfig.Marshaler, nil) + keeper.messenger = NewDefaultMessageHandler(router, nil, nil, nil, nil, keepers.EncodingConfig.Marshaler, nil) // overwrite wasmvm in response handler keeper.wasmVMResponseHandler = NewDefaultWasmVMContractResponseHandler(NewMessageDispatcher(keeper.messenger, keeper)) diff --git a/x/wasm/keeper/options_test.go b/x/wasm/keeper/options_test.go index 29557c3c..e6d055e9 100644 --- a/x/wasm/keeper/options_test.go +++ b/x/wasm/keeper/options_test.go @@ -113,7 +113,7 @@ func TestConstructorOptions(t *testing.T) { } for name, spec := range specs { t.Run(name, func(t *testing.T) { - k := NewKeeper(nil, nil, authkeeper.AccountKeeper{}, &bankkeeper.BaseKeeper{}, stakingkeeper.Keeper{}, nil, nil, nil, nil, nil, nil, nil, "tempDir", types.DefaultWasmConfig(), AvailableCapabilities, "", spec.srcOpt) + k := NewKeeper(nil, nil, authkeeper.AccountKeeper{}, &bankkeeper.BaseKeeper{}, stakingkeeper.Keeper{}, nil, nil, nil, nil, nil, nil, nil, nil, "tempDir", types.DefaultWasmConfig(), AvailableCapabilities, "", spec.srcOpt) spec.verify(t, k) }) } diff --git a/x/wasm/keeper/test_common.go b/x/wasm/keeper/test_common.go index d5cd2233..7f2d8cd0 100644 --- a/x/wasm/keeper/test_common.go +++ b/x/wasm/keeper/test_common.go @@ -425,6 +425,7 @@ func createTestInput( bankKeeper, stakingKeeper, distributionkeeper.NewQuerier(distKeeper), + ibcKeeper.ChannelKeeper, // ICS4Wrapper ibcKeeper.ChannelKeeper, &ibcKeeper.PortKeeper, scopedWasmKeeper, diff --git a/x/wasm/keeper/wasmtesting/mock_keepers.go b/x/wasm/keeper/wasmtesting/mock_keepers.go index edf9df09..b5e68869 100644 --- a/x/wasm/keeper/wasmtesting/mock_keepers.go +++ b/x/wasm/keeper/wasmtesting/mock_keepers.go @@ -10,12 +10,12 @@ import ( ) type MockChannelKeeper struct { - GetChannelFn func(ctx sdk.Context, srcPort, srcChan string) (channel channeltypes.Channel, found bool) - SendPacketFn func(ctx sdk.Context, channelCap *capabilitytypes.Capability, sourcePort string, sourceChannel string, timeoutHeight clienttypes.Height, timeoutTimestamp uint64, data []byte) (uint64, error) - ChanCloseInitFn func(ctx sdk.Context, portID, channelID string, chanCap *capabilitytypes.Capability) error - GetAllChannelsFn func(ctx sdk.Context) []channeltypes.IdentifiedChannel - IterateChannelsFn func(ctx sdk.Context, cb func(channeltypes.IdentifiedChannel) bool) - SetChannelFn func(ctx sdk.Context, portID, channelID string, channel channeltypes.Channel) + GetChannelFn func(ctx sdk.Context, srcPort, srcChan string) (channel channeltypes.Channel, found bool) + GetNextSequenceSendFn func(ctx sdk.Context, portID, channelID string) (uint64, bool) + ChanCloseInitFn func(ctx sdk.Context, portID, channelID string, chanCap *capabilitytypes.Capability) error + GetAllChannelsFn func(ctx sdk.Context) []channeltypes.IdentifiedChannel + IterateChannelsFn func(ctx sdk.Context, cb func(channeltypes.IdentifiedChannel) bool) + SetChannelFn func(ctx sdk.Context, portID, channelID string, channel channeltypes.Channel) } func (m *MockChannelKeeper) GetChannel(ctx sdk.Context, srcPort, srcChan string) (channel channeltypes.Channel, found bool) { @@ -32,11 +32,11 @@ func (m *MockChannelKeeper) GetAllChannels(ctx sdk.Context) []channeltypes.Ident return m.GetAllChannelsFn(ctx) } -func (m *MockChannelKeeper) SendPacket(ctx sdk.Context, channelCap *capabilitytypes.Capability, sourcePort string, sourceChannel string, timeoutHeight clienttypes.Height, timeoutTimestamp uint64, data []byte) (uint64, error) { - if m.SendPacketFn == nil { +func (m *MockChannelKeeper) GetNextSequenceSend(ctx sdk.Context, portID, channelID string) (uint64, bool) { + if m.GetNextSequenceSendFn == nil { panic("not supposed to be called!") } - return m.SendPacketFn(ctx, channelCap, sourcePort, sourceChannel, timeoutHeight, timeoutTimestamp, data) + return m.GetNextSequenceSendFn(ctx, portID, channelID) } func (m *MockChannelKeeper) ChanCloseInit(ctx sdk.Context, portID, channelID string, chanCap *capabilitytypes.Capability) error { @@ -60,6 +60,17 @@ func (m *MockChannelKeeper) SetChannel(ctx sdk.Context, portID, channelID string m.SetChannelFn(ctx, portID, channelID, channel) } +type MockIBCPacketSender struct { + SendPacketFn func(ctx sdk.Context, channelCap *capabilitytypes.Capability, sourcePort string, sourceChannel string, timeoutHeight clienttypes.Height, timeoutTimestamp uint64, data []byte) (uint64, error) +} + +func (m *MockIBCPacketSender) SendPacket(ctx sdk.Context, channelCap *capabilitytypes.Capability, sourcePort string, sourceChannel string, timeoutHeight clienttypes.Height, timeoutTimestamp uint64, data []byte) (uint64, error) { + if m.SendPacketFn == nil { + panic("not supposed to be called!") + } + return m.SendPacketFn(ctx, channelCap, sourcePort, sourceChannel, timeoutHeight, timeoutTimestamp, data) +} + func MockChannelKeeperIterator(s []channeltypes.IdentifiedChannel) func(ctx sdk.Context, cb func(channeltypes.IdentifiedChannel) bool) { return func(ctx sdk.Context, cb func(channeltypes.IdentifiedChannel) bool) { for _, channel := range s { diff --git a/x/wasm/types/expected_keepers.go b/x/wasm/types/expected_keepers.go index e9b52273..9f4595f4 100644 --- a/x/wasm/types/expected_keepers.go +++ b/x/wasm/types/expected_keepers.go @@ -3,12 +3,13 @@ package types import ( "context" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + 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" distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" connectiontypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" @@ -72,7 +73,21 @@ type StakingKeeper interface { // 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) + ChanCloseInit(ctx sdk.Context, portID, channelID string, chanCap *capabilitytypes.Capability) error + GetAllChannels(ctx sdk.Context) (channels []channeltypes.IdentifiedChannel) + IterateChannels(ctx sdk.Context, cb func(channeltypes.IdentifiedChannel) bool) + SetChannel(ctx sdk.Context, portID, channelID string, channel channeltypes.Channel) +} +// ICS4Wrapper defines the method for an IBC data package to be submitted. +// The interface is implemented by the channel keeper on the lowest level in ibc-go. Middlewares or other abstractions +// can add functionality on top of it. See ics4Wrapper in ibc-go. +// It is important to choose the right implementation that is configured for any middleware used in the ibc-stack of wasm. +// +// For example, when ics-29 fee middleware is set up for the wasm ibc-stack, then the IBCFeeKeeper should be used, so +// that they are in sync. +type ICS4Wrapper interface { // SendPacket is called by a module in order to send an IBC packet on a channel. // The packet sequence generated for the packet to be sent is returned. An error // is returned if one occurs. @@ -85,10 +100,6 @@ type ChannelKeeper interface { timeoutTimestamp uint64, data []byte, ) (uint64, error) - ChanCloseInit(ctx sdk.Context, portID, channelID string, chanCap *capabilitytypes.Capability) error - GetAllChannels(ctx sdk.Context) (channels []channeltypes.IdentifiedChannel) - IterateChannels(ctx sdk.Context, cb func(channeltypes.IdentifiedChannel) bool) - SetChannel(ctx sdk.Context, portID, channelID string, channel channeltypes.Channel) } // ClientKeeper defines the expected IBC client keeper