Skip to content

Commit

Permalink
Merge pull request from GHSA-832c-mq9v-367r
Browse files Browse the repository at this point in the history
* fix: use block app hash and tx list to generate interchain account address

Generate interchain account addresses using host connection ID, controller PortID, block app hash, and block data hash
Update tests to handle non-determinstic address creation
Add test case to ensure address generation is block dependent

* fix: return error on existing non-interchainaccounts for generated address

If an account exists and is not an interchain account return an error
Add test cases for existing accounts, both interchain and non interchain account
Refactor account tests to be table tests

* fix: refactor handshake code to account for block dependent address generation

* add more test cases, update error messaging

* self review fix

* increase test readability

* add test cases for controller side channel reopening

* chore: fix godoc
  • Loading branch information
colin-axner authored Sep 20, 2022
1 parent 2122a57 commit 8687242
Show file tree
Hide file tree
Showing 17 changed files with 263 additions and 132 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
"github.com/stretchr/testify/suite"
"github.com/tendermint/tendermint/crypto"

"github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller"
"github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/types"
Expand All @@ -20,12 +19,6 @@ import (
)

var (
// TODO: Cosmos-SDK ADR-28: Update crypto.AddressHash() when sdk uses address.Module()
// https://github.com/cosmos/cosmos-sdk/issues/10225
//
// TestAccAddress defines a reusable bech32 address for testing purposes
TestAccAddress = icatypes.GenerateAddress(sdk.AccAddress(crypto.AddressHash([]byte(icatypes.ModuleName))), ibctesting.FirstConnectionID, TestPortID)

// TestOwnerAddress defines a reusable bech32 address for testing purposes
TestOwnerAddress = "cosmos17dtl0mjt3t77kpuhg2edqzjpszulwhgzuj9ljs"

Expand Down Expand Up @@ -570,7 +563,7 @@ func (suite *InterchainAccountsTestSuite) TestOnRecvPacket() {
0,
)

ack := cbs.OnRecvPacket(suite.chainA.GetContext(), packet, TestAccAddress)
ack := cbs.OnRecvPacket(suite.chainA.GetContext(), packet, nil)
suite.Require().Equal(tc.expPass, ack.Success())
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import (
"github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/keeper"
"github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/types"
genesistypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/genesis/types"
icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types"
ibctesting "github.com/cosmos/ibc-go/v5/testing"
)

func (suite *KeeperTestSuite) TestInitGenesis() {
suite.SetupTest()

interchainAccAddr := icatypes.GenerateAddress(suite.chainB.GetContext(), ibctesting.FirstConnectionID, TestPortID)
genesisState := genesistypes.ControllerGenesisState{
ActiveChannels: []genesistypes.ActiveChannel{
{
Expand All @@ -23,7 +25,7 @@ func (suite *KeeperTestSuite) TestInitGenesis() {
{
ConnectionId: ibctesting.FirstConnectionID,
PortId: TestPortID,
AccountAddress: TestAccAddress.String(),
AccountAddress: interchainAccAddr.String(),
},
},
Ports: []string{TestPortID},
Expand All @@ -40,7 +42,7 @@ func (suite *KeeperTestSuite) TestInitGenesis() {

accountAdrr, found := suite.chainA.GetSimApp().ICAControllerKeeper.GetInterchainAccountAddress(suite.chainA.GetContext(), ibctesting.FirstConnectionID, TestPortID)
suite.Require().True(found)
suite.Require().Equal(TestAccAddress.String(), accountAdrr)
suite.Require().Equal(interchainAccAddr.String(), accountAdrr)

expParams := types.NewParams(false)
params := suite.chainA.GetSimApp().ICAControllerKeeper.GetParams(suite.chainA.GetContext())
Expand All @@ -56,13 +58,16 @@ func (suite *KeeperTestSuite) TestExportGenesis() {
err := SetupICAPath(path, TestOwnerAddress)
suite.Require().NoError(err)

interchainAccAddr, exists := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), path.EndpointB.ConnectionID, path.EndpointA.ChannelConfig.PortID)
suite.Require().True(exists)

genesisState := keeper.ExportGenesis(suite.chainA.GetContext(), suite.chainA.GetSimApp().ICAControllerKeeper)

suite.Require().Equal(path.EndpointA.ChannelID, genesisState.ActiveChannels[0].ChannelId)
suite.Require().Equal(path.EndpointA.ChannelConfig.PortID, genesisState.ActiveChannels[0].PortId)
suite.Require().True(genesisState.ActiveChannels[0].IsMiddlewareEnabled)

suite.Require().Equal(TestAccAddress.String(), genesisState.InterchainAccounts[0].AccountAddress)
suite.Require().Equal(interchainAccAddr, genesisState.InterchainAccounts[0].AccountAddress)
suite.Require().Equal(path.EndpointA.ChannelConfig.PortID, genesisState.InterchainAccounts[0].PortId)

suite.Require().Equal([]string{TestPortID}, genesisState.GetPorts())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/types"
icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types"
ibctesting "github.com/cosmos/ibc-go/v5/testing"
)

Expand Down Expand Up @@ -64,10 +63,11 @@ func (suite *KeeperTestSuite) TestQueryInterchainAccount() {
res, err := suite.chainA.GetSimApp().ICAControllerKeeper.InterchainAccount(sdk.WrapSDKContext(suite.chainA.GetContext()), req)

if tc.expPass {
expAddress := icatypes.GenerateAddress(suite.chainA.GetSimApp().AccountKeeper.GetModuleAddress(icatypes.ModuleName), path.EndpointB.ConnectionID, path.EndpointA.ChannelConfig.PortID)
expAddress, exists := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), path.EndpointB.ConnectionID, path.EndpointA.ChannelConfig.PortID)
suite.Require().True(exists)

suite.Require().NoError(err)
suite.Require().Equal(expAddress.String(), res.Address)
suite.Require().Equal(expAddress, res.Address)
} else {
suite.Require().Error(err)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ func (suite *KeeperTestSuite) TestOnChanOpenInit() {
}{
{
"success",
func() {
path.EndpointA.SetChannel(*channel)
},
func() {},
true,
},
{
Expand Down Expand Up @@ -54,30 +52,44 @@ func (suite *KeeperTestSuite) TestOnChanOpenInit() {
},
true,
},
{
"success: channel reopening",
func() {
err := SetupICAPath(path, TestOwnerAddress)
suite.Require().NoError(err)

err = path.EndpointA.SetChannelClosed()
suite.Require().NoError(err)

err = path.EndpointB.SetChannelClosed()
suite.Require().NoError(err)

path.EndpointA.ChannelID = ""
path.EndpointB.ChannelID = ""
},
true,
},
{
"invalid metadata - previous metadata is different",
func() {
// set active channel to closed
suite.chainA.GetSimApp().ICAControllerKeeper.SetActiveChannelID(suite.chainA.GetContext(), ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID)

// attempt to downgrade version by reinitializing channel with version 1, but setting channel to version 2
metadata.Version = "ics27-2"

versionBytes, err := icatypes.ModuleCdc.MarshalJSON(&metadata)
suite.Require().NoError(err)

counterparty := channeltypes.NewCounterparty(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID)
closedChannel := channeltypes.Channel{
State: channeltypes.CLOSED,
Ordering: channeltypes.ORDERED,
Counterparty: counterparty,
ConnectionHops: []string{path.EndpointA.ConnectionID},
Version: TestVersion,
Version: string(versionBytes),
}

path.EndpointA.SetChannel(closedChannel)

// modify metadata
metadata.Version = "ics27-2"

versionBytes, err := icatypes.ModuleCdc.MarshalJSON(&metadata)
suite.Require().NoError(err)

channel.Version = string(versionBytes)
},
false,
},
Expand Down Expand Up @@ -384,7 +396,10 @@ func (suite *KeeperTestSuite) TestOnChanOpenAck() {
err = path.EndpointB.ChanOpenTry()
suite.Require().NoError(err)

metadata = icatypes.NewMetadata(icatypes.Version, ibctesting.FirstConnectionID, ibctesting.FirstConnectionID, TestAccAddress.String(), icatypes.EncodingProtobuf, icatypes.TxTypeSDKMultiMsg)
interchainAccAddr, exists := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), path.EndpointB.ConnectionID, path.EndpointA.ChannelConfig.PortID)
suite.Require().True(exists)

metadata = icatypes.NewMetadata(icatypes.Version, ibctesting.FirstConnectionID, ibctesting.FirstConnectionID, interchainAccAddr, icatypes.EncodingProtobuf, icatypes.TxTypeSDKMultiMsg)
versionBytes, err := icatypes.ModuleCdc.MarshalJSON(&metadata)
suite.Require().NoError(err)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ package keeper_test
import (
"testing"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/suite"
"github.com/tendermint/tendermint/crypto"

genesistypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/genesis/types"
icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types"
Expand All @@ -14,12 +12,6 @@ import (
)

var (
// TODO: Cosmos-SDK ADR-28: Update crypto.AddressHash() when sdk uses address.Module()
// https://github.com/cosmos/cosmos-sdk/issues/10225
//
// TestAccAddress defines a reusable bech32 address for testing purposes
TestAccAddress = icatypes.GenerateAddress(sdk.AccAddress(crypto.AddressHash([]byte(icatypes.ModuleName))), ibctesting.FirstConnectionID, TestPortID)

// TestOwnerAddress defines a reusable bech32 address for testing purposes
TestOwnerAddress = "cosmos17dtl0mjt3t77kpuhg2edqzjpszulwhgzuj9ljs"

Expand Down Expand Up @@ -153,11 +145,10 @@ func (suite *KeeperTestSuite) TestGetInterchainAccountAddress() {
suite.Require().NoError(err)

counterpartyPortID := path.EndpointA.ChannelConfig.PortID
expectedAddr := icatypes.GenerateAddress(suite.chainA.GetSimApp().AccountKeeper.GetModuleAddress(icatypes.ModuleName), ibctesting.FirstConnectionID, counterpartyPortID)

retrievedAddr, found := suite.chainA.GetSimApp().ICAControllerKeeper.GetInterchainAccountAddress(suite.chainA.GetContext(), ibctesting.FirstConnectionID, counterpartyPortID)
suite.Require().True(found)
suite.Require().Equal(expectedAddr.String(), retrievedAddr)
suite.Require().NotEmpty(retrievedAddr)

retrievedAddr, found = suite.chainA.GetSimApp().ICAControllerKeeper.GetInterchainAccountAddress(suite.chainA.GetContext(), "invalid conn", "invalid port")
suite.Require().False(found)
Expand Down Expand Up @@ -214,13 +205,16 @@ func (suite *KeeperTestSuite) TestGetAllInterchainAccounts() {
err := SetupICAPath(path, TestOwnerAddress)
suite.Require().NoError(err)

interchainAccAddr, exists := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), path.EndpointB.ConnectionID, path.EndpointA.ChannelConfig.PortID)
suite.Require().True(exists)

suite.chainA.GetSimApp().ICAControllerKeeper.SetInterchainAccountAddress(suite.chainA.GetContext(), ibctesting.FirstConnectionID, expectedPortID, expectedAccAddr)

expectedAccounts := []genesistypes.RegisteredInterchainAccount{
{
ConnectionId: ibctesting.FirstConnectionID,
PortId: TestPortID,
AccountAddress: TestAccAddress.String(),
AccountAddress: interchainAccAddr,
},
{
ConnectionId: ibctesting.FirstConnectionID,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,13 @@ func (suite *KeeperTestSuite) TestSubmitTx() {
},
{
"failure - active channel does not exist for port ID", func() {
msg.Owner = TestAccAddress.String()
msg.Owner = "invalid-owner"
},
false,
},
{
"failure - controller module does not own capability for this channel", func() {
msg.Owner = TestAccAddress.String()
msg.Owner = "invalid-owner"
portID, err := icatypes.NewControllerPortID(msg.Owner)
suite.Require().NoError(err)

Expand Down
59 changes: 39 additions & 20 deletions modules/apps/27-interchain-accounts/host/ibc_module_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
"github.com/gogo/protobuf/proto"
"github.com/stretchr/testify/suite"
"github.com/tendermint/tendermint/crypto"

"github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/host/types"
icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types"
Expand All @@ -22,12 +21,6 @@ import (
)

var (
// TODO: Cosmos-SDK ADR-28: Update crypto.AddressHash() when sdk uses address.Module()
// https://github.com/cosmos/cosmos-sdk/issues/10225
//
// TestAccAddress defines a reusable bech32 address for testing purposes
TestAccAddress = icatypes.GenerateAddress(sdk.AccAddress(crypto.AddressHash([]byte(icatypes.ModuleName))), ibctesting.FirstConnectionID, TestPortID)

// TestOwnerAddress defines a reusable bech32 address for testing purposes
TestOwnerAddress = "cosmos17dtl0mjt3t77kpuhg2edqzjpszulwhgzuj9ljs"

Expand Down Expand Up @@ -148,6 +141,17 @@ func (suite *InterchainAccountsTestSuite) TestOnChanOpenTry() {
{
"success", func() {}, true,
},
{
"account address generation is block dependent", func() {
icaHostAccount := icatypes.GenerateAddress(suite.chainB.GetContext(), path.EndpointB.ConnectionID, path.EndpointA.ChannelConfig.PortID)
err := suite.chainB.GetSimApp().BankKeeper.SendCoins(suite.chainB.GetContext(), suite.chainB.SenderAccount.GetAddress(), icaHostAccount, sdk.Coins{sdk.NewCoin("stake", sdk.NewInt(1))})
suite.Require().NoError(err)
suite.Require().True(suite.chainB.GetSimApp().AccountKeeper.HasAccount(suite.chainB.GetContext(), icaHostAccount))

// ensure account registration is simulated in a separate block
suite.chainB.NextBlock()
}, true,
},
{
"host submodule disabled", func() {
suite.chainB.GetSimApp().ICAHostKeeper.SetParams(suite.chainB.GetContext(), types.NewParams(false, []string{}))
Expand Down Expand Up @@ -214,6 +218,10 @@ func (suite *InterchainAccountsTestSuite) TestOnChanOpenTry() {

if tc.expPass {
suite.Require().NoError(err)

addr, exists := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), path.EndpointB.ConnectionID, counterparty.PortId)
suite.Require().True(exists)
suite.Require().NotNil(addr)
} else {
suite.Require().Error(err)
suite.Require().Equal("", version)
Expand Down Expand Up @@ -527,7 +535,7 @@ func (suite *InterchainAccountsTestSuite) TestOnAcknowledgementPacket() {
0,
)

err = cbs.OnAcknowledgementPacket(suite.chainB.GetContext(), packet, []byte("ackBytes"), TestAccAddress)
err = cbs.OnAcknowledgementPacket(suite.chainB.GetContext(), packet, []byte("ackBytes"), nil)

if tc.expPass {
suite.Require().NoError(err)
Expand Down Expand Up @@ -580,7 +588,7 @@ func (suite *InterchainAccountsTestSuite) TestOnTimeoutPacket() {
0,
)

err = cbs.OnTimeoutPacket(suite.chainA.GetContext(), packet, TestAccAddress)
err = cbs.OnTimeoutPacket(suite.chainA.GetContext(), packet, nil)

if tc.expPass {
suite.Require().NoError(err)
Expand All @@ -606,21 +614,28 @@ func (suite *InterchainAccountsTestSuite) fundICAWallet(ctx sdk.Context, portID
suite.Require().NoError(err)
}

// TestControlAccountAfterChannelClose tests that a controller chain can control a registered interchain account after the currently active channel for that interchain account has been closed
// by opening a new channel on the associated portID
// TestControlAccountAfterChannelClose tests that a controller chain can control a registered interchain account after the currently active channel for that interchain account has been closed.
// A new channel will be opened for the controller portID. The interchain account address should remain unchanged.
func (suite *InterchainAccountsTestSuite) TestControlAccountAfterChannelClose() {
// create channel + init interchain account on a particular port
path := NewICAPath(suite.chainA, suite.chainB)
suite.coordinator.SetupConnections(path)

err := SetupICAPath(path, TestOwnerAddress)
suite.Require().NoError(err)

// two sends will be performed, one after initial creation of the account and one after channel closure and reopening
var (
startingBal = sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100000)))
tokenAmt = sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(5000)))
expBalAfterFirstSend = startingBal.Sub(tokenAmt...)
expBalAfterSecondSend = expBalAfterFirstSend.Sub(tokenAmt...)
)

// check that the account is working as expected
suite.fundICAWallet(suite.chainB.GetContext(), path.EndpointA.ChannelConfig.PortID, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10000))))
interchainAccountAddr, found := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID)
suite.fundICAWallet(suite.chainB.GetContext(), path.EndpointA.ChannelConfig.PortID, startingBal)
interchainAccountAddr, found := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), path.EndpointB.ConnectionID, path.EndpointA.ChannelConfig.PortID)
suite.Require().True(found)

tokenAmt := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(5000)))
msg := &banktypes.MsgSend{
FromAddress: interchainAccountAddr,
ToAddress: suite.chainB.SenderAccount.GetAddress().String(),
Expand Down Expand Up @@ -651,8 +666,7 @@ func (suite *InterchainAccountsTestSuite) TestControlAccountAfterChannelClose()
icaAddr, err := sdk.AccAddressFromBech32(interchainAccountAddr)
suite.Require().NoError(err)

hasBalance := suite.chainB.GetSimApp().BankKeeper.HasBalance(suite.chainB.GetContext(), icaAddr, sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: sdk.NewInt(5000)})
suite.Require().True(hasBalance)
suite.assertBalance(icaAddr, expBalAfterFirstSend)

// close the channel
err = path.EndpointA.SetChannelClosed()
Expand All @@ -674,7 +688,12 @@ func (suite *InterchainAccountsTestSuite) TestControlAccountAfterChannelClose()
err = path.RelayPacket(packetRelay)
suite.Require().NoError(err) // relay committed

// check that the ica balance is updated
hasBalance = suite.chainB.GetSimApp().BankKeeper.HasBalance(suite.chainB.GetContext(), icaAddr, sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: sdk.NewInt(0)})
suite.Require().True(hasBalance)
suite.assertBalance(icaAddr, expBalAfterSecondSend)
}

// assertBalance asserts that the provided address has exactly the expected balance.
// CONTRACT: the expected balance must only contain one coin denom.
func (suite *InterchainAccountsTestSuite) assertBalance(addr sdk.AccAddress, expBalance sdk.Coins) {
balance := suite.chainB.GetSimApp().BankKeeper.GetBalance(suite.chainB.GetContext(), addr, sdk.DefaultBondDenom)
suite.Require().Equal(expBalance[0], balance)
}
Loading

0 comments on commit 8687242

Please sign in to comment.