From 825fe90f9765edbaef099282defe083c94b35fa8 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 11 Jan 2024 17:52:24 +0300 Subject: [PATCH] imp(core): allow huckleberry events with a prefix (backport #5541) (#5574) * imp(core): allow huckleberry events with a prefix (#5541) * feat: initial impl * imp: moved event helpers to types and added tests * imp(testing): added mock events to mock module * test: added msg_server tests for application events * imp: converted suffix to prefix * docs: updated inline comments * imp: review item * test: review items * imp: review items (cherry picked from commit 2375109ae0eea8ca5843a32470d7fd997abe2bf9) # Conflicts: # modules/core/keeper/msg_server.go # modules/core/keeper/msg_server_test.go * fix: merge conflicts * nit * ci: removed link checker --------- Co-authored-by: srdtrk <59252793+srdtrk@users.noreply.github.com> Co-authored-by: srdtrk --- .github/workflows/link-check-config.json | 10 --- .github/workflows/link-check.yml | 10 --- modules/core/keeper/events_test.go | 106 +++++++++++++++++++++++ modules/core/keeper/export_test.go | 9 ++ modules/core/keeper/msg_server.go | 23 +++++ modules/core/keeper/msg_server_test.go | 105 +++++++++++++++------- modules/core/types/events.go | 3 + testing/mock/events.go | 39 +++++++++ testing/mock/ibc_module.go | 6 ++ 9 files changed, 258 insertions(+), 53 deletions(-) delete mode 100644 .github/workflows/link-check-config.json delete mode 100644 .github/workflows/link-check.yml create mode 100644 modules/core/keeper/events_test.go create mode 100644 modules/core/keeper/export_test.go create mode 100644 modules/core/types/events.go create mode 100644 testing/mock/events.go diff --git a/.github/workflows/link-check-config.json b/.github/workflows/link-check-config.json deleted file mode 100644 index 27d3868fe31..00000000000 --- a/.github/workflows/link-check-config.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "ignorePatterns": [ - { - "pattern": "(localhost)" - } - ], - "retryOn429": true, - "retryCount": 3, - "fallbackRetryDelay": "10s" -} \ No newline at end of file diff --git a/.github/workflows/link-check.yml b/.github/workflows/link-check.yml deleted file mode 100644 index 6f237e14b79..00000000000 --- a/.github/workflows/link-check.yml +++ /dev/null @@ -1,10 +0,0 @@ -name: Check Markdown links -on: pull_request -jobs: - markdown-link-check: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: gaurav-nelson/github-action-markdown-link-check@v1 - with: - config-file: '.github/workflows/link-check-config.json' diff --git a/modules/core/keeper/events_test.go b/modules/core/keeper/events_test.go new file mode 100644 index 00000000000..416943fa49f --- /dev/null +++ b/modules/core/keeper/events_test.go @@ -0,0 +1,106 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/ibc-go/v7/modules/core/keeper" + "github.com/cosmos/ibc-go/v7/modules/core/types" +) + +func TestConvertToErrorEvents(t *testing.T) { + var ( + events sdk.Events + expEvents sdk.Events + ) + + tc := []struct { + name string + malleate func() + }{ + { + "success: nil events", + func() { + events = nil + expEvents = nil + }, + }, + { + "success: empty events", + func() { + events = sdk.Events{} + expEvents = sdk.Events{} + }, + }, + { + "success: event with no attributes", + func() { + events = sdk.Events{ + sdk.NewEvent("testevent"), + } + expEvents = sdk.Events{ + sdk.NewEvent(types.ErrorAttributeKeyPrefix + "testevent"), + } + }, + }, + { + "success: event with attributes", + func() { + events = sdk.Events{ + sdk.NewEvent("testevent", + sdk.NewAttribute("key1", "value1"), + sdk.NewAttribute("key2", "value2"), + ), + } + expEvents = sdk.Events{ + sdk.NewEvent(types.ErrorAttributeKeyPrefix+"testevent", + sdk.NewAttribute(types.ErrorAttributeKeyPrefix+"key1", "value1"), + sdk.NewAttribute(types.ErrorAttributeKeyPrefix+"key2", "value2"), + ), + } + }, + }, + { + "success: multiple events with attributes", + func() { + events = sdk.Events{ + sdk.NewEvent("testevent1", + sdk.NewAttribute("key1", "value1"), + sdk.NewAttribute("key2", "value2"), + ), + sdk.NewEvent("testevent2", + sdk.NewAttribute("key3", "value3"), + sdk.NewAttribute("key4", "value4"), + ), + } + expEvents = sdk.Events{ + sdk.NewEvent(types.ErrorAttributeKeyPrefix+"testevent1", + sdk.NewAttribute(types.ErrorAttributeKeyPrefix+"key1", "value1"), + sdk.NewAttribute(types.ErrorAttributeKeyPrefix+"key2", "value2"), + ), + sdk.NewEvent(types.ErrorAttributeKeyPrefix+"testevent2", + sdk.NewAttribute(types.ErrorAttributeKeyPrefix+"key3", "value3"), + sdk.NewAttribute(types.ErrorAttributeKeyPrefix+"key4", "value4"), + ), + } + }, + }, + } + + for _, tc := range tc { + t.Run(tc.name, func(t *testing.T) { + // initial events and expected events are reset so that the test fails if + // the malleate function does not set them + events = nil + expEvents = sdk.Events{} + + tc.malleate() + + newEvents := keeper.ConvertToErrorEvents(events) + require.Equal(t, expEvents, newEvents) + }) + } +} diff --git a/modules/core/keeper/export_test.go b/modules/core/keeper/export_test.go new file mode 100644 index 00000000000..81d79ea7aa9 --- /dev/null +++ b/modules/core/keeper/export_test.go @@ -0,0 +1,9 @@ +package keeper + +import sdk "github.com/cosmos/cosmos-sdk/types" + +// ConvertToErrorEvents is a wrapper around convertToErrorEvents +// to allow the function to be directly called in tests. +func ConvertToErrorEvents(events sdk.Events) sdk.Events { + return convertToErrorEvents(events) +} diff --git a/modules/core/keeper/msg_server.go b/modules/core/keeper/msg_server.go index 7bb3c33ae33..7587505c30d 100644 --- a/modules/core/keeper/msg_server.go +++ b/modules/core/keeper/msg_server.go @@ -454,6 +454,9 @@ func (k Keeper) RecvPacket(goCtx context.Context, msg *channeltypes.MsgRecvPacke if ack == nil || ack.Success() { // write application state changes for asynchronous and successful acknowledgements writeFn() + } else { + // Modify events in cached context to reflect unsuccessful acknowledgement + ctx.EventManager().EmitEvents(convertToErrorEvents(cacheCtx.EventManager().Events())) } // Set packet acknowledgement only if the acknowledgement is not nil. @@ -698,3 +701,23 @@ func (k Keeper) Acknowledgement(goCtx context.Context, msg *channeltypes.MsgAckn return &channeltypes.MsgAcknowledgementResponse{Result: channeltypes.SUCCESS}, nil } + +// convertToErrorEvents converts all events to error events by appending the +// error attribute prefix to each event's attribute key. +func convertToErrorEvents(events sdk.Events) sdk.Events { + if events == nil { + return nil + } + + newEvents := make(sdk.Events, len(events)) + for i, event := range events { + newAttributes := make([]sdk.Attribute, len(event.Attributes)) + for j, attribute := range event.Attributes { + newAttributes[j] = sdk.NewAttribute(coretypes.ErrorAttributeKeyPrefix+attribute.Key, attribute.Value) + } + + newEvents[i] = sdk.NewEvent(coretypes.ErrorAttributeKeyPrefix+event.Type, newAttributes...) + } + + return newEvents +} diff --git a/modules/core/keeper/msg_server_test.go b/modules/core/keeper/msg_server_test.go index b46dbcff4c8..eac3dda0f7a 100644 --- a/modules/core/keeper/msg_server_test.go +++ b/modules/core/keeper/msg_server_test.go @@ -29,7 +29,6 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { var ( packet channeltypes.Packet path *ibctesting.Path - async bool // indicate no ack written ) testCases := []struct { @@ -37,6 +36,8 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { malleate func() expPass bool expRevert bool + async bool // indicate no ack written + replay bool // indicate replay (no-op) }{ {"success: ORDERED", func() { path.SetChannelOrdered() @@ -46,7 +47,7 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { suite.Require().NoError(err) packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) - }, true, false}, + }, true, false, false, false}, {"success: UNORDERED", func() { suite.coordinator.Setup(path) @@ -54,7 +55,7 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { suite.Require().NoError(err) packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) - }, true, false}, + }, true, false, false, false}, {"success: UNORDERED out of order packet", func() { // setup uses an UNORDERED channel suite.coordinator.Setup(path) @@ -66,7 +67,7 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) } - }, true, false}, + }, true, false, false, false}, {"success: OnRecvPacket callback returns revert=true", func() { suite.coordinator.Setup(path) @@ -74,26 +75,24 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { suite.Require().NoError(err) packet = channeltypes.NewPacket(ibctesting.MockFailPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) - }, true, true}, + }, true, true, false, false}, {"success: ORDERED - async acknowledgement", func() { path.SetChannelOrdered() suite.coordinator.Setup(path) - async = true sequence, err := path.EndpointA.SendPacket(timeoutHeight, 0, ibcmock.MockAsyncPacketData) suite.Require().NoError(err) packet = channeltypes.NewPacket(ibcmock.MockAsyncPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) - }, true, false}, + }, true, false, true, false}, {"success: UNORDERED - async acknowledgement", func() { suite.coordinator.Setup(path) - async = true sequence, err := path.EndpointA.SendPacket(timeoutHeight, 0, ibcmock.MockAsyncPacketData) suite.Require().NoError(err) packet = channeltypes.NewPacket(ibcmock.MockAsyncPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) - }, true, false}, + }, true, false, true, false}, {"failure: ORDERED out of order packet", func() { path.SetChannelOrdered() suite.coordinator.Setup(path) @@ -105,15 +104,15 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) } - }, false, false}, + }, false, false, false, false}, {"channel does not exist", func() { // any non-nil value of packet is valid suite.Require().NotNil(packet) - }, false, false}, + }, false, false, false, false}, {"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) - }, false, false}, + }, false, false, false, false}, {"successful no-op: ORDERED - packet already received (replay)", func() { // mock will panic if application callback is called twice on the same packet path.SetChannelOrdered() @@ -125,7 +124,7 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) err = path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) - }, true, false}, + }, true, false, false, true}, {"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) @@ -136,7 +135,7 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) err = path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) - }, true, false}, + }, true, false, false, true}, } for _, tc := range testCases { @@ -144,7 +143,6 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { suite.Run(tc.name, func() { suite.SetupTest() // reset - async = false // reset path = ibctesting.NewPath(suite.chainA, suite.chainB) tc.malleate() @@ -162,7 +160,10 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { msg := channeltypes.NewMsgRecvPacket(packet, proof, proofHeight, suite.chainB.SenderAccount.GetAddress().String()) - _, err := keeper.Keeper.RecvPacket(*suite.chainB.App.GetIBCKeeper(), sdk.WrapSDKContext(suite.chainB.GetContext()), msg) + ctx := suite.chainB.GetContext() + _, err := keeper.Keeper.RecvPacket(*suite.chainB.App.GetIBCKeeper(), sdk.WrapSDKContext(ctx), msg) + + events := ctx.EventManager().Events() if tc.expPass { suite.Require().NoError(err) @@ -175,14 +176,27 @@ func (suite *KeeperTestSuite) TestHandleRecvPacket() { _, exists := suite.chainB.GetSimApp().ScopedIBCMockKeeper.GetCapability(suite.chainB.GetContext(), ibcmock.GetMockRecvCanaryCapabilityName(packet)) if tc.expRevert { suite.Require().False(exists, "capability exists in store even after callback reverted") + + // context events should contain error events + suite.Require().Contains(events, keeper.ConvertToErrorEvents(sdk.Events{ibcmock.NewMockRecvPacketEvent()})[0]) + suite.Require().NotContains(events, ibcmock.NewMockRecvPacketEvent()) } else { suite.Require().True(exists, "callback state not persisted when revert is false") + + if tc.replay { + // context should not contain application events + suite.Require().NotContains(events, ibcmock.NewMockRecvPacketEvent()) + suite.Require().NotContains(events, keeper.ConvertToErrorEvents(sdk.Events{ibcmock.NewMockRecvPacketEvent()})[0]) + } else { + // context events should contain application events + suite.Require().Contains(events, ibcmock.NewMockRecvPacketEvent()) + } } // verify if ack was written ack, found := suite.chainB.App.GetIBCKeeper().ChannelKeeper.GetPacketAcknowledgement(suite.chainB.GetContext(), packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) - if async { + if tc.async { suite.Require().Nil(ack) suite.Require().False(found) @@ -212,6 +226,7 @@ func (suite *KeeperTestSuite) TestHandleAcknowledgePacket() { name string malleate func() expPass bool + replay bool // indicate replay (no-op) }{ {"success: ORDERED", func() { path.SetChannelOrdered() @@ -223,7 +238,7 @@ func (suite *KeeperTestSuite) TestHandleAcknowledgePacket() { packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) err = path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) - }, true}, + }, true, false}, {"success: UNORDERED", func() { suite.coordinator.Setup(path) @@ -233,7 +248,7 @@ func (suite *KeeperTestSuite) TestHandleAcknowledgePacket() { packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) err = path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) - }, true}, + }, true, false}, {"success: UNORDERED acknowledge out of order packet", func() { // setup uses an UNORDERED channel suite.coordinator.Setup(path) @@ -247,7 +262,7 @@ func (suite *KeeperTestSuite) TestHandleAcknowledgePacket() { err = path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) } - }, true}, + }, true, false}, {"failure: ORDERED acknowledge out of order packet", func() { path.SetChannelOrdered() suite.coordinator.Setup(path) @@ -261,11 +276,11 @@ func (suite *KeeperTestSuite) TestHandleAcknowledgePacket() { err = path.EndpointB.RecvPacket(packet) suite.Require().NoError(err) } - }, false}, + }, false, false}, {"channel does not exist", func() { // any non-nil value of packet is valid suite.Require().NotNil(packet) - }, false}, + }, false, false}, {"packet not received", func() { suite.coordinator.Setup(path) @@ -273,7 +288,7 @@ func (suite *KeeperTestSuite) TestHandleAcknowledgePacket() { suite.Require().NoError(err) packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, 0) - }, false}, + }, false, false}, {"successful no-op: ORDERED - packet already acknowledged (replay)", func() { suite.coordinator.Setup(path) @@ -286,7 +301,7 @@ func (suite *KeeperTestSuite) TestHandleAcknowledgePacket() { err = path.EndpointA.AcknowledgePacket(packet, ibctesting.MockAcknowledgement) suite.Require().NoError(err) - }, true}, + }, true, true}, {"successful no-op: UNORDERED - packet already acknowledged (replay)", func() { suite.coordinator.Setup(path) @@ -299,7 +314,7 @@ func (suite *KeeperTestSuite) TestHandleAcknowledgePacket() { err = path.EndpointA.AcknowledgePacket(packet, ibctesting.MockAcknowledgement) suite.Require().NoError(err) - }, true}, + }, true, true}, } for _, tc := range testCases { @@ -322,7 +337,10 @@ func (suite *KeeperTestSuite) TestHandleAcknowledgePacket() { msg := channeltypes.NewMsgAcknowledgement(packet, ibcmock.MockAcknowledgement.Acknowledgement(), proof, proofHeight, suite.chainA.SenderAccount.GetAddress().String()) - _, err := keeper.Keeper.Acknowledgement(*suite.chainA.App.GetIBCKeeper(), sdk.WrapSDKContext(suite.chainA.GetContext()), msg) + ctx := suite.chainA.GetContext() + _, err := keeper.Keeper.Acknowledgement(*suite.chainA.App.GetIBCKeeper(), sdk.WrapSDKContext(ctx), msg) + + events := ctx.EventManager().Events() if tc.expPass { suite.Require().NoError(err) @@ -334,6 +352,14 @@ func (suite *KeeperTestSuite) TestHandleAcknowledgePacket() { // 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) + + if tc.replay { + // context should not contain application events + suite.Require().NotContains(events, ibcmock.NewMockAckPacketEvent()) + } else { + // context events should contain application events + suite.Require().Contains(events, ibcmock.NewMockAckPacketEvent()) + } } else { suite.Require().Error(err) } @@ -357,6 +383,7 @@ func (suite *KeeperTestSuite) TestHandleTimeoutPacket() { name string malleate func() expPass bool + noop bool // indicate no-op }{ {"success: ORDERED", func() { path.SetChannelOrdered() @@ -375,7 +402,7 @@ func (suite *KeeperTestSuite) TestHandleTimeoutPacket() { packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, timeoutTimestamp) packetKey = host.NextSequenceRecvKey(packet.GetDestPort(), packet.GetDestChannel()) - }, true}, + }, true, false}, {"success: UNORDERED", func() { suite.coordinator.Setup(path) @@ -392,7 +419,7 @@ func (suite *KeeperTestSuite) TestHandleTimeoutPacket() { packet = channeltypes.NewPacket(ibctesting.MockPacketData, sequence, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, timeoutHeight, timeoutTimestamp) packetKey = host.PacketReceiptKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) - }, true}, + }, true, false}, {"success: UNORDERED timeout out of order packet", func() { // setup uses an UNORDERED channel suite.coordinator.Setup(path) @@ -413,7 +440,7 @@ func (suite *KeeperTestSuite) TestHandleTimeoutPacket() { suite.Require().NoError(err) packetKey = host.PacketReceiptKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) - }, true}, + }, true, false}, {"success: ORDERED timeout out of order packet", func() { path.SetChannelOrdered() suite.coordinator.Setup(path) @@ -434,18 +461,19 @@ func (suite *KeeperTestSuite) TestHandleTimeoutPacket() { suite.Require().NoError(err) packetKey = host.NextSequenceRecvKey(packet.GetDestPort(), packet.GetDestChannel()) - }, true}, + }, true, false}, {"channel does not exist", func() { // any non-nil value of packet is valid suite.Require().NotNil(packet) packetKey = host.NextSequenceRecvKey(packet.GetDestPort(), packet.GetDestChannel()) - }, false}, + }, false, false}, {"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, clienttypes.NewHeight(0, 1), 0) packetKey = host.PacketReceiptKey(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()) - }, true}, + }, true, true}, } for _, tc := range testCases { @@ -467,7 +495,10 @@ func (suite *KeeperTestSuite) TestHandleTimeoutPacket() { msg := channeltypes.NewMsgTimeout(packet, 1, proof, proofHeight, suite.chainA.SenderAccount.GetAddress().String()) - _, err := keeper.Keeper.Timeout(*suite.chainA.App.GetIBCKeeper(), sdk.WrapSDKContext(suite.chainA.GetContext()), msg) + ctx := suite.chainA.GetContext() + _, err := keeper.Keeper.Timeout(*suite.chainA.App.GetIBCKeeper(), sdk.WrapSDKContext(ctx), msg) + + events := ctx.EventManager().Events() if tc.expPass { suite.Require().NoError(err) @@ -480,6 +511,14 @@ func (suite *KeeperTestSuite) TestHandleTimeoutPacket() { has := suite.chainA.App.GetIBCKeeper().ChannelKeeper.HasPacketCommitment(suite.chainA.GetContext(), packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) suite.Require().False(has) + if tc.noop { + // context should not contain application events + suite.Require().NotContains(events, ibcmock.NewMockTimeoutPacketEvent()) + } else { + // context should contain application events + suite.Require().Contains(events, ibcmock.NewMockTimeoutPacketEvent()) + } + } else { suite.Require().Error(err) } diff --git a/modules/core/types/events.go b/modules/core/types/events.go new file mode 100644 index 00000000000..be5c20efe12 --- /dev/null +++ b/modules/core/types/events.go @@ -0,0 +1,3 @@ +package types + +const ErrorAttributeKeyPrefix = "ibccallbackerror-" diff --git a/testing/mock/events.go b/testing/mock/events.go new file mode 100644 index 00000000000..e92592c0326 --- /dev/null +++ b/testing/mock/events.go @@ -0,0 +1,39 @@ +package mock + +import sdk "github.com/cosmos/cosmos-sdk/types" + +const ( + MockEventTypeRecvPacket = "mock-recv-packet" + MockEventTypeAckPacket = "mock-ack-packet" + MockEventTypeTimeoutPacket = "mock-timeout" + + MockAttributeKey1 = "mock-attribute-key-1" + MockAttributeKey2 = "mock-attribute-key-2" + + MockAttributeValue1 = "mock-attribute-value-1" + MockAttributeValue2 = "mock-attribute-value-2" +) + +// NewMockRecvPacketEvent returns a mock receive packet event +func NewMockRecvPacketEvent() sdk.Event { + return newMockEvent(MockEventTypeRecvPacket) +} + +// NewMockAckPacketEvent returns a mock acknowledgement packet event +func NewMockAckPacketEvent() sdk.Event { + return newMockEvent(MockEventTypeAckPacket) +} + +// NewMockTimeoutPacketEvent emits a mock timeout packet event +func NewMockTimeoutPacketEvent() sdk.Event { + return newMockEvent(MockEventTypeTimeoutPacket) +} + +// emitMockEvent returns a mock event with the given event type +func newMockEvent(eventType string) sdk.Event { + return sdk.NewEvent( + eventType, + sdk.NewAttribute(MockAttributeKey1, MockAttributeValue1), + sdk.NewAttribute(MockAttributeKey2, MockAttributeValue2), + ) +} diff --git a/testing/mock/ibc_module.go b/testing/mock/ibc_module.go index 798a1e7eeb1..ae136e34fee 100644 --- a/testing/mock/ibc_module.go +++ b/testing/mock/ibc_module.go @@ -135,6 +135,8 @@ func (im IBCModule) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet, re panic(err) } + ctx.EventManager().EmitEvent(NewMockRecvPacketEvent()) + if bytes.Equal(MockPacketData, packet.GetData()) { return MockAcknowledgement } else if bytes.Equal(MockAsyncPacketData, packet.GetData()) { @@ -157,6 +159,8 @@ func (im IBCModule) OnAcknowledgementPacket(ctx sdk.Context, packet channeltypes panic(err) } + ctx.EventManager().EmitEvent(NewMockAckPacketEvent()) + return nil } @@ -173,6 +177,8 @@ func (im IBCModule) OnTimeoutPacket(ctx sdk.Context, packet channeltypes.Packet, panic(err) } + ctx.EventManager().EmitEvent(NewMockTimeoutPacketEvent()) + return nil }