github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/ibc-go/modules/apps/27-interchain-accounts/host/ibc_module_test.go (about)

     1  package host_test
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  
     7  	types3 "github.com/fibonacci-chain/fbc/libs/tendermint/types"
     8  
     9  	types2 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/ibc-adapter"
    10  
    11  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    12  
    13  	banktypes "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/bank"
    14  	clienttypes "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/02-client/types"
    15  	channeltypes "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/04-channel/types"
    16  	"github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/exported"
    17  
    18  	capabilitytypes "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/capability/types"
    19  	"github.com/fibonacci-chain/fbc/libs/ibc-go/modules/apps/27-interchain-accounts/host/types"
    20  	icatypes "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/apps/27-interchain-accounts/types"
    21  	host "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/24-host"
    22  	ibctesting "github.com/fibonacci-chain/fbc/libs/ibc-go/testing"
    23  	"github.com/fibonacci-chain/fbc/libs/tendermint/crypto"
    24  	"github.com/gogo/protobuf/proto"
    25  	"github.com/stretchr/testify/suite"
    26  )
    27  
    28  var (
    29  	// TODO: Cosmos-SDK ADR-28: Update crypto.AddressHash() when sdk uses address.Module()
    30  	// https://github.com/cosmos/cosmos-sdk/issues/10225
    31  	//
    32  	// TestAccAddress defines a resuable bech32 address for testing purposes
    33  	addr, _        = sdk.AccAddressFromHex(string(crypto.AddressHash([]byte(icatypes.ModuleName))))
    34  	TestAccAddress = icatypes.GenerateAddress(addr, ibctesting.FirstConnectionID, TestPortID)
    35  
    36  	// TestOwnerAddress defines a reusable bech32 address for testing purposes
    37  	TestOwnerAddress = "cosmos17dtl0mjt3t77kpuhg2edqzjpszulwhgzuj9ljs"
    38  
    39  	// TestPortID defines a resuable port identifier for testing purposes
    40  	TestPortID, _ = icatypes.NewControllerPortID(TestOwnerAddress)
    41  
    42  	// TestVersion defines a resuable interchainaccounts version string for testing purposes
    43  	TestVersion = string(icatypes.ModuleCdc.MustMarshalJSON(&icatypes.Metadata{
    44  		Version:                icatypes.Version,
    45  		ControllerConnectionId: ibctesting.FirstConnectionID,
    46  		HostConnectionId:       ibctesting.FirstConnectionID,
    47  		Encoding:               icatypes.EncodingProtobuf,
    48  		TxType:                 icatypes.TxTypeSDKMultiMsg,
    49  	}))
    50  )
    51  
    52  type InterchainAccountsTestSuite struct {
    53  	suite.Suite
    54  
    55  	coordinator *ibctesting.Coordinator
    56  
    57  	// testing chains used for convenience and readability
    58  	chainA ibctesting.TestChainI
    59  	chainB ibctesting.TestChainI
    60  }
    61  
    62  func TestICATestSuite(t *testing.T) {
    63  	suite.Run(t, new(InterchainAccountsTestSuite))
    64  }
    65  
    66  func (suite *InterchainAccountsTestSuite) SetupTest() {
    67  	types3.UnittestOnlySetMilestoneVenus1Height(-1)
    68  	types3.UnittestOnlySetMilestoneVenus4Height(-1)
    69  	suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2)
    70  	suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(0))
    71  	suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(1))
    72  }
    73  
    74  func NewICAPath(chainA, chainB ibctesting.TestChainI) *ibctesting.Path {
    75  	path := ibctesting.NewPath(chainA, chainB)
    76  	path.EndpointA.ChannelConfig.PortID = icatypes.PortID
    77  	path.EndpointB.ChannelConfig.PortID = icatypes.PortID
    78  	path.EndpointA.ChannelConfig.Order = channeltypes.ORDERED
    79  	path.EndpointB.ChannelConfig.Order = channeltypes.ORDERED
    80  	path.EndpointA.ChannelConfig.Version = TestVersion
    81  	path.EndpointB.ChannelConfig.Version = TestVersion
    82  
    83  	return path
    84  }
    85  
    86  func RegisterInterchainAccount(endpoint *ibctesting.Endpoint, owner string) error {
    87  	portID, err := icatypes.NewControllerPortID(owner)
    88  	if err != nil {
    89  		return err
    90  	}
    91  
    92  	channelSequence := endpoint.Chain.GetSimApp().GetIBCKeeper().ChannelKeeper.GetNextChannelSequence(endpoint.Chain.GetContext())
    93  
    94  	if err := endpoint.Chain.GetSimApp().ICAControllerKeeper.RegisterInterchainAccount(endpoint.Chain.GetContext(), endpoint.ConnectionID, owner, TestVersion); err != nil {
    95  		return err
    96  	}
    97  
    98  	// commit state changes for proof verification
    99  	endpoint.Chain.Coordinator().CommitBlock(endpoint.Chain)
   100  
   101  	// update port/channel ids
   102  	endpoint.ChannelID = channeltypes.FormatChannelIdentifier(channelSequence)
   103  	endpoint.ChannelConfig.PortID = portID
   104  
   105  	return nil
   106  }
   107  
   108  // SetupICAPath invokes the InterchainAccounts entrypoint and subsequent channel handshake handlers
   109  func SetupICAPath(path *ibctesting.Path, owner string) error {
   110  	if err := RegisterInterchainAccount(path.EndpointA, owner); err != nil {
   111  		return err
   112  	}
   113  
   114  	if err := path.EndpointB.ChanOpenTry(); err != nil {
   115  		return err
   116  	}
   117  
   118  	if err := path.EndpointA.ChanOpenAck(); err != nil {
   119  		return err
   120  	}
   121  
   122  	if err := path.EndpointB.ChanOpenConfirm(); err != nil {
   123  		return err
   124  	}
   125  
   126  	return nil
   127  }
   128  
   129  // Test initiating a ChanOpenInit using the host chain instead of the controller chain
   130  // ChainA is the controller chain. ChainB is the host chain
   131  func (suite *InterchainAccountsTestSuite) TestChanOpenInit() {
   132  	suite.SetupTest() // reset
   133  	path := NewICAPath(suite.chainA, suite.chainB)
   134  	suite.coordinator.SetupConnections(path)
   135  
   136  	// use chainB (host) for ChanOpenInit
   137  	msg := channeltypes.NewMsgChannelOpenInitV4(path.EndpointB.ChannelConfig.PortID,
   138  		icatypes.Version, channeltypes.ORDERED, []string{path.EndpointB.ConnectionID},
   139  		path.EndpointA.ChannelConfig.PortID, icatypes.ModuleName)
   140  	handler := suite.chainB.GetSimApp().MsgServiceRouter().HandlerWithMsg(msg)
   141  	_, err := handler(suite.chainB.GetContext(), msg)
   142  
   143  	suite.Require().Error(err)
   144  }
   145  
   146  func (suite *InterchainAccountsTestSuite) TestOnChanOpenTry() {
   147  	var (
   148  		path    *ibctesting.Path
   149  		channel *channeltypes.Channel
   150  	)
   151  
   152  	testCases := []struct {
   153  		name     string
   154  		malleate func()
   155  		expPass  bool
   156  	}{
   157  		{
   158  			"success", func() {}, true,
   159  		},
   160  		{
   161  			"host submodule disabled", func() {
   162  				suite.chainB.GetSimApp().ICAHostKeeper.SetParams(suite.chainB.GetContext(), types.NewParams(false, []string{}))
   163  			}, false,
   164  		},
   165  		{
   166  			"success: ICA auth module callback returns error", func() {
   167  				// mock module callback should not be called on host side
   168  				suite.chainB.GetSimApp().ICAAuthModule.IBCApp.OnChanOpenTry = func(ctx sdk.Context, order channeltypes.Order, connectionHops []string,
   169  					portID, channelID string, chanCap *capabilitytypes.Capability,
   170  					counterparty channeltypes.Counterparty, counterpartyVersion string,
   171  				) (string, error) {
   172  					return "", fmt.Errorf("mock ica auth fails")
   173  				}
   174  			}, true,
   175  		},
   176  		{
   177  			"ICA callback fails - invalid channel order", func() {
   178  				channel.Ordering = channeltypes.UNORDERED
   179  			}, false,
   180  		},
   181  	}
   182  
   183  	for _, tc := range testCases {
   184  		tc := tc
   185  
   186  		suite.Run(tc.name, func() {
   187  			suite.SetupTest() // reset
   188  
   189  			path = NewICAPath(suite.chainA, suite.chainB)
   190  			suite.coordinator.SetupConnections(path)
   191  
   192  			err := RegisterInterchainAccount(path.EndpointA, TestOwnerAddress)
   193  			suite.Require().NoError(err)
   194  			path.EndpointB.ChannelID = ibctesting.FirstChannelID
   195  
   196  			// default values
   197  			counterparty := channeltypes.NewCounterparty(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID)
   198  			channel = &channeltypes.Channel{
   199  				State:          channeltypes.TRYOPEN,
   200  				Ordering:       channeltypes.ORDERED,
   201  				Counterparty:   counterparty,
   202  				ConnectionHops: []string{path.EndpointB.ConnectionID},
   203  				Version:        path.EndpointB.ChannelConfig.Version,
   204  			}
   205  
   206  			tc.malleate()
   207  
   208  			// ensure channel on chainB is set in state
   209  			suite.chainB.GetSimApp().IBCKeeper.V2Keeper.ChannelKeeper.SetChannel(suite.chainB.GetContext(), path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, *channel)
   210  
   211  			module, _, err := suite.chainB.GetSimApp().GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainB.GetContext(), path.EndpointB.ChannelConfig.PortID)
   212  			suite.Require().NoError(err)
   213  
   214  			chanCap, err := suite.chainB.GetSimApp().GetScopedIBCKeeper().NewCapability(suite.chainB.GetContext(), host.ChannelCapabilityPath(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID))
   215  			suite.Require().NoError(err)
   216  
   217  			cbs, ok := suite.chainB.GetSimApp().GetIBCKeeper().Router.GetRoute(module)
   218  			suite.Require().True(ok)
   219  
   220  			version, err := cbs.OnChanOpenTry(suite.chainB.GetContext(), channel.Ordering, channel.GetConnectionHops(),
   221  				path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, chanCap, channel.Counterparty, path.EndpointB.ChannelConfig.Version, path.EndpointA.ChannelConfig.Version,
   222  			)
   223  
   224  			if tc.expPass {
   225  				suite.Require().NoError(err)
   226  			} else {
   227  				suite.Require().Error(err)
   228  				suite.Require().Equal("", version)
   229  			}
   230  		})
   231  	}
   232  }
   233  
   234  // Test initiating a ChanOpenAck using the host chain instead of the controller chain
   235  // ChainA is the controller chain. ChainB is the host chain
   236  func (suite *InterchainAccountsTestSuite) TestChanOpenAck() {
   237  	suite.SetupTest() // reset
   238  	path := NewICAPath(suite.chainA, suite.chainB)
   239  	suite.coordinator.SetupConnections(path)
   240  
   241  	err := RegisterInterchainAccount(path.EndpointA, TestOwnerAddress)
   242  	suite.Require().NoError(err)
   243  
   244  	err = path.EndpointB.ChanOpenTry()
   245  	suite.Require().NoError(err)
   246  
   247  	// chainA maliciously sets channel to TRYOPEN
   248  	channel := channeltypes.NewChannel(channeltypes.TRYOPEN, channeltypes.ORDERED, channeltypes.NewCounterparty(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID), []string{path.EndpointA.ConnectionID}, TestVersion)
   249  	suite.chainA.GetSimApp().GetIBCKeeper().ChannelKeeper.SetChannel(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, channel)
   250  
   251  	// commit state changes so proof can be created
   252  	//suite.chainA.NextBlock()
   253  	suite.chainA.Coordinator().CommitBlock(suite.chainA)
   254  
   255  	path.EndpointB.UpdateClient()
   256  
   257  	// query proof from ChainA
   258  	channelKey := host.ChannelKey(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID)
   259  	proofTry, proofHeight := path.EndpointA.Chain.QueryProof(channelKey)
   260  
   261  	// use chainB (host) for ChanOpenAck
   262  	msg := channeltypes.NewMsgChannelOpenAck(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, path.EndpointA.ChannelID, TestVersion, proofTry, proofHeight, icatypes.ModuleName)
   263  	handler := suite.chainB.GetSimApp().MsgServiceRouter().HandlerWithMsg(msg)
   264  	_, err = handler(suite.chainB.GetContext(), msg)
   265  
   266  	suite.Require().Error(err)
   267  }
   268  
   269  func (suite *InterchainAccountsTestSuite) TestOnChanOpenConfirm() {
   270  	testCases := []struct {
   271  		name     string
   272  		malleate func()
   273  		expPass  bool
   274  	}{
   275  		{
   276  			"success", func() {}, true,
   277  		},
   278  		{
   279  			"host submodule disabled", func() {
   280  				suite.chainB.GetSimApp().ICAHostKeeper.SetParams(suite.chainB.GetContext(), types.NewParams(false, []string{}))
   281  			}, false,
   282  		},
   283  		{
   284  			"success: ICA auth module callback returns error", func() {
   285  				// mock module callback should not be called on host side
   286  				suite.chainB.GetSimApp().ICAAuthModule.IBCApp.OnChanOpenConfirm = func(
   287  					ctx sdk.Context, portID, channelID string,
   288  				) error {
   289  					return fmt.Errorf("mock ica auth fails")
   290  				}
   291  			}, true,
   292  		},
   293  	}
   294  
   295  	for _, tc := range testCases {
   296  		tc := tc
   297  
   298  		suite.Run(tc.name, func() {
   299  			suite.SetupTest()
   300  			path := NewICAPath(suite.chainA, suite.chainB)
   301  			suite.coordinator.SetupConnections(path)
   302  
   303  			err := RegisterInterchainAccount(path.EndpointA, TestOwnerAddress)
   304  			suite.Require().NoError(err)
   305  
   306  			err = path.EndpointB.ChanOpenTry()
   307  			suite.Require().NoError(err)
   308  
   309  			err = path.EndpointA.ChanOpenAck()
   310  			suite.Require().NoError(err)
   311  
   312  			tc.malleate()
   313  
   314  			module, _, err := suite.chainB.GetSimApp().GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainB.GetContext(), path.EndpointB.ChannelConfig.PortID)
   315  			suite.Require().NoError(err)
   316  
   317  			cbs, ok := suite.chainB.GetSimApp().GetIBCKeeper().Router.GetRoute(module)
   318  			suite.Require().True(ok)
   319  
   320  			err = cbs.OnChanOpenConfirm(suite.chainB.GetContext(), path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID)
   321  
   322  			if tc.expPass {
   323  				suite.Require().NoError(err)
   324  			} else {
   325  				suite.Require().Error(err)
   326  			}
   327  		})
   328  	}
   329  }
   330  
   331  // OnChanCloseInit on host (chainB)
   332  func (suite *InterchainAccountsTestSuite) TestOnChanCloseInit() {
   333  	path := NewICAPath(suite.chainA, suite.chainB)
   334  	suite.coordinator.SetupConnections(path)
   335  
   336  	err := SetupICAPath(path, TestOwnerAddress)
   337  	suite.Require().NoError(err)
   338  
   339  	module, _, err := suite.chainB.GetSimApp().GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainB.GetContext(), path.EndpointB.ChannelConfig.PortID)
   340  	suite.Require().NoError(err)
   341  
   342  	cbs, ok := suite.chainB.GetSimApp().GetIBCKeeper().Router.GetRoute(module)
   343  	suite.Require().True(ok)
   344  
   345  	err = cbs.OnChanCloseInit(
   346  		suite.chainB.GetContext(), path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID,
   347  	)
   348  
   349  	suite.Require().Error(err)
   350  }
   351  
   352  func (suite *InterchainAccountsTestSuite) TestOnChanCloseConfirm() {
   353  	var path *ibctesting.Path
   354  
   355  	testCases := []struct {
   356  		name     string
   357  		malleate func()
   358  		expPass  bool
   359  	}{
   360  		{
   361  			"success", func() {}, true,
   362  		},
   363  	}
   364  
   365  	for _, tc := range testCases {
   366  		suite.Run(tc.name, func() {
   367  			suite.SetupTest() // reset
   368  
   369  			path = NewICAPath(suite.chainA, suite.chainB)
   370  			suite.coordinator.SetupConnections(path)
   371  
   372  			err := SetupICAPath(path, TestOwnerAddress)
   373  			suite.Require().NoError(err)
   374  
   375  			tc.malleate() // malleate mutates test data
   376  			module, _, err := suite.chainB.GetSimApp().GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainB.GetContext(), path.EndpointB.ChannelConfig.PortID)
   377  			suite.Require().NoError(err)
   378  
   379  			cbs, ok := suite.chainB.GetSimApp().GetIBCKeeper().Router.GetRoute(module)
   380  			suite.Require().True(ok)
   381  
   382  			err = cbs.OnChanCloseConfirm(
   383  				suite.chainB.GetContext(), path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID)
   384  
   385  			if tc.expPass {
   386  				suite.Require().NoError(err)
   387  			} else {
   388  				suite.Require().Error(err)
   389  			}
   390  		})
   391  	}
   392  }
   393  
   394  func (suite *InterchainAccountsTestSuite) TestOnRecvPacket() {
   395  	var packetData []byte
   396  	testCases := []struct {
   397  		name          string
   398  		malleate      func()
   399  		expAckSuccess bool
   400  	}{
   401  		{
   402  			"success", func() {}, true,
   403  		},
   404  		{
   405  			"host submodule disabled", func() {
   406  				suite.chainB.GetSimApp().ICAHostKeeper.SetParams(suite.chainB.GetContext(), types.NewParams(false, []string{}))
   407  			}, false,
   408  		},
   409  		{
   410  			"success with ICA auth module callback failure", func() {
   411  				suite.chainB.GetSimApp().ICAAuthModule.IBCApp.OnRecvPacket = func(
   412  					ctx sdk.Context, packet channeltypes.Packet, relayer sdk.AccAddress,
   413  				) exported.Acknowledgement {
   414  					return channeltypes.NewErrorAcknowledgementV4(fmt.Errorf("failed OnRecvPacket mock callback"))
   415  				}
   416  			}, true,
   417  		},
   418  		{
   419  			"ICA OnRecvPacket fails - cannot unmarshal packet data", func() {
   420  				packetData = []byte("invalid data")
   421  			}, false,
   422  		},
   423  	}
   424  
   425  	for _, tc := range testCases {
   426  		tc := tc
   427  
   428  		suite.Run(tc.name, func() {
   429  			suite.SetupTest() // reset
   430  
   431  			path := NewICAPath(suite.chainA, suite.chainB)
   432  			suite.coordinator.SetupConnections(path)
   433  			err := SetupICAPath(path, TestOwnerAddress)
   434  			suite.Require().NoError(err)
   435  
   436  			// send 100stake to interchain account wallet
   437  			amount, _ := sdk.ParseCoinsNormalized("1000wei")
   438  			interchainAccountAddr, _ := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID)
   439  			bankMsg := &banktypes.MsgSendAdapter{FromAddress: suite.chainB.SenderAccount().GetAddress().String(), ToAddress: interchainAccountAddr, Amount: sdk.CoinsToCoinAdapters(amount)}
   440  
   441  			_, err = suite.chainB.SendMsgs(bankMsg)
   442  			suite.Require().NoError(err)
   443  
   444  			// build packet data
   445  			msg := &banktypes.MsgSendAdapter{
   446  				FromAddress: interchainAccountAddr,
   447  				ToAddress:   suite.chainB.SenderAccount().GetAddress().String(),
   448  				Amount:      sdk.CoinsToCoinAdapters(amount),
   449  			}
   450  			data, err := icatypes.SerializeCosmosTx(suite.chainA.Codec(), []sdk.MsgAdapter{msg})
   451  			suite.Require().NoError(err)
   452  
   453  			icaPacketData := icatypes.InterchainAccountPacketData{
   454  				Type: icatypes.EXECUTE_TX,
   455  				Data: data,
   456  			}
   457  			packetData = icaPacketData.GetBytes()
   458  
   459  			// build expected ack
   460  			msgResponseBz, err := proto.Marshal(&banktypes.MsgSendResponseAdapter{})
   461  			suite.Require().NoError(err)
   462  
   463  			msgData := &types2.MsgData{
   464  				MsgType: sdk.MsgTypeURL(msg),
   465  				Data:    msgResponseBz,
   466  			}
   467  
   468  			expectedTxResponse, err := proto.Marshal(&types2.TxMsgData{
   469  				Data: []*types2.MsgData{msgData},
   470  			})
   471  			suite.Require().NoError(err)
   472  
   473  			expectedAck := channeltypes.NewResultAcknowledgement(expectedTxResponse)
   474  
   475  			params := types.NewParams(true, []string{sdk.MsgTypeURL(msg)})
   476  			suite.chainB.GetSimApp().ICAHostKeeper.SetParams(suite.chainB.GetContext(), params)
   477  
   478  			// malleate packetData for test cases
   479  			tc.malleate()
   480  
   481  			seq := uint64(1)
   482  			packet := channeltypes.NewPacket(packetData, seq, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(0, 100), 0)
   483  
   484  			tc.malleate()
   485  
   486  			module, _, err := suite.chainB.GetSimApp().GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainB.GetContext(), path.EndpointB.ChannelConfig.PortID)
   487  			suite.Require().NoError(err)
   488  
   489  			cbs, ok := suite.chainB.GetSimApp().GetIBCKeeper().Router.GetRoute(module)
   490  			suite.Require().True(ok)
   491  
   492  			ack := cbs.OnRecvPacket(suite.chainB.GetContext(), packet, nil)
   493  			if tc.expAckSuccess {
   494  				suite.Require().True(ack.Success())
   495  				suite.Require().Equal(expectedAck, ack)
   496  			} else {
   497  				suite.Require().False(ack.Success())
   498  			}
   499  		})
   500  	}
   501  }
   502  
   503  func (suite *InterchainAccountsTestSuite) TestOnAcknowledgementPacket() {
   504  	testCases := []struct {
   505  		name     string
   506  		malleate func()
   507  		expPass  bool
   508  	}{
   509  		{
   510  			"ICA OnAcknowledgementPacket fails with ErrInvalidChannelFlow", func() {}, false,
   511  		},
   512  	}
   513  
   514  	for _, tc := range testCases {
   515  		tc := tc
   516  
   517  		suite.Run(tc.name, func() {
   518  			suite.SetupTest() // reset
   519  
   520  			path := NewICAPath(suite.chainA, suite.chainB)
   521  			suite.coordinator.SetupConnections(path)
   522  
   523  			err := SetupICAPath(path, TestOwnerAddress)
   524  			suite.Require().NoError(err)
   525  
   526  			tc.malleate() // malleate mutates test data
   527  
   528  			module, _, err := suite.chainB.GetSimApp().GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainB.GetContext(), path.EndpointB.ChannelConfig.PortID)
   529  			suite.Require().NoError(err)
   530  
   531  			cbs, ok := suite.chainB.GetSimApp().GetIBCKeeper().Router.GetRoute(module)
   532  			suite.Require().True(ok)
   533  
   534  			packet := channeltypes.NewPacket(
   535  				[]byte("empty packet data"),
   536  				suite.chainA.SenderAccount().GetSequence(),
   537  				path.EndpointB.ChannelConfig.PortID,
   538  				path.EndpointB.ChannelID,
   539  				path.EndpointA.ChannelConfig.PortID,
   540  				path.EndpointA.ChannelID,
   541  				clienttypes.NewHeight(0, 100),
   542  				0,
   543  			)
   544  
   545  			err = cbs.OnAcknowledgementPacket(suite.chainB.GetContext(), packet, []byte("ackBytes"), TestAccAddress)
   546  
   547  			if tc.expPass {
   548  				suite.Require().NoError(err)
   549  			} else {
   550  				suite.Require().Error(err)
   551  			}
   552  		})
   553  	}
   554  }
   555  
   556  func (suite *InterchainAccountsTestSuite) TestOnTimeoutPacket() {
   557  	testCases := []struct {
   558  		name     string
   559  		malleate func()
   560  		expPass  bool
   561  	}{
   562  		{
   563  			"ICA OnTimeoutPacket fails with ErrInvalidChannelFlow", func() {}, false,
   564  		},
   565  	}
   566  
   567  	for _, tc := range testCases {
   568  		tc := tc
   569  
   570  		suite.Run(tc.name, func() {
   571  			suite.SetupTest() // reset
   572  
   573  			path := NewICAPath(suite.chainA, suite.chainB)
   574  			suite.coordinator.SetupConnections(path)
   575  
   576  			err := SetupICAPath(path, TestOwnerAddress)
   577  			suite.Require().NoError(err)
   578  
   579  			tc.malleate() // malleate mutates test data
   580  
   581  			module, _, err := suite.chainA.GetSimApp().GetIBCKeeper().PortKeeper.LookupModuleByPort(suite.chainA.GetContext(), path.EndpointB.ChannelConfig.PortID)
   582  			suite.Require().NoError(err)
   583  
   584  			cbs, ok := suite.chainA.GetSimApp().GetIBCKeeper().Router.GetRoute(module)
   585  			suite.Require().True(ok)
   586  
   587  			packet := channeltypes.NewPacket(
   588  				[]byte("empty packet data"),
   589  				suite.chainA.SenderAccount().GetSequence(),
   590  				path.EndpointB.ChannelConfig.PortID,
   591  				path.EndpointB.ChannelID,
   592  				path.EndpointA.ChannelConfig.PortID,
   593  				path.EndpointA.ChannelID,
   594  				clienttypes.NewHeight(0, 100),
   595  				0,
   596  			)
   597  
   598  			err = cbs.OnTimeoutPacket(suite.chainA.GetContext(), packet, TestAccAddress)
   599  
   600  			if tc.expPass {
   601  				suite.Require().NoError(err)
   602  			} else {
   603  				suite.Require().Error(err)
   604  			}
   605  		})
   606  	}
   607  }
   608  
   609  func (suite *InterchainAccountsTestSuite) fundICAWallet(ctx sdk.Context, portID string, amount sdk.Coins) {
   610  	interchainAccountAddr, found := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(ctx, ibctesting.FirstConnectionID, portID)
   611  	suite.Require().True(found)
   612  
   613  	msgBankSend := &banktypes.MsgSendAdapter{
   614  		FromAddress: suite.chainB.SenderAccount().GetAddress().String(),
   615  		ToAddress:   interchainAccountAddr,
   616  		Amount:      sdk.CoinsToCoinAdapters(amount),
   617  	}
   618  
   619  	res, err := suite.chainB.SendMsgs(msgBankSend)
   620  	suite.Require().NotEmpty(res)
   621  	suite.Require().NoError(err)
   622  }
   623  
   624  // TestControlAccountAfterChannelClose tests that a controller chain can control a registered interchain account after the currently active channel for that interchain account has been closed
   625  // by opening a new channel on the associated portID
   626  func (suite *InterchainAccountsTestSuite) TestControlAccountAfterChannelClose() {
   627  	// create channel + init interchain account on a particular port
   628  	path := NewICAPath(suite.chainA, suite.chainB)
   629  	suite.coordinator.SetupConnections(path)
   630  	err := SetupICAPath(path, TestOwnerAddress)
   631  	suite.Require().NoError(err)
   632  
   633  	// check that the account is working as expected
   634  	suite.fundICAWallet(suite.chainB.GetContext(), path.EndpointA.ChannelConfig.PortID, sdk.NewCoins(sdk.NewCoin(sdk.DefaultIbcWei, sdk.NewInt(10000))))
   635  	interchainAccountAddr, found := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID)
   636  	suite.Require().True(found)
   637  
   638  	tokenAmt := sdk.NewCoins(sdk.NewCoin(sdk.DefaultIbcWei, sdk.NewInt(5000)))
   639  	msg := &banktypes.MsgSendAdapter{
   640  		FromAddress: interchainAccountAddr,
   641  		ToAddress:   suite.chainB.SenderAccount().GetAddress().String(),
   642  		Amount:      sdk.CoinsToCoinAdapters(tokenAmt),
   643  	}
   644  
   645  	data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []sdk.MsgAdapter{msg})
   646  	suite.Require().NoError(err)
   647  
   648  	icaPacketData := icatypes.InterchainAccountPacketData{
   649  		Type: icatypes.EXECUTE_TX,
   650  		Data: data,
   651  	}
   652  
   653  	params := types.NewParams(true, []string{sdk.MsgTypeURL(msg)})
   654  	suite.chainB.GetSimApp().ICAHostKeeper.SetParams(suite.chainB.GetContext(), params)
   655  
   656  	chanCap, ok := suite.chainA.GetSimApp().ScopedICAMockKeeper.GetCapability(path.EndpointA.Chain.GetContext(), host.ChannelCapabilityPath(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID))
   657  	suite.Require().True(ok)
   658  
   659  	_, err = suite.chainA.GetSimApp().ICAControllerKeeper.SendTx(suite.chainA.GetContext(), chanCap, ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID, icaPacketData, ^uint64(0))
   660  	suite.Require().NoError(err)
   661  	path.EndpointB.UpdateClient()
   662  
   663  	// relay the packet
   664  	packetRelay := channeltypes.NewPacket(icaPacketData.GetBytes(), 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.ZeroHeight(), ^uint64(0))
   665  	err = path.RelayPacketV4(packetRelay)
   666  	suite.Require().NoError(err) // relay committed
   667  
   668  	// check that the ica balance is updated
   669  	icaAddr, err := sdk.AccAddressFromBech32(interchainAccountAddr)
   670  	suite.Require().NoError(err)
   671  
   672  	hasBalance := suite.chainB.GetSimApp().BankKeeper.HasBalance(suite.chainB.GetContext(), icaAddr, sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecFromInt(sdk.NewInt(5000))})
   673  	suite.Require().True(hasBalance)
   674  
   675  	// close the channel
   676  	err = path.EndpointA.SetChannelClosed()
   677  	suite.Require().NoError(err)
   678  	err = path.EndpointB.SetChannelClosed()
   679  	suite.Require().NoError(err)
   680  
   681  	// open a new channel on the same port
   682  	path.EndpointA.ChannelID = ""
   683  	path.EndpointB.ChannelID = ""
   684  	suite.coordinator.CreateChannels(path)
   685  
   686  	// try to control the interchain account again
   687  	chanCap, ok = suite.chainA.GetSimApp().ScopedICAMockKeeper.GetCapability(path.EndpointA.Chain.GetContext(), host.ChannelCapabilityPath(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID))
   688  	suite.Require().True(ok)
   689  
   690  	_, err = suite.chainA.GetSimApp().ICAControllerKeeper.SendTx(suite.chainA.GetContext(), chanCap, ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID, icaPacketData, ^uint64(0))
   691  	suite.Require().NoError(err)
   692  	path.EndpointB.UpdateClient()
   693  
   694  	// relay the packet
   695  	packetRelay = channeltypes.NewPacket(icaPacketData.GetBytes(), 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.ZeroHeight(), ^uint64(0))
   696  	err = path.RelayPacketV4(packetRelay)
   697  	suite.Require().NoError(err) // relay committed
   698  
   699  	// check that the ica balance is updated
   700  	hasBalance = suite.chainB.GetSimApp().BankKeeper.HasBalance(suite.chainB.GetContext(), icaAddr, sdk.Coin{Denom: sdk.DefaultIbcWei, Amount: sdk.NewDecFromInt(sdk.NewInt(0))})
   701  	suite.Require().True(hasBalance)
   702  }