//go:build !test_e2e package upgrades import ( "context" "testing" "time" "github.com/cosmos/gogoproto/proto" "github.com/cosmos/interchaintest/v10" "github.com/cosmos/interchaintest/v10/chain/cosmos" "github.com/cosmos/interchaintest/v10/ibc" test "github.com/cosmos/interchaintest/v10/testutil" "github.com/stretchr/testify/suite" "go.uber.org/zap" sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/ibc-go/e2e/testsuite" "github.com/cosmos/ibc-go/e2e/testsuite/query" "github.com/cosmos/ibc-go/e2e/testvalues" controllertypes "github.com/cosmos/ibc-go/v10/modules/apps/27-interchain-accounts/controller/types" icatypes "github.com/cosmos/ibc-go/v10/modules/apps/27-interchain-accounts/types" channeltypes "github.com/cosmos/ibc-go/v10/modules/core/04-channel/types" ibctesting "github.com/cosmos/ibc-go/v10/testing" ) // compatibility:from_version: v8.7.0 func TestGenesisTestSuite(t *testing.T) { suite.Run(t, new(GenesisTestSuite)) } type GenesisTestSuite struct { testsuite.E2ETestSuite } // SetupSuite sets up chains for the current test suite func (s *GenesisTestSuite) SetupSuite() { s.SetupChains(context.TODO(), 2, nil) } // TODO: this configuration was originally being applied to `GetChains` in the test body, but it is not // actually being propagated correctly. If we want to apply the configuration, we can uncomment this code // however the test actually fails when this is done. // func (s *GenesisTestSuite) SetupSuite() { // configFileOverrides := make(map[string]any) // appTomlOverrides := make(test.Toml) // // appTomlOverrides["halt-height"] = haltHeight // configFileOverrides["config/app.toml"] = appTomlOverrides // // s.SetupChains(context.TODO(), nil, func(options *testsuite.ChainOptions) { // // create chains with specified chain configuration options // options.ChainSpecs[0].ConfigFileOverrides = configFileOverrides // }) // } func (s *GenesisTestSuite) TestIBCGenesis() { t := s.T() haltHeight := int64(100) chainA, chainB := s.GetChains() ctx := context.Background() testName := t.Name() relayer := s.CreateDefaultPaths(testName) channelA := s.GetChainAChannelForTest(testName) var ( chainADenom = chainA.Config().Denom chainBIBCToken = testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID) // IBC token sent to chainB ) chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) chainAAddress := chainAWallet.FormattedAddress() chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) chainBAddress := chainBWallet.FormattedAddress() s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") t.Run("ics20: native IBC token transfer from chainA to chainB, sender is source of tokens", 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) }) t.Run("ics20: tokens are escrowed", func(t *testing.T) { actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) s.Require().NoError(err) expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount s.Require().Equal(expected, actualBalance) }) // 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) var hostAccount string t.Run("ics27: broadcast MsgRegisterInterchainAccount", func(t *testing.T) { // explicitly set the version string because we don't want to use incentivized channels. version := icatypes.NewDefaultMetadataString(ibctesting.FirstConnectionID, ibctesting.FirstConnectionID) msgRegisterAccount := controllertypes.NewMsgRegisterInterchainAccount(ibctesting.FirstConnectionID, controllerAddress, version, channeltypes.ORDERED) txResp := s.BroadcastMessages(ctx, chainA, controllerAccount, msgRegisterAccount) s.AssertTxSuccess(txResp) }) t.Run("start relayer", func(t *testing.T) { s.StartRelayer(relayer, testName) }) t.Run("ics20: packets are relayed", func(t *testing.T) { s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) s.Require().NoError(err) expected := testvalues.IBCTransferAmount s.Require().Equal(expected, actualBalance.Int64()) }) s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB), "failed to wait for blocks") t.Run("ics27: verify interchain account", func(t *testing.T) { res, err := query.GRPCQuery[controllertypes.QueryInterchainAccountResponse](ctx, chainA, &controllertypes.QueryInterchainAccountRequest{ Owner: controllerAddress, ConnectionId: ibctesting.FirstConnectionID, }) s.Require().NoError(err) s.Require().NotZero(len(res.Address)) hostAccount = res.Address s.Require().NotEmpty(hostAccount) channels, err := relayer.GetChannels(ctx, s.GetRelayerExecReporter(), chainA.Config().ChainID) s.Require().NoError(err) s.Require().Equal(len(channels), 2) }) s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB), "failed to wait for blocks") t.Run("Halt chain and export genesis", func(t *testing.T) { s.HaltChainAndExportGenesis(ctx, chainA.(*cosmos.CosmosChain), haltHeight) }) t.Run("ics20: native IBC token transfer from chainA to chainB, sender is source of tokens", 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) }) t.Run("ics20: tokens are escrowed", func(t *testing.T) { actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) s.Require().NoError(err) expected := testvalues.StartingTokenAmount - 2*testvalues.IBCTransferAmount s.Require().Equal(expected, actualBalance) }) t.Run("ics27: interchain account executes a bank transfer on behalf of the corresponding owner account", func(t *testing.T) { t.Run("fund interchain account wallet", func(t *testing.T) { // fund the host account so it has some $$ to send err := chainB.SendFunds(ctx, interchaintest.FaucetAccountKeyName, ibc.WalletAmount{ Address: hostAccount, Amount: sdkmath.NewInt(testvalues.StartingTokenAmount), Denom: chainB.Config().Denom, }) 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: hostAccount, ToAddress: chainBAccount.FormattedAddress(), Amount: sdk.NewCoins(testvalues.DefaultTransferAmount(chainB.Config().Denom)), } 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", } msgSendTx := controllertypes.NewMsgSendTx(controllerAccount.FormattedAddress(), ibctesting.FirstConnectionID, uint64(time.Hour.Nanoseconds()), packetData) resp := s.BroadcastMessages( ctx, chainA, controllerAccount, msgSendTx, ) s.AssertTxSuccess(resp) s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB)) }) }) s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") } func (s *GenesisTestSuite) HaltChainAndExportGenesis(ctx context.Context, chain *cosmos.CosmosChain, haltHeight int64) { timeoutCtx, timeoutCtxCancel := context.WithTimeout(ctx, time.Minute*2) defer timeoutCtxCancel() err := test.WaitForBlocks(timeoutCtx, int(haltHeight), chain) s.Require().Error(err, "chain did not halt at halt height") err = chain.StopAllNodes(ctx) s.Require().NoError(err, "error stopping node(s)") state, err := chain.ExportState(ctx, haltHeight) s.Require().NoError(err) appTomlOverrides := make(test.Toml) appTomlOverrides["halt-height"] = 0 for _, node := range chain.Nodes() { err := node.OverwriteGenesisFile(ctx, []byte(state)) s.Require().NoError(err) } for _, node := range chain.Nodes() { err := test.ModifyTomlConfigFile( ctx, zap.NewExample(), node.DockerClient, node.TestName, node.VolumeName, "config/app.toml", appTomlOverrides, ) s.Require().NoError(err) _, _, err = node.ExecBin(ctx, "comet", "unsafe-reset-all") s.Require().NoError(err) } err = chain.StartAllNodes(ctx) s.Require().NoError(err) timeoutCtx, timeoutCtxCancel = context.WithTimeout(ctx, time.Minute*2) defer timeoutCtxCancel() err = test.WaitForBlocks(timeoutCtx, int(blocksAfterUpgrade), chain) s.Require().NoError(err, "chain did not produce blocks after halt") height, err := chain.Height(ctx) s.Require().NoError(err, "error fetching height after halt") s.Require().Greater(height, haltHeight, "height did not increment after halt") }