mukan-ibc/modules/apps/callbacks/transfer_test.go
Mukan Erkin Törük 88dd97a9f8
Some checks failed
CodeQL / Analyze (push) Waiting to run
golangci-lint / lint (push) Waiting to run
Tests / Code Coverage / build (amd64) (push) Waiting to run
Tests / Code Coverage / build (arm64) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[additional-args:-tags="test_e2e" name:e2e path:./e2e]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:08-wasm path:./modules/light-clients/08-wasm]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:ibc-go path:.]) (push) Waiting to run
Docker Build & Push Simapp (main) / docker-build (push) Has been cancelled
refactor: replace all github.com upstream refs with git.cw.tr/mukan-network
2026-05-11 03:36:22 +03:00

273 lines
9.3 KiB
Go

package ibccallbacks_test
import (
"fmt"
sdkmath "cosmossdk.io/math"
sdk "git.cw.tr/mukan-network/mukan-sdk/types"
"git.cw.tr/mukan-network/mukan-ibc/modules/apps/callbacks/testing/simapp"
"git.cw.tr/mukan-network/mukan-ibc/modules/apps/callbacks/types"
transfertypes "git.cw.tr/mukan-network/mukan-ibc/modules/apps/transfer/types"
clienttypes "git.cw.tr/mukan-network/mukan-ibc/modules/core/02-client/types"
channeltypes "git.cw.tr/mukan-network/mukan-ibc/modules/core/04-channel/types"
ibctesting "git.cw.tr/mukan-network/mukan-ibc/testing"
)
func (s *CallbacksTestSuite) TestTransferCallbacks() {
testCases := []struct {
name string
transferMemo string
expCallback types.CallbackType
expSuccess bool
}{
{
"success: transfer with no memo",
"",
"none",
true,
},
{
"success: dest callback",
fmt.Sprintf(`{"dest_callback": {"address": "%s"}}`, simapp.SuccessContract),
types.CallbackTypeReceivePacket,
true,
},
{
"success: dest callback with other json fields",
fmt.Sprintf(`{"dest_callback": {"address": "%s"}, "something_else": {}}`, simapp.SuccessContract),
types.CallbackTypeReceivePacket,
true,
},
{
"success: dest callback with malformed json",
fmt.Sprintf(`{"dest_callback": {"address": "%s"}, malformed}`, simapp.SuccessContract),
"none",
true,
},
{
"failure: dest callback with missing address",
`{"dest_callback": {"address": ""}}`,
"none",
false,
},
{
"success: source callback",
fmt.Sprintf(`{"src_callback": {"address": "%s"}}`, simapp.SuccessContract),
types.CallbackTypeAcknowledgementPacket,
true,
},
{
"success: source callback with other json fields",
fmt.Sprintf(`{"src_callback": {"address": "%s"}, "something_else": {}}`, simapp.SuccessContract),
types.CallbackTypeAcknowledgementPacket,
true,
},
{
"success: source callback with malformed json",
fmt.Sprintf(`{"src_callback": {"address": "%s"}, malformed}`, simapp.SuccessContract),
"none",
true,
},
{
"success: source callback with missing address",
`{"src_callback": {"address": ""}}`,
"none",
true,
},
{
"failure: dest callback with low gas (panic)",
fmt.Sprintf(`{"dest_callback": {"address": "%s"}}`, simapp.OogPanicContract),
types.CallbackTypeReceivePacket,
false,
},
{
"failure: source callback with low gas (panic)",
fmt.Sprintf(`{"src_callback": {"address": "%s"}}`, simapp.OogPanicContract),
types.CallbackTypeSendPacket,
false,
},
{
"failure: dest callback with low gas (error)",
fmt.Sprintf(`{"dest_callback": {"address": "%s"}}`, simapp.OogErrorContract),
types.CallbackTypeReceivePacket,
false,
},
{
"failure: source callback with low gas (error)",
fmt.Sprintf(`{"src_callback": {"address": "%s"}}`, simapp.OogErrorContract),
types.CallbackTypeSendPacket,
false,
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
s.SetupTransferTest()
s.ExecuteTransfer(tc.transferMemo, tc.expSuccess)
s.AssertHasExecutedExpectedCallback(tc.expCallback, tc.expSuccess)
})
}
}
func (s *CallbacksTestSuite) TestTransferTimeoutCallbacks() {
testCases := []struct {
name string
transferMemo string
expCallback types.CallbackType
expSuccess bool
}{
{
"success: transfer with no memo",
"",
"none",
true,
},
{
"success: dest callback",
fmt.Sprintf(`{"dest_callback": {"address": "%s"}}`, simapp.SuccessContract),
"none", // timeouts don't reach destination chain execution
true,
},
{
"success: source callback",
fmt.Sprintf(`{"src_callback": {"address": "%s"}}`, simapp.SuccessContract),
types.CallbackTypeTimeoutPacket,
true,
},
{
"success: dest callback with low gas (panic)",
fmt.Sprintf(`{"dest_callback": {"address": "%s"}}`, simapp.OogPanicContract),
"none", // timeouts don't reach destination chain execution
true,
},
{
"success: dest callback with low gas (error)",
fmt.Sprintf(`{"dest_callback": {"address": "%s"}}`, simapp.OogErrorContract),
"none", // timeouts don't reach destination chain execution
true,
},
{
"failure: source callback with low gas (panic)",
fmt.Sprintf(`{"src_callback": {"address": "%s"}}`, simapp.OogPanicContract),
types.CallbackTypeSendPacket,
false,
},
{
"failure: source callback with low gas (error)",
fmt.Sprintf(`{"src_callback": {"address": "%s"}}`, simapp.OogErrorContract),
types.CallbackTypeSendPacket,
false,
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
s.SetupTransferTest()
s.ExecuteTransferTimeout(tc.transferMemo)
s.AssertHasExecutedExpectedCallback(tc.expCallback, tc.expSuccess)
})
}
}
// ExecuteTransfer executes a transfer message on chainA for ibctesting.TestCoin (100 "stake").
// It checks that the transfer is successful and that the packet is relayed to chainB.
func (s *CallbacksTestSuite) ExecuteTransfer(memo string, recvSuccess bool) {
escrowAddress := transfertypes.GetEscrowAddress(s.path.EndpointA.ChannelConfig.PortID, s.path.EndpointA.ChannelID)
// record the balance of the escrow address before the transfer
escrowBalance := GetSimApp(s.chainA).BankKeeper.GetBalance(s.chainA.GetContext(), escrowAddress, sdk.DefaultBondDenom)
// record the balance of the receiving address before the transfer
denom := transfertypes.NewDenom(sdk.DefaultBondDenom, transfertypes.NewHop(s.path.EndpointB.ChannelConfig.PortID, s.path.EndpointB.ChannelID))
receiverBalance := GetSimApp(s.chainB).BankKeeper.GetBalance(s.chainB.GetContext(), s.chainB.SenderAccount.GetAddress(), denom.IBCDenom())
// record the balance of the sending address before the transfer
senderBalance := GetSimApp(s.chainA).BankKeeper.GetBalance(s.chainA.GetContext(), s.chainA.SenderAccount.GetAddress(), sdk.DefaultBondDenom)
amount := ibctesting.TestCoin
msg := transfertypes.NewMsgTransfer(
s.path.EndpointA.ChannelConfig.PortID,
s.path.EndpointA.ChannelID,
amount,
s.chainA.SenderAccount.GetAddress().String(),
s.chainB.SenderAccount.GetAddress().String(),
clienttypes.NewHeight(1, 100), 0, memo,
)
res, err := s.chainA.SendMsgs(msg)
if err != nil {
return // we return if send packet is rejected
}
// packet found, relay from A to B
err = s.path.EndpointB.UpdateClient()
s.Require().NoError(err)
packet, err := ibctesting.ParseV1PacketFromEvents(res.GetEvents())
s.Require().NoError(err)
res, err = s.path.EndpointB.RecvPacketWithResult(packet)
s.Require().NoError(err)
acknowledgement, err := ibctesting.ParseAckFromEvents(res.Events)
s.Require().NoError(err)
err = s.path.EndpointA.AcknowledgePacket(packet, acknowledgement)
s.Require().NoError(err)
var ack channeltypes.Acknowledgement
err = transfertypes.ModuleCdc.UnmarshalJSON(acknowledgement, &ack)
s.Require().NoError(err)
s.Require().Equal(recvSuccess, ack.Success(), "acknowledgement success is not as expected")
if recvSuccess {
// check that the escrow address balance increased by 100
s.Require().Equal(escrowBalance.Add(amount), GetSimApp(s.chainA).BankKeeper.GetBalance(s.chainA.GetContext(), escrowAddress, sdk.DefaultBondDenom))
// check that the receiving address balance increased by 100
s.Require().Equal(receiverBalance.AddAmount(sdkmath.NewInt(100)), GetSimApp(s.chainB).BankKeeper.GetBalance(s.chainB.GetContext(), s.chainB.SenderAccount.GetAddress(), denom.IBCDenom()))
// check that the sending address balance decreased by 100
s.Require().Equal(senderBalance.Sub(amount), GetSimApp(s.chainA).BankKeeper.GetBalance(s.chainA.GetContext(), s.chainA.SenderAccount.GetAddress(), sdk.DefaultBondDenom))
} else {
// check that the escrow address balance is the same as before the transfer
s.Require().Equal(escrowBalance, GetSimApp(s.chainA).BankKeeper.GetBalance(s.chainA.GetContext(), escrowAddress, sdk.DefaultBondDenom))
// check that the receiving address balance is the same as before the transfer
s.Require().Equal(receiverBalance, GetSimApp(s.chainB).BankKeeper.GetBalance(s.chainB.GetContext(), s.chainB.SenderAccount.GetAddress(), denom.IBCDenom()))
// check that the sending address balance is the same as before the transfer
s.Require().Equal(senderBalance, GetSimApp(s.chainA).BankKeeper.GetBalance(s.chainA.GetContext(), s.chainA.SenderAccount.GetAddress(), sdk.DefaultBondDenom))
}
}
// ExecuteTransferTimeout executes a transfer message on chainA for 100 denom.
// This message is not relayed to chainB, and it times out on chainA.
func (s *CallbacksTestSuite) ExecuteTransferTimeout(memo string) {
timeoutHeight := clienttypes.GetSelfHeight(s.chainB.GetContext())
timeoutTimestamp := uint64(s.chainB.GetContext().BlockTime().UnixNano())
amount := ibctesting.TestCoin
msg := transfertypes.NewMsgTransfer(
s.path.EndpointA.ChannelConfig.PortID,
s.path.EndpointA.ChannelID,
amount,
s.chainA.SenderAccount.GetAddress().String(),
s.chainB.SenderAccount.GetAddress().String(),
timeoutHeight, timeoutTimestamp, memo,
)
res, err := s.chainA.SendMsgs(msg)
if err != nil {
return // we return if send packet is rejected
}
packet, err := ibctesting.ParseV1PacketFromEvents(res.GetEvents())
s.Require().NoError(err) // packet committed
s.Require().NotNil(packet)
// need to update chainA's client representing chainB to prove missing ack
err = s.path.EndpointA.UpdateClient()
s.Require().NoError(err)
err = s.path.EndpointA.TimeoutPacket(packet)
s.Require().NoError(err) // timeout committed
}