mukan-ibc/modules/apps/27-interchain-accounts/host/keeper/relay_test.go
Mukan Erkin Törük 88dd97a9f8
Some checks failed
CodeQL / Analyze (push) Waiting to run
golangci-lint / lint (push) Waiting to run
Tests / Code Coverage / build (amd64) (push) Waiting to run
Tests / Code Coverage / build (arm64) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[additional-args:-tags="test_e2e" name:e2e path:./e2e]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:08-wasm path:./modules/light-clients/08-wasm]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:ibc-go path:.]) (push) Waiting to run
Docker Build & Push Simapp (main) / docker-build (push) Has been cancelled
refactor: replace all github.com upstream refs with git.cw.tr/mukan-network
2026-05-11 03:36:22 +03:00

898 lines
31 KiB
Go

package keeper_test
import (
"fmt"
"strings"
"time"
"github.com/cosmos/gogoproto/proto"
sdkmath "cosmossdk.io/math"
"git.cw.tr/mukan-network/mukan-sdk/testutil/testdata"
sdk "git.cw.tr/mukan-network/mukan-sdk/types"
authtypes "git.cw.tr/mukan-network/mukan-sdk/x/auth/types"
banktypes "git.cw.tr/mukan-network/mukan-sdk/x/bank/types"
disttypes "git.cw.tr/mukan-network/mukan-sdk/x/distribution/types"
govtypesv1 "git.cw.tr/mukan-network/mukan-sdk/x/gov/types/v1"
stakingtypes "git.cw.tr/mukan-network/mukan-sdk/x/staking/types"
"git.cw.tr/mukan-network/mukan-ibc/modules/apps/27-interchain-accounts/host/types"
icatypes "git.cw.tr/mukan-network/mukan-ibc/modules/apps/27-interchain-accounts/types"
transfertypes "git.cw.tr/mukan-network/mukan-ibc/modules/apps/transfer/types"
channeltypes "git.cw.tr/mukan-network/mukan-ibc/modules/core/04-channel/types"
ibcerrors "git.cw.tr/mukan-network/mukan-ibc/modules/core/errors"
ibctesting "git.cw.tr/mukan-network/mukan-ibc/testing"
)
func (suite *KeeperTestSuite) TestOnRecvPacket() {
testedOrderings := []channeltypes.Order{channeltypes.UNORDERED, channeltypes.ORDERED}
testedEncodings := []string{icatypes.EncodingProtobuf, icatypes.EncodingProto3JSON}
var (
path *ibctesting.Path
packetData []byte
)
testCases := []struct {
msg string
malleate func(encoding string)
expErr error
}{
{
"interchain account successfully executes an arbitrary message type using the * (allow all message types) param",
func(encoding string) {
interchainAccountAddr, found := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID)
suite.Require().True(found)
proposal, err := govtypesv1.NewProposal([]sdk.Msg{getTestProposalMessage()}, govtypesv1.DefaultStartingProposalID, time.Now(), time.Now().Add(time.Hour), "test proposal", "title", "Description", sdk.AccAddress(interchainAccountAddr), false)
suite.Require().NoError(err)
err = suite.chainB.GetSimApp().GovKeeper.SetProposal(suite.chainB.GetContext(), proposal)
suite.Require().NoError(err)
err = suite.chainB.GetSimApp().GovKeeper.ActivateVotingPeriod(suite.chainB.GetContext(), proposal)
suite.Require().NoError(err)
msg := &govtypesv1.MsgVote{
ProposalId: govtypesv1.DefaultStartingProposalID,
Voter: interchainAccountAddr,
Option: govtypesv1.OptionYes,
}
data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}, encoding)
suite.Require().NoError(err)
icaPacketData := icatypes.InterchainAccountPacketData{
Type: icatypes.EXECUTE_TX,
Data: data,
}
packetData = icaPacketData.GetBytes()
params := types.NewParams(true, []string{"*"})
suite.chainB.GetSimApp().ICAHostKeeper.SetParams(suite.chainB.GetContext(), params)
},
nil,
},
{
"interchain account successfully executes banktypes.MsgSend",
func(encoding string) {
interchainAccountAddr, found := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID)
suite.Require().True(found)
msg := &banktypes.MsgSend{
FromAddress: interchainAccountAddr,
ToAddress: suite.chainB.SenderAccount.GetAddress().String(),
Amount: sdk.NewCoins(ibctesting.TestCoin),
}
data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}, encoding)
suite.Require().NoError(err)
icaPacketData := icatypes.InterchainAccountPacketData{
Type: icatypes.EXECUTE_TX,
Data: data,
}
packetData = icaPacketData.GetBytes()
params := types.NewParams(true, []string{sdk.MsgTypeURL(msg)})
suite.chainB.GetSimApp().ICAHostKeeper.SetParams(suite.chainB.GetContext(), params)
},
nil,
},
{
"interchain account successfully executes stakingtypes.MsgDelegate",
func(encoding string) {
interchainAccountAddr, found := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID)
suite.Require().True(found)
validatorAddr := (sdk.ValAddress)(suite.chainB.Vals.Validators[0].Address)
msg := &stakingtypes.MsgDelegate{
DelegatorAddress: interchainAccountAddr,
ValidatorAddress: validatorAddr.String(),
Amount: sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(5000)),
}
data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}, encoding)
suite.Require().NoError(err)
icaPacketData := icatypes.InterchainAccountPacketData{
Type: icatypes.EXECUTE_TX,
Data: data,
}
packetData = icaPacketData.GetBytes()
params := types.NewParams(true, []string{sdk.MsgTypeURL(msg)})
suite.chainB.GetSimApp().ICAHostKeeper.SetParams(suite.chainB.GetContext(), params)
},
nil,
},
{
"interchain account successfully executes stakingtypes.MsgDelegate and stakingtypes.MsgUndelegate sequentially",
func(encoding string) {
interchainAccountAddr, found := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID)
suite.Require().True(found)
validatorAddr := (sdk.ValAddress)(suite.chainB.Vals.Validators[0].Address)
msgDelegate := &stakingtypes.MsgDelegate{
DelegatorAddress: interchainAccountAddr,
ValidatorAddress: validatorAddr.String(),
Amount: sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(5000)),
}
msgUndelegate := &stakingtypes.MsgUndelegate{
DelegatorAddress: interchainAccountAddr,
ValidatorAddress: validatorAddr.String(),
Amount: sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(5000)),
}
data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msgDelegate, msgUndelegate}, encoding)
suite.Require().NoError(err)
icaPacketData := icatypes.InterchainAccountPacketData{
Type: icatypes.EXECUTE_TX,
Data: data,
}
packetData = icaPacketData.GetBytes()
params := types.NewParams(true, []string{sdk.MsgTypeURL(msgDelegate), sdk.MsgTypeURL(msgUndelegate)})
suite.chainB.GetSimApp().ICAHostKeeper.SetParams(suite.chainB.GetContext(), params)
},
nil,
},
{
"interchain account successfully executes govtypesv1.MsgSubmitProposal",
func(encoding string) {
interchainAccountAddr, found := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID)
suite.Require().True(found)
msg, err := govtypesv1.NewMsgSubmitProposal([]sdk.Msg{}, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(100000))), interchainAccountAddr, "metadata", "title", "summary", false)
suite.Require().NoError(err)
data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}, encoding)
suite.Require().NoError(err)
icaPacketData := icatypes.InterchainAccountPacketData{
Type: icatypes.EXECUTE_TX,
Data: data,
}
packetData = icaPacketData.GetBytes()
params := types.NewParams(true, []string{sdk.MsgTypeURL(msg)})
suite.chainB.GetSimApp().ICAHostKeeper.SetParams(suite.chainB.GetContext(), params)
},
nil,
},
{
"interchain account successfully executes govtypesv1.MsgVote",
func(encoding string) {
interchainAccountAddr, found := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID)
suite.Require().True(found)
proposal, err := govtypesv1.NewProposal([]sdk.Msg{getTestProposalMessage()}, govtypesv1.DefaultStartingProposalID, time.Now(), time.Now().Add(time.Hour), "test proposal", "title", "Description", sdk.AccAddress(interchainAccountAddr), false)
suite.Require().NoError(err)
err = suite.chainB.GetSimApp().GovKeeper.SetProposal(suite.chainB.GetContext(), proposal)
suite.Require().NoError(err)
err = suite.chainB.GetSimApp().GovKeeper.ActivateVotingPeriod(suite.chainB.GetContext(), proposal)
suite.Require().NoError(err)
msg := &govtypesv1.MsgVote{
ProposalId: govtypesv1.DefaultStartingProposalID,
Voter: interchainAccountAddr,
Option: govtypesv1.OptionYes,
}
data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}, encoding)
suite.Require().NoError(err)
icaPacketData := icatypes.InterchainAccountPacketData{
Type: icatypes.EXECUTE_TX,
Data: data,
}
packetData = icaPacketData.GetBytes()
params := types.NewParams(true, []string{sdk.MsgTypeURL(msg)})
suite.chainB.GetSimApp().ICAHostKeeper.SetParams(suite.chainB.GetContext(), params)
},
nil,
},
{
"interchain account successfully executes disttypes.MsgFundCommunityPool",
func(encoding string) {
interchainAccountAddr, found := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID)
suite.Require().True(found)
msg := &disttypes.MsgFundCommunityPool{
Amount: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(5000))),
Depositor: interchainAccountAddr,
}
data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}, encoding)
suite.Require().NoError(err)
icaPacketData := icatypes.InterchainAccountPacketData{
Type: icatypes.EXECUTE_TX,
Data: data,
}
packetData = icaPacketData.GetBytes()
params := types.NewParams(true, []string{sdk.MsgTypeURL(msg)})
suite.chainB.GetSimApp().ICAHostKeeper.SetParams(suite.chainB.GetContext(), params)
},
nil,
},
{
"interchain account successfully executes icahosttypes.MsgModuleQuerySafe",
func(encoding string) {
interchainAccountAddr, found := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID)
suite.Require().True(found)
balanceQuery := banktypes.NewQueryBalanceRequest(suite.chainB.SenderAccount.GetAddress(), sdk.DefaultBondDenom)
queryBz, err := balanceQuery.Marshal()
suite.Require().NoError(err)
msg := types.NewMsgModuleQuerySafe(interchainAccountAddr, []types.QueryRequest{
{
Path: "/cosmos.bank.v1beta1.Query/Balance",
Data: queryBz,
},
})
data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}, encoding)
suite.Require().NoError(err)
icaPacketData := icatypes.InterchainAccountPacketData{
Type: icatypes.EXECUTE_TX,
Data: data,
}
packetData = icaPacketData.GetBytes()
params := types.NewParams(true, []string{sdk.MsgTypeURL(msg)})
suite.chainB.GetSimApp().ICAHostKeeper.SetParams(suite.chainB.GetContext(), params)
},
nil,
},
{
"interchain account successfully executes disttypes.MsgSetWithdrawAddress",
func(encoding string) {
interchainAccountAddr, found := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID)
suite.Require().True(found)
msg := &disttypes.MsgSetWithdrawAddress{
DelegatorAddress: interchainAccountAddr,
WithdrawAddress: suite.chainB.SenderAccount.GetAddress().String(),
}
data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}, encoding)
suite.Require().NoError(err)
icaPacketData := icatypes.InterchainAccountPacketData{
Type: icatypes.EXECUTE_TX,
Data: data,
}
packetData = icaPacketData.GetBytes()
params := types.NewParams(true, []string{sdk.MsgTypeURL(msg)})
suite.chainB.GetSimApp().ICAHostKeeper.SetParams(suite.chainB.GetContext(), params)
},
nil,
},
{
"interchain account successfully executes transfertypes.MsgTransfer",
func(encoding string) {
transferPath := ibctesting.NewTransferPath(suite.chainB, suite.chainC)
transferPath.Setup()
interchainAccountAddr, found := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID)
suite.Require().True(found)
msg := transfertypes.NewMsgTransfer(
transferPath.EndpointA.ChannelConfig.PortID,
transferPath.EndpointA.ChannelID,
ibctesting.TestCoin,
interchainAccountAddr,
suite.chainA.SenderAccount.GetAddress().String(),
suite.chainB.GetTimeoutHeight(),
0,
"",
)
data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}, encoding)
suite.Require().NoError(err)
icaPacketData := icatypes.InterchainAccountPacketData{
Type: icatypes.EXECUTE_TX,
Data: data,
}
packetData = icaPacketData.GetBytes()
params := types.NewParams(true, []string{sdk.MsgTypeURL(msg)})
suite.chainB.GetSimApp().ICAHostKeeper.SetParams(suite.chainB.GetContext(), params)
},
nil,
},
{
"Msg fails its ValidateBasic: MsgTransfer has an empty receiver",
func(encoding string) {
transferPath := ibctesting.NewTransferPath(suite.chainB, suite.chainC)
transferPath.Setup()
interchainAccountAddr, found := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID)
suite.Require().True(found)
msg := transfertypes.NewMsgTransfer(
transferPath.EndpointA.ChannelConfig.PortID,
transferPath.EndpointA.ChannelID,
ibctesting.TestCoin,
interchainAccountAddr,
"",
suite.chainB.GetTimeoutHeight(),
0,
"",
)
data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}, encoding)
suite.Require().NoError(err)
icaPacketData := icatypes.InterchainAccountPacketData{
Type: icatypes.EXECUTE_TX,
Data: data,
}
packetData = icaPacketData.GetBytes()
params := types.NewParams(true, []string{sdk.MsgTypeURL(msg)})
suite.chainB.GetSimApp().ICAHostKeeper.SetParams(suite.chainB.GetContext(), params)
},
ibcerrors.ErrInvalidAddress,
},
{
"unregistered sdk.Msg",
func(encoding string) {
msg := &banktypes.MsgSendResponse{}
data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}, encoding)
suite.Require().NoError(err)
icaPacketData := icatypes.InterchainAccountPacketData{
Type: icatypes.EXECUTE_TX,
Data: data,
}
packetData = icaPacketData.GetBytes()
params := types.NewParams(true, []string{"/" + proto.MessageName(msg)})
suite.chainB.GetSimApp().ICAHostKeeper.SetParams(suite.chainB.GetContext(), params)
},
ibcerrors.ErrInvalidType,
},
{
"cannot unmarshal interchain account packet data",
func(encoding string) {
packetData = []byte{}
},
ibcerrors.ErrInvalidType,
},
{
"cannot deserialize interchain account packet data messages",
func(encoding string) {
data := []byte("invalid packet data")
icaPacketData := icatypes.InterchainAccountPacketData{
Type: icatypes.EXECUTE_TX,
Data: data,
}
packetData = icaPacketData.GetBytes()
},
ibcerrors.ErrInvalidType,
},
{
"invalid packet type - UNSPECIFIED",
func(encoding string) {
data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{&banktypes.MsgSend{}}, encoding)
suite.Require().NoError(err)
icaPacketData := icatypes.InterchainAccountPacketData{
Type: icatypes.UNSPECIFIED,
Data: data,
}
packetData = icaPacketData.GetBytes()
},
icatypes.ErrUnknownDataType,
},
{
"unauthorised: interchain account not found for controller port ID",
func(encoding string) {
path.EndpointA.ChannelConfig.PortID = "invalid-port-id" //nolint:goconst
data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{&banktypes.MsgSend{}}, encoding)
suite.Require().NoError(err)
icaPacketData := icatypes.InterchainAccountPacketData{
Type: icatypes.EXECUTE_TX,
Data: data,
}
packetData = icaPacketData.GetBytes()
},
icatypes.ErrInterchainAccountNotFound,
},
{
"unauthorised: message type not allowed", // NOTE: do not update params to explicitly force the error
func(encoding string) {
msg := &banktypes.MsgSend{
FromAddress: suite.chainB.SenderAccount.GetAddress().String(),
ToAddress: suite.chainB.SenderAccount.GetAddress().String(),
Amount: sdk.NewCoins(ibctesting.TestCoin),
}
data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}, encoding)
suite.Require().NoError(err)
icaPacketData := icatypes.InterchainAccountPacketData{
Type: icatypes.EXECUTE_TX,
Data: data,
}
packetData = icaPacketData.GetBytes()
},
ibcerrors.ErrUnauthorized,
},
{
"unauthorised: signer address is not the interchain account associated with the controller portID",
func(encoding string) {
msg := &banktypes.MsgSend{
FromAddress: suite.chainB.SenderAccount.GetAddress().String(), // unexpected signer
ToAddress: suite.chainB.SenderAccount.GetAddress().String(),
Amount: sdk.NewCoins(ibctesting.TestCoin),
}
data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}, encoding)
suite.Require().NoError(err)
icaPacketData := icatypes.InterchainAccountPacketData{
Type: icatypes.EXECUTE_TX,
Data: data,
}
packetData = icaPacketData.GetBytes()
params := types.NewParams(true, []string{sdk.MsgTypeURL(msg)})
suite.chainB.GetSimApp().ICAHostKeeper.SetParams(suite.chainB.GetContext(), params)
},
ibcerrors.ErrUnauthorized,
},
}
for _, ordering := range testedOrderings {
for _, encoding := range testedEncodings {
for _, tc := range testCases {
suite.Run(tc.msg, func() {
suite.SetupTest() // reset
path = NewICAPath(suite.chainA, suite.chainB, encoding, ordering)
path.SetupConnections()
err := SetupICAPath(path, TestOwnerAddress)
suite.Require().NoError(err)
portID, err := icatypes.NewControllerPortID(TestOwnerAddress)
suite.Require().NoError(err)
// Get the address of the interchain account stored in state during handshake step
storedAddr, found := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), ibctesting.FirstConnectionID, portID)
suite.Require().True(found)
icaAddr, err := sdk.AccAddressFromBech32(storedAddr)
suite.Require().NoError(err)
// Check if account is created
interchainAccount := suite.chainB.GetSimApp().AccountKeeper.GetAccount(suite.chainB.GetContext(), icaAddr)
suite.Require().Equal(interchainAccount.GetAddress().String(), storedAddr)
suite.fundICAWallet(suite.chainB.GetContext(), path.EndpointA.ChannelConfig.PortID, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(1000000))))
tc.malleate(encoding) // malleate mutates test data
packet := channeltypes.NewPacket(
packetData,
suite.chainA.SenderAccount.GetSequence(),
path.EndpointA.ChannelConfig.PortID,
path.EndpointA.ChannelID,
path.EndpointB.ChannelConfig.PortID,
path.EndpointB.ChannelID,
suite.chainB.GetTimeoutHeight(),
0,
)
txResponse, err := suite.chainB.GetSimApp().ICAHostKeeper.OnRecvPacket(suite.chainB.GetContext(), packet)
if tc.expErr == nil {
suite.Require().NoError(err)
suite.Require().NotNil(txResponse)
} else {
suite.Require().ErrorIs(err, tc.expErr)
suite.Require().Nil(txResponse)
}
})
}
}
}
}
func (suite *KeeperTestSuite) TestJSONOnRecvPacket() {
var (
path *ibctesting.Path
packetData []byte
)
interchainAccountAddr := "cosmos15ulrf36d4wdtrtqzkgaan9ylwuhs7k7qz753uk"
testCases := []struct {
msg string
malleate func(icaAddress string)
expErr error
}{
{
"interchain account successfully executes an arbitrary message type using the * (allow all message types) param",
func(icaAddress string) {
proposal, err := govtypesv1.NewProposal([]sdk.Msg{getTestProposalMessage()}, govtypesv1.DefaultStartingProposalID, suite.chainA.GetContext().BlockTime(), suite.chainA.GetContext().BlockTime(), "test proposal", "title", "Description", sdk.AccAddress(interchainAccountAddr), false)
suite.Require().NoError(err)
err = suite.chainB.GetSimApp().GovKeeper.SetProposal(suite.chainB.GetContext(), proposal)
suite.Require().NoError(err)
err = suite.chainB.GetSimApp().GovKeeper.ActivateVotingPeriod(suite.chainB.GetContext(), proposal)
suite.Require().NoError(err)
msgBytes := []byte(`{
"messages": [
{
"@type": "/cosmos.gov.v1.MsgVote",
"voter": "` + icaAddress + `",
"proposal_id": 1,
"option": 1
}
]
}`)
// this is the way cosmwasm encodes byte arrays by default
// golang doesn't use this encoding by default, but it can still deserialize:
byteArrayString := strings.Join(strings.Fields(fmt.Sprint(msgBytes)), ",") //nolint:staticcheck
packetData = []byte(`{
"type": 1,
"data":` + byteArrayString + `
}`)
params := types.NewParams(true, []string{"*"})
suite.chainB.GetSimApp().ICAHostKeeper.SetParams(suite.chainB.GetContext(), params)
},
nil,
},
{
"interchain account successfully executes banktypes.MsgSend",
func(icaAddress string) {
msgBytes := []byte(`{
"messages": [
{
"@type": "/cosmos.bank.v1beta1.MsgSend",
"from_address": "` + icaAddress + `",
"to_address": "cosmos17dtl0mjt3t77kpuhg2edqzjpszulwhgzuj9ljs",
"amount": [{ "denom": "stake", "amount": "100" }]
}
]
}`)
byteArrayString := strings.Join(strings.Fields(fmt.Sprint(msgBytes)), ",") //nolint:staticcheck
packetData = []byte(`{
"type": 1,
"data":` + byteArrayString + `
}`)
params := types.NewParams(true, []string{sdk.MsgTypeURL((*banktypes.MsgSend)(nil))})
suite.chainB.GetSimApp().ICAHostKeeper.SetParams(suite.chainB.GetContext(), params)
},
nil,
},
{
"interchain account successfully executes govtypesv1.MsgSubmitProposal",
func(icaAddress string) {
msgBytes := []byte(`{
"messages": [
{
"@type": "/cosmos.gov.v1.MsgSubmitProposal",
"messages": [],
"metadata": "ipfs://CID",
"title": "IBC Gov Proposal",
"summary": "tokens for all!",
"expedited": false,
"initial_deposit": [{ "denom": "stake", "amount": "100000" }],
"proposer": "` + icaAddress + `"
}
]
}`)
byteArrayString := strings.Join(strings.Fields(fmt.Sprint(msgBytes)), ",") //nolint:staticcheck
packetData = []byte(`{
"type": 1,
"data":` + byteArrayString + `
}`)
params := types.NewParams(true, []string{sdk.MsgTypeURL((*govtypesv1.MsgSubmitProposal)(nil))})
suite.chainB.GetSimApp().ICAHostKeeper.SetParams(suite.chainB.GetContext(), params)
},
nil,
},
{
"interchain account successfully executes govtypesv1.MsgVote",
func(icaAddress string) {
proposal, err := govtypesv1.NewProposal([]sdk.Msg{getTestProposalMessage()}, govtypesv1.DefaultStartingProposalID, suite.chainA.GetContext().BlockTime(), suite.chainA.GetContext().BlockTime(), "test proposal", "title", "Description", sdk.AccAddress(interchainAccountAddr), false)
suite.Require().NoError(err)
err = suite.chainB.GetSimApp().GovKeeper.SetProposal(suite.chainB.GetContext(), proposal)
suite.Require().NoError(err)
err = suite.chainB.GetSimApp().GovKeeper.ActivateVotingPeriod(suite.chainB.GetContext(), proposal)
suite.Require().NoError(err)
msgBytes := []byte(`{
"messages": [
{
"@type": "/cosmos.gov.v1.MsgVote",
"voter": "` + icaAddress + `",
"proposal_id": 1,
"option": 1
}
]
}`)
byteArrayString := strings.Join(strings.Fields(fmt.Sprint(msgBytes)), ",") //nolint:staticcheck
packetData = []byte(`{
"type": 1,
"data":` + byteArrayString + `
}`)
params := types.NewParams(true, []string{sdk.MsgTypeURL((*govtypesv1.MsgVote)(nil))})
suite.chainB.GetSimApp().ICAHostKeeper.SetParams(suite.chainB.GetContext(), params)
},
nil,
},
{
"interchain account successfully executes govtypesv1.MsgSubmitProposal, govtypesv1.MsgDeposit, and then govtypesv1.MsgVote sequentially",
func(icaAddress string) {
msgBytes := []byte(`{
"messages": [
{
"@type": "/cosmos.gov.v1.MsgSubmitProposal",
"messages": [],
"metadata": "ipfs://CID",
"title": "IBC Gov Proposal",
"summary": "tokens for all!",
"expedited": false,
"initial_deposit": [{ "denom": "stake", "amount": "100000" }],
"proposer": "` + icaAddress + `"
},
{
"@type": "/cosmos.gov.v1.MsgDeposit",
"proposal_id": 1,
"depositor": "` + icaAddress + `",
"amount": [{ "denom": "stake", "amount": "10000000" }]
},
{
"@type": "/cosmos.gov.v1.MsgVote",
"voter": "` + icaAddress + `",
"proposal_id": 1,
"option": 1
}
]
}`)
byteArrayString := strings.Join(strings.Fields(fmt.Sprint(msgBytes)), ",") //nolint:staticcheck
packetData = []byte(`{
"type": 1,
"data":` + byteArrayString + `
}`)
params := types.NewParams(true, []string{sdk.MsgTypeURL((*govtypesv1.MsgSubmitProposal)(nil)), sdk.MsgTypeURL((*govtypesv1.MsgDeposit)(nil)), sdk.MsgTypeURL((*govtypesv1.MsgVote)(nil))})
suite.chainB.GetSimApp().ICAHostKeeper.SetParams(suite.chainB.GetContext(), params)
},
nil,
},
{
"interchain account successfully executes transfertypes.MsgTransfer",
func(icaAddress string) {
transferPath := ibctesting.NewTransferPath(suite.chainB, suite.chainC)
transferPath.Setup()
msgBytes := []byte(`{
"messages": [
{
"@type": "/ibc.applications.transfer.v1.MsgTransfer",
"source_port": "transfer",
"source_channel": "` + transferPath.EndpointA.ChannelID + `",
"token": { "denom": "stake", "amount": "100" },
"sender": "` + icaAddress + `",
"receiver": "cosmos15ulrf36d4wdtrtqzkgaan9ylwuhs7k7qz753uk",
"timeout_height": { "revision_number": 1, "revision_height": 100 },
"timeout_timestamp": 0,
"memo": ""
}
]
}`)
byteArrayString := strings.Join(strings.Fields(fmt.Sprint(msgBytes)), ",") //nolint:staticcheck
packetData = []byte(`{
"type": 1,
"data":` + byteArrayString + `
}`)
params := types.NewParams(true, []string{sdk.MsgTypeURL((*transfertypes.MsgTransfer)(nil))})
suite.chainB.GetSimApp().ICAHostKeeper.SetParams(suite.chainB.GetContext(), params)
},
nil,
},
{
"unregistered sdk.Msg",
func(icaAddress string) {
msgBytes := []byte(`{"messages":[{}]}`)
byteArrayString := strings.Join(strings.Fields(fmt.Sprint(msgBytes)), ",") //nolint:staticcheck
packetData = []byte(`{
"type": 1,
"data":` + byteArrayString + `
}`)
params := types.NewParams(true, []string{"*"})
suite.chainB.GetSimApp().ICAHostKeeper.SetParams(suite.chainB.GetContext(), params)
},
ibcerrors.ErrInvalidType,
},
{
"message type not allowed banktypes.MsgSend",
func(icaAddress string) {
msgBytes := []byte(`{
"messages": [
{
"@type": "/cosmos.bank.v1beta1.MsgSend",
"from_address": "` + icaAddress + `",
"to_address": "cosmos17dtl0mjt3t77kpuhg2edqzjpszulwhgzuj9ljs",
"amount": [{ "denom": "stake", "amount": "100" }]
}
]
}`)
byteArrayString := strings.Join(strings.Fields(fmt.Sprint(msgBytes)), ",") //nolint:staticcheck
packetData = []byte(`{
"type": 1,
"data":` + byteArrayString + `
}`)
params := types.NewParams(true, []string{sdk.MsgTypeURL((*transfertypes.MsgTransfer)(nil))})
suite.chainB.GetSimApp().ICAHostKeeper.SetParams(suite.chainB.GetContext(), params)
},
ibcerrors.ErrUnauthorized,
},
{
"unauthorised: signer address is not the interchain account associated with the controller portID",
func(icaAddress string) {
msgBytes := []byte(`{
"messages": [
{
"@type": "/cosmos.bank.v1beta1.MsgSend",
"from_address": "` + suite.chainB.SenderAccount.GetAddress().String() + `", // unexpected signer
"to_address": "cosmos17dtl0mjt3t77kpuhg2edqzjpszulwhgzuj9ljs",
"amount": [{ "denom": "stake", "amount": "100" }]
}
]
}`)
byteArrayString := strings.Join(strings.Fields(fmt.Sprint(msgBytes)), ",") //nolint:staticcheck
packetData = []byte(`{
"type": 1,
"data":` + byteArrayString + `
}`)
params := types.NewParams(true, []string{sdk.MsgTypeURL((*banktypes.MsgSend)(nil))})
suite.chainB.GetSimApp().ICAHostKeeper.SetParams(suite.chainB.GetContext(), params)
},
ibcerrors.ErrInvalidType,
},
}
for _, ordering := range []channeltypes.Order{channeltypes.UNORDERED, channeltypes.ORDERED} {
for _, tc := range testCases {
suite.Run(tc.msg, func() {
suite.SetupTest() // reset
path = NewICAPath(suite.chainA, suite.chainB, icatypes.EncodingProto3JSON, ordering)
path.SetupConnections()
err := SetupICAPath(path, TestOwnerAddress)
suite.Require().NoError(err)
portID, err := icatypes.NewControllerPortID(TestOwnerAddress)
suite.Require().NoError(err)
// Get the address of the interchain account stored in state during handshake step
icaAddress, found := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), ibctesting.FirstConnectionID, portID)
suite.Require().True(found)
suite.fundICAWallet(suite.chainB.GetContext(), path.EndpointA.ChannelConfig.PortID, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(100000000))))
tc.malleate(icaAddress) // malleate mutates test data
packet := channeltypes.NewPacket(
packetData,
suite.chainA.SenderAccount.GetSequence(),
path.EndpointA.ChannelConfig.PortID,
path.EndpointA.ChannelID,
path.EndpointB.ChannelConfig.PortID,
path.EndpointB.ChannelID,
suite.chainB.GetTimeoutHeight(),
0,
)
txResponse, err := suite.chainB.GetSimApp().ICAHostKeeper.OnRecvPacket(suite.chainB.GetContext(), packet)
if tc.expErr == nil {
suite.Require().NoError(err)
suite.Require().NotNil(txResponse)
} else {
suite.Require().ErrorIs(err, tc.expErr)
suite.Require().Nil(txResponse)
}
})
}
}
}
func (suite *KeeperTestSuite) fundICAWallet(ctx sdk.Context, portID string, amount sdk.Coins) {
interchainAccountAddr, found := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(ctx, ibctesting.FirstConnectionID, portID)
suite.Require().True(found)
msgBankSend := &banktypes.MsgSend{
FromAddress: suite.chainB.SenderAccount.GetAddress().String(),
ToAddress: interchainAccountAddr,
Amount: amount,
}
res, err := suite.chainB.SendMsgs(msgBankSend)
suite.Require().NotEmpty(res)
suite.Require().NoError(err)
}
func getTestProposalMessage() sdk.Msg {
_, _, addr := testdata.KeyTestPubAddr()
return banktypes.NewMsgSend(authtypes.NewModuleAddress("gov"), addr, sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(1000))))
}