Skip to content

Commit

Permalink
test: add unwind + forwarding to sender test (#6905) (#7136)
Browse files Browse the repository at this point in the history
* chore: added unit test

* chore: added unit test

* wip

* take test to finish line

* Update modules/apps/transfer/keeper/relay_forwarding_test.go

Co-authored-by: Nikolas De Giorgis <[email protected]>

* fix typo

---------

Co-authored-by: Carlos Rodriguez <[email protected]>
Co-authored-by: Nikolas De Giorgis <[email protected]>
(cherry picked from commit 3f34f29)

Co-authored-by: Vishal Potpelliwar <[email protected]>
Co-authored-by: Carlos Rodriguez <[email protected]>
  • Loading branch information
3 people authored Aug 14, 2024
1 parent 54b6e5f commit fc6233d
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 1 deletion.
131 changes: 131 additions & 0 deletions modules/apps/transfer/keeper/relay_forwarding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,137 @@ func (suite *ForwardingTestSuite) TestSuccessfulUnwind() {
suite.assertAmountOnChain(suite.chainA, balance, originalABalance.Amount.Add(amount), denomA.IBCDenom())
}

// TestForwardingBackAfterUnwind tests the scenario where tokens are unwound and then forwarded
// back to the sending chain.
func (suite *ForwardingTestSuite) TestForwardingBackAfterUnwind() {
/*
Given the following topology:
chain A (channel 0) -> (channel-0) chain B (channel-0)
stake transfer/channel-0/stake
We want to trigger:
1. A sends to B over channel-0.
2. B receives and transfers back to A over channel-0 with unwind set to true.
3. A receives the packet back.
4. A sends back to B over channel-0.
At this point we want to assert:
A: finalReceiver = amount,transfer/channel-0/denom
B: no tokens should be held in escrow.
*/
amount := sdkmath.NewInt(100)
pathAtoB, _ := suite.setupForwardingPaths()

accountA := suite.chainA.SenderAccount
accountB := suite.chainB.SenderAccount

denomA := types.NewDenom(sdk.DefaultBondDenom)
denomAB := types.NewDenom(sdk.DefaultBondDenom, types.NewHop(pathAtoB.EndpointB.ChannelConfig.PortID, pathAtoB.EndpointB.ChannelID))

// Send tokens from A to B
coin := ibctesting.TestCoin
transferMsg := types.NewMsgTransfer(
pathAtoB.EndpointA.ChannelConfig.PortID,
pathAtoB.EndpointA.ChannelID,
sdk.NewCoins(coin),
accountA.GetAddress().String(),
accountB.GetAddress().String(),
clienttypes.ZeroHeight(),
suite.chainA.GetTimeoutTimestamp(), "",
nil,
)

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

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

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

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

// Check that vouchers are received on chain B
suite.assertAmountOnChain(suite.chainB, balance, amount, denomAB.IBCDenom())

// Unwind tokens back from B to A
coinOnB := sdk.NewCoin(denomAB.IBCDenom(), amount)
forwarding := types.NewForwarding(true, types.NewHop(
pathAtoB.EndpointA.ChannelConfig.PortID,
pathAtoB.EndpointA.ChannelID,
))

transferMsg = types.NewMsgTransfer(
"",
"",
sdk.NewCoins(coinOnB),
accountB.GetAddress().String(),
accountB.GetAddress().String(),
clienttypes.ZeroHeight(),
suite.chainB.GetTimeoutTimestamp(), "",
forwarding,
)

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

suite.assertAmountOnChain(suite.chainB, balance, sdkmath.NewInt(0), denomAB.IBCDenom())

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

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

result, err = pathAtoB.EndpointA.RecvPacketWithResult(packetFromBtoA)
suite.Require().NoError(err)
suite.Require().NotNil(result)

// Check that Escrow A has 100
suite.assertAmountOnChain(suite.chainA, escrow, sdkmath.NewInt(100), denomA.IBCDenom())

// 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)

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 100
suite.assertAmountOnChain(suite.chainA, escrow, amount, denomA.IBCDenom())
// Check that vouchers are back on chain B
suite.assertAmountOnChain(suite.chainB, balance, amount, denomAB.IBCDenom())

successAck := channeltypes.NewResultAcknowledgement([]byte{byte(1)})
successAckBz := channeltypes.CommitAcknowledgement(successAck.Acknowledgement())
ackOnB := suite.chainB.GetAcknowledgement(packetFromAtoB)
suite.Require().Equal(successAckBz, ackOnB)

// Ack back to A
err = pathAtoB.EndpointA.UpdateClient()
suite.Require().NoError(err)

err = pathAtoB.EndpointA.AcknowledgePacket(packetFromAtoB, successAck.Acknowledgement())
suite.Require().NoError(err)

// Ack back to B
err = pathAtoB.EndpointB.UpdateClient()
suite.Require().NoError(err)

err = pathAtoB.EndpointB.AcknowledgePacket(packetFromBtoA, successAck.Acknowledgement())
suite.Require().NoError(err)
}

// TestAcknowledgementFailureWithMiddleChainAsNativeTokenSource tests a failure in the last hop where the
// middle chain is native source when receiving and sending the packet. In other words, the middle chain's native
// token has been sent to chain C, and the multi-hop transfer from C -> B -> A has chain B being the source of
Expand Down
5 changes: 5 additions & 0 deletions modules/apps/transfer/types/forwarding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ func TestForwarding_Validate(t *testing.T) {
),
types.ErrInvalidForwarding,
},
{
"unwind with hop forwarding back to itself",
types.NewForwarding(true, types.NewHop(types.PortID, types.PortID)),
nil,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion testing/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func ParseChannelIDFromEvents(events []abci.Event) (string, error) {
return "", fmt.Errorf("channel identifier event attribute not found")
}

// ParsePacketFromEvents parses events emitted from a MsgRecvPacket and returns
// ParsePacketFromEvents parses events emitted from a send packet and returns
// the first EventTypeSendPacket packet found.
// Returns an error if no packet is found.
func ParsePacketFromEvents(events []abci.Event) (channeltypes.Packet, error) {
Expand Down

0 comments on commit fc6233d

Please sign in to comment.