Skip to content

Commit

Permalink
e2e(upgrades): ordered ICA channel can be closed after upgrade and pa…
Browse files Browse the repository at this point in the history
…cket timeout (#5537)

* e2e: upgrade ICA channel to wire up fee middleware, then close it by timing out a packet

* fix version used in upgrade

* check channel b also is closed

* fix test

* update hermes repository

* update hermes repository

* Update e2e/tests/interchain_accounts/upgrades_test.go

Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com>

---------

Co-authored-by: colin axnér <25233464+colin-axner@users.noreply.github.com>
  • Loading branch information
crodriguezvega and colin-axner authored Jan 29, 2024
1 parent 22a1727 commit 9a00ffe
Showing 1 changed file with 221 additions and 0 deletions.
221 changes: 221 additions & 0 deletions e2e/tests/interchain_accounts/upgrades_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
//go:build !test_e2e

package interchainaccounts

import (
"context"
"testing"
"time"

sdkmath "cosmossdk.io/math"
"github.com/strangelove-ventures/interchaintest/v8"
"github.com/strangelove-ventures/interchaintest/v8/ibc"
test "github.com/strangelove-ventures/interchaintest/v8/testutil"
testifysuite "github.com/stretchr/testify/suite"

sdk "github.com/cosmos/cosmos-sdk/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
"github.com/cosmos/gogoproto/proto"

"github.com/cosmos/ibc-go/e2e/testsuite"
"github.com/cosmos/ibc-go/e2e/testvalues"
controllertypes "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/controller/types"
icatypes "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/types"
feetypes "github.com/cosmos/ibc-go/v8/modules/apps/29-fee/types"
channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types"
ibctesting "github.com/cosmos/ibc-go/v8/testing"
)

func TestInterchainAccountsChannelUpgradesTestSuite(t *testing.T) {
testifysuite.Run(t, new(InterchainAccountsChannelUpgradesTestSuite))
}

type InterchainAccountsChannelUpgradesTestSuite struct {
testsuite.E2ETestSuite
}

// TestChannelUpgrade_ICAChannelClosesAfterTimeout_Succeeds tests upgrading an ICA channel to
// wire up fee middleware and then forces it to close it by timing out a packet.
func (s *InterchainAccountsChannelUpgradesTestSuite) TestChannelUpgrade_ICAChannelClosesAfterTimeout_Succeeds() {
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, nil)
chainA, chainB := s.GetChains()

// setup 2 accounts: controller account on chain A, a second chain B account.
// host account will be created when the ICA is registered
controllerAccount := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount)
controllerAddress := controllerAccount.FormattedAddress()
chainBAccount := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount)

chainBDenom := chainB.Config().Denom
chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount)

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

var (
channelID = "channel-1"
controllerPortID string
hostPortID = icatypes.HostPortID
interchainAccount string
channelA channeltypes.Channel
)

t.Run("register interchain account", func(t *testing.T) {
var err error
// explicitly set the version string because we don't want to use incentivized channels.
version := icatypes.NewDefaultMetadataString(ibctesting.FirstConnectionID, ibctesting.FirstConnectionID)
msgRegisterInterchainAccount := controllertypes.NewMsgRegisterInterchainAccount(ibctesting.FirstConnectionID, controllerAddress, version, channeltypes.ORDERED)
txResp := s.BroadcastMessages(ctx, chainA, controllerAccount, msgRegisterInterchainAccount)
s.AssertTxSuccess(txResp)

controllerPortID, err = icatypes.NewControllerPortID(controllerAddress)
s.Require().NoError(err)
})

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

t.Run("verify interchain account", func(t *testing.T) {
var err error
interchainAccount, err = s.QueryInterchainAccount(ctx, chainA, controllerAddress, ibctesting.FirstConnectionID)
s.Require().NoError(err)
s.Require().NotZero(len(interchainAccount))

channelA, err = s.QueryChannel(ctx, chainA, controllerPortID, channelID)
s.Require().NoError(err)
})

t.Run("execute gov proposal to initiate channel upgrade", func(t *testing.T) {
s.initiateChannelUpgrade(ctx, chainA, chainAWallet, controllerPortID, channelID, s.createUpgradeFields(channelA))
})

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

t.Run("verify channel A upgraded and is fee enabled", func(t *testing.T) {
channel, err := s.QueryChannel(ctx, chainA, controllerPortID, channelID)
s.Require().NoError(err)

// check the channel version include the fee version
version, err := feetypes.MetadataFromVersion(channel.Version)
s.Require().NoError(err)
s.Require().Equal(feetypes.Version, version.FeeVersion, "the channel version did not include ics29")

// extra check
feeEnabled, err := s.QueryFeeEnabledChannel(ctx, chainA, controllerPortID, channelID)
s.Require().NoError(err)
s.Require().Equal(true, feeEnabled)
})

t.Run("verify channel B upgraded and is fee enabled", func(t *testing.T) {
channel, err := s.QueryChannel(ctx, chainB, hostPortID, channelID)
s.Require().NoError(err)

// check the channel version include the fee version
version, err := feetypes.MetadataFromVersion(channel.Version)
s.Require().NoError(err)
s.Require().Equal(feetypes.Version, version.FeeVersion, "the channel version did not include ics29")

// extra check
feeEnabled, err := s.QueryFeeEnabledChannel(ctx, chainB, hostPortID, channelID)
s.Require().NoError(err)
s.Require().Equal(true, feeEnabled)
})

// stop the relayer to let the submit tx message time out
t.Run("stop relayer", func(t *testing.T) {
s.StopRelayer(ctx, relayer)
})

t.Run("submit tx message with bank transfer message that times out", func(t *testing.T) {
t.Run("fund interchain account wallet", func(t *testing.T) {
// fund the host account account so it has some $$ to send
err := chainB.SendFunds(ctx, interchaintest.FaucetAccountKeyName, ibc.WalletAmount{
Address: interchainAccount,
Amount: sdkmath.NewInt(testvalues.StartingTokenAmount),
Denom: chainBDenom,
})
s.Require().NoError(err)
})

t.Run("broadcast MsgSendTx", func(t *testing.T) {
// assemble bank transfer message from host account to user account on host chain
msgSend := &banktypes.MsgSend{
FromAddress: interchainAccount,
ToAddress: chainBAccount.FormattedAddress(),
Amount: sdk.NewCoins(testvalues.DefaultTransferAmount(chainBDenom)),
}

cdc := testsuite.Codec()

bz, err := icatypes.SerializeCosmosTx(cdc, []proto.Message{msgSend}, icatypes.EncodingProtobuf)
s.Require().NoError(err)

packetData := icatypes.InterchainAccountPacketData{
Type: icatypes.EXECUTE_TX,
Data: bz,
Memo: "e2e",
}

timeout := uint64(1)
msgSendTx := controllertypes.NewMsgSendTx(controllerAddress, ibctesting.FirstConnectionID, timeout, packetData)

resp := s.BroadcastMessages(
ctx,
chainA,
controllerAccount,
msgSendTx,
)

s.AssertTxSuccess(resp)

// this sleep is to allow the packet to timeout
time.Sleep(1 * time.Second)
})
})

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

t.Run("verify channel A is closed due to timeout on ordered channel", func(t *testing.T) {
channel, err := s.QueryChannel(ctx, chainA, controllerPortID, channelID)
s.Require().NoError(err)

s.Require().Equal(channeltypes.CLOSED, channel.State, "the channel was not in an expected state")
})

t.Run("verify channel B is closed due to timeout on ordered channel", func(t *testing.T) {
channel, err := s.QueryChannel(ctx, chainB, hostPortID, channelID)
s.Require().NoError(err)

s.Require().Equal(channeltypes.CLOSED, channel.State, "the channel was not in an expected state")
})
}

// createUpgradeFields created the upgrade fields for channel
func (s *InterchainAccountsChannelUpgradesTestSuite) createUpgradeFields(channel channeltypes.Channel) channeltypes.UpgradeFields {
versionMetadata := feetypes.Metadata{
FeeVersion: feetypes.Version,
AppVersion: channel.Version,
}
versionBytes, err := feetypes.ModuleCdc.MarshalJSON(&versionMetadata)
s.Require().NoError(err)

return channeltypes.NewUpgradeFields(channel.Ordering, channel.ConnectionHops, string(versionBytes))
}

// initiateChannelUpgrade creates and submits a governance proposal to execute the message to initiate a channel upgrade
func (s *InterchainAccountsChannelUpgradesTestSuite) initiateChannelUpgrade(ctx context.Context, chain ibc.Chain, wallet ibc.Wallet, portID, channelID string, upgradeFields channeltypes.UpgradeFields) {
govModuleAddress, err := s.QueryModuleAccountAddress(ctx, govtypes.ModuleName, chain)
s.Require().NoError(err)
s.Require().NotNil(govModuleAddress)

msg := channeltypes.NewMsgChannelUpgradeInit(portID, channelID, upgradeFields, govModuleAddress.String())
s.ExecuteAndPassGovV1Proposal(ctx, msg, chain, wallet)
}

0 comments on commit 9a00ffe

Please sign in to comment.