Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

E2E: Interchain Account failed bank transfer over incentivised channel #2049

Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 162 additions & 0 deletions e2e/interchain_accounts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -389,3 +389,165 @@ func (s *InterchainAccountsTestSuite) TestMsgSubmitTx_SuccessfulBankSend_Incenti
})
})
}

func (s *InterchainAccountsTestSuite) TestMsgSubmitTx_FailedBankSend_Incentivized() {
t := s.T()
ctx := context.TODO()

// setup relayers and connection-0 between two chains
// channel-0 is a transfer channel but it will not be used in this test case
relayer, _ := s.SetupChainsRelayerAndChannel(ctx)
chainA, chainB := s.GetChains()

var (
chainADenom = chainA.Config().Denom
interchainAcc = ""
testFee = testvalues.DefaultFee(chainADenom)
)

t.Run("relayer wallets recovered", func(t *testing.T) {
err := s.RecoverRelayerWallets(ctx, relayer)
s.Require().NoError(err)
})

chainARelayerWallet, chainBRelayerWallet, err := s.GetRelayerWallets(relayer)
t.Run("relayer wallets fetched", func(t *testing.T) {
s.Require().NoError(err)
})

s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks")

chainARelayerUser, chainBRelayerUser := s.GetRelayerUsers(ctx)
relayerAStartingBalance, err := s.GetChainANativeBalance(ctx, chainARelayerUser)
s.Require().NoError(err)
t.Logf("relayer A user starting with balance: %d", relayerAStartingBalance)

controllerAccount := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount)
chainBAccount := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount)

t.Run("register interchain account", func(t *testing.T) {
version := "" // allow app to handle the version as appropriate.
msgRegisterAccount := intertxtypes.NewMsgRegisterAccount(controllerAccount.Bech32Address(chainA.Config().Bech32Prefix), ibctesting.FirstConnectionID, version)
err := s.RegisterInterchainAccount(ctx, chainA, controllerAccount, msgRegisterAccount)
s.Require().NoError(err)
})

t.Run("start relayer", func(t *testing.T) {
s.StartRelayer(relayer)
})

var channelOutput ibc.ChannelOutput
t.Run("verify interchain account", func(t *testing.T) {
var err error
interchainAcc, err = s.QueryInterchainAccount(ctx, chainA, controllerAccount.Bech32Address(chainA.Config().Bech32Prefix), ibctesting.FirstConnectionID)
s.Require().NoError(err)
s.Require().NotZero(len(interchainAcc))

channels, err := relayer.GetChannels(ctx, s.GetRelayerExecReporter(), chainA.Config().ChainID)
s.Require().NoError(err)
s.Require().Equal(len(channels), 2)

// interchain accounts channel at index: 0
channelOutput = channels[0]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do you know that the ICA channel is at index 0?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah good question, @damiannolan do you recall why it is at index 0 rather than 1?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Originally I would've expected it to be at index 1 (as the transfer channel is created first), but after trying that and having a failing test, I quickly switched it around and had no failures after.
It's not ideal and I haven't dug into it much more than taking a look at this function https://github.com/strangelove-ventures/ibctest/blob/main/relayer/rly/cosmos_relayer.go#L247

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just looking into it here, it looks like this is output of that variable

CHANNELS=[{State:STATE_OPEN Ordering:ORDER_ORDERED Counterparty:{PortID:icahost ChannelID:channel-1} ConnectionHops:[connection-0] Version:{"fee_version":"ics29-1","app_version":"{\"version\":\"ics27-1\",\"controller_connection_id\":\"connection-0\",\"host_connection_id\":\"connection-0\",\"address\":\"cosmos1vlmvwtdcwrdczsn97t6w8dezegvp3fpsvml28d2fx6xz8k8rpzuqqg6lr4\",\"encoding\":\"proto3\",\"tx_type\":\"sdk_multi_msg\"}"} PortID:icacontroller-cosmos1qjkpfk26cfftrj096q759ftv4ap3lfqgatngdc ChannelID:channel-1} {State:STATE_OPEN Ordering:ORDER_UNORDERED Counterparty:{PortID:transfer ChannelID:channel-0} ConnectionHops:[connection-0] Version:ics20-1 PortID:transfer ChannelID:channel-0}]

The order seems unexpected but the channel we care about is definitely first.


s.Require().NoError(test.WaitForBlocks(ctx, 2, chainA, chainB))
})

t.Run("execute interchain account bank send through controller", func(t *testing.T) {

t.Run("register counterparty payee", func(t *testing.T) {
resp, err := s.RegisterCounterPartyPayee(ctx, chainB, chainBRelayerUser, channelOutput.Counterparty.PortID, channelOutput.Counterparty.ChannelID, chainBRelayerWallet.Address, chainARelayerWallet.Address)
s.Require().NoError(err)
s.AssertValidTxResponse(resp)
})

t.Run("verify counterparty payee", func(t *testing.T) {
address, err := s.QueryCounterPartyPayee(ctx, chainB, chainBRelayerWallet.Address, channelOutput.Counterparty.ChannelID)
s.Require().NoError(err)
s.Require().Equal(chainARelayerWallet.Address, address)
})

t.Run("no incentivized packets", func(t *testing.T) {
packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelOutput.PortID, channelOutput.ChannelID)
s.Require().NoError(err)
s.Require().Empty(packets)
})

t.Run("stop relayer", func(t *testing.T) {
err := relayer.StopRelayer(ctx, s.GetRelayerExecReporter())
s.Require().NoError(err)
})

t.Run("broadcast incentivized MsgSubmitTx", func(t *testing.T) {
msgPayPacketFee := &feetypes.MsgPayPacketFee{
Fee: testvalues.DefaultFee(chainADenom),
SourcePortId: channelOutput.PortID,
SourceChannelId: channelOutput.ChannelID,
Signer: controllerAccount.Bech32Address(chainA.Config().Bech32Prefix),
}

msgSend := &banktypes.MsgSend{
FromAddress: interchainAcc,
ToAddress: chainBAccount.Bech32Address(chainB.Config().Bech32Prefix),
Amount: sdk.NewCoins(testvalues.DefaultTransferAmount(chainB.Config().Denom)),
}

msgSubmitTx, err := intertxtypes.NewMsgSubmitTx(msgSend, ibctesting.FirstConnectionID, controllerAccount.Bech32Address(chainA.Config().Bech32Prefix))
s.Require().NoError(err)

resp, err := s.BroadcastMessages(ctx, chainA, controllerAccount, msgPayPacketFee, msgSubmitTx)
s.AssertValidTxResponse(resp)
s.Require().NoError(err)

s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB))
})

t.Run("there should be incentivized packets", func(t *testing.T) {
packets, err := s.QueryIncentivizedPacketsForChannel(ctx, chainA, channelOutput.PortID, channelOutput.ChannelID)
s.Require().NoError(err)
s.Require().Len(packets, 1)
actualFee := packets[0].PacketFees[0].Fee

s.Require().True(actualFee.RecvFee.IsEqual(testFee.RecvFee))
s.Require().True(actualFee.AckFee.IsEqual(testFee.AckFee))
s.Require().True(actualFee.TimeoutFee.IsEqual(testFee.TimeoutFee))
})

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, channelOutput.PortID, channelOutput.ChannelID)
s.Require().NoError(err)
s.Require().Empty(packets)
})

t.Run("verify interchain account did not send tokens", func(t *testing.T) {
balance, err := chainB.GetBalance(ctx, chainBAccount.Bech32Address(chainB.Config().Bech32Prefix), chainB.Config().Denom)
s.Require().NoError(err)

_, err = chainB.GetBalance(ctx, interchainAcc, chainB.Config().Denom)
s.Require().NoError(err)

expected := testvalues.StartingTokenAmount
s.Require().Equal(expected, balance, "tokens should not have been sent as interchain account was not funded")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the interchain account is not funded, shouldn't the balance be zero?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is just because all of the accounts are initialized like this

chainBAccount := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount)

It is not possible to initialize an account with a balance of 0 with the framework as far as I understand.

})

t.Run("timeout fee is refunded", func(t *testing.T) {
actualBalance, err := s.GetChainANativeBalance(ctx, controllerAccount)
s.Require().NoError(err)

expected := testvalues.StartingTokenAmount - 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, chainARelayerUser)
s.Require().NoError(err)

expected := relayerAStartingBalance + testFee.AckFee.AmountOf(chainADenom).Int64() + testFee.RecvFee.AmountOf(chainADenom).Int64()
s.Require().Equal(expected, actualBalance)
})
})
}