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 }