From 9de61ef2bdebd6b1f067dc34b89732dde91c155b Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Mon, 8 Jan 2024 09:43:46 +0100 Subject: [PATCH 01/11] e2e test: send incentivised packet after upgrade, add extra tests for cbs --- e2e/tests/core/04-channel/upgrades_test.go | 109 +++++++++++++++++- .../controller/ibc_middleware_test.go | 27 +++++ .../host/ibc_module_test.go | 52 +++++++++ 3 files changed, 183 insertions(+), 5 deletions(-) diff --git a/e2e/tests/core/04-channel/upgrades_test.go b/e2e/tests/core/04-channel/upgrades_test.go index cb44b549a50..a4def8be07b 100644 --- a/e2e/tests/core/04-channel/upgrades_test.go +++ b/e2e/tests/core/04-channel/upgrades_test.go @@ -10,6 +10,8 @@ import ( test "github.com/strangelove-ventures/interchaintest/v8/testutil" testifysuite "github.com/stretchr/testify/suite" + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/cosmos/ibc-go/e2e/testsuite" @@ -47,15 +49,29 @@ func (s *ChannelTestSuite) TestChannelUpgrade_WithFeeMiddleware_Succeeds() { chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) chainBAddress := chainBWallet.FormattedAddress() + chainAwalletAmount := ibc.WalletAmount{ + Address: chainBWallet.FormattedAddress(), // destination address + Denom: chainADenom, + Amount: sdkmath.NewInt(testvalues.IBCTransferAmount), + } + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") // trying to create some inflight packets, although they might get relayed before the upgrade starts t.Run("create inflight transfer packets between chain A and chain B", func(t *testing.T) { - transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") - s.AssertTxSuccess(transferTxResp) + transferTxResp, err := chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet.KeyName(), chainAwalletAmount, ibc.TransferOptions{}) + s.Require().NoError(err) + s.Require().NoError(transferTxResp.Validate(), "chain-a ibc transfer tx is invalid") + + chainBwalletAmount := ibc.WalletAmount{ + Address: chainAWallet.FormattedAddress(), // destination address + Denom: chainBDenom, + Amount: sdkmath.NewInt(testvalues.IBCTransferAmount), + } - transferTxResp = s.Transfer(ctx, chainB, chainBWallet, channelB.PortID, channelB.ChannelID, testvalues.DefaultTransferAmount(chainBDenom), chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA), 0, "") - s.AssertTxSuccess(transferTxResp) + transferTxResp, err = chainB.SendIBCTransfer(ctx, channelB.ChannelID, chainBWallet.KeyName(), chainBwalletAmount, ibc.TransferOptions{}) + s.Require().NoError(err) + s.Require().NoError(transferTxResp.Validate(), "chain-a ibc transfer tx is invalid") }) t.Run("execute gov proposal to initiate channel upgrade", func(t *testing.T) { @@ -102,7 +118,7 @@ func (s *ChannelTestSuite) TestChannelUpgrade_WithFeeMiddleware_Succeeds() { s.Require().Equal(true, feeEnabled) }) - t.Run("verify channel B upgraded and has version with ics29", func(t *testing.T) { + t.Run("verify channel B upgraded and is fee enabled", func(t *testing.T) { channel, err := s.QueryChannel(ctx, chainB, channelB.PortID, channelB.ChannelID) s.Require().NoError(err) @@ -116,6 +132,89 @@ func (s *ChannelTestSuite) TestChannelUpgrade_WithFeeMiddleware_Succeeds() { s.Require().NoError(err) s.Require().Equal(true, feeEnabled) }) + + t.Run("stop relayer", func(t *testing.T) { + s.StopRelayer(ctx, relayer) + }) + + var ( + chainATx ibc.Tx + payPacketFeeTxResp sdk.TxResponse + chainARelayerWallet, chainBRelayerWallet ibc.Wallet + testFee = testvalues.DefaultFee(chainADenom) + ) + + t.Run("recover relayer wallets", func(t *testing.T) { + err := s.RecoverRelayerWallets(ctx, relayer) + s.Require().NoError(err) + + chainARelayerWallet, chainBRelayerWallet, err = s.GetRelayerWallets(relayer) + s.Require().NoError(err) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + t.Run("register and verify counterparty payee", func(t *testing.T) { + _, chainBRelayerUser := s.GetRelayerUsers(ctx) + resp := s.RegisterCounterPartyPayee(ctx, chainB, chainBRelayerUser, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, chainBRelayerWallet.FormattedAddress(), chainARelayerWallet.FormattedAddress()) + s.AssertTxSuccess(resp) + + address, err := s.QueryCounterPartyPayee(ctx, chainB, chainBRelayerWallet.FormattedAddress(), channelA.Counterparty.ChannelID) + s.Require().NoError(err) + s.Require().Equal(chainARelayerWallet.FormattedAddress(), address) + }) + + t.Run("send IBC transfer from chain A to chain B", func(t *testing.T) { + transferTxResp, err := chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet.KeyName(), chainAwalletAmount, ibc.TransferOptions{}) + s.Require().NoError(err) + s.Require().NoError(transferTxResp.Validate(), "chain-a ibc transfer tx is invalid") + }) + + t.Run("pay packet fee", func(t *testing.T) { + t.Run("no incentivized packets", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) + }) + + packetID := channeltypes.NewPacketID(channelA.PortID, channelA.ChannelID, chainATx.Packet.Sequence) + packetFee := feetypes.NewPacketFee(testFee, chainAWallet.FormattedAddress(), nil) + + t.Run("incentivize packet", func(t *testing.T) { + payPacketFeeTxResp = s.PayPacketFeeAsync(ctx, chainA, chainAWallet, packetID, packetFee) + s.AssertTxSuccess(payPacketFeeTxResp) + }) + + t.Run("there should be incentivized packets", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Len(packets, 1) + actualFee := packets[0].PacketFees[0].Fee + + s.Require().True(actualFee.RecvFee.Equal(testFee.RecvFee)) + s.Require().True(actualFee.AckFee.Equal(testFee.AckFee)) + s.Require().True(actualFee.TimeoutFee.Equal(testFee.TimeoutFee)) + }) + + t.Run("balance should be lowered by sum of recv, ack and timeout", func(t *testing.T) { + // The balance should be lowered by the sum of the recv, ack and timeout fees. + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - chainAwalletAmount.Amount.Int64() - testFee.Total().AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + t.Run("packets are relayed", func(t *testing.T) { + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) + }) } // createUpgradeFields created the upgrade fields for channel diff --git a/modules/apps/27-interchain-accounts/controller/ibc_middleware_test.go b/modules/apps/27-interchain-accounts/controller/ibc_middleware_test.go index 568edb2b031..fb5d54bf740 100644 --- a/modules/apps/27-interchain-accounts/controller/ibc_middleware_test.go +++ b/modules/apps/27-interchain-accounts/controller/ibc_middleware_test.go @@ -863,6 +863,33 @@ func (suite *InterchainAccountsTestSuite) TestOnChanUpgradeInit() { } } +// OnChanUpgradeTry callback returns error on controller chains +func (suite *InterchainAccountsTestSuite) TestOnChanUpgradeTry() { + suite.SetupTest() // reset + path := NewICAPath(suite.chainA, suite.chainB) + suite.coordinator.SetupConnections(path) + + err := SetupICAPath(path, TestOwnerAddress) + suite.Require().NoError(err) + + // call application callback directly + module, _, err := suite.chainA.App.GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID) + suite.Require().NoError(err) + + app, ok := suite.chainA.App.GetIBCKeeper().Router.GetRoute(module) + suite.Require().True(ok) + cbs, ok := app.(porttypes.UpgradableModule) + suite.Require().True(ok) + + version, err := cbs.OnChanUpgradeTry( + suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, + path.EndpointA.ChannelConfig.Order, []string{path.EndpointA.ConnectionID}, path.EndpointB.ChannelConfig.Version, + ) + suite.Require().Error(err) + suite.Require().ErrorIs(err, icatypes.ErrInvalidChannelFlow) + suite.Require().Equal("", version) +} + func (suite *InterchainAccountsTestSuite) TestOnChanUpgradeAck() { var ( path *ibctesting.Path diff --git a/modules/apps/27-interchain-accounts/host/ibc_module_test.go b/modules/apps/27-interchain-accounts/host/ibc_module_test.go index f5fdde6c5c1..3491f47d52d 100644 --- a/modules/apps/27-interchain-accounts/host/ibc_module_test.go +++ b/modules/apps/27-interchain-accounts/host/ibc_module_test.go @@ -604,6 +604,33 @@ func (suite *InterchainAccountsTestSuite) TestOnTimeoutPacket() { } } +// OnChanUpgradeInit callback returns error on host chains +func (suite *InterchainAccountsTestSuite) TestOnChanUpgradeInit() { + path := NewICAPath(suite.chainA, suite.chainB) + suite.coordinator.SetupConnections(path) + + err := SetupICAPath(path, TestOwnerAddress) + suite.Require().NoError(err) + + // call application callback directly + module, _, err := suite.chainB.App.GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainB.GetContext(), path.EndpointB.ChannelConfig.PortID) + suite.Require().NoError(err) + + app, ok := suite.chainB.App.GetIBCKeeper().Router.GetRoute(module) + suite.Require().True(ok) + cbs, ok := app.(porttypes.UpgradableModule) + suite.Require().True(ok) + + version, err := cbs.OnChanUpgradeInit( + suite.chainB.GetContext(), path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, + path.EndpointB.ChannelConfig.Order, []string{path.EndpointB.ConnectionID}, path.EndpointB.ChannelConfig.Version, + ) + + suite.Require().Error(err) + suite.Require().ErrorIs(err, icatypes.ErrInvalidChannelFlow) + suite.Require().Equal("", version) +} + func (suite *InterchainAccountsTestSuite) TestOnChanUpgradeTry() { testCases := []struct { name string @@ -672,6 +699,31 @@ func (suite *InterchainAccountsTestSuite) TestOnChanUpgradeTry() { } } +// OnChanUpgradeAck callback returns error on host chains +func (suite *InterchainAccountsTestSuite) TestOnChanUpgradeAck() { + path := NewICAPath(suite.chainA, suite.chainB) + suite.coordinator.SetupConnections(path) + + err := SetupICAPath(path, TestOwnerAddress) + suite.Require().NoError(err) + + // call application callback directly + module, _, err := suite.chainB.App.GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainB.GetContext(), path.EndpointB.ChannelConfig.PortID) + suite.Require().NoError(err) + + app, ok := suite.chainB.App.GetIBCKeeper().Router.GetRoute(module) + suite.Require().True(ok) + cbs, ok := app.(porttypes.UpgradableModule) + suite.Require().True(ok) + + err = cbs.OnChanUpgradeAck( + suite.chainB.GetContext(), path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, path.EndpointA.ChannelConfig.Version, + ) + + suite.Require().Error(err) + suite.Require().ErrorIs(err, icatypes.ErrInvalidChannelFlow) +} + func (suite *InterchainAccountsTestSuite) fundICAWallet(ctx sdk.Context, portID string, amount sdk.Coins) { interchainAccountAddr, found := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(ctx, ibctesting.FirstConnectionID, portID) suite.Require().True(found) From b90664139bbba14de8b97300c5a9263f0961f056 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Mon, 8 Jan 2024 09:45:40 +0100 Subject: [PATCH 02/11] update hermes docker image --- e2e/testsuite/testconfig.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/testsuite/testconfig.go b/e2e/testsuite/testconfig.go index c88646fc6d9..79fb493113e 100644 --- a/e2e/testsuite/testconfig.go +++ b/e2e/testsuite/testconfig.go @@ -58,7 +58,7 @@ const ( // TODO: https://github.com/cosmos/ibc-go/issues/4965 defaultHyperspaceTag = "20231122v39" // defaultHermesTag is the tag that will be used if no relayer tag is specified for hermes. - defaultHermesTag = "luca_joss-channel-upgrade-authority" + defaultHermesTag = "luca_joss-docker-image-channel-upgrade" // defaultChainTag is the tag that will be used for the chains if none is specified. defaultChainTag = "main" // defaultConfigFileName is the default filename for the config file that can be used to configure From 8573214602e80e7c28a4cd75b7b1efced547e6eb Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Mon, 8 Jan 2024 14:20:46 +0100 Subject: [PATCH 03/11] add prune acknowledgements to successful upgrade test --- e2e/tests/core/04-channel/upgrades_test.go | 34 +++++++++++++++------- e2e/testsuite/grpc_query.go | 14 +++++++++ e2e/testsuite/tx.go | 12 ++++++++ 3 files changed, 50 insertions(+), 10 deletions(-) diff --git a/e2e/tests/core/04-channel/upgrades_test.go b/e2e/tests/core/04-channel/upgrades_test.go index a4def8be07b..8effa33504d 100644 --- a/e2e/tests/core/04-channel/upgrades_test.go +++ b/e2e/tests/core/04-channel/upgrades_test.go @@ -133,6 +133,22 @@ func (s *ChannelTestSuite) TestChannelUpgrade_WithFeeMiddleware_Succeeds() { s.Require().Equal(true, feeEnabled) }) + t.Run("prune packet acknowledgements", func(t *testing.T) { + // there should be one ack for the packet that we sent before the upgrade + acks, err := s.QueryPacketAcknowledgements(ctx, chainA, channelA.PortID, channelA.ChannelID, []uint64{}) + s.Require().NoError(err) + s.Require().Len(acks, 1) + s.Require().Equal(uint64(1), acks[0].Sequence) + + pruneAcksTxResponse := s.PruneAcknowledgements(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, uint64(1)) + s.AssertTxSuccess(pruneAcksTxResponse) + + // after pruning there should not be any acks + acks, err = s.QueryPacketAcknowledgements(ctx, chainA, channelA.PortID, channelA.ChannelID, []uint64{}) + s.Require().NoError(err) + s.Require().Empty(acks) + }) + t.Run("stop relayer", func(t *testing.T) { s.StopRelayer(ctx, relayer) }) @@ -170,23 +186,21 @@ func (s *ChannelTestSuite) TestChannelUpgrade_WithFeeMiddleware_Succeeds() { s.Require().NoError(transferTxResp.Validate(), "chain-a ibc transfer tx is invalid") }) - t.Run("pay packet fee", func(t *testing.T) { - t.Run("no incentivized packets", func(t *testing.T) { + t.Run("incentivize packet", func(t *testing.T) { + t.Run("pay packet fee", func(t *testing.T) { + // before adding fees for the packet, there should not be incentivized packets packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) s.Require().NoError(err) s.Require().Empty(packets) - }) - packetID := channeltypes.NewPacketID(channelA.PortID, channelA.ChannelID, chainATx.Packet.Sequence) - packetFee := feetypes.NewPacketFee(testFee, chainAWallet.FormattedAddress(), nil) + packetID := channeltypes.NewPacketID(channelA.PortID, channelA.ChannelID, chainATx.Packet.Sequence) + packetFee := feetypes.NewPacketFee(testFee, chainAWallet.FormattedAddress(), nil) - t.Run("incentivize packet", func(t *testing.T) { payPacketFeeTxResp = s.PayPacketFeeAsync(ctx, chainA, chainAWallet, packetID, packetFee) s.AssertTxSuccess(payPacketFeeTxResp) - }) - t.Run("there should be incentivized packets", func(t *testing.T) { - packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + // after adding fees for the packet, there should be incentivized packets + packets, err = s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) s.Require().NoError(err) s.Require().Len(packets, 1) actualFee := packets[0].PacketFees[0].Fee @@ -196,7 +210,7 @@ func (s *ChannelTestSuite) TestChannelUpgrade_WithFeeMiddleware_Succeeds() { s.Require().True(actualFee.TimeoutFee.Equal(testFee.TimeoutFee)) }) - t.Run("balance should be lowered by sum of recv, ack and timeout", func(t *testing.T) { + t.Run("balance should be lowered by sum of recv ack and timeout", func(t *testing.T) { // The balance should be lowered by the sum of the recv, ack and timeout fees. actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) s.Require().NoError(err) diff --git a/e2e/testsuite/grpc_query.go b/e2e/testsuite/grpc_query.go index 23e9a5fa5f8..07c2eec109f 100644 --- a/e2e/testsuite/grpc_query.go +++ b/e2e/testsuite/grpc_query.go @@ -214,6 +214,20 @@ func (s *E2ETestSuite) QueryPacketCommitment(ctx context.Context, chain ibc.Chai return res.Commitment, nil } +// QueryPacketAcknowledgements queries the packet acknowledgements on the given chain for the provided channel (optional) list of packet commitment sequences. +func (s *E2ETestSuite) QueryPacketAcknowledgements(ctx context.Context, chain ibc.Chain, portID, channelID string, packetCommitmentSequences []uint64) ([]*channeltypes.PacketState, error) { + queryClient := s.GetChainGRCPClients(chain).ChannelQueryClient + res, err := queryClient.PacketAcknowledgements(ctx, &channeltypes.QueryPacketAcknowledgementsRequest{ + PortId: portID, + ChannelId: channelID, + PacketCommitmentSequences: packetCommitmentSequences, + }) + if err != nil { + return nil, err + } + return res.Acknowledgements, nil +} + // QueryTotalEscrowForDenom queries the total amount of tokens in escrow for a denom func (s *E2ETestSuite) QueryTotalEscrowForDenom(ctx context.Context, chain ibc.Chain, denom string) (sdk.Coin, error) { queryClient := s.GetChainGRCPClients(chain).TransferQueryClient diff --git a/e2e/testsuite/tx.go b/e2e/testsuite/tx.go index 6f867b0e5c8..c3f39b8685c 100644 --- a/e2e/testsuite/tx.go +++ b/e2e/testsuite/tx.go @@ -285,3 +285,15 @@ func (s *E2ETestSuite) PayPacketFeeAsync( msg := feetypes.NewMsgPayPacketFeeAsync(packetID, packetFee) return s.BroadcastMessages(ctx, chain, user, msg) } + +// PruneAcknowledgements broadcasts a MsgPruneAcknowledgements message. +func (s *E2ETestSuite) PruneAcknowledgements( + ctx context.Context, + chain ibc.Chain, + user ibc.Wallet, + portID, channelID string, + limit uint64, +) sdk.TxResponse { + msg := channeltypes.NewMsgPruneAcknowledgements(portID, channelID, limit, user.FormattedAddress()) + return s.BroadcastMessages(ctx, chain, user, msg) +} From f5f9b83a797bda67d8cefbbd644005f6b8d2dde8 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Wed, 10 Jan 2024 11:19:44 +0100 Subject: [PATCH 04/11] use correct tx response --- e2e/tests/core/04-channel/upgrades_test.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/e2e/tests/core/04-channel/upgrades_test.go b/e2e/tests/core/04-channel/upgrades_test.go index 8effa33504d..1cc37bc8f7d 100644 --- a/e2e/tests/core/04-channel/upgrades_test.go +++ b/e2e/tests/core/04-channel/upgrades_test.go @@ -55,6 +55,13 @@ func (s *ChannelTestSuite) TestChannelUpgrade_WithFeeMiddleware_Succeeds() { Amount: sdkmath.NewInt(testvalues.IBCTransferAmount), } + var ( + transferTxResp ibc.Tx + payPacketFeeTxResp sdk.TxResponse + chainARelayerWallet, chainBRelayerWallet ibc.Wallet + testFee = testvalues.DefaultFee(chainADenom) + ) + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") // trying to create some inflight packets, although they might get relayed before the upgrade starts @@ -153,13 +160,6 @@ func (s *ChannelTestSuite) TestChannelUpgrade_WithFeeMiddleware_Succeeds() { s.StopRelayer(ctx, relayer) }) - var ( - chainATx ibc.Tx - payPacketFeeTxResp sdk.TxResponse - chainARelayerWallet, chainBRelayerWallet ibc.Wallet - testFee = testvalues.DefaultFee(chainADenom) - ) - t.Run("recover relayer wallets", func(t *testing.T) { err := s.RecoverRelayerWallets(ctx, relayer) s.Require().NoError(err) @@ -193,7 +193,7 @@ func (s *ChannelTestSuite) TestChannelUpgrade_WithFeeMiddleware_Succeeds() { s.Require().NoError(err) s.Require().Empty(packets) - packetID := channeltypes.NewPacketID(channelA.PortID, channelA.ChannelID, chainATx.Packet.Sequence) + packetID := channeltypes.NewPacketID(channelA.PortID, channelA.ChannelID, transferTxResp.Packet.Sequence) packetFee := feetypes.NewPacketFee(testFee, chainAWallet.FormattedAddress(), nil) payPacketFeeTxResp = s.PayPacketFeeAsync(ctx, chainA, chainAWallet, packetID, packetFee) From f9889df493a55b3ed53d85bf750d8d91a9899c72 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Thu, 11 Jan 2024 10:42:35 +0100 Subject: [PATCH 05/11] getting further with the e2e test, addressing a couple of other review items --- e2e/tests/core/04-channel/upgrades_test.go | 5 +++-- modules/core/04-channel/keeper/upgrade_test.go | 2 +- modules/core/04-channel/types/msgs.go | 4 +++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/e2e/tests/core/04-channel/upgrades_test.go b/e2e/tests/core/04-channel/upgrades_test.go index 20b505b1e51..aad1773b7f4 100644 --- a/e2e/tests/core/04-channel/upgrades_test.go +++ b/e2e/tests/core/04-channel/upgrades_test.go @@ -180,8 +180,9 @@ func (s *ChannelTestSuite) TestChannelUpgrade_WithFeeMiddleware_Succeeds() { s.Require().Equal(chainARelayerWallet.FormattedAddress(), address) }) + var err error t.Run("send IBC transfer from chain A to chain B", func(t *testing.T) { - transferTxResp, err := chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet.KeyName(), chainAwalletAmount, ibc.TransferOptions{}) + transferTxResp, err = chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet.KeyName(), chainAwalletAmount, ibc.TransferOptions{}) s.Require().NoError(err) s.Require().NoError(transferTxResp.Validate(), "chain-a ibc transfer tx is invalid") }) @@ -215,7 +216,7 @@ func (s *ChannelTestSuite) TestChannelUpgrade_WithFeeMiddleware_Succeeds() { actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) s.Require().NoError(err) - expected := testvalues.StartingTokenAmount - chainAwalletAmount.Amount.Int64() - testFee.Total().AmountOf(chainADenom).Int64() + expected := testvalues.StartingTokenAmount - 2*chainAwalletAmount.Amount.Int64() - testFee.Total().AmountOf(chainADenom).Int64() s.Require().Equal(expected, actualBalance) }) }) diff --git a/modules/core/04-channel/keeper/upgrade_test.go b/modules/core/04-channel/keeper/upgrade_test.go index d270d93e493..515b4aa8351 100644 --- a/modules/core/04-channel/keeper/upgrade_test.go +++ b/modules/core/04-channel/keeper/upgrade_test.go @@ -956,7 +956,7 @@ func (suite *KeeperTestSuite) TestChanUpgradeConfirm() { { "invalid counterparty channel state", func() { - counterpartyChannelState = types.CLOSED + counterpartyChannelState = types.FLUSHING }, types.ErrInvalidCounterparty, }, diff --git a/modules/core/04-channel/types/msgs.go b/modules/core/04-channel/types/msgs.go index 24f4fcaecd1..facbc235d3f 100644 --- a/modules/core/04-channel/types/msgs.go +++ b/modules/core/04-channel/types/msgs.go @@ -701,7 +701,9 @@ func NewMsgChannelUpgradeCancel( } } -// ValidateBasic implements sdk.Msg +// ValidateBasic implements sdk.Msg. No checks are done for ErrorReceipt and ProofErrorReceipt +// since they are not required if the current channel state is not in FLUSHCOMPLETE and the signer +// is the designated authority (e.g. the governance module). func (msg MsgChannelUpgradeCancel) ValidateBasic() error { if err := host.PortIdentifierValidator(msg.PortId); err != nil { return errorsmod.Wrap(err, "invalid port ID") From 32c5778bbf53bf1b040c69002eba87c6250757c6 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Sun, 14 Jan 2024 13:49:37 +0100 Subject: [PATCH 06/11] refactor test to use sync incentivization instead of async --- e2e/tests/core/04-channel/upgrades_test.go | 88 +++++++++++----------- 1 file changed, 45 insertions(+), 43 deletions(-) diff --git a/e2e/tests/core/04-channel/upgrades_test.go b/e2e/tests/core/04-channel/upgrades_test.go index aad1773b7f4..1f381cfe32d 100644 --- a/e2e/tests/core/04-channel/upgrades_test.go +++ b/e2e/tests/core/04-channel/upgrades_test.go @@ -11,7 +11,6 @@ import ( testifysuite "github.com/stretchr/testify/suite" sdkmath "cosmossdk.io/math" - sdk "github.com/cosmos/cosmos-sdk/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/cosmos/ibc-go/e2e/testsuite" @@ -56,9 +55,8 @@ func (s *ChannelTestSuite) TestChannelUpgrade_WithFeeMiddleware_Succeeds() { } var ( - transferTxResp ibc.Tx - payPacketFeeTxResp sdk.TxResponse chainARelayerWallet, chainBRelayerWallet ibc.Wallet + relayerAStartingBalance int64 testFee = testvalues.DefaultFee(chainADenom) ) @@ -166,6 +164,10 @@ func (s *ChannelTestSuite) TestChannelUpgrade_WithFeeMiddleware_Succeeds() { chainARelayerWallet, chainBRelayerWallet, err = s.GetRelayerWallets(relayer) s.Require().NoError(err) + + relayerAStartingBalance, err = s.GetChainANativeBalance(ctx, chainARelayerWallet) + s.Require().NoError(err) + t.Logf("relayer A user starting with balance: %d", relayerAStartingBalance) }) s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") @@ -180,49 +182,22 @@ func (s *ChannelTestSuite) TestChannelUpgrade_WithFeeMiddleware_Succeeds() { s.Require().Equal(chainARelayerWallet.FormattedAddress(), address) }) - var err error - t.Run("send IBC transfer from chain A to chain B", func(t *testing.T) { - transferTxResp, err = chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet.KeyName(), chainAwalletAmount, ibc.TransferOptions{}) - s.Require().NoError(err) - s.Require().NoError(transferTxResp.Validate(), "chain-a ibc transfer tx is invalid") + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) }) - t.Run("incentivize packet", func(t *testing.T) { - t.Run("pay packet fee", func(t *testing.T) { - // before adding fees for the packet, there should not be incentivized packets - packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) - s.Require().NoError(err) - s.Require().Empty(packets) - - packetID := channeltypes.NewPacketID(channelA.PortID, channelA.ChannelID, transferTxResp.Packet.Sequence) - packetFee := feetypes.NewPacketFee(testFee, chainAWallet.FormattedAddress(), nil) - - payPacketFeeTxResp = s.PayPacketFeeAsync(ctx, chainA, chainAWallet, packetID, packetFee) - s.AssertTxSuccess(payPacketFeeTxResp) - - // after adding fees for the packet, there should be incentivized packets - packets, err = s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) - s.Require().NoError(err) - s.Require().Len(packets, 1) - actualFee := packets[0].PacketFees[0].Fee - - s.Require().True(actualFee.RecvFee.Equal(testFee.RecvFee)) - s.Require().True(actualFee.AckFee.Equal(testFee.AckFee)) - s.Require().True(actualFee.TimeoutFee.Equal(testFee.TimeoutFee)) - }) - - t.Run("balance should be lowered by sum of recv ack and timeout", func(t *testing.T) { - // The balance should be lowered by the sum of the recv, ack and timeout fees. - actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) - s.Require().NoError(err) - - expected := testvalues.StartingTokenAmount - 2*chainAwalletAmount.Amount.Int64() - testFee.Total().AmountOf(chainADenom).Int64() - s.Require().Equal(expected, actualBalance) - }) - }) + t.Run("send incentivizes packet", func(t *testing.T) { + // before adding fees for the packet, there should not be incentivized packets + packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) + s.Require().NoError(err) + s.Require().Empty(packets) - t.Run("start relayer", func(t *testing.T) { - s.StartRelayer(relayer) + transferAmount := testvalues.DefaultTransferAmount(chainA.Config().Denom) + + payPacketFeeMsg := feetypes.NewMsgPayPacketFee(testFee, channelA.PortID, channelA.ChannelID, chainAWallet.FormattedAddress(), nil) + transferMsg := transfertypes.NewMsgTransfer(channelA.PortID, channelA.ChannelID, transferAmount, chainAWallet.FormattedAddress(), chainBWallet.FormattedAddress(), s.GetTimeoutHeight(ctx, chainB), 0, "") + resp := s.BroadcastMessages(ctx, chainA, chainAWallet, payPacketFeeMsg, transferMsg) + s.AssertTxSuccess(resp) }) t.Run("packets are relayed", func(t *testing.T) { @@ -230,6 +205,33 @@ func (s *ChannelTestSuite) TestChannelUpgrade_WithFeeMiddleware_Succeeds() { s.Require().NoError(err) s.Require().Empty(packets) }) + + t.Run("tokens are received by walletB", func(t *testing.T) { + actualBalance, err := s.QueryBalance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + // walletB has received two IBC transfers of value testvalues.IBCTransferAmount since the start of the test. + expected := 2 * testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance.Int64()) + }) + + t.Run("timeout fee is refunded", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + // once the relayer has relayed the packets, the timeout fee should be refunded. + // walletA has done to IBC transfers of value testvalues.IBCTransferAmount since the start of the test. + expected := testvalues.StartingTokenAmount - 2*testvalues.IBCTransferAmount - testFee.AckFee.AmountOf(chainADenom).Int64() - testFee.RecvFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) + + t.Run("relayerA is paid ack and recv fee", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainARelayerWallet) + s.Require().NoError(err) + + expected := relayerAStartingBalance + testFee.AckFee.AmountOf(chainADenom).Int64() + testFee.RecvFee.AmountOf(chainADenom).Int64() + s.Require().Equal(expected, actualBalance) + }) } // TestChannelUpgrade_WithFeeMiddleware_FailsWithTimeoutOnAck tests upgrading a transfer channel to wire up fee middleware but fails on ACK because of timeout From 2dddce26092ebb267d05c14fb9bdee667a893278 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Sun, 14 Jan 2024 14:26:03 +0100 Subject: [PATCH 07/11] update hermes image tag --- e2e/testsuite/testconfig.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/testsuite/testconfig.go b/e2e/testsuite/testconfig.go index 79fb493113e..c88646fc6d9 100644 --- a/e2e/testsuite/testconfig.go +++ b/e2e/testsuite/testconfig.go @@ -58,7 +58,7 @@ const ( // TODO: https://github.com/cosmos/ibc-go/issues/4965 defaultHyperspaceTag = "20231122v39" // defaultHermesTag is the tag that will be used if no relayer tag is specified for hermes. - defaultHermesTag = "luca_joss-docker-image-channel-upgrade" + defaultHermesTag = "luca_joss-channel-upgrade-authority" // defaultChainTag is the tag that will be used for the chains if none is specified. defaultChainTag = "main" // defaultConfigFileName is the default filename for the config file that can be used to configure From a0df5832027db84df4cef18fe886c22f4b4dc831 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Sun, 14 Jan 2024 21:41:27 +0100 Subject: [PATCH 08/11] revert change that was breaking a test --- modules/core/04-channel/keeper/upgrade_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/core/04-channel/keeper/upgrade_test.go b/modules/core/04-channel/keeper/upgrade_test.go index 49727d5e0fa..690fe77b22f 100644 --- a/modules/core/04-channel/keeper/upgrade_test.go +++ b/modules/core/04-channel/keeper/upgrade_test.go @@ -956,7 +956,7 @@ func (suite *KeeperTestSuite) TestChanUpgradeConfirm() { { "invalid counterparty channel state", func() { - counterpartyChannelState = types.FLUSHING + counterpartyChannelState = types.CLOSED }, types.ErrInvalidCounterparty, }, From fea2e4b5b2c61842d305d645afc2c36b81a00a1c Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Mon, 15 Jan 2024 13:43:02 +0100 Subject: [PATCH 09/11] Apply suggestions from code review Co-authored-by: Damian Nolan Co-authored-by: DimitrisJim --- e2e/tests/core/04-channel/upgrades_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/e2e/tests/core/04-channel/upgrades_test.go b/e2e/tests/core/04-channel/upgrades_test.go index 1f381cfe32d..ca951e9fb65 100644 --- a/e2e/tests/core/04-channel/upgrades_test.go +++ b/e2e/tests/core/04-channel/upgrades_test.go @@ -76,7 +76,7 @@ func (s *ChannelTestSuite) TestChannelUpgrade_WithFeeMiddleware_Succeeds() { transferTxResp, err = chainB.SendIBCTransfer(ctx, channelB.ChannelID, chainBWallet.KeyName(), chainBwalletAmount, ibc.TransferOptions{}) s.Require().NoError(err) - s.Require().NoError(transferTxResp.Validate(), "chain-a ibc transfer tx is invalid") + s.Require().NoError(transferTxResp.Validate(), "chain-b ibc transfer tx is invalid") }) t.Run("execute gov proposal to initiate channel upgrade", func(t *testing.T) { @@ -186,7 +186,7 @@ func (s *ChannelTestSuite) TestChannelUpgrade_WithFeeMiddleware_Succeeds() { s.StartRelayer(relayer) }) - t.Run("send incentivizes packet", func(t *testing.T) { + t.Run("send incentivized transfer packet", func(t *testing.T) { // before adding fees for the packet, there should not be incentivized packets packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelA.PortID, channelA.ChannelID) s.Require().NoError(err) @@ -220,8 +220,8 @@ func (s *ChannelTestSuite) TestChannelUpgrade_WithFeeMiddleware_Succeeds() { s.Require().NoError(err) // once the relayer has relayed the packets, the timeout fee should be refunded. - // walletA has done to IBC transfers of value testvalues.IBCTransferAmount since the start of the test. - expected := testvalues.StartingTokenAmount - 2*testvalues.IBCTransferAmount - testFee.AckFee.AmountOf(chainADenom).Int64() - testFee.RecvFee.AmountOf(chainADenom).Int64() + // walletA has done two IBC transfers of value testvalues.IBCTransferAmount since the start of the test. + expected := testvalues.StartingTokenAmount - (2 * testvalues.IBCTransferAmount) - testFee.AckFee.AmountOf(chainADenom).Int64() - testFee.RecvFee.AmountOf(chainADenom).Int64() s.Require().Equal(expected, actualBalance) }) From 72e20980ada3ab1baee2c646c2a2e9f4778c61f5 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Mon, 15 Jan 2024 13:45:31 +0100 Subject: [PATCH 10/11] rename variables for consistency --- e2e/README.md | 6 +++--- e2e/tests/core/04-channel/upgrades_test.go | 6 +++--- e2e/tests/transfer/incentivized_test.go | 10 +++++----- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/e2e/README.md b/e2e/README.md index a0a7f3451a9..306b52cf1ac 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -161,9 +161,9 @@ chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) t.Run("broadcast multi message transaction", func(t *testing.T){ - payPacketFeeMsg := feetypes.NewMsgPayPacketFee(testFee, channelA.PortID, channelA.ChannelID, chainAWallet.Bech32Address(chainA.Config().Bech32Prefix), nil) - transferMsg := transfertypes.NewMsgTransfer(channelA.PortID, channelA.ChannelID, transferAmount, chainAWallet.Bech32Address(chainA.Config().Bech32Prefix), chainBWallet.Bech32Address(chainB.Config().Bech32Prefix), clienttypes.NewHeight(1, 1000), 0) - resp, err := s.BroadcastMessages(ctx, chainA, chainAWallet, payPacketFeeMsg, transferMsg) + msgPayPacketFee := feetypes.NewMsgPayPacketFee(testFee, channelA.PortID, channelA.ChannelID, chainAWallet.Bech32Address(chainA.Config().Bech32Prefix), nil) + msgTransfer := transfertypes.NewMsgTransfer(channelA.PortID, channelA.ChannelID, transferAmount, chainAWallet.Bech32Address(chainA.Config().Bech32Prefix), chainBWallet.Bech32Address(chainB.Config().Bech32Prefix), clienttypes.NewHeight(1, 1000), 0) + resp, err := s.BroadcastMessages(ctx, chainA, chainAWallet, msgPayPacketFee, msgTransfer) s.AssertValidTxResponse(resp) s.Require().NoError(err) diff --git a/e2e/tests/core/04-channel/upgrades_test.go b/e2e/tests/core/04-channel/upgrades_test.go index ca951e9fb65..614e20ed45a 100644 --- a/e2e/tests/core/04-channel/upgrades_test.go +++ b/e2e/tests/core/04-channel/upgrades_test.go @@ -194,9 +194,9 @@ func (s *ChannelTestSuite) TestChannelUpgrade_WithFeeMiddleware_Succeeds() { transferAmount := testvalues.DefaultTransferAmount(chainA.Config().Denom) - payPacketFeeMsg := feetypes.NewMsgPayPacketFee(testFee, channelA.PortID, channelA.ChannelID, chainAWallet.FormattedAddress(), nil) - transferMsg := transfertypes.NewMsgTransfer(channelA.PortID, channelA.ChannelID, transferAmount, chainAWallet.FormattedAddress(), chainBWallet.FormattedAddress(), s.GetTimeoutHeight(ctx, chainB), 0, "") - resp := s.BroadcastMessages(ctx, chainA, chainAWallet, payPacketFeeMsg, transferMsg) + msgPayPacketFee := feetypes.NewMsgPayPacketFee(testFee, channelA.PortID, channelA.ChannelID, chainAWallet.FormattedAddress(), nil) + msgTransfer := transfertypes.NewMsgTransfer(channelA.PortID, channelA.ChannelID, transferAmount, chainAWallet.FormattedAddress(), chainBWallet.FormattedAddress(), s.GetTimeoutHeight(ctx, chainB), 0, "") + resp := s.BroadcastMessages(ctx, chainA, chainAWallet, msgPayPacketFee, msgTransfer) s.AssertTxSuccess(resp) }) diff --git a/e2e/tests/transfer/incentivized_test.go b/e2e/tests/transfer/incentivized_test.go index 92a8109924a..4bcfc14b1af 100644 --- a/e2e/tests/transfer/incentivized_test.go +++ b/e2e/tests/transfer/incentivized_test.go @@ -189,8 +189,8 @@ func (s *IncentivizedTransferTestSuite) TestMsgPayPacketFee_InvalidReceiverAccou transferAmount := testvalues.DefaultTransferAmount(chainADenom) t.Run("send IBC transfer", func(t *testing.T) { - transferMsg := transfertypes.NewMsgTransfer(channelA.PortID, channelA.ChannelID, transferAmount, chainAWallet.FormattedAddress(), testvalues.InvalidAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") - txResp := s.BroadcastMessages(ctx, chainA, chainAWallet, transferMsg) + msgTransfer := transfertypes.NewMsgTransfer(channelA.PortID, channelA.ChannelID, transferAmount, chainAWallet.FormattedAddress(), testvalues.InvalidAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + txResp := s.BroadcastMessages(ctx, chainA, chainAWallet, msgTransfer) // this message should be successful, as receiver account is not validated on the sending chain. s.AssertTxSuccess(txResp) }) @@ -313,9 +313,9 @@ func (s *IncentivizedTransferTestSuite) TestMultiMsg_MsgPayPacketFeeSingleSender s.Require().Empty(packets) }) - payPacketFeeMsg := feetypes.NewMsgPayPacketFee(testFee, channelA.PortID, channelA.ChannelID, chainAWallet.FormattedAddress(), nil) - transferMsg := transfertypes.NewMsgTransfer(channelA.PortID, channelA.ChannelID, transferAmount, chainAWallet.FormattedAddress(), chainBWallet.FormattedAddress(), s.GetTimeoutHeight(ctx, chainB), 0, "") - resp := s.BroadcastMessages(ctx, chainA, chainAWallet, payPacketFeeMsg, transferMsg) + msgPayPacketFee := feetypes.NewMsgPayPacketFee(testFee, channelA.PortID, channelA.ChannelID, chainAWallet.FormattedAddress(), nil) + msgTransfer := transfertypes.NewMsgTransfer(channelA.PortID, channelA.ChannelID, transferAmount, chainAWallet.FormattedAddress(), chainBWallet.FormattedAddress(), s.GetTimeoutHeight(ctx, chainB), 0, "") + resp := s.BroadcastMessages(ctx, chainA, chainAWallet, msgPayPacketFee, msgTransfer) s.AssertTxSuccess(resp) t.Run("there should be incentivized packets", func(t *testing.T) { From 7bfd7bc793dcf21c3bbbb3c703c4d4fb055da57a Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Tue, 16 Jan 2024 11:29:43 +0100 Subject: [PATCH 11/11] rename variables for clarification --- e2e/tests/core/04-channel/upgrades_test.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/e2e/tests/core/04-channel/upgrades_test.go b/e2e/tests/core/04-channel/upgrades_test.go index 614e20ed45a..2e7a8a8f60c 100644 --- a/e2e/tests/core/04-channel/upgrades_test.go +++ b/e2e/tests/core/04-channel/upgrades_test.go @@ -48,12 +48,6 @@ func (s *ChannelTestSuite) TestChannelUpgrade_WithFeeMiddleware_Succeeds() { chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) chainBAddress := chainBWallet.FormattedAddress() - chainAwalletAmount := ibc.WalletAmount{ - Address: chainBWallet.FormattedAddress(), // destination address - Denom: chainADenom, - Amount: sdkmath.NewInt(testvalues.IBCTransferAmount), - } - var ( chainARelayerWallet, chainBRelayerWallet ibc.Wallet relayerAStartingBalance int64 @@ -64,17 +58,23 @@ func (s *ChannelTestSuite) TestChannelUpgrade_WithFeeMiddleware_Succeeds() { // trying to create some inflight packets, although they might get relayed before the upgrade starts t.Run("create inflight transfer packets between chain A and chain B", func(t *testing.T) { - transferTxResp, err := chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet.KeyName(), chainAwalletAmount, ibc.TransferOptions{}) + chainBWalletAmount := ibc.WalletAmount{ + Address: chainBWallet.FormattedAddress(), // destination address + Denom: chainADenom, + Amount: sdkmath.NewInt(testvalues.IBCTransferAmount), + } + + transferTxResp, err := chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet.KeyName(), chainBWalletAmount, ibc.TransferOptions{}) s.Require().NoError(err) s.Require().NoError(transferTxResp.Validate(), "chain-a ibc transfer tx is invalid") - chainBwalletAmount := ibc.WalletAmount{ + chainAwalletAmount := ibc.WalletAmount{ Address: chainAWallet.FormattedAddress(), // destination address Denom: chainBDenom, Amount: sdkmath.NewInt(testvalues.IBCTransferAmount), } - transferTxResp, err = chainB.SendIBCTransfer(ctx, channelB.ChannelID, chainBWallet.KeyName(), chainBwalletAmount, ibc.TransferOptions{}) + transferTxResp, err = chainB.SendIBCTransfer(ctx, channelB.ChannelID, chainBWallet.KeyName(), chainAwalletAmount, ibc.TransferOptions{}) s.Require().NoError(err) s.Require().NoError(transferTxResp.Validate(), "chain-b ibc transfer tx is invalid") })