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

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