Skip to content

Commit

Permalink
Merge pull request #77 from strangelove-ventures/joel/ibc-composabili…
Browse files Browse the repository at this point in the history
…ty-mw-testing

chore(wormchain): ibc composability mw testing
  • Loading branch information
joelsmith-2019 authored Sep 18, 2024
2 parents eba107e + cc26a15 commit 49beda5
Show file tree
Hide file tree
Showing 5 changed files with 455 additions and 0 deletions.
45 changes: 45 additions & 0 deletions wormchain/x/ibc-composability-mw/keeper/genesis_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package keeper_test

import (
"testing"

"github.com/stretchr/testify/require"
keepertest "github.com/wormhole-foundation/wormchain/testutil/keeper"
"github.com/wormhole-foundation/wormchain/x/ibc-composability-mw/types"
)

// TestGenesis ensures genesis state can be initialiazed and exported correctly.
func TestGenesis(t *testing.T) {
for _, tc := range []struct {
dataInFlight map[string][]byte
}{
{
dataInFlight: map[string][]byte{},
},
{
dataInFlight: map[string][]byte{
"key1": []byte("value1"),
},
},
{
dataInFlight: map[string][]byte{
"key1": []byte("value1"),
"key2": []byte("value2"),
"key3": []byte("value3"),
},
},
} {
genesisState := types.GenesisState{
TransposedDataInFlight: tc.dataInFlight,
}

app, ctx := keepertest.SetupWormchainAndContext(t)
keeper := app.IbcComposabilityMwKeeper

keeper.InitGenesis(ctx, genesisState)

outputState := keeper.ExportGenesis(ctx)

require.Equal(t, genesisState, *outputState)
}
}
114 changes: 114 additions & 0 deletions wormchain/x/ibc-composability-mw/keeper/keeper_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package keeper_test

import (
_ "embed"
"encoding/json"
"testing"

transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types"
channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types"
"github.com/stretchr/testify/require"
keepertest "github.com/wormhole-foundation/wormchain/testutil/keeper"
"github.com/wormhole-foundation/wormchain/x/ibc-composability-mw/types"
wormholetypes "github.com/wormhole-foundation/wormchain/x/wormhole/types"
)

// TestPackets ensure that packets are handled correctly by the ibc composability middleware.
// This test will only be able to process IBC hooks messages and not PFM because the test
// requires a full chain setup with interchaintest.
func TestPackets(t *testing.T) {
// setup app & get keepers & ctx
app, ctx := keepertest.SetupWormchainAndContext(t)
whKeeper := app.WormholeKeeper
keeper := app.IbcComposabilityMwKeeper

// set ibc composability contract
whKeeper.StoreIbcComposabilityMwContract(ctx, wormholetypes.IbcComposabilityMwContract{
ContractAddress: "wormhole1du4amsmvx8yqr8whw7qc5m3c0zpwknmzelwqy6",
})

// define a packet with no ibc token bridge payload
packetDataNoPayload, err := json.Marshal(transfertypes.FungibleTokenPacketData{
Denom: "uworm",
Amount: "100",
Sender: "sender",
Receiver: "receiver",
Memo: "",
})
require.NoError(t, err)

// define gateway payload for packet
gatewayTBPayload, err := json.Marshal(types.GatewayIbcTokenBridgePayload{
GatewayIbcTokenBridgePayloadObj: types.GatewayIbcTokenBridgePayloadObj{
Transfer: types.GatewayTransfer{
Chain: 1,
Recipient: []byte("recipient"),
Fee: "0uworm",
Nonce: 1,
},
},
})
require.NoError(t, err)

// define a packet with a valid ibc token bridge payload
packetDataWithPayload, err := json.Marshal(transfertypes.FungibleTokenPacketData{
Denom: "uworm",
Amount: "100",
Sender: "sender",
Receiver: "receiver",
Memo: string(gatewayTBPayload),
})
require.NoError(t, err)

for _, tc := range []struct {
testName string
packet channeltypes.Packet
shouldErr bool
}{
{
testName: "empty packet - expect error",
shouldErr: true,
},
{
testName: "packet with no data - expect error",
packet: channeltypes.Packet{
Data: []byte("wrong data format"),
},
shouldErr: true,
},
{
testName: "packet with no memo in data - expect error",
packet: channeltypes.Packet{
Data: packetDataNoPayload,
},
shouldErr: true,
},
{
testName: "packet with payload - expect success",
packet: channeltypes.Packet{
Sequence: 1,
SourcePort: "transfer",
SourceChannel: "channel-0",
DestinationPort: "transfer",
DestinationChannel: "channel-0",
Data: packetDataWithPayload,
},
shouldErr: false,
},
} {
packet, ack := keeper.OnRecvPacket(ctx, tc.packet)

t.Run(tc.testName, func(t *testing.T) {
if tc.shouldErr {
require.NotNil(t, ack)
} else {
require.NotNil(t, packet)
require.Nil(t, ack)

// Should return nil because the packet is not transposed (it is an ibc hooks packet)
res := keeper.GetAndClearTransposedData(ctx, tc.packet.DestinationChannel, tc.packet.DestinationPort, tc.packet.Sequence)
require.Nil(t, res)
}
})
}
}
89 changes: 89 additions & 0 deletions wormchain/x/ibc-composability-mw/types/gateway_payload_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package types_test

import (
"encoding/json"
"testing"

"github.com/stretchr/testify/require"
"github.com/wormhole-foundation/wormchain/x/ibc-composability-mw/types"
)

// TestGatewayPayloads tests the VerifyAndParseGatewayPayload function.
func TestGatewayPayloads(t *testing.T) {
for _, tc := range []struct {
testName string
memo string // use memo if present, otherwise marshal tbPayload with json
tbPayload types.GatewayIbcTokenBridgePayload
shouldErr bool
}{
{
testName: "memo present, payload abscent - should error",
memo: "abc123",
shouldErr: true,
},
{
testName: "memo abscent, invalid payload - should error",
tbPayload: types.GatewayIbcTokenBridgePayload{},
shouldErr: true,
},
{
testName: "valid transfer no payload - should pass",
tbPayload: types.GatewayIbcTokenBridgePayload{
GatewayIbcTokenBridgePayloadObj: types.GatewayIbcTokenBridgePayloadObj{
Transfer: types.GatewayTransfer{
Chain: 1,
Recipient: []byte("recipient"),
Fee: "0uworm",
Nonce: 1,
},
},
},
shouldErr: false,
},
{
testName: "valid transfer with payload - should pass",
tbPayload: types.GatewayIbcTokenBridgePayload{
GatewayIbcTokenBridgePayloadObj: types.GatewayIbcTokenBridgePayloadObj{
TransferWithPayload: types.GatewayTransferWithPayload{
Chain: 1,
Contract: []byte("contract"),
Payload: []byte("{\"payload\":\"data\"}"),
Nonce: 1,
},
},
},
shouldErr: false,
},
} {
t.Run(tc.testName, func(t *testing.T) {
memo := tc.memo

if memo == "" {
bz, err := json.Marshal(tc.tbPayload)
require.NoError(t, err)
memo = string(bz)
}

payload, err := types.VerifyAndParseGatewayPayload(memo)

if tc.shouldErr {
require.Error(t, err)
} else {
require.NoError(t, err)

// validate payload was parsed correctly
if payload.NoPayload {
require.Equal(t, tc.tbPayload.GatewayIbcTokenBridgePayloadObj.Transfer.Chain, payload.ChainId)
require.Equal(t, tc.tbPayload.GatewayIbcTokenBridgePayloadObj.Transfer.Recipient, payload.Recipient)
require.Equal(t, tc.tbPayload.GatewayIbcTokenBridgePayloadObj.Transfer.Fee, payload.Fee)
require.Equal(t, tc.tbPayload.GatewayIbcTokenBridgePayloadObj.Transfer.Nonce, payload.Nonce)
} else {
require.Equal(t, tc.tbPayload.GatewayIbcTokenBridgePayloadObj.TransferWithPayload.Chain, payload.ChainId)
require.Equal(t, tc.tbPayload.GatewayIbcTokenBridgePayloadObj.TransferWithPayload.Contract, payload.Recipient)
require.Equal(t, tc.tbPayload.GatewayIbcTokenBridgePayloadObj.TransferWithPayload.Payload, payload.Payload)
require.Equal(t, tc.tbPayload.GatewayIbcTokenBridgePayloadObj.TransferWithPayload.Nonce, payload.Nonce)
}
}
})
}
}
89 changes: 89 additions & 0 deletions wormchain/x/ibc-composability-mw/types/ibc_hooks_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package types_test

import (
"testing"

"github.com/stretchr/testify/require"
"github.com/wormhole-foundation/wormchain/x/ibc-composability-mw/types"
)

// TestFormatIbcHooksMemo tests the FormatIbcHooksMemo function.
func TestFormatIbcHooksMemo(t *testing.T) {
ibcTranslatorContract := "wormhole123abc"

for _, tc := range []struct {
testName string
payload types.ParsedPayload
shouldErr bool
}{
{
testName: "Normal w/o payload - should pass",
payload: types.ParsedPayload{
NoPayload: true,
ChainId: 1,
Recipient: []byte{'a', 'b', 'c'},
Fee: "0uworm",
Nonce: 1,
Payload: nil,
},
shouldErr: false,
},
{
testName: "Provide payload when unnecessary - should pass",
payload: types.ParsedPayload{
NoPayload: true,
ChainId: 1,
Recipient: []byte{'a', 'b', 'c'},
Fee: "0uworm",
Nonce: 1,
Payload: []byte("{\"payload\":\"data\"}"),
},
shouldErr: false,
},
{
testName: "Normal w/ payload - should pass",
payload: types.ParsedPayload{
NoPayload: false,
ChainId: 1,
Recipient: []byte{'a', 'b', 'c'},
Fee: "0uworm",
Nonce: 1,
Payload: []byte("{\"payload\":\"data\"}"),
},
shouldErr: false,
},
{
testName: "Nil payload - should pass",
payload: types.ParsedPayload{
NoPayload: true,
ChainId: 1,
Recipient: []byte{'a', 'b', 'c'},
Fee: "0uworm",
Nonce: 1,
Payload: nil,
},
shouldErr: false,
},
} {
t.Run(tc.testName, func(t *testing.T) {
res, err := types.FormatIbcHooksMemo(tc.payload, ibcTranslatorContract)

if tc.shouldErr {
require.Error(t, err)
} else {
require.NoError(t, err)
require.NotNil(t, res)

// validate payload was formatted correctly
if tc.payload.NoPayload {
require.NotContains(t, res, "gateway_convert_and_transfer_with_payload")
require.Contains(t, res, "recipient")
} else {
require.Contains(t, res, "gateway_convert_and_transfer_with_payload")
require.NotContains(t, res, "recipient")
require.Contains(t, res, "payload")
}
}
})
}
}
Loading

0 comments on commit 49beda5

Please sign in to comment.