From 87d1f91c4a6e5758c3a3dc508b4269df003c6c29 Mon Sep 17 00:00:00 2001 From: DimitrisJim Date: Thu, 20 Jun 2024 22:22:40 +0300 Subject: [PATCH] Refactor forwarding messages for Transfer and Packet (#6655) * feat(transfer): add unwind, refactor proto structure. gen-all * tests(transfer/types): fix test failures in types tests. * tests(transfer/keeper): fix test failures in keeper tests. * cli(transfer): fix cli usage. pending flag for unwind. * tests(callbacks): fix failing tests in callbacks. * tests(transfer/internal): fix failures in internal package. * tests(transfer): fix test failures in top level tranfer package. * tests(ica/host/keeper): fix repr of msg transfer in ica host msg execution. * lint(all): lint this bad boy * chore(transfer/types): amend validation for MsgTransfer's ShouldBeForwarded, add tests for ForwardedPacketData, minor nits. * nit(self): only pass relevant fields to create packet data; minor comment improvement. * Apply suggestions from code review Co-authored-by: Carlos Rodriguez * chore(merge): fix merge issues. * chore(proto): mention optional nature of fields. * memo: do not drop it * validation: drop requirement on memo being empty on msg transfer. --------- Co-authored-by: Carlos Rodriguez --- .../host/keeper/relay_test.go | 2 +- modules/apps/callbacks/ibc_middleware_test.go | 14 +- modules/apps/transfer/client/cli/tx.go | 10 +- modules/apps/transfer/ibc_module_test.go | 4 +- .../apps/transfer/internal/convert/convert.go | 2 +- .../apps/transfer/internal/events/events.go | 6 +- modules/apps/transfer/internal/packet_test.go | 18 +- modules/apps/transfer/keeper/export_test.go | 4 +- modules/apps/transfer/keeper/forwarding.go | 10 +- .../apps/transfer/keeper/mbt_relay_test.go | 2 +- modules/apps/transfer/keeper/relay.go | 13 +- .../transfer/keeper/relay_forwarding_test.go | 20 +- modules/apps/transfer/keeper/relay_test.go | 23 +- modules/apps/transfer/types/forwarding.go | 42 ++- .../apps/transfer/types/forwarding_test.go | 138 +++++++- modules/apps/transfer/types/msgs.go | 7 +- modules/apps/transfer/types/msgs_test.go | 12 +- modules/apps/transfer/types/packet.go | 2 +- modules/apps/transfer/types/packet.pb.go | 295 ++++++++++++++++-- modules/apps/transfer/types/packet_test.go | 60 ++-- modules/apps/transfer/types/transfer.pb.go | 132 ++++---- .../transfer/types/transfer_authorization.go | 8 +- .../types/transfer_authorization_test.go | 43 +-- .../applications/transfer/v1/transfer.proto | 10 +- .../ibc/applications/transfer/v2/packet.proto | 12 +- 25 files changed, 613 insertions(+), 276 deletions(-) diff --git a/modules/apps/27-interchain-accounts/host/keeper/relay_test.go b/modules/apps/27-interchain-accounts/host/keeper/relay_test.go index fbd05e641cd..bcef1c4f900 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/relay_test.go +++ b/modules/apps/27-interchain-accounts/host/keeper/relay_test.go @@ -798,7 +798,7 @@ func (suite *KeeperTestSuite) TestJSONOnRecvPacket() { "timeout_height": { "revision_number": 1, "revision_height": 100 }, "timeout_timestamp": 0, "memo": "", - "forwarding": { "hops": [], "memo": "" } + "forwarding": { "hops": [], "unwind": false } } ] }`) diff --git a/modules/apps/callbacks/ibc_middleware_test.go b/modules/apps/callbacks/ibc_middleware_test.go index 923b4bb6c8e..d0d7505d514 100644 --- a/modules/apps/callbacks/ibc_middleware_test.go +++ b/modules/apps/callbacks/ibc_middleware_test.go @@ -24,7 +24,7 @@ import ( ibcmock "github.com/cosmos/ibc-go/v8/testing/mock" ) -var emptyForwarding = transfertypes.Forwarding{} +var emptyForwardingPacketData = transfertypes.ForwardingPacketData{} func (s *CallbacksTestSuite) TestNewIBCMiddleware() { testCases := []struct { @@ -188,7 +188,7 @@ func (s *CallbacksTestSuite) TestSendPacket() { ibctesting.TestAccAddress, ibctesting.TestAccAddress, fmt.Sprintf(`{"src_callback": {"address": "%s"}}`, simapp.SuccessContract), - emptyForwarding, + emptyForwardingPacketData, ) chanCap := s.path.EndpointA.Chain.GetChannelCapability(s.path.EndpointA.ChannelConfig.PortID, s.path.EndpointA.ChannelID) @@ -330,7 +330,7 @@ func (s *CallbacksTestSuite) TestOnAcknowledgementPacket() { ibctesting.TestAccAddress, ibctesting.TestAccAddress, fmt.Sprintf(`{"src_callback": {"address":"%s", "gas_limit":"%d"}}`, simapp.SuccessContract, userGasLimit), - emptyForwarding, + emptyForwardingPacketData, ) packet = channeltypes.Packet{ @@ -496,7 +496,7 @@ func (s *CallbacksTestSuite) TestOnTimeoutPacket() { sdk.NewCoins(ibctesting.TestCoin), s.chainA.SenderAccount.GetAddress().String(), s.chainB.SenderAccount.GetAddress().String(), clienttypes.ZeroHeight(), timeoutTimestamp, fmt.Sprintf(`{"src_callback": {"address":"%s", "gas_limit":"%d"}}`, ibctesting.TestAccAddress, userGasLimit), // set user gas limit above panic level in mock contract keeper - emptyForwarding, + transfertypes.Forwarding{}, ) res, err := s.chainA.SendMsgs(msg) @@ -664,7 +664,7 @@ func (s *CallbacksTestSuite) TestOnRecvPacket() { ibctesting.TestAccAddress, s.chainB.SenderAccount.GetAddress().String(), fmt.Sprintf(`{"dest_callback": {"address":"%s", "gas_limit":"%d"}}`, ibctesting.TestAccAddress, userGasLimit), - emptyForwarding, + emptyForwardingPacketData, ) packet = channeltypes.Packet{ @@ -796,7 +796,7 @@ func (s *CallbacksTestSuite) TestWriteAcknowledgement() { ibctesting.TestAccAddress, s.chainB.SenderAccount.GetAddress().String(), fmt.Sprintf(`{"dest_callback": {"address":"%s", "gas_limit":"600000"}}`, ibctesting.TestAccAddress), - emptyForwarding, + emptyForwardingPacketData, ) packet = channeltypes.Packet{ @@ -1020,7 +1020,7 @@ func (s *CallbacksTestSuite) TestUnmarshalPacketDataV1() { Sender: ibctesting.TestAccAddress, Receiver: ibctesting.TestAccAddress, Memo: fmt.Sprintf(`{"src_callback": {"address": "%s"}, "dest_callback": {"address":"%s"}}`, ibctesting.TestAccAddress, ibctesting.TestAccAddress), - Forwarding: emptyForwarding, + Forwarding: emptyForwardingPacketData, } portID := s.path.EndpointA.ChannelConfig.PortID diff --git a/modules/apps/transfer/client/cli/tx.go b/modules/apps/transfer/client/cli/tx.go index e4fd1a67511..550f5c34620 100644 --- a/modules/apps/transfer/client/cli/tx.go +++ b/modules/apps/transfer/client/cli/tx.go @@ -96,12 +96,6 @@ using the {packet-timeout-timestamp} flag. If no timeout value is set then a def return err } - // If parsed, set and replace memo. - if len(forwarding.Hops) > 0 { - forwarding.Memo = memo - memo = "" - } - // NOTE: relative timeouts using block height are not supported. // if the timeouts are not absolute, CLI users rely solely on local clock time in order to calculate relative timestamps. if !absoluteTimeouts { @@ -164,6 +158,6 @@ func parseForwarding(cmd *cobra.Command) (types.Forwarding, error) { hops = append(hops, hop) } - forwarding := types.NewForwarding("", hops...) - return forwarding, nil + // TODO(jim): Add flag for unwind value + return types.NewForwarding(false, hops...), nil } diff --git a/modules/apps/transfer/ibc_module_test.go b/modules/apps/transfer/ibc_module_test.go index 0b30c6718e6..327009b0ef4 100644 --- a/modules/apps/transfer/ibc_module_test.go +++ b/modules/apps/transfer/ibc_module_test.go @@ -303,7 +303,7 @@ func (suite *TransferTestSuite) TestOnRecvPacket() { suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), "", - types.NewForwarding("", types.Hop{PortId: "transfer", ChannelId: "channel-0"}), + types.NewForwardingPacketData("", types.Hop{PortId: "transfer", ChannelId: "channel-0"}), ) packet.Data = packetData.GetBytes() @@ -368,7 +368,7 @@ func (suite *TransferTestSuite) TestOnRecvPacket() { suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), "", - types.Forwarding{}, + types.ForwardingPacketData{}, ) tokensBz, err := json.Marshal(packetData.Tokens) diff --git a/modules/apps/transfer/internal/convert/convert.go b/modules/apps/transfer/internal/convert/convert.go index c266d42519a..fbb90ec7e56 100644 --- a/modules/apps/transfer/internal/convert/convert.go +++ b/modules/apps/transfer/internal/convert/convert.go @@ -24,6 +24,6 @@ func PacketDataV1ToV2(packetData types.FungibleTokenPacketData) (types.FungibleT Sender: packetData.Sender, Receiver: packetData.Receiver, Memo: packetData.Memo, - Forwarding: types.Forwarding{}, + Forwarding: types.ForwardingPacketData{}, }, nil } diff --git a/modules/apps/transfer/internal/events/events.go b/modules/apps/transfer/internal/events/events.go index 82121c0e067..70556ab5c97 100644 --- a/modules/apps/transfer/internal/events/events.go +++ b/modules/apps/transfer/internal/events/events.go @@ -34,7 +34,7 @@ func EmitTransferEvent(ctx sdk.Context, sender, receiver string, tokens types.To // EmitOnRecvPacketEvent emits a fungible token packet event in the OnRecvPacket callback func EmitOnRecvPacketEvent(ctx sdk.Context, packetData types.FungibleTokenPacketDataV2, ack channeltypes.Acknowledgement, ackErr error) { tokensStr := mustMarshalType[types.Tokens](packetData.Tokens) - forwardingStr := mustMarshalType[types.Forwarding](packetData.Forwarding) + forwardingStr := mustMarshalType[types.ForwardingPacketData](packetData.Forwarding) eventAttributes := []sdk.Attribute{ sdk.NewAttribute(types.AttributeKeySender, packetData.Sender), @@ -64,7 +64,7 @@ func EmitOnRecvPacketEvent(ctx sdk.Context, packetData types.FungibleTokenPacket // EmitOnAcknowledgementPacketEvent emits a fungible token packet event in the OnAcknowledgementPacket callback func EmitOnAcknowledgementPacketEvent(ctx sdk.Context, packetData types.FungibleTokenPacketDataV2, ack channeltypes.Acknowledgement) { tokensStr := mustMarshalType[types.Tokens](packetData.Tokens) - forwardingStr := mustMarshalType[types.Forwarding](packetData.Forwarding) + forwardingStr := mustMarshalType[types.ForwardingPacketData](packetData.Forwarding) ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( @@ -103,7 +103,7 @@ func EmitOnAcknowledgementPacketEvent(ctx sdk.Context, packetData types.Fungible // EmitOnTimeoutEvent emits a fungible token packet event in the OnTimeoutPacket callback func EmitOnTimeoutEvent(ctx sdk.Context, packetData types.FungibleTokenPacketDataV2) { tokensStr := mustMarshalType[types.Tokens](packetData.Tokens) - forwardingStr := mustMarshalType[types.Forwarding](packetData.Forwarding) + forwardingStr := mustMarshalType[types.ForwardingPacketData](packetData.Forwarding) ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( diff --git a/modules/apps/transfer/internal/packet_test.go b/modules/apps/transfer/internal/packet_test.go index 4cb975622fe..ab56c593111 100644 --- a/modules/apps/transfer/internal/packet_test.go +++ b/modules/apps/transfer/internal/packet_test.go @@ -10,7 +10,7 @@ import ( "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" ) -var emptyForwarding = types.Forwarding{} +var emptyForwardingPacketData = types.ForwardingPacketData{} func TestUnmarshalPacketData(t *testing.T) { var ( @@ -37,7 +37,7 @@ func TestUnmarshalPacketData(t *testing.T) { Denom: types.NewDenom("atom", types.NewTrace("transfer", "channel-0")), Amount: "1000", }, - }, "sender", "receiver", "", emptyForwarding) + }, "sender", "receiver", "", emptyForwardingPacketData) packetDataBz = packetData.GetBytes() version = types.V2 @@ -94,7 +94,7 @@ func TestPacketV1ToPacketV2(t *testing.T) { Denom: types.NewDenom("atom", types.NewTrace("transfer", "channel-0")), Amount: "1000", }, - }, sender, receiver, "", emptyForwarding), + }, sender, receiver, "", emptyForwardingPacketData), nil, }, { @@ -106,7 +106,7 @@ func TestPacketV1ToPacketV2(t *testing.T) { Denom: types.NewDenom("atom"), Amount: "1000", }, - }, sender, receiver, "", emptyForwarding), + }, sender, receiver, "", emptyForwardingPacketData), nil, }, { @@ -118,7 +118,7 @@ func TestPacketV1ToPacketV2(t *testing.T) { Denom: types.NewDenom("atom/withslash", types.NewTrace("transfer", "channel-0")), Amount: "1000", }, - }, sender, receiver, "", emptyForwarding), + }, sender, receiver, "", emptyForwardingPacketData), nil, }, { @@ -130,7 +130,7 @@ func TestPacketV1ToPacketV2(t *testing.T) { Denom: types.NewDenom("atom/", types.NewTrace("transfer", "channel-0")), Amount: "1000", }, - }, sender, receiver, "", emptyForwarding), + }, sender, receiver, "", emptyForwardingPacketData), nil, }, { @@ -142,7 +142,7 @@ func TestPacketV1ToPacketV2(t *testing.T) { Denom: types.NewDenom("atom/pool", types.NewTrace("transfer", "channel-0"), types.NewTrace("transfer", "channel-1")), Amount: "1000", }, - }, sender, receiver, "", emptyForwarding), + }, sender, receiver, "", emptyForwardingPacketData), nil, }, { @@ -154,7 +154,7 @@ func TestPacketV1ToPacketV2(t *testing.T) { Denom: types.NewDenom("atom", types.NewTrace("transfer", "channel-0"), types.NewTrace("transfer", "channel-1"), types.NewTrace("transfer-custom", "channel-2")), Amount: "1000", }, - }, sender, receiver, "", emptyForwarding), + }, sender, receiver, "", emptyForwardingPacketData), nil, }, { @@ -166,7 +166,7 @@ func TestPacketV1ToPacketV2(t *testing.T) { Denom: types.NewDenom("atom/pool", types.NewTrace("transfer", "channel-0"), types.NewTrace("transfer", "channel-1"), types.NewTrace("transfer-custom", "channel-2")), Amount: "1000", }, - }, sender, receiver, "", emptyForwarding), + }, sender, receiver, "", emptyForwardingPacketData), nil, }, { diff --git a/modules/apps/transfer/keeper/export_test.go b/modules/apps/transfer/keeper/export_test.go index 582ef3ea5bd..b5fbc235504 100644 --- a/modules/apps/transfer/keeper/export_test.go +++ b/modules/apps/transfer/keeper/export_test.go @@ -34,6 +34,6 @@ func (k Keeper) TokenFromCoin(ctx sdk.Context, coin sdk.Coin) (types.Token, erro } // CreatePacketDataBytesFromVersion is a wrapper around createPacketDataBytesFromVersion for testing purposes -func CreatePacketDataBytesFromVersion(appVersion, sender, receiver, memo string, tokens types.Tokens, forwarding types.Forwarding) []byte { - return createPacketDataBytesFromVersion(appVersion, sender, receiver, memo, tokens, forwarding) +func CreatePacketDataBytesFromVersion(appVersion, sender, receiver, memo string, tokens types.Tokens, hops []types.Hop) []byte { + return createPacketDataBytesFromVersion(appVersion, sender, receiver, memo, tokens, hops) } diff --git a/modules/apps/transfer/keeper/forwarding.go b/modules/apps/transfer/keeper/forwarding.go index 3e5f0d3718a..c33dc58f460 100644 --- a/modules/apps/transfer/keeper/forwarding.go +++ b/modules/apps/transfer/keeper/forwarding.go @@ -14,13 +14,9 @@ import ( // forwardPacket forwards a fungible FungibleTokenPacketDataV2 to the next hop in the forwarding path. func (k Keeper) forwardPacket(ctx sdk.Context, data types.FungibleTokenPacketDataV2, packet channeltypes.Packet, receivedCoins sdk.Coins) error { - var memo string - var nextForwardingPath types.Forwarding - if len(data.Forwarding.Hops) == 1 { - memo = data.Forwarding.Memo - } else { - nextForwardingPath = types.NewForwarding(data.Forwarding.Memo, data.Forwarding.Hops[1:]...) + if len(data.Forwarding.Hops) > 1 { + nextForwardingPath = types.NewForwarding(false, data.Forwarding.Hops[1:]...) } // sending from the forward escrow address to the original receiver address. @@ -34,7 +30,7 @@ func (k Keeper) forwardPacket(ctx sdk.Context, data types.FungibleTokenPacketDat data.Receiver, clienttypes.ZeroHeight(), packet.TimeoutTimestamp, - memo, + data.Forwarding.DestinationMemo, nextForwardingPath, ) diff --git a/modules/apps/transfer/keeper/mbt_relay_test.go b/modules/apps/transfer/keeper/mbt_relay_test.go index 6d99322d105..33eeab9bee7 100644 --- a/modules/apps/transfer/keeper/mbt_relay_test.go +++ b/modules/apps/transfer/keeper/mbt_relay_test.go @@ -159,7 +159,7 @@ func FungibleTokenPacketFromTla(packet TlaFungibleTokenPacket) FungibleTokenPack AddressFromString(packet.Data.Sender), AddressFromString(packet.Data.Receiver), "", - types.Forwarding{}, + types.ForwardingPacketData{}, ), } } diff --git a/modules/apps/transfer/keeper/relay.go b/modules/apps/transfer/keeper/relay.go index 5b555eae9c6..ce65244452e 100644 --- a/modules/apps/transfer/keeper/relay.go +++ b/modules/apps/transfer/keeper/relay.go @@ -140,7 +140,7 @@ func (k Keeper) sendTransfer( tokens = append(tokens, token) } - packetDataBytes := createPacketDataBytesFromVersion(appVersion, sender.String(), receiver, memo, tokens, forwarding) + packetDataBytes := createPacketDataBytesFromVersion(appVersion, sender.String(), receiver, memo, tokens, forwarding.Hops) sequence, err := k.ics4Wrapper.SendPacket(ctx, channelCap, sourcePort, sourceChannel, timeoutHeight, timeoutTimestamp, packetDataBytes) if err != nil { @@ -431,7 +431,7 @@ func (k Keeper) tokenFromCoin(ctx sdk.Context, coin sdk.Coin) (types.Token, erro } // createPacketDataBytesFromVersion creates the packet data bytes to be sent based on the application version. -func createPacketDataBytesFromVersion(appVersion, sender, receiver, memo string, tokens types.Tokens, forwarding types.Forwarding) []byte { +func createPacketDataBytesFromVersion(appVersion, sender, receiver, memo string, tokens types.Tokens, hops []types.Hop) []byte { var packetDataBytes []byte switch appVersion { case types.V1: @@ -444,7 +444,14 @@ func createPacketDataBytesFromVersion(appVersion, sender, receiver, memo string, packetData := types.NewFungibleTokenPacketData(token.Denom.Path(), token.Amount, sender, receiver, memo) packetDataBytes = packetData.GetBytes() case types.V2: - packetData := types.NewFungibleTokenPacketDataV2(tokens, sender, receiver, memo, forwarding) + // If forwarding is needed, move memo to forwarding packet data and set packet.Memo to empty string. + var forwardingPacketData types.ForwardingPacketData + if len(hops) > 0 { + forwardingPacketData = types.NewForwardingPacketData(memo, hops...) + memo = "" + } + + packetData := types.NewFungibleTokenPacketDataV2(tokens, sender, receiver, memo, forwardingPacketData) packetDataBytes = packetData.GetBytes() default: panic(fmt.Errorf("app version must be one of %s", types.SupportedVersions)) diff --git a/modules/apps/transfer/keeper/relay_forwarding_test.go b/modules/apps/transfer/keeper/relay_forwarding_test.go index 3a2a3543195..591321db516 100644 --- a/modules/apps/transfer/keeper/relay_forwarding_test.go +++ b/modules/apps/transfer/keeper/relay_forwarding_test.go @@ -28,7 +28,7 @@ func (suite *KeeperTestSuite) TestPathForwarding() { coin := sdk.NewCoin(sdk.DefaultBondDenom, amount) sender := suite.chainA.SenderAccounts[0].SenderAccount receiver := suite.chainA.SenderAccounts[1].SenderAccount - forwarding := types.NewForwarding("", types.Hop{ + forwarding := types.NewForwarding(false, types.Hop{ PortId: path2.EndpointA.ChannelConfig.PortID, ChannelId: path2.EndpointA.ChannelID, }) @@ -85,7 +85,7 @@ func (suite *KeeperTestSuite) TestEscrowsAreSetAfterForwarding() { coin := sdk.NewCoin(sdk.DefaultBondDenom, amount) sender := suite.chainA.SenderAccounts[0].SenderAccount receiver := suite.chainA.SenderAccounts[1].SenderAccount - forwarding := types.NewForwarding("", types.Hop{ + forwarding := types.NewForwarding(false, types.Hop{ PortId: path2.EndpointB.ChannelConfig.PortID, ChannelId: path2.EndpointB.ChannelID, }) @@ -163,7 +163,7 @@ func (suite *KeeperTestSuite) TestHappyPathForwarding() { coin = sdk.NewCoin(sdk.DefaultBondDenom, amount) sender := suite.chainA.SenderAccounts[0].SenderAccount receiver := suite.chainA.SenderAccounts[1].SenderAccount - forwarding := types.NewForwarding("", types.Hop{ + forwarding := types.NewForwarding(false, types.Hop{ PortId: path2.EndpointB.ChannelConfig.PortID, ChannelId: path2.EndpointB.ChannelID, }) @@ -187,6 +187,7 @@ func (suite *KeeperTestSuite) TestHappyPathForwarding() { suite.Require().NoError(err) suite.Require().NotNil(packet) + forwardingPacketData := types.NewForwardingPacketData("", forwarding.Hops...) denom := types.Denom{Base: sdk.DefaultBondDenom} data := types.NewFungibleTokenPacketDataV2( []types.Token{ @@ -194,7 +195,7 @@ func (suite *KeeperTestSuite) TestHappyPathForwarding() { Denom: denom, Amount: amount.String(), }, - }, sender.GetAddress().String(), receiver.GetAddress().String(), "", forwarding) + }, sender.GetAddress().String(), receiver.GetAddress().String(), "", forwardingPacketData) packetRecv := channeltypes.NewPacket(data.GetBytes(), 2, path1.EndpointA.ChannelConfig.PortID, path1.EndpointA.ChannelID, path1.EndpointB.ChannelConfig.PortID, path1.EndpointB.ChannelID, clienttypes.ZeroHeight(), suite.chainA.GetTimeoutTimestamp()) err = suite.chainB.GetSimApp().TransferKeeper.OnRecvPacket(suite.chainB.GetContext(), packetRecv, data) @@ -227,7 +228,7 @@ func (suite *KeeperTestSuite) TestHappyPathForwarding() { Denom: denom, Amount: amount.String(), }, - }, types.GetForwardAddress(path2.EndpointB.ChannelConfig.PortID, path2.EndpointB.ChannelID).String(), receiver.GetAddress().String(), "", types.Forwarding{}) + }, types.GetForwardAddress(path2.EndpointB.ChannelConfig.PortID, path2.EndpointB.ChannelID).String(), receiver.GetAddress().String(), "", types.ForwardingPacketData{}) packetRecv = channeltypes.NewPacket(data.GetBytes(), 3, path2.EndpointB.ChannelConfig.PortID, path2.EndpointB.ChannelID, path2.EndpointA.ChannelConfig.PortID, path2.EndpointA.ChannelID, clienttypes.NewHeight(1, 100), 0) // execute onRecvPacket, when chaninA receives the tokens the escrow amount on B should increase to amount @@ -263,7 +264,7 @@ func (suite *KeeperTestSuite) TestSimplifiedHappyPathForwarding() { coinOnA := sdk.NewCoin(sdk.DefaultBondDenom, amount) sender := suite.chainA.SenderAccounts[0].SenderAccount receiver := suite.chainC.SenderAccounts[0].SenderAccount - forwarding := types.NewForwarding("", types.Hop{ + forwarding := types.NewForwarding(false, types.Hop{ PortId: path2.EndpointA.ChannelConfig.PortID, ChannelId: path2.EndpointA.ChannelID, }) @@ -477,7 +478,7 @@ func (suite *KeeperTestSuite) TestAcknowledgementFailureScenario5Forwarding() { sender = suite.chainC.SenderAccounts[0].SenderAccount receiver = suite.chainA.SenderAccounts[0].SenderAccount // Receiver is the A chain account - forwarding := types.NewForwarding("", types.Hop{ + forwarding := types.NewForwarding(false, types.Hop{ PortId: path1.EndpointB.ChannelConfig.PortID, ChannelId: path1.EndpointB.ChannelID, }) @@ -711,7 +712,7 @@ func (suite *KeeperTestSuite) TestOnTimeoutPacketForwarding() { }, address, receiver.GetAddress().String(), - "", types.Forwarding{}, + "", types.ForwardingPacketData{}, ) packet = channeltypes.NewPacket( @@ -744,6 +745,7 @@ func (suite *KeeperTestSuite) TestOnTimeoutPacketForwarding() { ackbytes := channeltypes.CommitAcknowledgement(ack.Acknowledgement()) suite.Require().Equal(ackbytes, storedAck) + forwardingPacketData := types.NewForwardingPacketData("", forwarding.Hops...) data = types.NewFungibleTokenPacketDataV2( []types.Token{ { @@ -753,7 +755,7 @@ func (suite *KeeperTestSuite) TestOnTimeoutPacketForwarding() { }, sender.GetAddress().String(), receiver.GetAddress().String(), - "", forwarding, + "", forwardingPacketData, ) packet = channeltypes.NewPacket( diff --git a/modules/apps/transfer/keeper/relay_test.go b/modules/apps/transfer/keeper/relay_test.go index 7376823f6c5..937815d9344 100644 --- a/modules/apps/transfer/keeper/relay_test.go +++ b/modules/apps/transfer/keeper/relay_test.go @@ -21,7 +21,10 @@ import ( ibcmock "github.com/cosmos/ibc-go/v8/testing/mock" ) -var emptyForwarding = types.Forwarding{} +var ( + emptyForwarding = types.Forwarding{} + emptyForwardingPacketData = types.ForwardingPacketData{} +) // TestSendTransfer tests sending from chainA to chainB using both coin // that originate on chainA and coin that originate on chainB. @@ -383,7 +386,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket_ReceiverIsNotSource() { Denom: types.NewDenom(sdk.DefaultBondDenom, []types.Trace{}...), Amount: amount.String(), }, - }, suite.chainA.SenderAccount.GetAddress().String(), receiver, memo, emptyForwarding) + }, suite.chainA.SenderAccount.GetAddress().String(), receiver, memo, emptyForwardingPacketData) packet := channeltypes.NewPacket(data.GetBytes(), seq, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) err = suite.chainB.GetSimApp().TransferKeeper.OnRecvPacket(suite.chainB.GetContext(), packet, data) @@ -534,7 +537,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket_ReceiverIsSource() { Denom: denom, Amount: amount.String(), }, - }, suite.chainA.SenderAccount.GetAddress().String(), receiver, memo, emptyForwarding) + }, suite.chainA.SenderAccount.GetAddress().String(), receiver, memo, emptyForwardingPacketData) packet = channeltypes.NewPacket(data.GetBytes(), seq, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) err = suite.chainB.GetSimApp().TransferKeeper.OnRecvPacket(suite.chainB.GetContext(), packet, data) @@ -612,7 +615,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacketSetsTotalEscrowAmountForSourceIBCT Denom: denom, Amount: amount.String(), }, - }, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), "", emptyForwarding) + }, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), "", emptyForwardingPacketData) packet := channeltypes.NewPacket( data.GetBytes(), seq, @@ -743,7 +746,7 @@ func (suite *KeeperTestSuite) TestOnAcknowledgementPacket() { Denom: denom, Amount: amount.String(), }, - }, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), "", emptyForwarding) + }, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), "", emptyForwardingPacketData) packet := channeltypes.NewPacket(data.GetBytes(), 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) preAcknowledgementBalance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), denom.IBCDenom()) @@ -838,7 +841,7 @@ func (suite *KeeperTestSuite) TestOnAcknowledgementPacketSetsTotalEscrowAmountFo suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), "", - emptyForwarding, + emptyForwardingPacketData, ) packet := channeltypes.NewPacket( data.GetBytes(), @@ -978,7 +981,7 @@ func (suite *KeeperTestSuite) TestOnTimeoutPacket() { Denom: denom, Amount: amount, }, - }, sender, suite.chainB.SenderAccount.GetAddress().String(), "", emptyForwarding) + }, sender, suite.chainB.SenderAccount.GetAddress().String(), "", emptyForwardingPacketData) packet := channeltypes.NewPacket(data.GetBytes(), 1, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, clienttypes.NewHeight(1, 100), 0) preTimeoutBalance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), denom.IBCDenom()) @@ -1064,7 +1067,7 @@ func (suite *KeeperTestSuite) TestOnTimeoutPacketSetsTotalEscrowAmountForSourceI Denom: denom, Amount: amount.String(), }, - }, suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), "", emptyForwarding) + }, suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), "", emptyForwardingPacketData) packet := channeltypes.NewPacket( data.GetBytes(), seq, @@ -1271,7 +1274,7 @@ func (suite *KeeperTestSuite) TestCreatePacketDataBytesFromVersion() { types.V2, func() {}, func(bz []byte) { - expPacketData := types.NewFungibleTokenPacketDataV2(types.Tokens{types.Token{}}, "", "", "", emptyForwarding) + expPacketData := types.NewFungibleTokenPacketDataV2(types.Tokens{types.Token{}}, "", "", "", emptyForwardingPacketData) suite.Require().Equal(bz, expPacketData.GetBytes()) }, nil, @@ -1301,7 +1304,7 @@ func (suite *KeeperTestSuite) TestCreatePacketDataBytesFromVersion() { tc.malleate() createFunc := func() { - bz = transferkeeper.CreatePacketDataBytesFromVersion(tc.appVersion, "", "", "", tokens, emptyForwarding) + bz = transferkeeper.CreatePacketDataBytesFromVersion(tc.appVersion, "", "", "", tokens, nil) } expPanic := tc.expPanicErr != nil diff --git a/modules/apps/transfer/types/forwarding.go b/modules/apps/transfer/types/forwarding.go index a95efebff85..895bfb9e286 100644 --- a/modules/apps/transfer/types/forwarding.go +++ b/modules/apps/transfer/types/forwarding.go @@ -8,26 +8,52 @@ import ( const MaximumNumberOfForwardingHops = 16 // denotes the maximum number of forwarding hops allowed -// NewForwarding creates a new Forwarding instance given a memo and a variable number of hops. -func NewForwarding(memo string, hops ...Hop) Forwarding { +// NewForwarding creates a new Forwarding instance given an unwind value and a variable number of hops. +func NewForwarding(unwind bool, hops ...Hop) Forwarding { return Forwarding{ - Memo: memo, - Hops: hops, + Unwind: unwind, + Hops: hops, } } // Validate performs a basic validation of the Forwarding fields. func (fi Forwarding) Validate() error { if len(fi.Hops) > MaximumNumberOfForwardingHops { - return errorsmod.Wrapf(ErrInvalidForwarding, "number of hops in forwarding path cannot exceed %d", MaximumNumberOfForwardingHops) + return errorsmod.Wrapf(ErrInvalidForwarding, "number of hops in forwarding cannot exceed %d", MaximumNumberOfForwardingHops) } - if len(fi.Memo) > MaximumMemoLength { + for _, hop := range fi.Hops { + if err := host.PortIdentifierValidator(hop.PortId); err != nil { + return errorsmod.Wrapf(err, "invalid hop source port ID %s", hop.PortId) + } + if err := host.ChannelIdentifierValidator(hop.ChannelId); err != nil { + return errorsmod.Wrapf(err, "invalid source channel ID %s", hop.ChannelId) + } + } + + return nil +} + +// NewForwardingPacketData creates a new ForwardingPacketData instance given a memo and a variable number of hops. +func NewForwardingPacketData(destinationMemo string, hops ...Hop) ForwardingPacketData { + return ForwardingPacketData{ + DestinationMemo: destinationMemo, + Hops: hops, + } +} + +// Validate performs a basic validation of the ForwardingPacketData fields. +func (fi ForwardingPacketData) Validate() error { + if len(fi.Hops) > MaximumNumberOfForwardingHops { + return errorsmod.Wrapf(ErrInvalidForwarding, "number of hops in forwarding packet data cannot exceed %d", MaximumNumberOfForwardingHops) + } + + if len(fi.DestinationMemo) > MaximumMemoLength { return errorsmod.Wrapf(ErrInvalidMemo, "memo length cannot exceed %d", MaximumMemoLength) } - if len(fi.Hops) == 0 && fi.Memo != "" { - return errorsmod.Wrap(ErrInvalidForwarding, "memo specified when forwarding hops is empty") + if len(fi.Hops) == 0 && fi.DestinationMemo != "" { + return errorsmod.Wrap(ErrInvalidForwarding, "memo specified when forwarding packet data hops is empty") } for _, hop := range fi.Hops { diff --git a/modules/apps/transfer/types/forwarding_test.go b/modules/apps/transfer/types/forwarding_test.go index 236f78d65d6..c8633c7f72f 100644 --- a/modules/apps/transfer/types/forwarding_test.go +++ b/modules/apps/transfer/types/forwarding_test.go @@ -23,47 +23,156 @@ func TestForwarding_Validate(t *testing.T) { }{ { "valid forwarding with no hops", - types.NewForwarding(""), + types.NewForwarding(false), nil, }, { "valid forwarding with hops", - types.NewForwarding("", validHop), + types.NewForwarding(false, validHop), + nil, + }, + { + "valid forwarding with max hops", + types.NewForwarding(false, generateHops(types.MaximumNumberOfForwardingHops)...), + nil, + }, + { + "invalid forwarding with too many hops", + types.NewForwarding(false, generateHops(types.MaximumNumberOfForwardingHops+1)...), + types.ErrInvalidForwarding, + }, + { + "invalid forwarding with too short hop port ID", + types.NewForwarding( + false, + types.Hop{ + PortId: invalidShortPort, + ChannelId: ibctesting.FirstChannelID, + }, + ), + host.ErrInvalidID, + }, + { + "invalid forwarding with too long hop port ID", + types.NewForwarding( + false, + types.Hop{ + PortId: invalidLongPort, + ChannelId: ibctesting.FirstChannelID, + }, + ), + host.ErrInvalidID, + }, + { + "invalid forwarding with non-alpha hop port ID", + types.NewForwarding( + false, + types.Hop{ + PortId: invalidPort, + ChannelId: ibctesting.FirstChannelID, + }, + ), + host.ErrInvalidID, + }, + { + "invalid forwarding with too long hop channel ID", + types.NewForwarding( + false, + types.Hop{ + PortId: types.PortID, + ChannelId: invalidLongChannel, + }, + ), + host.ErrInvalidID, + }, + { + "invalid forwarding with too short hop channel ID", + types.NewForwarding( + false, + types.Hop{ + PortId: types.PortID, + ChannelId: invalidShortChannel, + }, + ), + host.ErrInvalidID, + }, + { + "invalid forwarding with non-alpha hop channel ID", + types.NewForwarding( + false, + types.Hop{ + PortId: types.PortID, + ChannelId: invalidChannel, + }, + ), + host.ErrInvalidID, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + tc := tc + + err := tc.forwarding.Validate() + + expPass := tc.expError == nil + if expPass { + require.NoError(t, err) + } else { + require.ErrorIs(t, err, tc.expError) + } + }) + } +} + +func TestForwardingPacketData_Validate(t *testing.T) { + tests := []struct { + name string + forwarding types.ForwardingPacketData + expError error + }{ + { + "valid forwarding with no hops", + types.NewForwardingPacketData(""), + nil, + }, + { + "valid forwarding with hops", + types.NewForwardingPacketData("", validHop), nil, }, { "valid forwarding with memo", - types.NewForwarding(testMemo1, validHop, validHop), + types.NewForwardingPacketData(testMemo1, validHop, validHop), nil, }, { "valid forwarding with max hops", - types.NewForwarding("", generateHops(types.MaximumNumberOfForwardingHops)...), + types.NewForwardingPacketData("", generateHops(types.MaximumNumberOfForwardingHops)...), nil, }, { "valid forwarding with max memo length", - types.NewForwarding(ibctesting.GenerateString(types.MaximumMemoLength), validHop), + types.NewForwardingPacketData(ibctesting.GenerateString(types.MaximumMemoLength), validHop), nil, }, { "invalid forwarding with too many hops", - types.NewForwarding("", generateHops(types.MaximumNumberOfForwardingHops+1)...), + types.NewForwardingPacketData("", generateHops(types.MaximumNumberOfForwardingHops+1)...), types.ErrInvalidForwarding, }, { "invalid forwarding with too long memo", - types.NewForwarding(ibctesting.GenerateString(types.MaximumMemoLength+1), validHop), + types.NewForwardingPacketData(ibctesting.GenerateString(types.MaximumMemoLength+1), validHop), types.ErrInvalidMemo, }, { "invalid forwarding with empty hops and specified memo", - types.NewForwarding("memo"), + types.NewForwardingPacketData("memo"), types.ErrInvalidForwarding, }, { "invalid forwarding with too short hop port ID", - types.NewForwarding( + types.NewForwardingPacketData( "", types.Hop{ PortId: invalidShortPort, @@ -74,7 +183,7 @@ func TestForwarding_Validate(t *testing.T) { }, { "invalid forwarding with too long hop port ID", - types.NewForwarding( + types.NewForwardingPacketData( "", types.Hop{ PortId: invalidLongPort, @@ -85,7 +194,7 @@ func TestForwarding_Validate(t *testing.T) { }, { "invalid forwarding with non-alpha hop port ID", - types.NewForwarding( + types.NewForwardingPacketData( "", types.Hop{ PortId: invalidPort, @@ -96,7 +205,7 @@ func TestForwarding_Validate(t *testing.T) { }, { "invalid forwarding with too long hop channel ID", - types.NewForwarding( + types.NewForwardingPacketData( "", types.Hop{ PortId: types.PortID, @@ -107,7 +216,7 @@ func TestForwarding_Validate(t *testing.T) { }, { "invalid forwarding with too short hop channel ID", - types.NewForwarding( + types.NewForwardingPacketData( "", types.Hop{ PortId: types.PortID, @@ -118,7 +227,7 @@ func TestForwarding_Validate(t *testing.T) { }, { "invalid forwarding with non-alpha hop channel ID", - types.NewForwarding( + types.NewForwardingPacketData( "", types.Hop{ PortId: types.PortID, @@ -144,6 +253,7 @@ func TestForwarding_Validate(t *testing.T) { } } +// generateHops generates a slice of n correctly initialized hops. func generateHops(n int) []types.Hop { hops := make([]types.Hop, n) for i := 0; i < n; i++ { diff --git a/modules/apps/transfer/types/msgs.go b/modules/apps/transfer/types/msgs.go index 2e6bf3d5ba6..01289ff42eb 100644 --- a/modules/apps/transfer/types/msgs.go +++ b/modules/apps/transfer/types/msgs.go @@ -111,11 +111,6 @@ func (msg MsgTransfer) ValidateBasic() error { if !msg.TimeoutHeight.IsZero() { return errorsmod.Wrapf(ErrInvalidPacketTimeout, "timeout height must not be set if forwarding path hops is not empty: %s, %s", msg.TimeoutHeight, msg.Forwarding.Hops) } - - // when forwarding, the memo must be empty - if msg.Memo != "" { - return errorsmod.Wrapf(ErrInvalidMemo, "memo must be empty if forwarding path hops is not empty: %s, %s", msg.Memo, msg.Forwarding.Hops) - } } for _, coin := range msg.GetCoins() { @@ -140,7 +135,7 @@ func (msg MsgTransfer) GetCoins() sdk.Coins { // ShouldBeForwarded determines if the transfer should be forwarded to the next hop. func (msg MsgTransfer) ShouldBeForwarded() bool { - return len(msg.Forwarding.Hops) > 0 + return len(msg.Forwarding.Hops) > 0 || msg.Forwarding.Unwind } // isValidIBCCoin returns true if the token provided is valid, diff --git a/modules/apps/transfer/types/msgs_test.go b/modules/apps/transfer/types/msgs_test.go index 03fc39f61fb..b09992f57a2 100644 --- a/modules/apps/transfer/types/msgs_test.go +++ b/modules/apps/transfer/types/msgs_test.go @@ -61,6 +61,8 @@ func TestMsgTransferValidation(t *testing.T) { {"valid msg with base denom", types.NewMsgTransfer(validPort, validChannel, coins, sender, receiver, clienttypes.ZeroHeight(), 100, "", emptyForwarding), nil}, {"valid msg with trace hash", types.NewMsgTransfer(validPort, validChannel, ibcCoins, sender, receiver, clienttypes.ZeroHeight(), 100, "", emptyForwarding), nil}, {"multidenom", types.NewMsgTransfer(validPort, validChannel, coins.Add(ibcCoins...), sender, receiver, clienttypes.ZeroHeight(), 100, "", emptyForwarding), nil}, + {"memo with forwarding path hops not empty", types.NewMsgTransfer(validPort, validChannel, coins, sender, receiver, clienttypes.ZeroHeight(), 100, "memo", types.NewForwarding(false, validHop)), nil}, + {"memo with forwarding unwind set to true", types.NewMsgTransfer(validPort, validChannel, coins, sender, receiver, clienttypes.ZeroHeight(), 100, "memo", types.NewForwarding(true)), nil}, {"invalid ibc denom", types.NewMsgTransfer(validPort, validChannel, invalidIBCCoins, sender, receiver, clienttypes.ZeroHeight(), 100, "", emptyForwarding), ibcerrors.ErrInvalidCoins}, {"too short port id", types.NewMsgTransfer(invalidShortPort, validChannel, coins, sender, receiver, clienttypes.ZeroHeight(), 100, "", emptyForwarding), host.ErrInvalidID}, {"too long port id", types.NewMsgTransfer(invalidLongPort, validChannel, coins, sender, receiver, clienttypes.ZeroHeight(), 100, "", emptyForwarding), host.ErrInvalidID}, @@ -80,12 +82,10 @@ func TestMsgTransferValidation(t *testing.T) { {"multidenom: zero coins", types.NewMsgTransfer(validPort, validChannel, zeroCoins, sender, receiver, clienttypes.ZeroHeight(), 100, "", emptyForwarding), ibcerrors.ErrInvalidCoins}, {"multidenom: too many coins", types.NewMsgTransfer(validPort, validChannel, make([]sdk.Coin, types.MaximumTokensLength+1), sender, receiver, clienttypes.ZeroHeight(), 100, "", emptyForwarding), ibcerrors.ErrInvalidCoins}, {"multidenom: both token and tokens are set", &types.MsgTransfer{validPort, validChannel, coin, sender, receiver, clienttypes.ZeroHeight(), 100, "", coins, emptyForwarding}, ibcerrors.ErrInvalidCoins}, - {"timeout height must be zero if forwarding path hops is not empty", types.NewMsgTransfer(validPort, validChannel, coins, sender, receiver, timeoutHeight, 100, "memo", types.NewForwarding("", validHop)), types.ErrInvalidPacketTimeout}, - {"memo must be empty if forwarding path hops is not empty", types.NewMsgTransfer(validPort, validChannel, coins, sender, receiver, clienttypes.ZeroHeight(), 100, "memo", types.NewForwarding("", validHop)), types.ErrInvalidMemo}, - {"invalid forwarding info port", types.NewMsgTransfer(validPort, validChannel, coins, sender, receiver, clienttypes.ZeroHeight(), 100, "", types.NewForwarding("", types.Hop{PortId: invalidPort, ChannelId: validChannel})), host.ErrInvalidID}, - {"invalid forwarding info channel", types.NewMsgTransfer(validPort, validChannel, coins, sender, receiver, clienttypes.ZeroHeight(), 100, "", types.NewForwarding("", types.Hop{PortId: validPort, ChannelId: invalidChannel})), host.ErrInvalidID}, - {"invalid forwarding info too many hops", types.NewMsgTransfer(validPort, validChannel, coins, sender, receiver, clienttypes.ZeroHeight(), 100, "", types.NewForwarding("", generateHops(types.MaximumNumberOfForwardingHops+1)...)), types.ErrInvalidForwarding}, - {"invalid forwarding info too long memo", types.NewMsgTransfer(validPort, validChannel, coins, sender, receiver, clienttypes.ZeroHeight(), 100, "", types.NewForwarding(ibctesting.GenerateString(types.MaximumMemoLength+1), validHop)), types.ErrInvalidMemo}, + {"timeout height must be zero if forwarding path hops is not empty", types.NewMsgTransfer(validPort, validChannel, coins, sender, receiver, timeoutHeight, 100, "memo", types.NewForwarding(false, validHop)), types.ErrInvalidPacketTimeout}, + {"invalid forwarding info port", types.NewMsgTransfer(validPort, validChannel, coins, sender, receiver, clienttypes.ZeroHeight(), 100, "", types.NewForwarding(false, types.Hop{PortId: invalidPort, ChannelId: validChannel})), host.ErrInvalidID}, + {"invalid forwarding info channel", types.NewMsgTransfer(validPort, validChannel, coins, sender, receiver, clienttypes.ZeroHeight(), 100, "", types.NewForwarding(false, types.Hop{PortId: validPort, ChannelId: invalidChannel})), host.ErrInvalidID}, + {"invalid forwarding info too many hops", types.NewMsgTransfer(validPort, validChannel, coins, sender, receiver, clienttypes.ZeroHeight(), 100, "", types.NewForwarding(false, generateHops(types.MaximumNumberOfForwardingHops+1)...)), types.ErrInvalidForwarding}, } for _, tc := range testCases { diff --git a/modules/apps/transfer/types/packet.go b/modules/apps/transfer/types/packet.go index 6f8a34a4e24..c75d31b5fc2 100644 --- a/modules/apps/transfer/types/packet.go +++ b/modules/apps/transfer/types/packet.go @@ -103,7 +103,7 @@ func NewFungibleTokenPacketDataV2( tokens []Token, sender, receiver string, memo string, - forwarding Forwarding, + forwarding ForwardingPacketData, ) FungibleTokenPacketDataV2 { return FungibleTokenPacketDataV2{ Tokens: tokens, diff --git a/modules/apps/transfer/types/packet.pb.go b/modules/apps/transfer/types/packet.pb.go index fd02225dc97..2622fc514bd 100644 --- a/modules/apps/transfer/types/packet.pb.go +++ b/modules/apps/transfer/types/packet.pb.go @@ -120,7 +120,7 @@ type FungibleTokenPacketDataV2 struct { // optional memo Memo string `protobuf:"bytes,4,opt,name=memo,proto3" json:"memo,omitempty"` // optional forwarding information - Forwarding Forwarding `protobuf:"bytes,5,opt,name=forwarding,proto3" json:"forwarding"` + Forwarding ForwardingPacketData `protobuf:"bytes,5,opt,name=forwarding,proto3" json:"forwarding"` } func (m *FungibleTokenPacketDataV2) Reset() { *m = FungibleTokenPacketDataV2{} } @@ -184,16 +184,74 @@ func (m *FungibleTokenPacketDataV2) GetMemo() string { return "" } -func (m *FungibleTokenPacketDataV2) GetForwarding() Forwarding { +func (m *FungibleTokenPacketDataV2) GetForwarding() ForwardingPacketData { if m != nil { return m.Forwarding } - return Forwarding{} + return ForwardingPacketData{} +} + +// ForwardingPacketData defines a list of port ID, channel ID pairs determining the path +// through which a packet must be forwarded, and the destination memo string to be used in the +// final destination of the tokens. +type ForwardingPacketData struct { + // optional memo consumed by final destination chain + DestinationMemo string `protobuf:"bytes,1,opt,name=destination_memo,json=destinationMemo,proto3" json:"destination_memo,omitempty"` + // optional intermediate path through which packet will be forwarded. + Hops []Hop `protobuf:"bytes,2,rep,name=hops,proto3" json:"hops"` +} + +func (m *ForwardingPacketData) Reset() { *m = ForwardingPacketData{} } +func (m *ForwardingPacketData) String() string { return proto.CompactTextString(m) } +func (*ForwardingPacketData) ProtoMessage() {} +func (*ForwardingPacketData) Descriptor() ([]byte, []int) { + return fileDescriptor_653ca2ce9a5ca313, []int{2} +} +func (m *ForwardingPacketData) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ForwardingPacketData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ForwardingPacketData.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ForwardingPacketData) XXX_Merge(src proto.Message) { + xxx_messageInfo_ForwardingPacketData.Merge(m, src) +} +func (m *ForwardingPacketData) XXX_Size() int { + return m.Size() +} +func (m *ForwardingPacketData) XXX_DiscardUnknown() { + xxx_messageInfo_ForwardingPacketData.DiscardUnknown(m) +} + +var xxx_messageInfo_ForwardingPacketData proto.InternalMessageInfo + +func (m *ForwardingPacketData) GetDestinationMemo() string { + if m != nil { + return m.DestinationMemo + } + return "" +} + +func (m *ForwardingPacketData) GetHops() []Hop { + if m != nil { + return m.Hops + } + return nil } func init() { proto.RegisterType((*FungibleTokenPacketData)(nil), "ibc.applications.transfer.v2.FungibleTokenPacketData") proto.RegisterType((*FungibleTokenPacketDataV2)(nil), "ibc.applications.transfer.v2.FungibleTokenPacketDataV2") + proto.RegisterType((*ForwardingPacketData)(nil), "ibc.applications.transfer.v2.ForwardingPacketData") } func init() { @@ -201,30 +259,34 @@ func init() { } var fileDescriptor_653ca2ce9a5ca313 = []byte{ - // 365 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x91, 0xb1, 0xae, 0xd3, 0x30, - 0x18, 0x85, 0xe3, 0x36, 0xad, 0xc0, 0xdd, 0xac, 0x0a, 0x42, 0x85, 0x42, 0x55, 0x96, 0x20, 0x84, - 0xad, 0x86, 0x01, 0x56, 0x2a, 0xd4, 0x11, 0x41, 0x85, 0x18, 0xd8, 0x1c, 0xc7, 0x0d, 0x56, 0x1b, - 0x3b, 0xb2, 0x9d, 0x20, 0x9e, 0x02, 0x1e, 0xab, 0x63, 0x47, 0xa6, 0xab, 0xab, 0xf6, 0x1d, 0xee, - 0x7c, 0x15, 0x27, 0xb7, 0x37, 0x4b, 0xb3, 0xfd, 0xe7, 0xe4, 0xf8, 0xe8, 0xcb, 0xff, 0xc3, 0x37, - 0x22, 0x61, 0x84, 0x16, 0xc5, 0x5e, 0x30, 0x6a, 0x85, 0x92, 0x86, 0x58, 0x4d, 0xa5, 0xd9, 0x72, - 0x4d, 0xaa, 0x98, 0x14, 0x94, 0xed, 0xb8, 0xc5, 0x85, 0x56, 0x56, 0xa1, 0x97, 0x22, 0x61, 0xb8, - 0x1b, 0xc5, 0x0f, 0x51, 0x5c, 0xc5, 0xb3, 0xa8, 0xb7, 0xc8, 0xaa, 0x1d, 0x97, 0x4d, 0xcf, 0x6c, - 0x9a, 0xa9, 0x4c, 0xb9, 0x91, 0xd4, 0x53, 0xeb, 0xbe, 0xed, 0x79, 0xbf, 0xbc, 0xcc, 0x4d, 0x78, - 0xf1, 0x17, 0xc0, 0xe7, 0xeb, 0x52, 0x66, 0x22, 0xd9, 0xf3, 0xef, 0x75, 0xf5, 0x57, 0x07, 0xfa, - 0x99, 0x5a, 0x8a, 0xa6, 0x70, 0x94, 0x72, 0xa9, 0xf2, 0x00, 0xcc, 0x41, 0xf4, 0x74, 0xd3, 0x08, - 0xf4, 0x0c, 0x8e, 0x69, 0xae, 0x4a, 0x69, 0x83, 0x81, 0xb3, 0x5b, 0x55, 0xfb, 0x86, 0xcb, 0x94, - 0xeb, 0x60, 0xd8, 0xf8, 0x8d, 0x42, 0x33, 0xf8, 0x44, 0x73, 0xc6, 0x45, 0xc5, 0x75, 0xe0, 0xbb, - 0x2f, 0x17, 0x8d, 0x10, 0xf4, 0x73, 0x9e, 0xab, 0x60, 0xe4, 0x7c, 0x37, 0x2f, 0xee, 0x00, 0x7c, - 0x71, 0x85, 0xe8, 0x47, 0x8c, 0x3e, 0xc1, 0xb1, 0xdb, 0x80, 0x09, 0xc0, 0x7c, 0x18, 0x4d, 0xe2, - 0xd7, 0xb8, 0x6f, 0x97, 0xd8, 0x15, 0xac, 0xfc, 0xc3, 0xcd, 0x2b, 0x6f, 0xd3, 0x3e, 0xec, 0x80, - 0x0e, 0xae, 0x82, 0x0e, 0xaf, 0x80, 0xfa, 0x8f, 0xa0, 0xe8, 0x0b, 0x84, 0x5b, 0xa5, 0x7f, 0x53, - 0x9d, 0x0a, 0x99, 0xb9, 0x5f, 0x98, 0xc4, 0x51, 0x1f, 0xce, 0x12, 0xaf, 0x2f, 0xf9, 0x96, 0xa9, - 0xd3, 0xb0, 0xfa, 0x76, 0x38, 0x85, 0xe0, 0x78, 0x0a, 0xc1, 0xed, 0x29, 0x04, 0xff, 0xce, 0xa1, - 0x77, 0x3c, 0x87, 0xde, 0xff, 0x73, 0xe8, 0xfd, 0xfc, 0x90, 0x09, 0xfb, 0xab, 0x4c, 0x30, 0x53, - 0x39, 0x61, 0xca, 0xe4, 0xca, 0x10, 0x91, 0xb0, 0x77, 0x99, 0x22, 0xd5, 0x47, 0x92, 0xab, 0xb4, - 0xdc, 0x73, 0x53, 0x5f, 0xbc, 0x73, 0x69, 0xfb, 0xa7, 0xe0, 0x26, 0x19, 0xbb, 0x23, 0xbf, 0xbf, - 0x0f, 0x00, 0x00, 0xff, 0xff, 0xfe, 0xa2, 0xd3, 0x30, 0x9c, 0x02, 0x00, 0x00, + // 418 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x92, 0x41, 0x8b, 0xd3, 0x40, + 0x14, 0xc7, 0x93, 0x34, 0x5b, 0x74, 0xf6, 0xa0, 0x0c, 0x45, 0x63, 0x91, 0xb8, 0xd6, 0x4b, 0x17, + 0x71, 0x86, 0x8d, 0x07, 0x05, 0x4f, 0x2e, 0xb2, 0x78, 0x11, 0x74, 0x11, 0x11, 0x2f, 0x32, 0x99, + 0xcc, 0x66, 0x87, 0x36, 0xf3, 0xc2, 0xcc, 0x24, 0xe2, 0x45, 0xfc, 0x06, 0xfa, 0xb1, 0x7a, 0xec, + 0xd1, 0x93, 0x48, 0xfb, 0x45, 0x24, 0x93, 0xd8, 0xe6, 0x60, 0x73, 0x7b, 0xef, 0x9f, 0xff, 0xfb, + 0xf3, 0x9b, 0x97, 0x87, 0x4e, 0x65, 0xca, 0x29, 0x2b, 0xcb, 0xa5, 0xe4, 0xcc, 0x4a, 0x50, 0x86, + 0x5a, 0xcd, 0x94, 0xb9, 0x12, 0x9a, 0xd6, 0x09, 0x2d, 0x19, 0x5f, 0x08, 0x4b, 0x4a, 0x0d, 0x16, + 0xf0, 0x7d, 0x99, 0x72, 0xd2, 0xb7, 0x92, 0x7f, 0x56, 0x52, 0x27, 0xd3, 0xf9, 0x60, 0x90, 0x85, + 0x85, 0x50, 0x6d, 0xce, 0x74, 0x92, 0x43, 0x0e, 0xae, 0xa4, 0x4d, 0xd5, 0xa9, 0x8f, 0x07, 0xe6, + 0xcf, 0x76, 0x75, 0x6b, 0x9e, 0xfd, 0xf0, 0xd1, 0xdd, 0x8b, 0x4a, 0xe5, 0x32, 0x5d, 0x8a, 0xf7, + 0x4d, 0xf4, 0x5b, 0x07, 0xfa, 0x8a, 0x59, 0x86, 0x27, 0xe8, 0x28, 0x13, 0x0a, 0x8a, 0xc8, 0x3f, + 0xf1, 0xe7, 0x37, 0x2f, 0xdb, 0x06, 0xdf, 0x41, 0x63, 0x56, 0x40, 0xa5, 0x6c, 0x14, 0x38, 0xb9, + 0xeb, 0x1a, 0xdd, 0x08, 0x95, 0x09, 0x1d, 0x8d, 0x5a, 0xbd, 0xed, 0xf0, 0x14, 0xdd, 0xd0, 0x82, + 0x0b, 0x59, 0x0b, 0x1d, 0x85, 0xee, 0xcb, 0xae, 0xc7, 0x18, 0x85, 0x85, 0x28, 0x20, 0x3a, 0x72, + 0xba, 0xab, 0x67, 0xdf, 0x03, 0x74, 0xef, 0x00, 0xd1, 0x87, 0x04, 0xbf, 0x44, 0x63, 0xb7, 0x01, + 0x13, 0xf9, 0x27, 0xa3, 0xf9, 0x71, 0xf2, 0x88, 0x0c, 0xed, 0x92, 0xb8, 0x80, 0xf3, 0x70, 0xf5, + 0xfb, 0x81, 0x77, 0xd9, 0x0d, 0xf6, 0x40, 0x83, 0x83, 0xa0, 0xa3, 0x03, 0xa0, 0xe1, 0x1e, 0x14, + 0x7f, 0x44, 0xe8, 0x0a, 0xf4, 0x17, 0xa6, 0x33, 0xa9, 0x72, 0xf7, 0x84, 0xe3, 0x24, 0x19, 0xc6, + 0xb9, 0xd8, 0xf9, 0xf7, 0x8f, 0xea, 0xe8, 0x7a, 0x59, 0xb3, 0x6f, 0x68, 0xf2, 0x3f, 0x27, 0x3e, + 0x45, 0xb7, 0x33, 0x61, 0xac, 0x54, 0x2e, 0xfa, 0xb3, 0x23, 0x6a, 0xff, 0xcd, 0xad, 0x9e, 0xfe, + 0xa6, 0x81, 0x7b, 0x81, 0xc2, 0x6b, 0x28, 0x4d, 0x14, 0xb8, 0x2d, 0x3d, 0x1c, 0xc2, 0x3a, 0x23, + 0xaf, 0xa1, 0xec, 0x28, 0xdc, 0xd0, 0xf9, 0xbb, 0xd5, 0x26, 0xf6, 0xd7, 0x9b, 0xd8, 0xff, 0xb3, + 0x89, 0xfd, 0x9f, 0xdb, 0xd8, 0x5b, 0x6f, 0x63, 0xef, 0xd7, 0x36, 0xf6, 0x3e, 0x3d, 0xcb, 0xa5, + 0xbd, 0xae, 0x52, 0xc2, 0xa1, 0xa0, 0x1c, 0x4c, 0x01, 0x86, 0xca, 0x94, 0x3f, 0xc9, 0x81, 0xd6, + 0xcf, 0x69, 0x01, 0x59, 0xb5, 0x14, 0xa6, 0xb9, 0xbd, 0xde, 0xcd, 0xd9, 0xaf, 0xa5, 0x30, 0xe9, + 0xd8, 0x9d, 0xdb, 0xd3, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xf8, 0x64, 0x17, 0xb6, 0x26, 0x03, + 0x00, 0x00, } func (m *FungibleTokenPacketData) Marshal() (dAtA []byte, err error) { @@ -353,6 +415,50 @@ func (m *FungibleTokenPacketDataV2) MarshalToSizedBuffer(dAtA []byte) (int, erro return len(dAtA) - i, nil } +func (m *ForwardingPacketData) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ForwardingPacketData) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ForwardingPacketData) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Hops) > 0 { + for iNdEx := len(m.Hops) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Hops[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPacket(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.DestinationMemo) > 0 { + i -= len(m.DestinationMemo) + copy(dAtA[i:], m.DestinationMemo) + i = encodeVarintPacket(dAtA, i, uint64(len(m.DestinationMemo))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintPacket(dAtA []byte, offset int, v uint64) int { offset -= sovPacket(v) base := offset @@ -422,6 +528,25 @@ func (m *FungibleTokenPacketDataV2) Size() (n int) { return n } +func (m *ForwardingPacketData) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DestinationMemo) + if l > 0 { + n += 1 + l + sovPacket(uint64(l)) + } + if len(m.Hops) > 0 { + for _, e := range m.Hops { + l = e.Size() + n += 1 + l + sovPacket(uint64(l)) + } + } + return n +} + func sovPacket(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -851,6 +976,122 @@ func (m *FungibleTokenPacketDataV2) Unmarshal(dAtA []byte) error { } return nil } +func (m *ForwardingPacketData) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPacket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ForwardingPacketData: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ForwardingPacketData: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DestinationMemo", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPacket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPacket + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPacket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DestinationMemo = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hops", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPacket + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthPacket + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthPacket + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Hops = append(m.Hops, Hop{}) + if err := m.Hops[len(m.Hops)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipPacket(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthPacket + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipPacket(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/modules/apps/transfer/types/packet_test.go b/modules/apps/transfer/types/packet_test.go index 69b7a30068b..3e1cfbe8e46 100644 --- a/modules/apps/transfer/types/packet_test.go +++ b/modules/apps/transfer/types/packet_test.go @@ -20,6 +20,8 @@ const ( invalidLargeAmount = "115792089237316195423570985008687907853269984665640564039457584007913129639936" // 2^256 ) +var emptyForwardingPacketData = types.ForwardingPacketData{} + // TestFungibleTokenPacketDataValidateBasic tests ValidateBasic for FungibleTokenPacketData func TestFungibleTokenPacketDataValidateBasic(t *testing.T) { testCases := []struct { @@ -183,7 +185,7 @@ func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { sender, receiver, "", - emptyForwarding, + emptyForwardingPacketData, ), nil, }, @@ -199,7 +201,7 @@ func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { sender, receiver, "memo", - emptyForwarding, + emptyForwardingPacketData, ), nil, }, @@ -215,7 +217,7 @@ func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { sender, receiver, "memo", - emptyForwarding, + emptyForwardingPacketData, ), nil, }, @@ -231,7 +233,7 @@ func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { sender, receiver, "", - types.NewForwarding("", validHop, validHop), + types.NewForwardingPacketData("", validHop, validHop), ), nil, }, @@ -247,7 +249,7 @@ func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { sender, receiver, "", - types.NewForwarding("memo", validHop), + types.NewForwardingPacketData("memo", validHop), ), nil, }, @@ -263,7 +265,7 @@ func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { sender, receiver, "", - emptyForwarding, + emptyForwardingPacketData, ), types.ErrInvalidDenomForTransfer, }, @@ -279,7 +281,7 @@ func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { sender, receiver, "", - emptyForwarding, + emptyForwardingPacketData, ), types.ErrInvalidAmount, }, @@ -290,7 +292,7 @@ func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { sender, receiver, "", - emptyForwarding, + emptyForwardingPacketData, ), types.ErrInvalidAmount, }, @@ -306,7 +308,7 @@ func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { sender, receiver, "", - emptyForwarding, + emptyForwardingPacketData, ), types.ErrInvalidAmount, }, @@ -322,7 +324,7 @@ func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { sender, receiver, "", - emptyForwarding, + emptyForwardingPacketData, ), types.ErrInvalidAmount, }, @@ -338,7 +340,7 @@ func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { sender, receiver, "memo", - emptyForwarding, + emptyForwardingPacketData, ), types.ErrInvalidAmount, }, @@ -354,7 +356,7 @@ func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { "", receiver, "memo", - emptyForwarding, + emptyForwardingPacketData, ), ibcerrors.ErrInvalidAddress, }, @@ -370,7 +372,7 @@ func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { sender, "", "", - emptyForwarding, + emptyForwardingPacketData, ), ibcerrors.ErrInvalidAddress, }, @@ -386,7 +388,7 @@ func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { sender, receiver, ibctesting.GenerateString(types.MaximumMemoLength+1), - emptyForwarding, + emptyForwardingPacketData, ), types.ErrInvalidMemo, }, @@ -402,7 +404,7 @@ func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { sender, receiver, "memo", - types.NewForwarding("", validHop), + types.NewForwardingPacketData("", validHop), ), types.ErrInvalidMemo, }, @@ -418,7 +420,7 @@ func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { sender, receiver, "", - types.NewForwarding( + types.NewForwardingPacketData( "", types.Hop{ PortId: invalidPort, @@ -440,7 +442,7 @@ func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { sender, receiver, "", - types.NewForwarding( + types.NewForwardingPacketData( "", types.Hop{ PortId: "transfer", @@ -462,7 +464,7 @@ func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { sender, receiver, "", - types.NewForwarding("", generateHops(types.MaximumNumberOfForwardingHops+1)...), + types.NewForwardingPacketData("", generateHops(types.MaximumNumberOfForwardingHops+1)...), ), types.ErrInvalidForwarding, }, @@ -478,7 +480,7 @@ func TestFungibleTokenPacketDataV2ValidateBasic(t *testing.T) { sender, receiver, "", - types.NewForwarding(ibctesting.GenerateString(types.MaximumMemoLength+1), validHop), + types.NewForwardingPacketData(ibctesting.GenerateString(types.MaximumMemoLength+1), validHop), ), types.ErrInvalidMemo, }, @@ -516,7 +518,7 @@ func TestGetPacketSender(t *testing.T) { sender, receiver, "", - emptyForwarding, + emptyForwardingPacketData, ), sender, }, @@ -532,7 +534,7 @@ func TestGetPacketSender(t *testing.T) { "", receiver, "abc", - emptyForwarding, + emptyForwardingPacketData, ), "", }, @@ -563,7 +565,7 @@ func TestPacketDataProvider(t *testing.T) { sender, receiver, fmt.Sprintf(`{"src_callback": {"address": "%s"}}`, receiver), - emptyForwarding, + emptyForwardingPacketData, ), map[string]interface{}{ @@ -582,7 +584,7 @@ func TestPacketDataProvider(t *testing.T) { sender, receiver, fmt.Sprintf(`{"src_callback": {"address": "%s", "gas_limit": "200000"}}`, receiver), - emptyForwarding, + emptyForwardingPacketData, ), map[string]interface{}{ "address": receiver, @@ -601,7 +603,7 @@ func TestPacketDataProvider(t *testing.T) { sender, receiver, `{"src_callback": "string"}`, - emptyForwarding, + emptyForwardingPacketData, ), "string", }, @@ -617,7 +619,7 @@ func TestPacketDataProvider(t *testing.T) { sender, receiver, fmt.Sprintf(`{"dest_callback": {"address": "%s", "min_gas": "200000"}}`, receiver), - emptyForwarding, + emptyForwardingPacketData, ), nil, }, @@ -633,7 +635,7 @@ func TestPacketDataProvider(t *testing.T) { sender, receiver, "", - emptyForwarding, + emptyForwardingPacketData, ), nil, }, @@ -649,7 +651,7 @@ func TestPacketDataProvider(t *testing.T) { sender, receiver, "invalid", - emptyForwarding, + emptyForwardingPacketData, ), nil, }, @@ -681,7 +683,7 @@ func TestFungibleTokenPacketDataOmitEmpty(t *testing.T) { sender, receiver, "", - emptyForwarding, + emptyForwardingPacketData, ), false, }, @@ -697,7 +699,7 @@ func TestFungibleTokenPacketDataOmitEmpty(t *testing.T) { sender, receiver, "abc", - emptyForwarding, + emptyForwardingPacketData, ), true, }, diff --git a/modules/apps/transfer/types/transfer.pb.go b/modules/apps/transfer/types/transfer.pb.go index 7311d98c072..f2825d90088 100644 --- a/modules/apps/transfer/types/transfer.pb.go +++ b/modules/apps/transfer/types/transfer.pb.go @@ -84,11 +84,13 @@ func (m *Params) GetReceiveEnabled() bool { } // Forwarding defines a list of port ID, channel ID pairs determining the path -// through which a packet must be forwarded, and the memo string to be used in the -// final destination of the tokens. +// through which a packet must be forwarded, and an unwind boolean indicating if +// the coin should be unwinded to its native chain before forwarding. type Forwarding struct { - Hops []Hop `protobuf:"bytes,1,rep,name=hops,proto3" json:"hops"` - Memo string `protobuf:"bytes,2,opt,name=memo,proto3" json:"memo,omitempty"` + // optional unwinding for the token transfered + Unwind bool `protobuf:"varint,1,opt,name=unwind,proto3" json:"unwind,omitempty"` + // optional intermediate path through which packet will be forwarded + Hops []Hop `protobuf:"bytes,2,rep,name=hops,proto3" json:"hops"` } func (m *Forwarding) Reset() { *m = Forwarding{} } @@ -124,18 +126,18 @@ func (m *Forwarding) XXX_DiscardUnknown() { var xxx_messageInfo_Forwarding proto.InternalMessageInfo -func (m *Forwarding) GetHops() []Hop { +func (m *Forwarding) GetUnwind() bool { if m != nil { - return m.Hops + return m.Unwind } - return nil + return false } -func (m *Forwarding) GetMemo() string { +func (m *Forwarding) GetHops() []Hop { if m != nil { - return m.Memo + return m.Hops } - return "" + return nil } // Hop defines a port ID, channel ID pair specifying where tokens must be forwarded @@ -203,28 +205,28 @@ func init() { } var fileDescriptor_5041673e96e97901 = []byte{ - // 324 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x91, 0x41, 0x4b, 0xf3, 0x30, - 0x18, 0xc7, 0xdb, 0x77, 0x63, 0xaf, 0x7b, 0x26, 0x0a, 0x41, 0x70, 0x88, 0xd6, 0x6d, 0x17, 0x07, - 0x62, 0xc3, 0xf4, 0xa0, 0x20, 0x5e, 0x06, 0xca, 0x76, 0xd3, 0xe2, 0x49, 0x90, 0x91, 0xa6, 0xb1, - 0x0b, 0xb4, 0x79, 0x42, 0xd2, 0x55, 0xfc, 0x16, 0x7e, 0xac, 0x1d, 0x77, 0xf4, 0x24, 0xb2, 0x7d, - 0x11, 0x69, 0x3b, 0xc6, 0x4e, 0xde, 0xfe, 0xf9, 0xe5, 0xf7, 0x27, 0x0f, 0x79, 0xe0, 0x5c, 0x86, - 0x9c, 0x32, 0xad, 0x13, 0xc9, 0x59, 0x26, 0x51, 0x59, 0x9a, 0x19, 0xa6, 0xec, 0x9b, 0x30, 0x34, - 0x1f, 0x6c, 0xb2, 0xaf, 0x0d, 0x66, 0x48, 0x8e, 0x65, 0xc8, 0xfd, 0x6d, 0xd9, 0xdf, 0x08, 0xf9, - 0xe0, 0xe8, 0x20, 0xc6, 0x18, 0x4b, 0x91, 0x16, 0xa9, 0xea, 0xf4, 0x9e, 0xa1, 0xf1, 0xc8, 0x0c, - 0x4b, 0x2d, 0xe9, 0xc2, 0xae, 0x15, 0x2a, 0x9a, 0x08, 0xc5, 0xc2, 0x44, 0x44, 0x6d, 0xb7, 0xe3, - 0xf6, 0x77, 0x82, 0x56, 0xc1, 0xee, 0x2b, 0x44, 0xce, 0x60, 0xdf, 0x08, 0x2e, 0x64, 0x2e, 0x36, - 0xd6, 0xbf, 0xd2, 0xda, 0x5b, 0xe3, 0xb5, 0xd8, 0x7b, 0x05, 0x78, 0x40, 0xf3, 0xce, 0x4c, 0x24, - 0x55, 0x4c, 0x6e, 0xa1, 0x3e, 0x45, 0x6d, 0xdb, 0x6e, 0xa7, 0xd6, 0x6f, 0x5d, 0x76, 0xfd, 0xbf, - 0xc6, 0xf4, 0x47, 0xa8, 0x87, 0xf5, 0xf9, 0xf7, 0xa9, 0x13, 0x94, 0x25, 0x42, 0xa0, 0x9e, 0x8a, - 0x14, 0xcb, 0x87, 0x9a, 0x41, 0x99, 0x7b, 0x77, 0x50, 0x1b, 0xa1, 0x26, 0x87, 0xf0, 0x5f, 0xa3, - 0xc9, 0x26, 0xb2, 0x1a, 0xb6, 0x19, 0x34, 0x8a, 0xe3, 0x38, 0x22, 0x27, 0x00, 0x7c, 0xca, 0x94, - 0x12, 0x49, 0x71, 0x57, 0x35, 0x9b, 0x6b, 0x32, 0x8e, 0x86, 0x4f, 0xf3, 0xa5, 0xe7, 0x2e, 0x96, - 0x9e, 0xfb, 0xb3, 0xf4, 0xdc, 0xcf, 0x95, 0xe7, 0x2c, 0x56, 0x9e, 0xf3, 0xb5, 0xf2, 0x9c, 0x97, - 0xeb, 0x58, 0x66, 0xd3, 0x59, 0xe8, 0x73, 0x4c, 0x29, 0x47, 0x9b, 0xa2, 0xa5, 0x32, 0xe4, 0x17, - 0x31, 0xd2, 0xfc, 0x86, 0xa6, 0x18, 0xcd, 0x12, 0x61, 0x8b, 0x75, 0x6c, 0xad, 0x21, 0xfb, 0xd0, - 0xc2, 0x86, 0x8d, 0xf2, 0x37, 0xaf, 0x7e, 0x03, 0x00, 0x00, 0xff, 0xff, 0x95, 0xd0, 0x0d, 0xf6, - 0xb0, 0x01, 0x00, 0x00, + // 323 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x91, 0x41, 0x4b, 0xc3, 0x30, + 0x14, 0xc7, 0xdb, 0x6d, 0x54, 0x97, 0x89, 0x42, 0x10, 0x1d, 0xa2, 0x75, 0xdb, 0xc5, 0x81, 0xd8, + 0x30, 0x3d, 0x28, 0x88, 0x97, 0x81, 0xb2, 0xdd, 0x74, 0x78, 0xf2, 0x32, 0xd2, 0x34, 0x76, 0x81, + 0x36, 0x2f, 0x24, 0x59, 0x87, 0xdf, 0xc2, 0x8f, 0xb5, 0xe3, 0x8e, 0x9e, 0x44, 0xb6, 0x2f, 0x22, + 0xed, 0xea, 0xd8, 0xc9, 0xdb, 0x7b, 0xbf, 0xf7, 0x7b, 0x8f, 0x90, 0x3f, 0xba, 0x14, 0x21, 0x23, + 0x54, 0xa9, 0x44, 0x30, 0x6a, 0x05, 0x48, 0x43, 0xac, 0xa6, 0xd2, 0xbc, 0x73, 0x4d, 0xb2, 0xde, + 0xa6, 0x0e, 0x94, 0x06, 0x0b, 0xf8, 0x54, 0x84, 0x2c, 0xd8, 0x96, 0x83, 0x8d, 0x90, 0xf5, 0x4e, + 0x0e, 0x63, 0x88, 0xa1, 0x10, 0x49, 0x5e, 0xad, 0x77, 0x3a, 0xaf, 0xc8, 0x7b, 0xa6, 0x9a, 0xa6, + 0x06, 0xb7, 0xd1, 0x9e, 0xe1, 0x32, 0x1a, 0x73, 0x49, 0xc3, 0x84, 0x47, 0x4d, 0xb7, 0xe5, 0x76, + 0x77, 0x47, 0x8d, 0x9c, 0x3d, 0xae, 0x11, 0xbe, 0x40, 0x07, 0x9a, 0x33, 0x2e, 0x32, 0xbe, 0xb1, + 0x2a, 0x85, 0xb5, 0x5f, 0xe2, 0x52, 0xec, 0x50, 0x84, 0x9e, 0x40, 0xcf, 0xa8, 0x8e, 0x84, 0x8c, + 0xf1, 0x11, 0xf2, 0xa6, 0x72, 0x26, 0xe4, 0xdf, 0xcd, 0xb2, 0xc3, 0xf7, 0xa8, 0x36, 0x01, 0x65, + 0x9a, 0x95, 0x56, 0xb5, 0xdb, 0xb8, 0x6e, 0x07, 0xff, 0x3d, 0x3f, 0x18, 0x80, 0xea, 0xd7, 0xe6, + 0xdf, 0xe7, 0xce, 0xa8, 0x58, 0xea, 0x3c, 0xa0, 0xea, 0x00, 0x14, 0x3e, 0x46, 0x3b, 0x0a, 0xb4, + 0x1d, 0x8b, 0xf5, 0xf1, 0xfa, 0xc8, 0xcb, 0xdb, 0x61, 0x84, 0xcf, 0x10, 0x62, 0x13, 0x2a, 0x25, + 0x4f, 0xf2, 0x59, 0xa5, 0x98, 0xd5, 0x4b, 0x32, 0x8c, 0xfa, 0x2f, 0xf3, 0xa5, 0xef, 0x2e, 0x96, + 0xbe, 0xfb, 0xb3, 0xf4, 0xdd, 0xcf, 0x95, 0xef, 0x2c, 0x56, 0xbe, 0xf3, 0xb5, 0xf2, 0x9d, 0xb7, + 0xdb, 0x58, 0xd8, 0xc9, 0x34, 0x0c, 0x18, 0xa4, 0x84, 0x81, 0x49, 0xc1, 0x10, 0x11, 0xb2, 0xab, + 0x18, 0x48, 0x76, 0x47, 0x52, 0x88, 0xa6, 0x09, 0x37, 0x79, 0x24, 0x5b, 0x51, 0xd8, 0x0f, 0xc5, + 0x4d, 0xe8, 0x15, 0x3f, 0x7a, 0xf3, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x94, 0x05, 0x3d, 0x15, 0xb4, + 0x01, 0x00, 0x00, } func (m *Params) Marshal() (dAtA []byte, err error) { @@ -290,13 +292,6 @@ func (m *Forwarding) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if len(m.Memo) > 0 { - i -= len(m.Memo) - copy(dAtA[i:], m.Memo) - i = encodeVarintTransfer(dAtA, i, uint64(len(m.Memo))) - i-- - dAtA[i] = 0x12 - } if len(m.Hops) > 0 { for iNdEx := len(m.Hops) - 1; iNdEx >= 0; iNdEx-- { { @@ -308,8 +303,18 @@ func (m *Forwarding) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintTransfer(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0xa + dAtA[i] = 0x12 + } + } + if m.Unwind { + i-- + if m.Unwind { + dAtA[i] = 1 + } else { + dAtA[i] = 0 } + i-- + dAtA[i] = 0x8 } return len(dAtA) - i, nil } @@ -383,16 +388,15 @@ func (m *Forwarding) Size() (n int) { } var l int _ = l + if m.Unwind { + n += 2 + } if len(m.Hops) > 0 { for _, e := range m.Hops { l = e.Size() n += 1 + l + sovTransfer(uint64(l)) } } - l = len(m.Memo) - if l > 0 { - n += 1 + l + sovTransfer(uint64(l)) - } return n } @@ -539,10 +543,10 @@ func (m *Forwarding) Unmarshal(dAtA []byte) error { } switch fieldNum { case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Hops", wireType) + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Unwind", wireType) } - var msglen int + var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTransfer @@ -552,31 +556,17 @@ func (m *Forwarding) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { - return ErrInvalidLengthTransfer - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthTransfer - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Hops = append(m.Hops, Hop{}) - if err := m.Hops[len(m.Hops)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex + m.Unwind = bool(v != 0) case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Memo", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Hops", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTransfer @@ -586,23 +576,25 @@ func (m *Forwarding) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { return ErrInvalidLengthTransfer } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthTransfer } if postIndex > l { return io.ErrUnexpectedEOF } - m.Memo = string(dAtA[iNdEx:postIndex]) + m.Hops = append(m.Hops, Hop{}) + if err := m.Hops[len(m.Hops)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex default: iNdEx = preIndex diff --git a/modules/apps/transfer/types/transfer_authorization.go b/modules/apps/transfer/types/transfer_authorization.go index 5419b503512..bac1092e02f 100644 --- a/modules/apps/transfer/types/transfer_authorization.go +++ b/modules/apps/transfer/types/transfer_authorization.go @@ -58,13 +58,7 @@ func (a TransferAuthorization) Accept(goCtx context.Context, msg proto.Message) return authz.AcceptResponse{}, errorsmod.Wrap(ibcerrors.ErrInvalidAddress, "not allowed receiver address for transfer") } - memo := msgTransfer.Memo - // in the case of forwarded transfers, the actual memo is stored in the forwarding path until the final destination - if msgTransfer.ShouldBeForwarded() { - memo = msgTransfer.Forwarding.Memo - } - - if err := validateMemo(ctx, memo, a.Allocations[index].AllowedPacketData); err != nil { + if err := validateMemo(ctx, msgTransfer.Memo, a.Allocations[index].AllowedPacketData); err != nil { return authz.AcceptResponse{}, err } diff --git a/modules/apps/transfer/types/transfer_authorization_test.go b/modules/apps/transfer/types/transfer_authorization_test.go index 0b108525709..14b7c7c1ad6 100644 --- a/modules/apps/transfer/types/transfer_authorization_test.go +++ b/modules/apps/transfer/types/transfer_authorization_test.go @@ -104,7 +104,8 @@ func (suite *TypesTestSuite) TestTransferAuthorizationAccept() { func() { allowedList := []string{} transferAuthz.Allocations[0].AllowedPacketData = allowedList - msgTransfer.Forwarding = types.NewForwarding("", validHop) + + msgTransfer.Forwarding = types.NewForwarding(false, validHop) }, func(res authz.AcceptResponse, err error) { suite.Require().NoError(err) @@ -134,7 +135,7 @@ func (suite *TypesTestSuite) TestTransferAuthorizationAccept() { func() { allowedList := []string{"*"} transferAuthz.Allocations[0].AllowedPacketData = allowedList - msgTransfer.Forwarding = types.NewForwarding(testMemo1, validHop) + msgTransfer.Forwarding = types.NewForwarding(false, validHop) }, func(res authz.AcceptResponse, err error) { suite.Require().NoError(err) @@ -159,21 +160,6 @@ func (suite *TypesTestSuite) TestTransferAuthorizationAccept() { suite.Require().Nil(res.Updated) }, }, - { - "success: transfer memo allowed in forwarding path", - func() { - allowedList := []string{testMemo1, testMemo2} - transferAuthz.Allocations[0].AllowedPacketData = allowedList - msgTransfer.Forwarding = types.NewForwarding(testMemo1, validHop) - }, - func(res authz.AcceptResponse, err error) { - suite.Require().NoError(err) - - suite.Require().True(res.Accept) - suite.Require().True(res.Delete) - suite.Require().Nil(res.Updated) - }, - }, { "empty AllowedPacketData but not empty memo", func() { @@ -185,17 +171,6 @@ func (suite *TypesTestSuite) TestTransferAuthorizationAccept() { suite.Require().Error(err) }, }, - { - "empty AllowedPacketData but not empty memo in forwarding path", - func() { - allowedList := []string{} - transferAuthz.Allocations[0].AllowedPacketData = allowedList - msgTransfer.Forwarding = types.NewForwarding(testMemo1, validHop) - }, - func(res authz.AcceptResponse, err error) { - suite.Require().Error(err) - }, - }, { "memo not allowed", func() { @@ -208,18 +183,6 @@ func (suite *TypesTestSuite) TestTransferAuthorizationAccept() { suite.Require().ErrorContains(err, fmt.Sprintf("not allowed memo: %s", testMemo2)) }, }, - { - "memo not allowed in forwarding path", - func() { - allowedList := []string{testMemo1} - transferAuthz.Allocations[0].AllowedPacketData = allowedList - msgTransfer.Forwarding = types.NewForwarding(testMemo2, validHop) - }, - func(res authz.AcceptResponse, err error) { - suite.Require().Error(err) - suite.Require().ErrorContains(err, fmt.Sprintf("not allowed memo: %s", testMemo2)) - }, - }, { "test multiple coins does not overspend", func() { diff --git a/proto/ibc/applications/transfer/v1/transfer.proto b/proto/ibc/applications/transfer/v1/transfer.proto index 8e3456e3a29..2c0b942e554 100644 --- a/proto/ibc/applications/transfer/v1/transfer.proto +++ b/proto/ibc/applications/transfer/v1/transfer.proto @@ -20,11 +20,13 @@ message Params { } // Forwarding defines a list of port ID, channel ID pairs determining the path -// through which a packet must be forwarded, and the memo string to be used in the -// final destination of the tokens. +// through which a packet must be forwarded, and an unwind boolean indicating if +// the coin should be unwinded to its native chain before forwarding. message Forwarding { - repeated Hop hops = 1 [(gogoproto.nullable) = false]; - string memo = 2; + // optional unwinding for the token transfered + bool unwind = 1; + // optional intermediate path through which packet will be forwarded + repeated Hop hops = 2 [(gogoproto.nullable) = false]; } // Hop defines a port ID, channel ID pair specifying where tokens must be forwarded diff --git a/proto/ibc/applications/transfer/v2/packet.proto b/proto/ibc/applications/transfer/v2/packet.proto index 9999e7ea87c..162833ce4e1 100644 --- a/proto/ibc/applications/transfer/v2/packet.proto +++ b/proto/ibc/applications/transfer/v2/packet.proto @@ -37,5 +37,15 @@ message FungibleTokenPacketDataV2 { // optional memo string memo = 4; // optional forwarding information - ibc.applications.transfer.v1.Forwarding forwarding = 5 [(gogoproto.nullable) = false]; + ForwardingPacketData forwarding = 5 [(gogoproto.nullable) = false]; +} + +// ForwardingPacketData defines a list of port ID, channel ID pairs determining the path +// through which a packet must be forwarded, and the destination memo string to be used in the +// final destination of the tokens. +message ForwardingPacketData { + // optional memo consumed by final destination chain + string destination_memo = 1; + // optional intermediate path through which packet will be forwarded. + repeated ibc.applications.transfer.v1.Hop hops = 2 [(gogoproto.nullable) = false]; }