Skip to content

Commit

Permalink
feat(transfer): allow non-cosmos-sdk AccAddress in final receiver for…
Browse files Browse the repository at this point in the history
… forwarded packets (#6709)

* allow non-cosmos-sdk AccAddress for forwarded packets

* cr fixes
  • Loading branch information
gjermundgaraba authored and bznein committed Jun 26, 2024
1 parent 43635f4 commit eb424be
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 4 deletions.
9 changes: 5 additions & 4 deletions modules/apps/transfer/keeper/forwarding.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,14 +134,15 @@ func (k Keeper) revertForwardedPacket(ctx sdk.Context, prevPacket channeltypes.P
// 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) {
if data.ShouldBeForwarded() {
// since data.Receiver can potentially be a non-CosmosSDK AccAddress, we return early if the packet should be forwarded
return types.GetForwardAddress(portID, channelID), nil
}

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
}
79 changes: 79 additions & 0 deletions modules/apps/transfer/keeper/relay_forwarding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,85 @@ func (suite *KeeperTestSuite) TestSuccessfulForward() {
suite.Require().NoError(err)
}

// TestSuccessfulPathForwarding tests that a packet is successfully forwarded with a non-Cosmos account address.
// The test stops before verifying the final receive, because we don't have a non-cosmos chain to test with.
func (suite *KeeperTestSuite) TestSuccessfulForwardWithNonCosmosAccAddress() {
/*
Given the following topology:
chain A (channel 0) -> (channel-0) chain B (channel-1) -> (channel-0) chain C
stake transfer/channel-0/stake transfer/channel-0/transfer/channel-0/stake
We want to trigger:
1. A sends B over channel-0.
2. Receive on B.
2.1 B sends C over channel-1
3. Receive on C.
At this point we want to assert:
A: packet gets relayed properly with final receiver intact
B: packet arrives and is forwarded with final receiver intact
C: no assertions as we don't have a non-cosmos chain to test with, so this would fail here
*/

amount := sdkmath.NewInt(100)

pathAtoB, pathBtoC := suite.setupForwardingPaths()

sender := suite.chainA.SenderAccounts[0].SenderAccount
nonCosmosReceiver := "0x42069163Ac5919fD49e6A67e6c211E0C86397fa2"
forwarding := types.NewForwarding(false, types.Hop{
PortId: pathBtoC.EndpointA.ChannelConfig.PortID,
ChannelId: pathBtoC.EndpointA.ChannelID,
})

transferMsg := types.NewMsgTransfer(
pathAtoB.EndpointA.ChannelConfig.PortID,
pathAtoB.EndpointA.ChannelID,
sdk.NewCoins(ibctesting.TestCoin),
sender.GetAddress().String(),
nonCosmosReceiver,
clienttypes.ZeroHeight(),
suite.chainA.GetTimeoutTimestamp(), "",
forwarding,
)

result, err := suite.chainA.SendMsgs(transferMsg)
suite.Require().NoError(err) // message committed

// parse the packet from result events and recv packet on chainB
packetFromAtoB, err := ibctesting.ParsePacketFromEvents(result.Events)
suite.Require().NoError(err)
suite.Require().NotNil(packetFromAtoB)

// Check that the token sent from A has final receiver intact
var tokenPacketOnA types.FungibleTokenPacketDataV2
err = suite.chainA.Codec.UnmarshalJSON(packetFromAtoB.Data, &tokenPacketOnA)
suite.Require().NoError(err)
suite.Require().Equal(nonCosmosReceiver, tokenPacketOnA.Receiver)

err = pathAtoB.EndpointB.UpdateClient()
suite.Require().NoError(err)

result, err = pathAtoB.EndpointB.RecvPacketWithResult(packetFromAtoB)
suite.Require().NoError(err)
suite.Require().NotNil(result)

// Check that Escrow A has amount
suite.assertAmountOnChain(suite.chainA, escrow, amount, sdk.DefaultBondDenom)

packetFromBtoC, err := ibctesting.ParsePacketFromEvents(result.Events)
suite.Require().NoError(err)
suite.Require().NotNil(packetFromBtoC)

// Check that the token sent from B has final receiver intact
var tokenPacketOnB types.FungibleTokenPacketDataV2
err = suite.chainB.Codec.UnmarshalJSON(packetFromBtoC.Data, &tokenPacketOnB)
suite.Require().NoError(err)
suite.Require().Equal(nonCosmosReceiver, tokenPacketOnB.Receiver)

// B should have stored the forwarded packet.
_, found := suite.chainB.GetSimApp().TransferKeeper.GetForwardedPacket(suite.chainB.GetContext(), packetFromBtoC.SourcePort, packetFromBtoC.SourceChannel, packetFromBtoC.Sequence)
suite.Require().True(found, "Chain B should have stored the forwarded packet")
}

// TestSuccessfulUnwind tests unwinding of tokens sent from A -> B -> C by
// forwarding the tokens back from C -> B -> A.
func (suite *KeeperTestSuite) TestSuccessfulUnwind() {
Expand Down

0 comments on commit eb424be

Please sign in to comment.