diff --git a/modules/apps/29-fee/ibc_middleware.go b/modules/apps/29-fee/ibc_middleware.go index d9161d7caba..6a0477f5508 100644 --- a/modules/apps/29-fee/ibc_middleware.go +++ b/modules/apps/29-fee/ibc_middleware.go @@ -267,10 +267,26 @@ func (im IBCMiddleware) OnAcknowledgementPacket( packetID := channeltypes.NewPacketId(packet.SourcePort, packet.SourceChannel, packet.Sequence) feesInEscrow, found := im.keeper.GetFeesInEscrow(ctx, packetID) - if found { + if !found { + // call underlying callback + return im.app.OnAcknowledgementPacket(ctx, packet, ack.AppAcknowledgement, relayer) + } + + payee, found := im.keeper.GetPayeeAddress(ctx, relayer.String(), packet.SourceChannel) + if !found { im.keeper.DistributePacketFeesOnAcknowledgement(ctx, ack.ForwardRelayerAddress, relayer, feesInEscrow.PacketFees, packetID) + + // call underlying callback + return im.app.OnAcknowledgementPacket(ctx, packet, ack.AppAcknowledgement, relayer) } + payeeAddr, err := sdk.AccAddressFromBech32(payee) + if err != nil { + return sdkerrors.Wrapf(err, "failed to create sdk.Address from payee: %s", payee) + } + + im.keeper.DistributePacketFeesOnAcknowledgement(ctx, ack.ForwardRelayerAddress, payeeAddr, feesInEscrow.PacketFees, packetID) + // call underlying callback return im.app.OnAcknowledgementPacket(ctx, packet, ack.AppAcknowledgement, relayer) } @@ -293,10 +309,26 @@ func (im IBCMiddleware) OnTimeoutPacket( packetID := channeltypes.NewPacketId(packet.SourcePort, packet.SourceChannel, packet.Sequence) feesInEscrow, found := im.keeper.GetFeesInEscrow(ctx, packetID) - if found { + if !found { + // call underlying callback + return im.app.OnTimeoutPacket(ctx, packet, relayer) + } + + payee, found := im.keeper.GetPayeeAddress(ctx, relayer.String(), packet.SourceChannel) + if !found { im.keeper.DistributePacketFeesOnTimeout(ctx, relayer, feesInEscrow.PacketFees, packetID) + + // call underlying callback + return im.app.OnTimeoutPacket(ctx, packet, relayer) } + payeeAddr, err := sdk.AccAddressFromBech32(payee) + if err != nil { + return sdkerrors.Wrapf(err, "failed to create sdk.Address from payee: %s", payee) + } + + im.keeper.DistributePacketFeesOnTimeout(ctx, payeeAddr, feesInEscrow.PacketFees, packetID) + // call underlying callback return im.app.OnTimeoutPacket(ctx, packet, relayer) } diff --git a/modules/apps/29-fee/ibc_middleware_test.go b/modules/apps/29-fee/ibc_middleware_test.go index 1aa8810453e..b5478fb1c51 100644 --- a/modules/apps/29-fee/ibc_middleware_test.go +++ b/modules/apps/29-fee/ibc_middleware_test.go @@ -607,112 +607,189 @@ func (suite *FeeTestSuite) TestOnRecvPacket() { } } -// different channel than sending chain func (suite *FeeTestSuite) TestOnAcknowledgementPacket() { var ( - ack []byte - packetFee types.PacketFee - originalBalance sdk.Coins - expectedBalance sdk.Coins - expectedRelayerBalance sdk.Coins - expLocked bool + ack []byte + packetID channeltypes.PacketId + packetFee types.PacketFee + refundAddr sdk.AccAddress + relayerAddr sdk.AccAddress + expRefundAccBalance sdk.Coins + expPayeeAccBalance sdk.Coins ) testCases := []struct { - name string - malleate func() - expFeesDistributed bool - expPass bool + name string + malleate func() + expPass bool + expResult func() }{ { "success", func() { - expectedRelayerBalance = packetFee.Fee.RecvFee.Add(packetFee.Fee.AckFee[0]) + // retrieve the relayer acc balance and add the expected recv and ack fees + relayerAccBalance := sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), relayerAddr, sdk.DefaultBondDenom)) + expPayeeAccBalance = relayerAccBalance.Add(packetFee.Fee.RecvFee...).Add(packetFee.Fee.AckFee...) + + // retrieve the refund acc balance and add the expected timeout fees + refundAccBalance := sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), refundAddr, sdk.DefaultBondDenom)) + expRefundAccBalance = refundAccBalance.Add(packetFee.Fee.TimeoutFee...) }, true, + func() { + // assert that the packet fees have been distributed + found := suite.chainA.GetSimApp().IBCFeeKeeper.HasFeesInEscrow(suite.chainA.GetContext(), packetID) + suite.Require().False(found) + + relayerAccBalance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), relayerAddr, sdk.DefaultBondDenom) + suite.Require().Equal(expPayeeAccBalance, sdk.NewCoins(relayerAccBalance)) + + refundAccBalance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), refundAddr, sdk.DefaultBondDenom) + suite.Require().Equal(expRefundAccBalance, sdk.NewCoins(refundAccBalance)) + }, + }, + { + "success: with registered payee address", + func() { + payeeAddr := suite.chainA.SenderAccounts[2].SenderAccount.GetAddress() + suite.chainA.GetSimApp().IBCFeeKeeper.SetPayeeAddress( + suite.chainA.GetContext(), + suite.chainA.SenderAccount.GetAddress().String(), + payeeAddr.String(), + suite.path.EndpointA.ChannelID, + ) + + // reassign ack.ForwardRelayerAddress to the registered payee address + ack = types.NewIncentivizedAcknowledgement(payeeAddr.String(), ibcmock.MockAcknowledgement.Acknowledgement(), true).Acknowledgement() + + // retrieve the payee acc balance and add the expected recv and ack fees + payeeAccBalance := sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), payeeAddr, sdk.DefaultBondDenom)) + expPayeeAccBalance = payeeAccBalance.Add(packetFee.Fee.RecvFee...).Add(packetFee.Fee.AckFee...) + + // retrieve the refund acc balance and add the expected timeout fees + refundAccBalance := sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), refundAddr, sdk.DefaultBondDenom)) + expRefundAccBalance = refundAccBalance.Add(packetFee.Fee.TimeoutFee...) + }, true, + func() { + // assert that the packet fees have been distributed + found := suite.chainA.GetSimApp().IBCFeeKeeper.HasFeesInEscrow(suite.chainA.GetContext(), packetID) + suite.Require().False(found) + + payeeAddr := suite.chainA.SenderAccounts[2].SenderAccount.GetAddress() + payeeAccBalance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), payeeAddr, sdk.DefaultBondDenom) + suite.Require().Equal(expPayeeAccBalance, sdk.NewCoins(payeeAccBalance)) + + refundAccBalance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), refundAddr, sdk.DefaultBondDenom) + suite.Require().Equal(expRefundAccBalance, sdk.NewCoins(refundAccBalance)) + }, }, { - "no op success without a packet fee", + "success: no op without a packet fee", func() { - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, suite.chainA.SenderAccount.GetSequence()) suite.chainA.GetSimApp().IBCFeeKeeper.DeleteFeesInEscrow(suite.chainA.GetContext(), packetID) ack = types.IncentivizedAcknowledgement{ AppAcknowledgement: ibcmock.MockAcknowledgement.Acknowledgement(), - ForwardRelayerAddress: suite.chainA.SenderAccount.GetAddress().String(), + ForwardRelayerAddress: "", }.Acknowledgement() - - expectedBalance = originalBalance }, true, - true, - }, - { - "ack wrong format", func() { - ack = []byte("unsupported acknowledgement format") - - expectedBalance = originalBalance + found := suite.chainA.GetSimApp().IBCFeeKeeper.HasFeesInEscrow(suite.chainA.GetContext(), packetID) + suite.Require().False(found) }, - false, - false, }, { - "channel is not fee not enabled, success", + "success: channel is not fee enabled", func() { suite.chainA.GetSimApp().IBCFeeKeeper.DeleteFeeEnabled(suite.chainA.GetContext(), suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID) ack = ibcmock.MockAcknowledgement.Acknowledgement() - - expectedBalance = originalBalance }, - false, true, + func() {}, }, { "success: fee module is disabled, skip fee logic", func() { lockFeeModule(suite.chainA) - expLocked = true - - expectedBalance = originalBalance }, - false, true, + func() { + suite.Require().Equal(true, suite.chainA.GetSimApp().IBCFeeKeeper.IsLocked(suite.chainA.GetContext())) + }, }, { - "fail on distribute receive fee (blocked address)", + "success: fail to distribute recv fee (blocked address), returned to refund account", func() { blockedAddr := suite.chainA.GetSimApp().AccountKeeper.GetModuleAccount(suite.chainA.GetContext(), transfertypes.ModuleName).GetAddress() - ack = types.IncentivizedAcknowledgement{ - AppAcknowledgement: ibcmock.MockAcknowledgement.Acknowledgement(), - ForwardRelayerAddress: blockedAddr.String(), - }.Acknowledgement() + // reassign ack.ForwardRelayerAddress to a blocked address + ack = types.NewIncentivizedAcknowledgement(blockedAddr.String(), ibcmock.MockAcknowledgement.Acknowledgement(), true).Acknowledgement() + + // retrieve the relayer acc balance and add the expected ack fees + relayerAccBalance := sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), relayerAddr, sdk.DefaultBondDenom)) + expPayeeAccBalance = relayerAccBalance.Add(packetFee.Fee.AckFee...) - expectedRelayerBalance = packetFee.Fee.AckFee - expectedBalance = expectedBalance.Add(packetFee.Fee.RecvFee...) + // retrieve the refund acc balance and add the expected recv fees and timeout fees + refundAccBalance := sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), refundAddr, sdk.DefaultBondDenom)) + expRefundAccBalance = refundAccBalance.Add(packetFee.Fee.RecvFee...).Add(packetFee.Fee.TimeoutFee...) }, - false, true, + func() { + // assert that the packet fees have been distributed + found := suite.chainA.GetSimApp().IBCFeeKeeper.HasFeesInEscrow(suite.chainA.GetContext(), packetID) + suite.Require().False(found) + + relayerAccBalance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), relayerAddr, sdk.DefaultBondDenom) + suite.Require().Equal(expPayeeAccBalance, sdk.NewCoins(relayerAccBalance)) + + refundAccBalance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), refundAddr, sdk.DefaultBondDenom) + suite.Require().Equal(expRefundAccBalance, sdk.NewCoins(refundAccBalance)) + }, }, { - "fail on no distribution by escrow account out of balance", + "fail: fee distribution fails and fee module is locked when escrow account does not have sufficient funds", func() { - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, suite.chainA.SenderAccount.GetSequence()) err := suite.chainA.GetSimApp().BankKeeper.SendCoinsFromModuleToAccount(suite.chainA.GetContext(), types.ModuleName, suite.chainA.SenderAccount.GetAddress(), smallAmount) suite.Require().NoError(err) - - // expectedBalance and expectedRelayerBalance don't change - expectedBalance = originalBalance.Add(smallAmount...) - expectedRelayerBalance = sdk.NewCoins() - - suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, types.NewPacketFees([]types.PacketFee{packetFee})) - - expLocked = true }, - false, true, + func() { + suite.Require().Equal(true, suite.chainA.GetSimApp().IBCFeeKeeper.IsLocked(suite.chainA.GetContext())) + }, + }, + { + "ack wrong format", + func() { + ack = []byte("unsupported acknowledgement format") + }, + false, + func() {}, + }, + { + "invalid registered payee address", + func() { + payeeAddr := "invalid-address" + suite.chainA.GetSimApp().IBCFeeKeeper.SetPayeeAddress( + suite.chainA.GetContext(), + suite.chainA.SenderAccount.GetAddress().String(), + payeeAddr, + suite.path.EndpointA.ChannelID, + ) + }, + false, + func() {}, + }, + { + "application callback fails", + func() { + suite.chainA.GetSimApp().FeeMockModule.IBCApp.OnAcknowledgementPacket = func(_ sdk.Context, _ channeltypes.Packet, _ []byte, _ sdk.AccAddress) error { + return fmt.Errorf("mock fee app callback fails") + } + }, + false, + func() {}, }, } @@ -720,53 +797,30 @@ func (suite *FeeTestSuite) TestOnAcknowledgementPacket() { tc := tc suite.Run(tc.name, func() { suite.SetupTest() - expLocked = false - suite.coordinator.Setup(suite.path) - packet := suite.CreateMockPacket() - - expectedRelayerBalance = sdk.Coins{} // reset - // set up module and callbacks - module, _, err := suite.chainA.App.GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), ibctesting.MockFeePort) - suite.Require().NoError(err) - - cbs, ok := suite.chainA.App.GetIBCKeeper().Router.GetRoute(module) - suite.Require().True(ok) + relayerAddr = suite.chainA.SenderAccounts[0].SenderAccount.GetAddress() + refundAddr = suite.chainA.SenderAccounts[1].SenderAccount.GetAddress() - // escrow the packet fee - packetID := channeltypes.NewPacketId(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) - packetFee = types.NewPacketFee( - types.Fee{ - RecvFee: defaultRecvFee, - AckFee: defaultAckFee, - TimeoutFee: defaultTimeoutFee, - }, - suite.chainA.SenderAccount.GetAddress().String(), - []string{}, - ) + packet := suite.CreateMockPacket() + packetID = channeltypes.NewPacketId(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + packetFee = types.NewPacketFee(types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee), refundAddr.String(), nil) suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, types.NewPacketFees([]types.PacketFee{packetFee})) - err = suite.chainA.GetSimApp().BankKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), types.ModuleName, packetFee.Fee.Total()) - suite.Require().NoError(err) - relayerAddr := suite.chainB.SenderAccount.GetAddress() + err := suite.chainA.GetSimApp().BankKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), refundAddr, types.ModuleName, packetFee.Fee.Total()) + suite.Require().NoError(err) - // must be changed explicitly - ack = types.IncentivizedAcknowledgement{ - AppAcknowledgement: ibcmock.MockAcknowledgement.Acknowledgement(), - ForwardRelayerAddress: relayerAddr.String(), - }.Acknowledgement() + ack = types.NewIncentivizedAcknowledgement(relayerAddr.String(), ibcmock.MockAcknowledgement.Acknowledgement(), true).Acknowledgement() - // log original sender balance - // NOTE: balance is logged after escrowing tokens - originalBalance = sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), ibctesting.TestCoin.Denom)) + tc.malleate() // malleate mutates test data - // default to success case - expectedBalance = originalBalance.Add(packetFee.Fee.TimeoutFee[0]) + // retrieve module callbacks + module, _, err := suite.chainA.App.GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), ibctesting.MockFeePort) + suite.Require().NoError(err) - // malleate test case - tc.malleate() + cbs, ok := suite.chainA.App.GetIBCKeeper().Router.GetRoute(module) + suite.Require().True(ok) err = cbs.OnAcknowledgementPacket(suite.chainA.GetContext(), packet, ack, relayerAddr) @@ -776,103 +830,152 @@ func (suite *FeeTestSuite) TestOnAcknowledgementPacket() { suite.Require().Error(err) } - suite.Require().Equal(expLocked, suite.chainA.GetSimApp().IBCFeeKeeper.IsLocked(suite.chainA.GetContext())) - - suite.Require().Equal( - expectedBalance, - sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), ibctesting.TestCoin.Denom)), - ) - - relayerBalance := sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), relayerAddr, ibctesting.TestCoin.Denom)) - if tc.expFeesDistributed { - // there should no longer be a fee in escrow for this packet - found := suite.chainA.GetSimApp().IBCFeeKeeper.HasFeesInEscrow(suite.chainA.GetContext(), packetID) - suite.Require().False(found) - } - - suite.Require().Equal( - expectedRelayerBalance, - relayerBalance, - ) + tc.expResult() }) } } func (suite *FeeTestSuite) TestOnTimeoutPacket() { var ( - relayerAddr sdk.AccAddress - packetFee types.PacketFee - originalBalance sdk.Coins - expectedBalance sdk.Coins - expLocked bool + packetID channeltypes.PacketId + packetFee types.PacketFee + refundAddr sdk.AccAddress + relayerAddr sdk.AccAddress + expRefundAccBalance sdk.Coins + expPayeeAccBalance sdk.Coins ) + testCases := []struct { - name string - malleate func() - expFeeDistributed bool + name string + malleate func() + expPass bool + expResult func() }{ { "success", - func() {}, + func() { + // retrieve the relayer acc balance and add the expected timeout fees + relayerAccBalance := sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), relayerAddr, sdk.DefaultBondDenom)) + expPayeeAccBalance = relayerAccBalance.Add(packetFee.Fee.TimeoutFee...) + + // retrieve the refund acc balance and add the expected recv and ack fees + refundAccBalance := sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), refundAddr, sdk.DefaultBondDenom)) + expRefundAccBalance = refundAccBalance.Add(packetFee.Fee.RecvFee...).Add(packetFee.Fee.AckFee...) + }, true, + func() { + // assert that the packet fees have been distributed + found := suite.chainA.GetSimApp().IBCFeeKeeper.HasFeesInEscrow(suite.chainA.GetContext(), packetID) + suite.Require().False(found) + + relayerAccBalance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), relayerAddr, sdk.DefaultBondDenom) + suite.Require().Equal(expPayeeAccBalance, sdk.NewCoins(relayerAccBalance)) + + refundAccBalance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), refundAddr, sdk.DefaultBondDenom) + suite.Require().Equal(expRefundAccBalance, sdk.NewCoins(refundAccBalance)) + }, }, { - "fee not enabled", + "success: with registered payee address", func() { - suite.chainA.GetSimApp().IBCFeeKeeper.DeleteFeeEnabled(suite.chainA.GetContext(), suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID) + payeeAddr := suite.chainA.SenderAccounts[2].SenderAccount.GetAddress() + suite.chainA.GetSimApp().IBCFeeKeeper.SetPayeeAddress( + suite.chainA.GetContext(), + suite.chainA.SenderAccount.GetAddress().String(), + payeeAddr.String(), + suite.path.EndpointA.ChannelID, + ) + + // retrieve the relayer acc balance and add the expected timeout fees + payeeAccBalance := sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), payeeAddr, sdk.DefaultBondDenom)) + expPayeeAccBalance = payeeAccBalance.Add(packetFee.Fee.TimeoutFee...) + + // retrieve the refund acc balance and add the expected recv and ack fees + refundAccBalance := sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), refundAddr, sdk.DefaultBondDenom)) + expRefundAccBalance = refundAccBalance.Add(packetFee.Fee.RecvFee...).Add(packetFee.Fee.AckFee...) + }, + true, + func() { + // assert that the packet fees have been distributed + found := suite.chainA.GetSimApp().IBCFeeKeeper.HasFeesInEscrow(suite.chainA.GetContext(), packetID) + suite.Require().False(found) + + payeeAddr := suite.chainA.SenderAccounts[2].SenderAccount.GetAddress() + payeeAccBalance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), payeeAddr, sdk.DefaultBondDenom) + suite.Require().Equal(expPayeeAccBalance, sdk.NewCoins(payeeAccBalance)) - expectedBalance = originalBalance + refundAccBalance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), refundAddr, sdk.DefaultBondDenom) + suite.Require().Equal(expRefundAccBalance, sdk.NewCoins(refundAccBalance)) }, - false, }, { - "fee module is disabled, skip fee logic", + "success: channel is not fee enabled", + func() { + suite.chainA.GetSimApp().IBCFeeKeeper.DeleteFeeEnabled(suite.chainA.GetContext(), suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID) + }, + true, + func() {}, + }, + { + "success: fee module is disabled, skip fee logic", func() { lockFeeModule(suite.chainA) - expLocked = true - - expectedBalance = originalBalance }, - false, + true, + func() { + suite.Require().Equal(true, suite.chainA.GetSimApp().IBCFeeKeeper.IsLocked(suite.chainA.GetContext())) + }, }, { - "no op if identified packet fee doesn't exist", + "success: no op if identified packet fee doesn't exist", func() { - // delete packet fee - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, suite.chainA.SenderAccount.GetSequence()) suite.chainA.GetSimApp().IBCFeeKeeper.DeleteFeesInEscrow(suite.chainA.GetContext(), packetID) - - expectedBalance = originalBalance }, - false, + true, + func() {}, }, { - "distribute fee fails for timeout fee (blocked address)", + "success: fail to distribute timeout fee (blocked address), returned to refund account", func() { relayerAddr = suite.chainA.GetSimApp().AccountKeeper.GetModuleAccount(suite.chainA.GetContext(), transfertypes.ModuleName).GetAddress() - - expectedBalance = originalBalance. - Add(packetFee.Fee.RecvFee...). - Add(packetFee.Fee.AckFee...). - Add(packetFee.Fee.TimeoutFee...) }, - false, + true, + func() {}, }, { - "fail on no distribution by escrow account out of balance", + "fee distribution fails and fee module is locked when escrow account does not have sufficient funds", func() { - packetID := channeltypes.NewPacketId(suite.path.EndpointA.ChannelConfig.PortID, suite.path.EndpointA.ChannelID, suite.chainA.SenderAccount.GetSequence()) err := suite.chainA.GetSimApp().BankKeeper.SendCoinsFromModuleToAccount(suite.chainA.GetContext(), types.ModuleName, suite.chainA.SenderAccount.GetAddress(), smallAmount) suite.Require().NoError(err) - - // expectedBalance don't change - expectedBalance = originalBalance.Add(smallAmount...) - - suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, types.NewPacketFees([]types.PacketFee{packetFee})) - - expLocked = true + }, + true, + func() { + suite.Require().Equal(true, suite.chainA.GetSimApp().IBCFeeKeeper.IsLocked(suite.chainA.GetContext())) + }, + }, + { + "invalid registered payee address", + func() { + payeeAddr := "invalid-address" + suite.chainA.GetSimApp().IBCFeeKeeper.SetPayeeAddress( + suite.chainA.GetContext(), + suite.chainA.SenderAccount.GetAddress().String(), + payeeAddr, + suite.path.EndpointA.ChannelID, + ) + }, + false, + func() {}, + }, + { + "application callback fails", + func() { + suite.chainA.GetSimApp().FeeMockModule.IBCApp.OnTimeoutPacket = func(_ sdk.Context, _ channeltypes.Packet, _ sdk.AccAddress) error { + return fmt.Errorf("mock fee app callback fails") + } }, false, + func() {}, }, } @@ -880,69 +983,37 @@ func (suite *FeeTestSuite) TestOnTimeoutPacket() { tc := tc suite.Run(tc.name, func() { suite.SetupTest() - expLocked = false - suite.coordinator.Setup(suite.path) - packet := suite.CreateMockPacket() - - // set up module and callbacks - module, _, err := suite.chainA.App.GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), ibctesting.MockFeePort) - suite.Require().NoError(err) - - cbs, ok := suite.chainA.App.GetIBCKeeper().Router.GetRoute(module) - suite.Require().True(ok) - - packetID := channeltypes.NewPacketId(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) - // must be explicitly changed - relayerAddr = suite.chainB.SenderAccount.GetAddress() + relayerAddr = suite.chainA.SenderAccounts[0].SenderAccount.GetAddress() + refundAddr = suite.chainA.SenderAccounts[1].SenderAccount.GetAddress() - packetFee = types.NewPacketFee( - types.Fee{ - RecvFee: defaultRecvFee, - AckFee: defaultAckFee, - TimeoutFee: defaultTimeoutFee, - }, - suite.chainA.SenderAccount.GetAddress().String(), - []string{}, - ) + packet := suite.CreateMockPacket() + packetID = channeltypes.NewPacketId(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence()) + packetFee = types.NewPacketFee(types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee), refundAddr.String(), nil) suite.chainA.GetSimApp().IBCFeeKeeper.SetFeesInEscrow(suite.chainA.GetContext(), packetID, types.NewPacketFees([]types.PacketFee{packetFee})) - err = suite.chainA.GetSimApp().BankKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), types.ModuleName, packetFee.Fee.Total()) + err := suite.chainA.GetSimApp().BankKeeper.SendCoinsFromAccountToModule(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), types.ModuleName, packetFee.Fee.Total()) suite.Require().NoError(err) - // log original sender balance - // NOTE: balance is logged after escrowing tokens - originalBalance = sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), ibctesting.TestCoin.Denom)) - - // default to success case - expectedBalance = originalBalance. - Add(packetFee.Fee.RecvFee[0]). - Add(packetFee.Fee.AckFee[0]) - - // malleate test case - tc.malleate() + tc.malleate() // malleate mutates test data - err = cbs.OnTimeoutPacket(suite.chainA.GetContext(), packet, relayerAddr) + // retrieve module callbacks + module, _, err := suite.chainA.App.GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), ibctesting.MockFeePort) suite.Require().NoError(err) - suite.Require().Equal(expLocked, suite.chainA.GetSimApp().IBCFeeKeeper.IsLocked(suite.chainA.GetContext())) - - suite.Require().Equal( - expectedBalance, - sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), ibctesting.TestCoin.Denom)), - ) + cbs, ok := suite.chainA.App.GetIBCKeeper().Router.GetRoute(module) + suite.Require().True(ok) - relayerBalance := sdk.NewCoins(suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), relayerAddr, ibctesting.TestCoin.Denom)) - if tc.expFeeDistributed { - // there should no longer be a fee in escrow for this packet - found := suite.chainA.GetSimApp().IBCFeeKeeper.HasFeesInEscrow(suite.chainA.GetContext(), packetID) - suite.Require().False(found) + err = cbs.OnTimeoutPacket(suite.chainA.GetContext(), packet, relayerAddr) - suite.Require().Equal(packetFee.Fee.TimeoutFee, relayerBalance) + if tc.expPass { + suite.Require().NoError(err) } else { - suite.Require().Empty(relayerBalance) + suite.Require().Error(err) } + + tc.expResult() }) } }