-
Notifications
You must be signed in to change notification settings - Fork 597
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
Add IBC hooks for every IBC method #2296
Changes from all commits
6f00b34
bb4f72c
58d094a
7876e32
f1e12bf
2322792
4142d48
1a6a518
3ea1a3f
9de2964
88efd13
314be04
3ec6fcd
facdb73
e4e8dad
bba15bc
d9ef44a
0b08a17
a1f93b3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
package ibc_hooks | ||
|
||
import ( | ||
// external libraries | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" | ||
|
||
// ibc-go | ||
channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" | ||
ibcexported "github.com/cosmos/ibc-go/v5/modules/core/exported" | ||
) | ||
|
||
type Hooks interface{} | ||
|
||
type OnChanOpenInitOverrideHooks interface { | ||
OnChanOpenInitOverride(im IBCMiddleware, ctx sdk.Context, order channeltypes.Order, connectionHops []string, portID string, channelID string, channelCap *capabilitytypes.Capability, counterparty channeltypes.Counterparty, version string) (string, error) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: do you mind to put each argument on a new line (like we do here)? I think it's easier for readability. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. changed it to only one line specifically for readability (so it's easy to scan which methods are defined on each interface, considering the arguments are the same as for the functions they override), but happy to change it back to one arg per line |
||
} | ||
type OnChanOpenInitBeforeHooks interface { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why don't the before and after hooks return errors? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe they should. I can update them to return errors |
||
OnChanOpenInitBeforeHook(ctx sdk.Context, order channeltypes.Order, connectionHops []string, portID string, channelID string, channelCap *capabilitytypes.Capability, counterparty channeltypes.Counterparty, version string) | ||
} | ||
type OnChanOpenInitAfterHooks interface { | ||
OnChanOpenInitAfterHook(ctx sdk.Context, order channeltypes.Order, connectionHops []string, portID string, channelID string, channelCap *capabilitytypes.Capability, counterparty channeltypes.Counterparty, version string, result string, err error) | ||
} | ||
|
||
// OnChanOpenTry Hooks | ||
type OnChanOpenTryOverrideHooks interface { | ||
OnChanOpenTryOverride(im IBCMiddleware, ctx sdk.Context, order channeltypes.Order, connectionHops []string, portID, channelID string, channelCap *capabilitytypes.Capability, counterparty channeltypes.Counterparty, counterpartyVersion string) (string, error) | ||
} | ||
type OnChanOpenTryBeforeHooks interface { | ||
OnChanOpenTryBeforeHook(ctx sdk.Context, order channeltypes.Order, connectionHops []string, portID, channelID string, channelCap *capabilitytypes.Capability, counterparty channeltypes.Counterparty, counterpartyVersion string) | ||
} | ||
type OnChanOpenTryAfterHooks interface { | ||
OnChanOpenTryAfterHook(ctx sdk.Context, order channeltypes.Order, connectionHops []string, portID, channelID string, channelCap *capabilitytypes.Capability, counterparty channeltypes.Counterparty, counterpartyVersion string, version string, err error) | ||
} | ||
|
||
// OnChanOpenAck Hooks | ||
type OnChanOpenAckOverrideHooks interface { | ||
OnChanOpenAckOverride(im IBCMiddleware, ctx sdk.Context, portID, channelID string, counterpartyChannelID string, counterpartyVersion string) error | ||
} | ||
type OnChanOpenAckBeforeHooks interface { | ||
OnChanOpenAckBeforeHook(ctx sdk.Context, portID, channelID string, counterpartyChannelID string, counterpartyVersion string) | ||
} | ||
type OnChanOpenAckAfterHooks interface { | ||
OnChanOpenAckAfterHook(ctx sdk.Context, portID, channelID string, counterpartyChannelID string, counterpartyVersion string, err error) | ||
} | ||
|
||
// OnChanOpenConfirm Hooks | ||
type OnChanOpenConfirmOverrideHooks interface { | ||
OnChanOpenConfirmOverride(im IBCMiddleware, ctx sdk.Context, portID, channelID string) error | ||
} | ||
type OnChanOpenConfirmBeforeHooks interface { | ||
OnChanOpenConfirmBeforeHook(ctx sdk.Context, portID, channelID string) | ||
} | ||
type OnChanOpenConfirmAfterHooks interface { | ||
OnChanOpenConfirmAfterHook(ctx sdk.Context, portID, channelID string, err error) | ||
} | ||
|
||
// OnChanCloseInit Hooks | ||
type OnChanCloseInitOverrideHooks interface { | ||
OnChanCloseInitOverride(im IBCMiddleware, ctx sdk.Context, portID, channelID string) error | ||
} | ||
type OnChanCloseInitBeforeHooks interface { | ||
OnChanCloseInitBeforeHook(ctx sdk.Context, portID, channelID string) | ||
} | ||
type OnChanCloseInitAfterHooks interface { | ||
OnChanCloseInitAfterHook(ctx sdk.Context, portID, channelID string, err error) | ||
} | ||
|
||
// OnChanCloseConfirm Hooks | ||
type OnChanCloseConfirmOverrideHooks interface { | ||
OnChanCloseConfirmOverride(im IBCMiddleware, ctx sdk.Context, portID, channelID string) error | ||
} | ||
type OnChanCloseConfirmBeforeHooks interface { | ||
OnChanCloseConfirmBeforeHook(ctx sdk.Context, portID, channelID string) | ||
} | ||
type OnChanCloseConfirmAfterHooks interface { | ||
OnChanCloseConfirmAfterHook(ctx sdk.Context, portID, channelID string, err error) | ||
} | ||
|
||
// OnRecvPacket Hooks | ||
type OnRecvPacketOverrideHooks interface { | ||
OnRecvPacketOverride(im IBCMiddleware, ctx sdk.Context, packet channeltypes.Packet, relayer sdk.AccAddress) ibcexported.Acknowledgement | ||
} | ||
type OnRecvPacketBeforeHooks interface { | ||
OnRecvPacketBeforeHook(ctx sdk.Context, packet channeltypes.Packet, relayer sdk.AccAddress) | ||
} | ||
type OnRecvPacketAfterHooks interface { | ||
OnRecvPacketAfterHook(ctx sdk.Context, packet channeltypes.Packet, relayer sdk.AccAddress, ack ibcexported.Acknowledgement) | ||
} | ||
|
||
// OnAcknowledgementPacket Hooks | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I would change the order and put the |
||
type OnAcknowledgementPacketOverrideHooks interface { | ||
OnAcknowledgementPacketOverride(im IBCMiddleware, ctx sdk.Context, packet channeltypes.Packet, acknowledgement []byte, relayer sdk.AccAddress) error | ||
} | ||
type OnAcknowledgementPacketBeforeHooks interface { | ||
OnAcknowledgementPacketBeforeHook(ctx sdk.Context, packet channeltypes.Packet, acknowledgement []byte, relayer sdk.AccAddress) | ||
} | ||
type OnAcknowledgementPacketAfterHooks interface { | ||
OnAcknowledgementPacketAfterHook(ctx sdk.Context, packet channeltypes.Packet, acknowledgement []byte, relayer sdk.AccAddress, err error) | ||
} | ||
|
||
// OnTimeoutPacket Hooks | ||
type OnTimeoutPacketOverrideHooks interface { | ||
OnTimeoutPacketOverride(im IBCMiddleware, ctx sdk.Context, packet channeltypes.Packet, relayer sdk.AccAddress) error | ||
} | ||
type OnTimeoutPacketBeforeHooks interface { | ||
OnTimeoutPacketBeforeHook(ctx sdk.Context, packet channeltypes.Packet, relayer sdk.AccAddress) | ||
} | ||
type OnTimeoutPacketAfterHooks interface { | ||
OnTimeoutPacketAfterHook(ctx sdk.Context, packet channeltypes.Packet, relayer sdk.AccAddress, err error) | ||
} | ||
|
||
// SendPacket Hooks | ||
type SendPacketOverrideHooks interface { | ||
SendPacketOverride(i ICS4Middleware, ctx sdk.Context, chanCap *capabilitytypes.Capability, packet ibcexported.PacketI) error | ||
} | ||
type SendPacketBeforeHooks interface { | ||
SendPacketBeforeHook(ctx sdk.Context, chanCap *capabilitytypes.Capability, packet ibcexported.PacketI) | ||
} | ||
type SendPacketAfterHooks interface { | ||
SendPacketAfterHook(ctx sdk.Context, chanCap *capabilitytypes.Capability, packet ibcexported.PacketI, err error) | ||
} | ||
|
||
// WriteAcknowledgement Hooks | ||
type WriteAcknowledgementOverrideHooks interface { | ||
WriteAcknowledgementOverride(i ICS4Middleware, ctx sdk.Context, chanCap *capabilitytypes.Capability, packet ibcexported.PacketI, ack ibcexported.Acknowledgement) error | ||
} | ||
type WriteAcknowledgementBeforeHooks interface { | ||
WriteAcknowledgementBeforeHook(ctx sdk.Context, chanCap *capabilitytypes.Capability, packet ibcexported.PacketI, ack ibcexported.Acknowledgement) | ||
} | ||
type WriteAcknowledgementAfterHooks interface { | ||
WriteAcknowledgementAfterHook(ctx sdk.Context, chanCap *capabilitytypes.Capability, packet ibcexported.PacketI, ack ibcexported.Acknowledgement, err error) | ||
} | ||
|
||
// GetAppVersion Hooks | ||
type GetAppVersionOverrideHooks interface { | ||
GetAppVersionOverride(i ICS4Middleware, ctx sdk.Context, portID, channelID string) (string, bool) | ||
} | ||
type GetAppVersionBeforeHooks interface { | ||
GetAppVersionBeforeHook(ctx sdk.Context, portID, channelID string) | ||
} | ||
type GetAppVersionAfterHooks interface { | ||
GetAppVersionAfterHook(ctx sdk.Context, portID, channelID string, result string, success bool) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
package ibc_hooks_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/suite" | ||
|
||
// external libraries | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
|
||
// ibc-go | ||
"github.com/cosmos/ibc-go/v5/modules/apps/hooks/testutils" | ||
transfertypes "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" | ||
clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" | ||
channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" | ||
ibctesting "github.com/cosmos/ibc-go/v5/testing" | ||
ibcmock "github.com/cosmos/ibc-go/v5/testing/mock" | ||
) | ||
|
||
type HooksTestSuite struct { | ||
suite.Suite | ||
|
||
coordinator *ibctesting.Coordinator | ||
|
||
chainA *ibctesting.TestChain | ||
chainB *ibctesting.TestChain | ||
|
||
path *ibctesting.Path | ||
} | ||
|
||
func (suite *HooksTestSuite) SetupTest() { | ||
suite.coordinator = ibctesting.NewCoordinator(suite.T(), 3) | ||
suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1)) | ||
suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2)) | ||
} | ||
|
||
func TestIBCHooksTestSuite(t *testing.T) { | ||
suite.Run(t, new(HooksTestSuite)) | ||
} | ||
|
||
func (suite *HooksTestSuite) CreateMockPacket() channeltypes.Packet { | ||
return channeltypes.NewPacket( | ||
ibcmock.MockPacketData, | ||
suite.chainA.SenderAccount.GetSequence(), | ||
suite.path.EndpointA.ChannelConfig.PortID, | ||
suite.path.EndpointA.ChannelID, | ||
suite.path.EndpointB.ChannelConfig.PortID, | ||
suite.path.EndpointB.ChannelID, | ||
clienttypes.NewHeight(0, 100), | ||
0, | ||
) | ||
} | ||
|
||
func NewTransferPath(chainA, chainB *ibctesting.TestChain) *ibctesting.Path { | ||
path := ibctesting.NewPath(chainA, chainB) | ||
path.EndpointA.ChannelConfig.PortID = ibctesting.TransferPort | ||
path.EndpointB.ChannelConfig.PortID = ibctesting.TransferPort | ||
path.EndpointA.ChannelConfig.Version = transfertypes.Version | ||
path.EndpointB.ChannelConfig.Version = transfertypes.Version | ||
|
||
return path | ||
} | ||
|
||
func (suite *HooksTestSuite) TestOnRecvPacketHooks() { | ||
var ( | ||
trace transfertypes.DenomTrace | ||
amount sdk.Int | ||
receiver string | ||
status testutils.Status | ||
) | ||
|
||
testCases := []struct { | ||
msg string | ||
malleate func(*testutils.Status) | ||
expPass bool | ||
}{ | ||
{"override", func(status *testutils.Status) { | ||
suite.chainB.GetSimApp().HooksMiddleware.Hooks = testutils.TestRecvOverrideHooks{Status: status} | ||
}, true}, | ||
{"before and after", func(status *testutils.Status) { | ||
suite.chainB.GetSimApp().HooksMiddleware.Hooks = testutils.TestRecvBeforeAfterHooks{Status: status} | ||
}, true}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
tc := tc | ||
suite.Run(tc.msg, func() { | ||
suite.SetupTest() // reset | ||
|
||
path := NewTransferPath(suite.chainA, suite.chainB) | ||
suite.coordinator.Setup(path) | ||
receiver = suite.chainB.SenderAccount.GetAddress().String() // must be explicitly changed in malleate | ||
status = testutils.Status{} | ||
|
||
amount = sdk.NewInt(100) // must be explicitly changed in malleate | ||
seq := uint64(1) | ||
|
||
trace = transfertypes.ParseDenomTrace(sdk.DefaultBondDenom) | ||
|
||
// send coin from chainA to chainB | ||
transferMsg := transfertypes.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, sdk.NewCoin(trace.IBCDenom(), amount), suite.chainA.SenderAccount.GetAddress().String(), receiver, clienttypes.NewHeight(1, 110), 0) | ||
_, err := suite.chainA.SendMsgs(transferMsg) | ||
suite.Require().NoError(err) // message committed | ||
|
||
tc.malleate(&status) | ||
|
||
data := transfertypes.NewFungibleTokenPacketData(trace.GetFullDenomPath(), amount.String(), suite.chainA.SenderAccount.GetAddress().String(), receiver) | ||
packet := channeltypes.NewPacket(data.GetBytes(), seq, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) | ||
|
||
ack := suite.chainB.GetSimApp().HooksMiddleware.OnRecvPacket(suite.chainB.GetContext(), packet, suite.chainA.SenderAccount.GetAddress()) | ||
|
||
if tc.expPass { | ||
suite.Require().True(ack.Success()) | ||
} else { | ||
suite.Require().False(ack.Success()) | ||
} | ||
|
||
if _, ok := suite.chainB.GetSimApp().HooksMiddleware.Hooks.(testutils.TestRecvOverrideHooks); ok { | ||
suite.Require().True(status.OverrideRan) | ||
suite.Require().False(status.BeforeRan) | ||
suite.Require().False(status.AfterRan) | ||
} | ||
|
||
if _, ok := suite.chainB.GetSimApp().HooksMiddleware.Hooks.(testutils.TestRecvBeforeAfterHooks); ok { | ||
suite.Require().False(status.OverrideRan) | ||
suite.Require().True(status.BeforeRan) | ||
suite.Require().True(status.AfterRan) | ||
} | ||
}) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to move this file inside the
types
folder?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that would create an import cycle, as ibc_module.go depends on the hooks and the hooks on the middleware struct defined in that file