From b30df5c973730ed9041d39f60aed3201fd6bc2d3 Mon Sep 17 00:00:00 2001 From: Sean King Date: Thu, 1 Sep 2022 09:49:50 +0200 Subject: [PATCH] feat: implementing SubmitTx gRPC endpoint (#2147) * feat: implementing SubmitTx gRPC endpoint * test: adding failure cases * add capability test * chore: comment * chore: cleanup * chore: changelog * import & sequence * nits * refactor: clean up tests (cherry picked from commit 9019adf468c0824c15155b0a47da63f84c5052cf) # Conflicts: # modules/apps/27-interchain-accounts/controller/keeper/msg_server.go --- CHANGELOG.md | 2 + .../controller/keeper/msg_server.go | 31 ++++- .../controller/keeper/msg_server_test.go | 116 +++++++++++++++++- 3 files changed, 145 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1678b579626..1064f6fd5d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,6 +64,8 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Features +* (apps/27-interchain-accounts) [\#2147](https://github.com/cosmos/ibc-go/pull/2147) Adding a `SubmitTx` gRPC endpoint for the ICS27 Controller module which allows owners of interchain accounts to submit transactions. This replaces the previously existing need for authentication modules to implement this standard functionality. + ### Bug Fixes * (makefile) [\#1785](https://github.com/cosmos/ibc-go/pull/1785) Fetch the correct versions of protocol buffers dependencies from tendermint, cosmos-sdk, and ics23. diff --git a/modules/apps/27-interchain-accounts/controller/keeper/msg_server.go b/modules/apps/27-interchain-accounts/controller/keeper/msg_server.go index aaf50bcb930..90a4204fc2f 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/msg_server.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/msg_server.go @@ -4,8 +4,15 @@ import ( "context" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/types" +<<<<<<< HEAD +======= + icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/types" + channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" + host "github.com/cosmos/ibc-go/v5/modules/core/24-host" +>>>>>>> 9019adf (feat: implementing SubmitTx gRPC endpoint (#2147)) ) var _ types.MsgServer = Keeper{} @@ -26,5 +33,27 @@ func (k Keeper) RegisterAccount(goCtx context.Context, msg *types.MsgRegisterAcc // SubmitTx defines a rpc handler for MsgSubmitTx func (k Keeper) SubmitTx(goCtx context.Context, msg *types.MsgSubmitTx) (*types.MsgSubmitTxResponse, error) { - return &types.MsgSubmitTxResponse{}, nil + ctx := sdk.UnwrapSDKContext(goCtx) + + portID, err := icatypes.NewControllerPortID(msg.Owner) + if err != nil { + return nil, err + } + + channelID, found := k.GetActiveChannelID(ctx, msg.ConnectionId, portID) + if !found { + return nil, sdkerrors.Wrapf(icatypes.ErrActiveChannelNotFound, "failed to retrieve active channel for port %s", portID) + } + + chanCap, found := k.scopedKeeper.GetCapability(ctx, host.ChannelCapabilityPath(portID, channelID)) + if !found { + return nil, sdkerrors.Wrap(channeltypes.ErrChannelCapabilityNotFound, "module does not own channel capability") + } + + seq, err := k.SendTx(ctx, chanCap, msg.ConnectionId, portID, msg.PacketData, msg.TimeoutTimestamp) + if err != nil { + return nil, err + } + + return &types.MsgSubmitTxResponse{Sequence: seq}, nil } diff --git a/modules/apps/27-interchain-accounts/controller/keeper/msg_server_test.go b/modules/apps/27-interchain-accounts/controller/keeper/msg_server_test.go index 461d16dfb44..38aa673015c 100644 --- a/modules/apps/27-interchain-accounts/controller/keeper/msg_server_test.go +++ b/modules/apps/27-interchain-accounts/controller/keeper/msg_server_test.go @@ -1,9 +1,16 @@ package keeper_test import ( + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" sdktypes "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - icatypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/types" + "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/types" + controllertypes "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" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v5/modules/core/04-channel/types" host "github.com/cosmos/ibc-go/v5/modules/core/24-host" ibctesting "github.com/cosmos/ibc-go/v5/testing" @@ -11,7 +18,7 @@ import ( func (suite *KeeperTestSuite) TestRegisterAccount() { var ( - msg *icatypes.MsgRegisterAccount + msg *controllertypes.MsgRegisterAccount expectedChannelID = "channel-0" ) @@ -63,7 +70,7 @@ func (suite *KeeperTestSuite) TestRegisterAccount() { path := NewICAPath(suite.chainA, suite.chainB) suite.coordinator.SetupConnections(path) - msg = icatypes.NewMsgRegisterAccount(ibctesting.FirstConnectionID, ibctesting.TestAccAddress, "") + msg = controllertypes.NewMsgRegisterAccount(ibctesting.FirstConnectionID, ibctesting.TestAccAddress, "") tc.malleate() @@ -85,3 +92,106 @@ func (suite *KeeperTestSuite) TestRegisterAccount() { } } } + +func (suite *KeeperTestSuite) TestSubmitTx() { + var ( + path *ibctesting.Path + msg *controllertypes.MsgSubmitTx + ) + + testCases := []struct { + name string + malleate func() + expPass bool + }{ + { + "success", func() { + }, + true, + }, + { + "failure - owner address is empty", func() { + msg.Owner = "" + }, + false, + }, + { + "failure - active channel does not exist for connection ID", func() { + msg.Owner = TestOwnerAddress + msg.ConnectionId = "connection-100" + }, + false, + }, + { + "failure - active channel does not exist for port ID", func() { + msg.Owner = TestAccAddress.String() + }, + false, + }, + { + "failure - controller module does not own capability for this channel", func() { + msg.Owner = TestAccAddress.String() + portID, err := icatypes.NewControllerPortID(msg.Owner) + suite.Require().NoError(err) + + // set the active channel with the incorrect portID in order to reach the capability check + suite.chainA.GetSimApp().ICAControllerKeeper.SetActiveChannelID(suite.chainA.GetContext(), path.EndpointA.ConnectionID, portID, path.EndpointA.ChannelID) + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + + suite.Run(tc.name, func() { + suite.SetupTest() + + owner := TestOwnerAddress + path = NewICAPath(suite.chainA, suite.chainB) + suite.coordinator.SetupConnections(path) + + err := SetupICAPath(path, owner) + 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 + interchainAccountAddr, found := suite.chainA.GetSimApp().ICAControllerKeeper.GetInterchainAccountAddress(suite.chainA.GetContext(), path.EndpointA.ConnectionID, portID) + suite.Require().True(found) + + // create bank transfer message that will execute on the host chain + icaMsg := &banktypes.MsgSend{ + FromAddress: interchainAccountAddr, + ToAddress: suite.chainB.SenderAccount.GetAddress().String(), + Amount: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))), + } + + data, err := icatypes.SerializeCosmosTx(suite.chainA.Codec, []sdk.Msg{icaMsg}) + suite.Require().NoError(err) + + packetData := icatypes.InterchainAccountPacketData{ + Type: icatypes.EXECUTE_TX, + Data: data, + Memo: "memo", + } + + timeoutTimestamp := uint64(suite.chainA.GetContext().BlockTime().Add(time.Minute).UnixNano()) + connectionID := path.EndpointA.ConnectionID + + msg = types.NewMsgSubmitTx(owner, connectionID, clienttypes.ZeroHeight(), timeoutTimestamp, packetData) + + tc.malleate() // malleate mutates test data + res, err := suite.chainA.GetSimApp().ICAControllerKeeper.SubmitTx(sdk.WrapSDKContext(suite.chainA.GetContext()), msg) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + } else { + suite.Require().Error(err) + suite.Require().Nil(res) + } + }) + } +}