diff --git a/modules/apps/transfer/keeper/forwarding.go b/modules/apps/transfer/keeper/forwarding.go index 52e31e6d606..dab78bb32a5 100644 --- a/modules/apps/transfer/keeper/forwarding.go +++ b/modules/apps/transfer/keeper/forwarding.go @@ -11,6 +11,7 @@ import ( "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" host "github.com/cosmos/ibc-go/v8/modules/core/24-host" + ibcerrors "github.com/cosmos/ibc-go/v8/modules/core/errors" ) // ackForwardPacketError reverts the receive packet logic that occurs in the middle chain and writes the async ack for the prevPacket @@ -38,8 +39,6 @@ func (k Keeper) ackForwardPacketTimeout(ctx sdk.Context, prevPacket channeltypes return err } - // the timeout is converted into an error acknowledgement in order to propagate the failed packet forwarding - // back to the original sender forwardAck := channeltypes.NewErrorAcknowledgement(errors.New("forwarded packet timed out")) return k.acknowledgeForwardedPacket(ctx, prevPacket, forwardAck) } @@ -97,3 +96,57 @@ func (k Keeper) revertForwardedPacket(ctx sdk.Context, prevPacket channeltypes.P } return nil } + +// 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 + nextForwardingPath = nil + } else { + nextForwardingPath = &types.Forwarding{ + Hops: data.Forwarding.Hops[1:], + Memo: data.Forwarding.Memo, + } + } + + // sending from the forward escrow address to the original receiver address. + sender := types.GetForwardAddress(packet.DestinationPort, packet.DestinationChannel) + + msg := types.NewMsgTransfer( + data.Forwarding.Hops[0].PortId, + data.Forwarding.Hops[0].ChannelId, + receivedCoins, + sender.String(), + data.Receiver, + packet.TimeoutHeight, + packet.TimeoutTimestamp, + memo, + nextForwardingPath, + ) + + resp, err := k.Transfer(ctx, msg) + if err != nil { + return err + } + + k.SetForwardedPacket(ctx, data.Forwarding.Hops[0].PortId, data.Forwarding.Hops[0].ChannelId, resp.Sequence, packet) + return nil +} + +// getReceiverFromPacketData returns either the sender specified in the packet data or the forwarding address +// if there are still hops left to perform. +func getReceiverFromPacketData(data types.FungibleTokenPacketDataV2, portID, channelID string) (sdk.AccAddress, error) { + receiver, err := sdk.AccAddressFromBech32(data.Receiver) + if err != nil { + return nil, errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "failed to decode receiver address %s: %v", data.Receiver, err) + } + + if data.ShouldBeForwarded() { + receiver = types.GetForwardAddress(portID, channelID) + } + + return receiver, nil +} diff --git a/modules/apps/transfer/keeper/relay.go b/modules/apps/transfer/keeper/relay.go index 928dca8121a..8b58787170d 100644 --- a/modules/apps/transfer/keeper/relay.go +++ b/modules/apps/transfer/keeper/relay.go @@ -169,19 +169,9 @@ func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet, data t return false, types.ErrReceiveDisabled } - var ( - err error - receiver sdk.AccAddress // final receiver of tokens if there is no forwarding, otherwise, receiver in the next hop - finalReceiver sdk.AccAddress // final receiver of tokens if there is forwarding - ) - - receiver, err = sdk.AccAddressFromBech32(data.Receiver) + receiver, err := getReceiverFromPacketData(data, packet.DestinationPort, packet.DestinationChannel) if err != nil { - return false, errorsmod.Wrapf(ibcerrors.ErrInvalidAddress, "failed to decode receiver address %s: %v", data.Receiver, err) - } - if data.Forwarding != nil && len(data.Forwarding.Hops) > 0 { - finalReceiver = receiver // , _ = sdk.AccAddressFromBech32(data.Receiver) - receiver = types.GetForwardAddress(packet.DestinationPort, packet.DestinationChannel) + return false, err } var receivedCoins sdk.Coins @@ -272,39 +262,11 @@ func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet, data t receivedCoins = append(receivedCoins, voucher) } - // Adding forwarding logic - if data.Forwarding != nil && len(data.Forwarding.Hops) > 0 { - memo := "" - - var nextForwarding *types.Forwarding - if len(data.Forwarding.Hops) == 1 { - memo = data.Forwarding.Memo - nextForwarding = nil - } else { - nextForwarding = &types.Forwarding{ - Hops: data.Forwarding.Hops[1:], - Memo: data.Forwarding.Memo, - } - } - - msg := types.NewMsgTransfer( - data.Forwarding.Hops[0].PortId, - data.Forwarding.Hops[0].ChannelId, - receivedCoins, - receiver.String(), - finalReceiver.String(), - packet.TimeoutHeight, - packet.TimeoutTimestamp, - memo, - nextForwarding, - ) - - resp, err := k.Transfer(ctx, msg) - if err != nil { + if data.ShouldBeForwarded() { + // we are now sending from the forward escrow address to the final receiver address. + if err := k.forwardPacket(ctx, data, packet, receivedCoins); err != nil { return false, err } - - k.SetForwardedPacket(ctx, data.Forwarding.Hops[0].PortId, data.Forwarding.Hops[0].ChannelId, resp.Sequence, packet) return true, nil } diff --git a/modules/apps/transfer/types/packet.go b/modules/apps/transfer/types/packet.go index fa1a45fadec..b00ccc6e957 100644 --- a/modules/apps/transfer/types/packet.go +++ b/modules/apps/transfer/types/packet.go @@ -196,3 +196,8 @@ func (ftpd FungibleTokenPacketDataV2) GetCustomPacketData(key string) interface{ func (ftpd FungibleTokenPacketDataV2) GetPacketSender(sourcePortID string) string { return ftpd.Sender } + +// ShouldBeForwarded determines if the packet should be forwarded to the next hop. +func (ftpd FungibleTokenPacketDataV2) ShouldBeForwarded() bool { + return ftpd.Forwarding != nil && len(ftpd.Forwarding.Hops) > 0 +}