diff --git a/examples/cosmos/light_client_test.go b/examples/cosmos/light_client_test.go deleted file mode 100644 index 3cb261049..000000000 --- a/examples/cosmos/light_client_test.go +++ /dev/null @@ -1,99 +0,0 @@ -package cosmos_test - -import ( - "context" - "testing" - - "cosmossdk.io/math" - clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" - "github.com/strangelove-ventures/interchaintest/v7" - "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" - "github.com/strangelove-ventures/interchaintest/v7/ibc" - "github.com/strangelove-ventures/interchaintest/v7/testreporter" - "github.com/stretchr/testify/require" - "go.uber.org/zap/zaptest" -) - -func TestUpdateLightClients(t *testing.T) { - if testing.Short() { - t.Skip("skipping in short mode") - } - - t.Parallel() - - ctx := context.Background() - - // Chains - chains := interchaintest.CreateChainsWithChainSpecs(t, []*interchaintest.ChainSpec{ - {Name: "gaia", Version: gaiaVersion}, - {Name: "osmosis", Version: osmosisVersion}, - }) - - gaia, osmosis := chains[0], chains[1] - - // Relayer - client, network := interchaintest.DockerSetup(t) - r := interchaintest.NewBuiltinRelayerFactory(ibc.CosmosRly, zaptest.NewLogger(t)).Build( - t, client, network) - - ic := interchaintest.NewInterchain(). - AddChain(gaia). - AddChain(osmosis). - AddRelayer(r, "relayer"). - AddLink(interchaintest.InterchainLink{ - Chain1: gaia, - Chain2: osmosis, - Relayer: r, - Path: "client-test-path", - }) - - // Build interchain - rep := testreporter.NewNopReporter() - eRep := rep.RelayerExecReporter(t) - require.NoError(t, ic.Build(ctx, eRep, interchaintest.InterchainBuildOptions{ - TestName: t.Name(), - Client: client, - NetworkID: network, - })) - t.Cleanup(func() { - _ = ic.Close() - }) - - require.NoError(t, r.StartRelayer(ctx, eRep)) - t.Cleanup(func() { - _ = r.StopRelayer(ctx, eRep) - }) - - // Create and Fund User Wallets - fundAmount := int64(10_000_000) - users := interchaintest.GetAndFundTestUsers(t, ctx, "default", fundAmount, gaia, osmosis) - gaiaUser, osmoUser := users[0], users[1] - - // Get Channel ID - gaiaChannelInfo, err := r.GetChannels(ctx, eRep, gaia.Config().ChainID) - require.NoError(t, err) - chanID := gaiaChannelInfo[0].ChannelID - - height, err := osmosis.Height(ctx) - require.NoError(t, err) - - amountToSend := int64(553255) // Unique amount to make log searching easier. - dstAddress := osmoUser.(*cosmos.CosmosWallet).FormattedAddressWithPrefix(osmosis.Config().Bech32Prefix) - transfer := ibc.WalletAmount{ - Address: dstAddress, - Denom: gaia.Config().Denom, - Amount: math.NewInt(amountToSend), - } - tx, err := gaia.SendIBCTransfer(ctx, chanID, gaiaUser.KeyName(), transfer, ibc.TransferOptions{}) - require.NoError(t, err) - require.NoError(t, tx.Validate()) - - chain := osmosis.(*cosmos.CosmosChain) - reg := chain.Config().EncodingConfig.InterfaceRegistry - msg, err := cosmos.PollForMessage[*clienttypes.MsgUpdateClient](ctx, chain, reg, height, height+10, nil) - require.NoError(t, err) - - require.Equal(t, "07-tendermint-0", msg.ClientId) - require.NotEmpty(t, msg.Signer) - // TODO: Assert header information -} diff --git a/examples/ibc/interchain_accounts_test.go b/examples/ibc/interchain_accounts_test.go deleted file mode 100644 index 0c6b335fb..000000000 --- a/examples/ibc/interchain_accounts_test.go +++ /dev/null @@ -1,319 +0,0 @@ -package ibc - -import ( - "encoding/json" - "strings" - "testing" - "time" - - "cosmossdk.io/math" - "github.com/cosmos/cosmos-sdk/crypto/keyring" - chantypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" - "github.com/strangelove-ventures/interchaintest/v7" - "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" - "github.com/strangelove-ventures/interchaintest/v7/ibc" - "github.com/strangelove-ventures/interchaintest/v7/testutil" - "github.com/stretchr/testify/require" -) - -// TestInterchainAccounts is a test case that performs simulations and assertions around some basic -// features and packet flows surrounding interchain accounts. See: https://github.com/cosmos/interchain-accounts-demo -func TestInterchainAccounts(t *testing.T) { - if testing.Short() { - t.Skip("skipping in short mode") - } - - t.Parallel() - - // Create 2 chains from the same chain spec - chains := interchaintest.CreateChainsWithChainSpecs(t, []*interchaintest.ChainSpec{ - { - Name: "icad", - ChainConfig: ibc.ChainConfig{ - Images: []ibc.DockerImage{{Repository: "ghcr.io/cosmos/ibc-go-icad", Version: "v0.5.0"}}, - }, - }, - { - Name: "icad", - ChainConfig: ibc.ChainConfig{ - Images: []ibc.DockerImage{{Repository: "ghcr.io/cosmos/ibc-go-icad", Version: "v0.5.0"}}, - }, - }, - }) - - chain1, chain2 := chains[0].(*cosmos.CosmosChain), chains[1].(*cosmos.CosmosChain) - - // Build the network; spin up the chains and configure the relayer - const pathName = "test-path" - relayerFlags := []string{"-p", "events", "-b", "100"} - enableBlockDB := false - skipRelayerPathCreation := true - - ctx, _, r, _, eRep, _, _ := interchaintest.BuildInitialChainWithRelayer( - t, - chains, - enableBlockDB, - ibc.CosmosRly, - relayerFlags, - []interchaintest.InterchainLink{ - { - Chain1: chain1, - Chain2: chain2, - Path: pathName, - }, - }, - skipRelayerPathCreation, - ) - - // Fund a user account on chain1 and chain2 - const userFunds = int64(10_000_000_000) - users := interchaintest.GetAndFundTestUsers(t, ctx, t.Name(), userFunds, chain1, chain2) - chain1User := users[0] - chain2User := users[1] - - // Generate a new IBC path - err := r.GeneratePath(ctx, eRep, chain1.Config().ChainID, chain2.Config().ChainID, pathName) - require.NoError(t, err) - - // Create new clients - err = r.CreateClients(ctx, eRep, pathName, ibc.CreateClientOptions{TrustingPeriod: "330h"}) - require.NoError(t, err) - - err = testutil.WaitForBlocks(ctx, 2, chain1, chain2) - require.NoError(t, err) - - // Create a new connection - err = r.CreateConnections(ctx, eRep, pathName) - require.NoError(t, err) - - err = testutil.WaitForBlocks(ctx, 2, chain1, chain2) - require.NoError(t, err) - - // Query for the newly created connection - connections, err := r.GetConnections(ctx, eRep, chain1.Config().ChainID) - require.NoError(t, err) - require.Equal(t, 1, len(connections)) - - // Start the relayer and set the cleanup function. - err = r.StartRelayer(ctx, eRep, pathName) - require.NoError(t, err) - - t.Cleanup( - func() { - err := r.StopRelayer(ctx, eRep) - if err != nil { - t.Logf("an error occurred while stopping the relayer: %s", err) - } - }, - ) - - // Register a new interchain account on chain2, on behalf of the user acc on chain1 - chain1Addr := chain1User.(*cosmos.CosmosWallet).FormattedAddressWithPrefix(chain1.Config().Bech32Prefix) - - registerICA := []string{ - chain1.Config().Bin, "tx", "intertx", "register", - "--from", chain1Addr, - "--connection-id", connections[0].ID, - "--chain-id", chain1.Config().ChainID, - "--home", chain1.HomeDir(), - "--node", chain1.GetRPCAddress(), - "--keyring-backend", keyring.BackendTest, - "-y", - } - _, _, err = chain1.Exec(ctx, registerICA, nil) - require.NoError(t, err) - - ir := cosmos.DefaultEncoding().InterfaceRegistry - - c2h, err := chain2.Height(ctx) - require.NoError(t, err) - - channelFound := func(found *chantypes.MsgChannelOpenConfirm) bool { - return found.PortId == "icahost" - } - - // Wait for channel open confirm - _, err = cosmos.PollForMessage(ctx, chain2, ir, - c2h, c2h+30, channelFound) - require.NoError(t, err) - - // Query for the newly registered interchain account - queryICA := []string{ - chain1.Config().Bin, "query", "intertx", "interchainaccounts", connections[0].ID, chain1Addr, - "--chain-id", chain1.Config().ChainID, - "--home", chain1.HomeDir(), - "--node", chain1.GetRPCAddress(), - } - stdout, _, err := chain1.Exec(ctx, queryICA, nil) - require.NoError(t, err) - - icaAddr := parseInterchainAccountField(stdout) - require.NotEmpty(t, icaAddr) - - // Get initial account balances - chain2Addr := chain2User.(*cosmos.CosmosWallet).FormattedAddressWithPrefix(chain2.Config().Bech32Prefix) - - chain2OrigBal, err := chain2.GetBalance(ctx, chain2Addr, chain2.Config().Denom) - require.NoError(t, err) - - icaOrigBal, err := chain2.GetBalance(ctx, icaAddr, chain2.Config().Denom) - require.NoError(t, err) - - // Send funds to ICA from user account on chain2 - transferAmount := math.NewInt(1000) - transfer := ibc.WalletAmount{ - Address: icaAddr, - Denom: chain2.Config().Denom, - Amount: transferAmount, - } - err = chain2.SendFunds(ctx, chain2User.KeyName(), transfer) - require.NoError(t, err) - - chain2Bal, err := chain2.GetBalance(ctx, chain2Addr, chain2.Config().Denom) - require.NoError(t, err) - require.True(t, chain2Bal.Equal(chain2OrigBal.Sub(transferAmount))) - - icaBal, err := chain2.GetBalance(ctx, icaAddr, chain2.Config().Denom) - require.NoError(t, err) - require.True(t, icaBal.Equal(icaOrigBal.Add(transferAmount))) - - // Build bank transfer msg - rawMsg, err := json.Marshal(map[string]any{ - "@type": "/cosmos.bank.v1beta1.MsgSend", - "from_address": icaAddr, - "to_address": chain2Addr, - "amount": []map[string]any{ - { - "denom": chain2.Config().Denom, - "amount": transferAmount.String(), - }, - }, - }) - require.NoError(t, err) - - // Send bank transfer msg to ICA on chain2 from the user account on chain1 - sendICATransfer := []string{ - chain1.Config().Bin, "tx", "intertx", "submit", string(rawMsg), - "--connection-id", connections[0].ID, - "--from", chain1Addr, - "--chain-id", chain1.Config().ChainID, - "--home", chain1.HomeDir(), - "--node", chain1.GetRPCAddress(), - "--keyring-backend", keyring.BackendTest, - "-y", - } - _, _, err = chain1.Exec(ctx, sendICATransfer, nil) - require.NoError(t, err) - - // Wait for tx to be relayed - c1h, err := chain1.Height(ctx) - require.NoError(t, err) - - ackFound := func(found *chantypes.MsgAcknowledgement) bool { - return found.Packet.Sequence == 1 && - found.Packet.SourcePort == "icacontroller-"+chain1Addr && - found.Packet.DestinationPort == "icahost" - } - - // Wait for ack - _, err = cosmos.PollForMessage(ctx, chain1, ir, c1h, c1h+10, ackFound) - require.NoError(t, err) - - // Assert that the funds have been received by the user account on chain2 - chain2Bal, err = chain2.GetBalance(ctx, chain2Addr, chain2.Config().Denom) - require.NoError(t, err) - require.True(t, chain2Bal.Equal(chain2OrigBal)) - - // Assert that the funds have been removed from the ICA on chain2 - icaBal, err = chain2.GetBalance(ctx, icaAddr, chain2.Config().Denom) - require.NoError(t, err) - require.True(t, icaBal.Equal(icaOrigBal)) - - // Stop the relayer and wait for the process to terminate - err = r.StopRelayer(ctx, eRep) - require.NoError(t, err) - - // Send another bank transfer msg to ICA on chain2 from the user account on chain1. - // This message should timeout and the channel will be closed when we re-start the relayer. - _, _, err = chain1.Exec(ctx, sendICATransfer, nil) - require.NoError(t, err) - - // Wait for approximately one minute to allow packet timeout threshold to be hit - time.Sleep(70 * time.Second) - - // Restart the relayer and wait for NextSeqRecv proof to be delivered and packet timed out - err = r.StartRelayer(ctx, eRep, pathName) - require.NoError(t, err) - - c2h, err = chain2.Height(ctx) - require.NoError(t, err) - - chanCloseFound := func(found *chantypes.MsgChannelCloseConfirm) bool { - return found.PortId == "icahost" - } - - // Wait for channel close confirm - _, err = cosmos.PollForMessage(ctx, chain2, ir, c2h, c2h+30, chanCloseFound) - require.NoError(t, err) - - // Assert that the packet timed out and that the acc balances are correct - chain2Bal, err = chain2.GetBalance(ctx, chain2Addr, chain2.Config().Denom) - require.NoError(t, err) - require.True(t, chain2Bal.Equal(chain2OrigBal)) - - icaBal, err = chain2.GetBalance(ctx, icaAddr, chain2.Config().Denom) - require.NoError(t, err) - require.True(t, icaBal.Equal(icaOrigBal)) - - // Assert that the channel ends are both closed - chain1Chans, err := r.GetChannels(ctx, eRep, chain1.Config().ChainID) - require.NoError(t, err) - require.Equal(t, 1, len(chain1Chans)) - require.Subset(t, []string{"STATE_CLOSED", "Closed"}, []string{chain1Chans[0].State}) - - chain2Chans, err := r.GetChannels(ctx, eRep, chain2.Config().ChainID) - require.NoError(t, err) - require.Equal(t, 1, len(chain2Chans)) - require.Subset(t, []string{"STATE_CLOSED", "Closed"}, []string{chain2Chans[0].State}) - - // Attempt to open another channel for the same ICA - _, _, err = chain1.Exec(ctx, registerICA, nil) - require.NoError(t, err) - - c2h, err = chain2.Height(ctx) - require.NoError(t, err) - - // Wait for channel open confirm - _, err = cosmos.PollForMessage(ctx, chain2, ir, - c2h, c2h+30, channelFound) - require.NoError(t, err) - - // Assert that a new channel has been opened and the same ICA is in use - stdout, _, err = chain1.Exec(ctx, queryICA, nil) - require.NoError(t, err) - - newICA := parseInterchainAccountField(stdout) - require.NotEmpty(t, newICA) - require.Equal(t, icaAddr, newICA) - - chain1Chans, err = r.GetChannels(ctx, eRep, chain1.Config().ChainID) - require.NoError(t, err) - require.Equal(t, 2, len(chain1Chans)) - require.Subset(t, []string{"STATE_OPEN", "Open"}, []string{chain1Chans[1].State}) - - chain2Chans, err = r.GetChannels(ctx, eRep, chain2.Config().ChainID) - require.NoError(t, err) - require.Equal(t, 2, len(chain2Chans)) - require.Subset(t, []string{"STATE_OPEN", "Open"}, []string{chain2Chans[1].State}) -} - -// parseInterchainAccountField takes a slice of bytes which should be returned when querying for an ICA via -// the 'intertx interchainaccounts' cmd and splices out the actual address portion. -func parseInterchainAccountField(stdout []byte) string { - // After querying an ICA the stdout should look like the following, - // interchain_account_address: cosmos1p76n3mnanllea4d3av0v0e42tjj03cae06xq8fwn9at587rqp23qvxsv0j - // So we split the string at the : and then grab the address and return. - parts := strings.SplitN(string(stdout), ":", 2) - icaAddr := strings.TrimSpace(parts[1]) - return icaAddr -} diff --git a/examples/ibc/interchain_queries_test.go b/examples/ibc/interchain_queries_test.go deleted file mode 100644 index d1f159584..000000000 --- a/examples/ibc/interchain_queries_test.go +++ /dev/null @@ -1,236 +0,0 @@ -package ibc_test - -import ( - "context" - "encoding/json" - "fmt" - "strconv" - "testing" - - "github.com/cosmos/cosmos-sdk/crypto/keyring" - "github.com/icza/dyno" - interchaintest "github.com/strangelove-ventures/interchaintest/v7" - "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" - "github.com/strangelove-ventures/interchaintest/v7/ibc" - "github.com/strangelove-ventures/interchaintest/v7/internal/dockerutil" - "github.com/strangelove-ventures/interchaintest/v7/relayer" - "github.com/strangelove-ventures/interchaintest/v7/testreporter" - "github.com/strangelove-ventures/interchaintest/v7/testutil" - "github.com/stretchr/testify/require" - "go.uber.org/zap/zaptest" -) - -// TestInterchainQueries is a test case that performs basic simulations and assertions around the packet implementation -// of interchain queries. See: https://github.com/quasar-finance/interchain-query-demo -func TestInterchainQueries(t *testing.T) { - if testing.Short() { - t.Skip("skipping in short mode") - } - - t.Parallel() - - client, network := interchaintest.DockerSetup(t) - - rep := testreporter.NewNopReporter() - eRep := rep.RelayerExecReporter(t) - - ctx := context.Background() - - dockerImage := ibc.DockerImage{ - Repository: "ghcr.io/strangelove-ventures/heighliner/icqd", - Version: "latest", - UidGid: dockerutil.GetHeighlinerUserString(), - } - - // Get both chains - cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ - { - ChainName: "sender", - ChainConfig: ibc.ChainConfig{ - Type: "cosmos", - Name: "sender", - ChainID: "sender", - Images: []ibc.DockerImage{dockerImage}, - Bin: "icq", - Bech32Prefix: "cosmos", - Denom: "atom", - GasPrices: "0.00atom", - TrustingPeriod: "300h", - GasAdjustment: 1.1, - }}, - { - ChainName: "receiver", - ChainConfig: ibc.ChainConfig{ - Type: "cosmos", - Name: "receiver", - ChainID: "receiver", - Images: []ibc.DockerImage{dockerImage}, - Bin: "icq", - Bech32Prefix: "cosmos", - Denom: "atom", - GasPrices: "0.00atom", - TrustingPeriod: "300h", - GasAdjustment: 1.1, - ModifyGenesis: modifyGenesisAllowICQQueries([]string{"/cosmos.bank.v1beta1.Query/AllBalances"}), // Add the whitelisted queries to the host chain - }}, - }) - - chains, err := cf.Chains(t.Name()) - require.NoError(t, err) - - chain1, chain2 := chains[0], chains[1] - - // Get a relayer instance - r := interchaintest.NewBuiltinRelayerFactory( - ibc.CosmosRly, - zaptest.NewLogger(t), - relayer.StartupFlags("-b", "100"), - ).Build(t, client, network) - - // Build the network; spin up the chains and configure the relayer - const pathName = "test1-test2" - const relayerName = "relayer" - - ic := interchaintest.NewInterchain(). - AddChain(chain1). - AddChain(chain2). - AddRelayer(r, relayerName). - AddLink(interchaintest.InterchainLink{ - Chain1: chain1, - Chain2: chain2, - Relayer: r, - Path: pathName, - CreateChannelOpts: ibc.CreateChannelOptions{ - SourcePortName: "interquery", - DestPortName: "icqhost", - Order: ibc.Unordered, - Version: "icq-1", - }, - }) - - require.NoError(t, ic.Build(ctx, eRep, interchaintest.InterchainBuildOptions{ - TestName: t.Name(), - Client: client, - NetworkID: network, - - SkipPathCreation: false, - })) - t.Cleanup(func() { - _ = ic.Close() - }) - - // Fund user accounts, so we can query balances and make assertions. - const userFunds = int64(10_000_000_000) - users := interchaintest.GetAndFundTestUsers(t, ctx, t.Name(), userFunds, chain1, chain2) - chain1User := users[0] - chain2User := users[1] - - // Wait a few blocks for user accounts to be created on chain. - err = testutil.WaitForBlocks(ctx, 5, chain1, chain2) - require.NoError(t, err) - - // Query for the recently created channel-id. - channels, err := r.GetChannels(ctx, eRep, chain1.Config().ChainID) - require.NoError(t, err) - - // Start the relayer and set the cleanup function. - err = r.StartRelayer(ctx, eRep, pathName) - require.NoError(t, err) - - t.Cleanup( - func() { - err := r.StopRelayer(ctx, eRep) - if err != nil { - t.Logf("an error occured while stopping the relayer: %s", err) - } - }, - ) - - // Wait a few blocks for the relayer to start. - err = testutil.WaitForBlocks(ctx, 5, chain1, chain2) - require.NoError(t, err) - - // Query for the balances of an account on the counterparty chain using interchain queries. - chanID := channels[0].Counterparty.ChannelID - require.NotEmpty(t, chanID) - - chain1Addr := chain1User.(*cosmos.CosmosWallet).FormattedAddressWithPrefix(chain1.Config().Bech32Prefix) - require.NotEmpty(t, chain1Addr) - - chain2Addr := chain2User.(*cosmos.CosmosWallet).FormattedAddressWithPrefix(chain2.Config().Bech32Prefix) - require.NotEmpty(t, chain2Addr) - - cmd := []string{"icq", "tx", "interquery", "send-query-all-balances", chanID, chain2Addr, - "--node", chain1.GetRPCAddress(), - "--home", chain1.HomeDir(), - "--chain-id", chain1.Config().ChainID, - "--from", chain1Addr, - "--keyring-dir", chain1.HomeDir(), - "--keyring-backend", keyring.BackendTest, - "-y", - } - _, _, err = chain1.Exec(ctx, cmd, nil) - require.NoError(t, err) - - // Wait a few blocks for query to be sent to counterparty. - err = testutil.WaitForBlocks(ctx, 10, chain1) - require.NoError(t, err) - - // Check the results from the interchain query above. - cmd = []string{"icq", "query", "interquery", "query-state", strconv.Itoa(1), - "--node", chain1.GetRPCAddress(), - "--home", chain1.HomeDir(), - "--chain-id", chain1.Config().ChainID, - "--output", "json", - } - stdout, _, err := chain1.Exec(ctx, cmd, nil) - require.NoError(t, err) - - results := &icqResults{} - err = json.Unmarshal(stdout, results) - require.NoError(t, err) - require.NotEmpty(t, results.Request) - require.NotEmpty(t, results.Response) -} - -type icqResults struct { - Request struct { - Type string `json:"@type"` - Address string `json:"address"` - Pagination struct { - Key interface{} `json:"key"` - Offset string `json:"offset"` - Limit string `json:"limit"` - CountTotal bool `json:"count_total"` - Reverse bool `json:"reverse"` - } `json:"pagination"` - } `json:"request"` - Response struct { - Type string `json:"@type"` - Balances []struct { - Amount string `json:"amount"` - Denom string `json:"denom"` - } `json:"balances"` - Pagination struct { - NextKey interface{} `json:"next_key"` - Total string `json:"total"` - } `json:"pagination"` - } `json:"response"` -} - -func modifyGenesisAllowICQQueries(allowQueries []string) func(ibc.ChainConfig, []byte) ([]byte, error) { - return func(chainConfig ibc.ChainConfig, genbz []byte) ([]byte, error) { - g := make(map[string]interface{}) - if err := json.Unmarshal(genbz, &g); err != nil { - return nil, fmt.Errorf("failed to unmarshal genesis file: %w", err) - } - if err := dyno.Set(g, allowQueries, "app_state", "interchainquery", "params", "allow_queries"); err != nil { - return nil, fmt.Errorf("failed to set allowed interchain queries in genesis json: %w", err) - } - out, err := json.Marshal(g) - if err != nil { - return nil, fmt.Errorf("failed to marshal genesis bytes to json: %w", err) - } - return out, nil - } -} diff --git a/examples/ibc/packet_forward_test.go b/examples/ibc/packet_forward_test.go deleted file mode 100644 index 396741328..000000000 --- a/examples/ibc/packet_forward_test.go +++ /dev/null @@ -1,661 +0,0 @@ -package ibc_test - -import ( - "context" - "encoding/json" - "testing" - "time" - - "cosmossdk.io/math" - transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" - "github.com/strangelove-ventures/interchaintest/v7" - "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" - "github.com/strangelove-ventures/interchaintest/v7/ibc" - "github.com/strangelove-ventures/interchaintest/v7/testreporter" - "github.com/strangelove-ventures/interchaintest/v7/testutil" - "github.com/stretchr/testify/require" - "go.uber.org/zap/zaptest" -) - -type PacketMetadata struct { - Forward *ForwardMetadata `json:"forward"` -} - -type ForwardMetadata struct { - Receiver string `json:"receiver"` - Port string `json:"port"` - Channel string `json:"channel"` - Timeout time.Duration `json:"timeout"` - Retries *uint8 `json:"retries,omitempty"` - Next *string `json:"next,omitempty"` - RefundSequence *uint64 `json:"refund_sequence,omitempty"` -} - -func TestPacketForwardMiddleware(t *testing.T) { - if testing.Short() { - t.Skip("skipping in short mode") - } - - client, network := interchaintest.DockerSetup(t) - - rep := testreporter.NewNopReporter() - eRep := rep.RelayerExecReporter(t) - - ctx := context.Background() - - chainID_A, chainID_B, chainID_C, chainID_D := "chain-a", "chain-b", "chain-c", "chain-d" - - cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ - {Name: "gaia", Version: "v9.0.1", ChainConfig: ibc.ChainConfig{ChainID: chainID_A, GasPrices: "0.0uatom"}}, - {Name: "gaia", Version: "v9.0.1", ChainConfig: ibc.ChainConfig{ChainID: chainID_B, GasPrices: "0.0uatom"}}, - {Name: "gaia", Version: "v9.0.1", ChainConfig: ibc.ChainConfig{ChainID: chainID_C, GasPrices: "0.0uatom"}}, - {Name: "gaia", Version: "v9.0.1", ChainConfig: ibc.ChainConfig{ChainID: chainID_D, GasPrices: "0.0uatom"}}, - }) - - chains, err := cf.Chains(t.Name()) - require.NoError(t, err) - - chainA, chainB, chainC, chainD := chains[0].(*cosmos.CosmosChain), chains[1].(*cosmos.CosmosChain), chains[2].(*cosmos.CosmosChain), chains[3].(*cosmos.CosmosChain) - - r := interchaintest.NewBuiltinRelayerFactory( - ibc.CosmosRly, - zaptest.NewLogger(t), - ).Build(t, client, network) - - const pathAB = "ab" - const pathBC = "bc" - const pathCD = "cd" - - ic := interchaintest.NewInterchain(). - AddChain(chainA). - AddChain(chainB). - AddChain(chainC). - AddChain(chainD). - AddRelayer(r, "relayer"). - AddLink(interchaintest.InterchainLink{ - Chain1: chainA, - Chain2: chainB, - Relayer: r, - Path: pathAB, - }). - AddLink(interchaintest.InterchainLink{ - Chain1: chainB, - Chain2: chainC, - Relayer: r, - Path: pathBC, - }). - AddLink(interchaintest.InterchainLink{ - Chain1: chainC, - Chain2: chainD, - Relayer: r, - Path: pathCD, - }) - - require.NoError(t, ic.Build(ctx, eRep, interchaintest.InterchainBuildOptions{ - TestName: t.Name(), - Client: client, - NetworkID: network, - BlockDatabaseFile: interchaintest.DefaultBlockDatabaseFilepath(), - - SkipPathCreation: false, - })) - t.Cleanup(func() { - _ = ic.Close() - }) - - initBal := math.NewInt(10_000_000_000) - users := interchaintest.GetAndFundTestUsers(t, ctx, t.Name(), initBal.Int64(), chainA, chainB, chainC, chainD) - - abChan, err := ibc.GetTransferChannel(ctx, r, eRep, chainID_A, chainID_B) - require.NoError(t, err) - - baChan := abChan.Counterparty - - cbChan, err := ibc.GetTransferChannel(ctx, r, eRep, chainID_C, chainID_B) - require.NoError(t, err) - - bcChan := cbChan.Counterparty - - dcChan, err := ibc.GetTransferChannel(ctx, r, eRep, chainID_D, chainID_C) - require.NoError(t, err) - - cdChan := dcChan.Counterparty - - // Start the relayer on both paths - err = r.StartRelayer(ctx, eRep, pathAB, pathBC, pathCD) - require.NoError(t, err) - - t.Cleanup( - func() { - err := r.StopRelayer(ctx, eRep) - if err != nil { - t.Logf("an error occured while stopping the relayer: %s", err) - } - }, - ) - - // Get original account balances - userA, userB, userC, userD := users[0], users[1], users[2], users[3] - - // Compose the prefixed denoms and ibc denom for asserting balances - firstHopDenom := transfertypes.GetPrefixedDenom(baChan.PortID, baChan.ChannelID, chainA.Config().Denom) - secondHopDenom := transfertypes.GetPrefixedDenom(cbChan.PortID, cbChan.ChannelID, firstHopDenom) - thirdHopDenom := transfertypes.GetPrefixedDenom(dcChan.PortID, dcChan.ChannelID, secondHopDenom) - - firstHopDenomTrace := transfertypes.ParseDenomTrace(firstHopDenom) - secondHopDenomTrace := transfertypes.ParseDenomTrace(secondHopDenom) - thirdHopDenomTrace := transfertypes.ParseDenomTrace(thirdHopDenom) - - firstHopIBCDenom := firstHopDenomTrace.IBCDenom() - secondHopIBCDenom := secondHopDenomTrace.IBCDenom() - thirdHopIBCDenom := thirdHopDenomTrace.IBCDenom() - - firstHopEscrowAccount := transfertypes.GetEscrowAddress(abChan.PortID, abChan.ChannelID).String() - secondHopEscrowAccount := transfertypes.GetEscrowAddress(bcChan.PortID, bcChan.ChannelID).String() - thirdHopEscrowAccount := transfertypes.GetEscrowAddress(cdChan.PortID, abChan.ChannelID).String() - - zeroBal := math.ZeroInt() - transferAmount := math.NewInt(100_000) - - t.Run("multi-hop a->b->c->d", func(t *testing.T) { - // Send packet from Chain A->Chain B->Chain C->Chain D - transfer := ibc.WalletAmount{ - Address: userB.FormattedAddress(), - Denom: chainA.Config().Denom, - Amount: transferAmount, - } - - secondHopMetadata := &PacketMetadata{ - Forward: &ForwardMetadata{ - Receiver: userD.FormattedAddress(), - Channel: cdChan.ChannelID, - Port: cdChan.PortID, - }, - } - nextBz, err := json.Marshal(secondHopMetadata) - require.NoError(t, err) - next := string(nextBz) - - firstHopMetadata := &PacketMetadata{ - Forward: &ForwardMetadata{ - Receiver: userC.FormattedAddress(), - Channel: bcChan.ChannelID, - Port: bcChan.PortID, - Next: &next, - }, - } - - memo, err := json.Marshal(firstHopMetadata) - require.NoError(t, err) - - chainAHeight, err := chainA.Height(ctx) - require.NoError(t, err) - - transferTx, err := chainA.SendIBCTransfer(ctx, abChan.ChannelID, userA.KeyName(), transfer, ibc.TransferOptions{Memo: string(memo)}) - require.NoError(t, err) - _, err = testutil.PollForAck(ctx, chainA, chainAHeight, chainAHeight+30, transferTx.Packet) - require.NoError(t, err) - err = testutil.WaitForBlocks(ctx, 1, chainA) - require.NoError(t, err) - - chainABalance, err := chainA.GetBalance(ctx, userA.FormattedAddress(), chainA.Config().Denom) - require.NoError(t, err) - - chainBBalance, err := chainB.GetBalance(ctx, userB.FormattedAddress(), firstHopIBCDenom) - require.NoError(t, err) - - chainCBalance, err := chainC.GetBalance(ctx, userC.FormattedAddress(), secondHopIBCDenom) - require.NoError(t, err) - - chainDBalance, err := chainD.GetBalance(ctx, userD.FormattedAddress(), thirdHopIBCDenom) - require.NoError(t, err) - - require.True(t, chainABalance.Equal(initBal.Sub(transferAmount))) - require.True(t, chainBBalance.Equal(zeroBal)) - require.True(t, chainCBalance.Equal(zeroBal)) - require.True(t, chainDBalance.Equal(transferAmount)) - - firstHopEscrowBalance, err := chainA.GetBalance(ctx, firstHopEscrowAccount, chainA.Config().Denom) - require.NoError(t, err) - - secondHopEscrowBalance, err := chainB.GetBalance(ctx, secondHopEscrowAccount, firstHopIBCDenom) - require.NoError(t, err) - - thirdHopEscrowBalance, err := chainC.GetBalance(ctx, thirdHopEscrowAccount, secondHopIBCDenom) - require.NoError(t, err) - - require.True(t, firstHopEscrowBalance.Equal(transferAmount)) - require.True(t, secondHopEscrowBalance.Equal(transferAmount)) - require.True(t, thirdHopEscrowBalance.Equal(transferAmount)) - }) - - t.Run("multi-hop denom unwind d->c->b->a", func(t *testing.T) { - // Send packet back from Chain D->Chain C->Chain B->Chain A - transfer := ibc.WalletAmount{ - Address: userC.FormattedAddress(), - Denom: thirdHopIBCDenom, - Amount: transferAmount, - } - - secondHopMetadata := &PacketMetadata{ - Forward: &ForwardMetadata{ - Receiver: userA.FormattedAddress(), - Channel: baChan.ChannelID, - Port: baChan.PortID, - }, - } - - nextBz, err := json.Marshal(secondHopMetadata) - require.NoError(t, err) - - next := string(nextBz) - - firstHopMetadata := &PacketMetadata{ - Forward: &ForwardMetadata{ - Receiver: userB.FormattedAddress(), - Channel: cbChan.ChannelID, - Port: cbChan.PortID, - Next: &next, - }, - } - - memo, err := json.Marshal(firstHopMetadata) - require.NoError(t, err) - - chainDHeight, err := chainD.Height(ctx) - require.NoError(t, err) - - transferTx, err := chainD.SendIBCTransfer(ctx, dcChan.ChannelID, userD.KeyName(), transfer, ibc.TransferOptions{Memo: string(memo)}) - require.NoError(t, err) - _, err = testutil.PollForAck(ctx, chainD, chainDHeight, chainDHeight+30, transferTx.Packet) - require.NoError(t, err) - err = testutil.WaitForBlocks(ctx, 1, chainA) - require.NoError(t, err) - - // assert balances for user controlled wallets - chainDBalance, err := chainD.GetBalance(ctx, userD.FormattedAddress(), thirdHopIBCDenom) - require.NoError(t, err) - - chainCBalance, err := chainC.GetBalance(ctx, userC.FormattedAddress(), secondHopIBCDenom) - require.NoError(t, err) - - chainBBalance, err := chainB.GetBalance(ctx, userB.FormattedAddress(), firstHopIBCDenom) - require.NoError(t, err) - - chainABalance, err := chainA.GetBalance(ctx, userA.FormattedAddress(), chainA.Config().Denom) - require.NoError(t, err) - - require.True(t, chainDBalance.Equal(zeroBal)) - require.True(t, chainCBalance.Equal(zeroBal)) - require.True(t, chainBBalance.Equal(zeroBal)) - require.True(t, chainABalance.Equal(initBal)) - - // assert balances for IBC escrow accounts - firstHopEscrowBalance, err := chainA.GetBalance(ctx, firstHopEscrowAccount, chainA.Config().Denom) - require.NoError(t, err) - - secondHopEscrowBalance, err := chainB.GetBalance(ctx, secondHopEscrowAccount, firstHopIBCDenom) - require.NoError(t, err) - - thirdHopEscrowBalance, err := chainC.GetBalance(ctx, thirdHopEscrowAccount, secondHopIBCDenom) - require.NoError(t, err) - - require.True(t, firstHopEscrowBalance.Equal(zeroBal)) - require.True(t, secondHopEscrowBalance.Equal(zeroBal)) - require.True(t, thirdHopEscrowBalance.Equal(zeroBal)) - }) - - t.Run("forward ack error refund", func(t *testing.T) { - // Send a malformed packet with invalid receiver address from Chain A->Chain B->Chain C - // This should succeed in the first hop and fail to make the second hop; funds should then be refunded to Chain A. - transfer := ibc.WalletAmount{ - Address: userB.FormattedAddress(), - Denom: chainA.Config().Denom, - Amount: transferAmount, - } - - metadata := &PacketMetadata{ - Forward: &ForwardMetadata{ - Receiver: "xyz1t8eh66t2w5k67kwurmn5gqhtq6d2ja0vp7jmmq", // malformed receiver address on Chain C - Channel: bcChan.ChannelID, - Port: bcChan.PortID, - }, - } - - memo, err := json.Marshal(metadata) - require.NoError(t, err) - - chainAHeight, err := chainA.Height(ctx) - require.NoError(t, err) - - transferTx, err := chainA.SendIBCTransfer(ctx, abChan.ChannelID, userA.KeyName(), transfer, ibc.TransferOptions{Memo: string(memo)}) - require.NoError(t, err) - _, err = testutil.PollForAck(ctx, chainA, chainAHeight, chainAHeight+25, transferTx.Packet) - require.NoError(t, err) - err = testutil.WaitForBlocks(ctx, 1, chainA) - require.NoError(t, err) - - // assert balances for user controlled wallets - chainABalance, err := chainA.GetBalance(ctx, userA.FormattedAddress(), chainA.Config().Denom) - require.NoError(t, err) - - chainBBalance, err := chainB.GetBalance(ctx, userB.FormattedAddress(), firstHopIBCDenom) - require.NoError(t, err) - - chainCBalance, err := chainC.GetBalance(ctx, userC.FormattedAddress(), secondHopIBCDenom) - require.NoError(t, err) - - require.True(t, chainABalance.Equal(initBal)) - require.True(t, chainBBalance.Equal(zeroBal)) - require.True(t, chainCBalance.Equal(zeroBal)) - - // assert balances for IBC escrow accounts - firstHopEscrowBalance, err := chainA.GetBalance(ctx, firstHopEscrowAccount, chainA.Config().Denom) - require.NoError(t, err) - - secondHopEscrowBalance, err := chainB.GetBalance(ctx, secondHopEscrowAccount, firstHopIBCDenom) - require.NoError(t, err) - - require.True(t, firstHopEscrowBalance.Equal(zeroBal)) - require.True(t, secondHopEscrowBalance.Equal(zeroBal)) - }) - - t.Run("forward timeout refund", func(t *testing.T) { - // Send packet from Chain A->Chain B->Chain C with the timeout so low for B->C transfer that it can not make it from B to C, which should result in a refund from B to A after two retries. - transfer := ibc.WalletAmount{ - Address: userB.FormattedAddress(), - Denom: chainA.Config().Denom, - Amount: transferAmount, - } - - retries := uint8(2) - metadata := &PacketMetadata{ - Forward: &ForwardMetadata{ - Receiver: userC.FormattedAddress(), - Channel: bcChan.ChannelID, - Port: bcChan.PortID, - Retries: &retries, - Timeout: 1 * time.Second, - }, - } - - memo, err := json.Marshal(metadata) - require.NoError(t, err) - - chainAHeight, err := chainA.Height(ctx) - require.NoError(t, err) - - transferTx, err := chainA.SendIBCTransfer(ctx, abChan.ChannelID, userA.KeyName(), transfer, ibc.TransferOptions{Memo: string(memo)}) - require.NoError(t, err) - _, err = testutil.PollForAck(ctx, chainA, chainAHeight, chainAHeight+25, transferTx.Packet) - require.NoError(t, err) - err = testutil.WaitForBlocks(ctx, 1, chainA) - require.NoError(t, err) - - // assert balances for user controlled wallets - chainABalance, err := chainA.GetBalance(ctx, userA.FormattedAddress(), chainA.Config().Denom) - require.NoError(t, err) - - chainBBalance, err := chainB.GetBalance(ctx, userB.FormattedAddress(), firstHopIBCDenom) - require.NoError(t, err) - - chainCBalance, err := chainC.GetBalance(ctx, userC.FormattedAddress(), secondHopIBCDenom) - require.NoError(t, err) - - require.True(t, chainABalance.Equal(initBal)) - require.True(t, chainBBalance.Equal(zeroBal)) - require.True(t, chainCBalance.Equal(zeroBal)) - - firstHopEscrowBalance, err := chainA.GetBalance(ctx, firstHopEscrowAccount, chainA.Config().Denom) - require.NoError(t, err) - - secondHopEscrowBalance, err := chainB.GetBalance(ctx, secondHopEscrowAccount, firstHopIBCDenom) - require.NoError(t, err) - - require.True(t, firstHopEscrowBalance.Equal(zeroBal)) - require.True(t, secondHopEscrowBalance.Equal(zeroBal)) - }) - - t.Run("multi-hop ack error refund", func(t *testing.T) { - // Send a malformed packet with invalid receiver address from Chain A->Chain B->Chain C->Chain D - // This should succeed in the first hop and second hop, then fail to make the third hop. - // Funds should be refunded to Chain B and then to Chain A via acknowledgements with errors. - transfer := ibc.WalletAmount{ - Address: userB.FormattedAddress(), - Denom: chainA.Config().Denom, - Amount: transferAmount, - } - - secondHopMetadata := &PacketMetadata{ - Forward: &ForwardMetadata{ - Receiver: "xyz1t8eh66t2w5k67kwurmn5gqhtq6d2ja0vp7jmmq", // malformed receiver address on chain D - Channel: cdChan.ChannelID, - Port: cdChan.PortID, - }, - } - - nextBz, err := json.Marshal(secondHopMetadata) - require.NoError(t, err) - - next := string(nextBz) - - firstHopMetadata := &PacketMetadata{ - Forward: &ForwardMetadata{ - Receiver: userC.FormattedAddress(), - Channel: bcChan.ChannelID, - Port: bcChan.PortID, - Next: &next, - }, - } - - memo, err := json.Marshal(firstHopMetadata) - require.NoError(t, err) - - chainAHeight, err := chainA.Height(ctx) - require.NoError(t, err) - - transferTx, err := chainA.SendIBCTransfer(ctx, abChan.ChannelID, userA.KeyName(), transfer, ibc.TransferOptions{Memo: string(memo)}) - require.NoError(t, err) - _, err = testutil.PollForAck(ctx, chainA, chainAHeight, chainAHeight+30, transferTx.Packet) - require.NoError(t, err) - err = testutil.WaitForBlocks(ctx, 1, chainA) - require.NoError(t, err) - - // assert balances for user controlled wallets - chainDBalance, err := chainD.GetBalance(ctx, userD.FormattedAddress(), thirdHopIBCDenom) - require.NoError(t, err) - - chainCBalance, err := chainC.GetBalance(ctx, userC.FormattedAddress(), secondHopIBCDenom) - require.NoError(t, err) - - chainBBalance, err := chainB.GetBalance(ctx, userB.FormattedAddress(), firstHopIBCDenom) - require.NoError(t, err) - - chainABalance, err := chainA.GetBalance(ctx, userA.FormattedAddress(), chainA.Config().Denom) - require.NoError(t, err) - - require.True(t, chainABalance.Equal(initBal)) - require.True(t, chainBBalance.Equal(zeroBal)) - require.True(t, chainCBalance.Equal(zeroBal)) - require.True(t, chainDBalance.Equal(zeroBal)) - - // assert balances for IBC escrow accounts - firstHopEscrowBalance, err := chainA.GetBalance(ctx, firstHopEscrowAccount, chainA.Config().Denom) - require.NoError(t, err) - - secondHopEscrowBalance, err := chainB.GetBalance(ctx, secondHopEscrowAccount, firstHopIBCDenom) - require.NoError(t, err) - - thirdHopEscrowBalance, err := chainC.GetBalance(ctx, thirdHopEscrowAccount, secondHopIBCDenom) - require.NoError(t, err) - - require.True(t, firstHopEscrowBalance.Equal(zeroBal)) - require.True(t, secondHopEscrowBalance.Equal(zeroBal)) - require.True(t, thirdHopEscrowBalance.Equal(zeroBal)) - }) - - t.Run("multi-hop through native chain ack error refund", func(t *testing.T) { - // send normal IBC transfer from B->A to get funds in IBC denom, then do multihop A->B(native)->C->D - // this lets us test the burn from escrow account on chain C and the escrow to escrow transfer on chain B. - - // Compose the prefixed denoms and ibc denom for asserting balances - baDenom := transfertypes.GetPrefixedDenom(abChan.PortID, abChan.ChannelID, chainB.Config().Denom) - bcDenom := transfertypes.GetPrefixedDenom(cbChan.PortID, cbChan.ChannelID, chainB.Config().Denom) - cdDenom := transfertypes.GetPrefixedDenom(dcChan.PortID, dcChan.ChannelID, bcDenom) - - baDenomTrace := transfertypes.ParseDenomTrace(baDenom) - bcDenomTrace := transfertypes.ParseDenomTrace(bcDenom) - cdDenomTrace := transfertypes.ParseDenomTrace(cdDenom) - - baIBCDenom := baDenomTrace.IBCDenom() - bcIBCDenom := bcDenomTrace.IBCDenom() - cdIBCDenom := cdDenomTrace.IBCDenom() - - transfer := ibc.WalletAmount{ - Address: userA.FormattedAddress(), - Denom: chainB.Config().Denom, - Amount: transferAmount, - } - - chainBHeight, err := chainB.Height(ctx) - require.NoError(t, err) - - transferTx, err := chainB.SendIBCTransfer(ctx, baChan.ChannelID, userB.KeyName(), transfer, ibc.TransferOptions{}) - require.NoError(t, err) - _, err = testutil.PollForAck(ctx, chainB, chainBHeight, chainBHeight+10, transferTx.Packet) - require.NoError(t, err) - err = testutil.WaitForBlocks(ctx, 1, chainB) - require.NoError(t, err) - - // assert balance for user controlled wallet - chainABalance, err := chainA.GetBalance(ctx, userA.FormattedAddress(), baIBCDenom) - require.NoError(t, err) - - baEscrowBalance, err := chainB.GetBalance(ctx, transfertypes.GetEscrowAddress(baChan.PortID, baChan.ChannelID).String(), chainB.Config().Denom) - require.NoError(t, err) - - require.True(t, chainABalance.Equal(transferAmount)) - require.True(t, baEscrowBalance.Equal(transferAmount)) - - // Send a malformed packet with invalid receiver address from Chain A->Chain B->Chain C->Chain D - // This should succeed in the first hop and second hop, then fail to make the third hop. - // Funds should be refunded to Chain B and then to Chain A via acknowledgements with errors. - transfer = ibc.WalletAmount{ - Address: userB.FormattedAddress(), - Denom: baIBCDenom, - Amount: transferAmount, - } - - secondHopMetadata := &PacketMetadata{ - Forward: &ForwardMetadata{ - Receiver: "xyz1t8eh66t2w5k67kwurmn5gqhtq6d2ja0vp7jmmq", // malformed receiver address on chain D - Channel: cdChan.ChannelID, - Port: cdChan.PortID, - }, - } - - nextBz, err := json.Marshal(secondHopMetadata) - require.NoError(t, err) - - next := string(nextBz) - - firstHopMetadata := &PacketMetadata{ - Forward: &ForwardMetadata{ - Receiver: userC.FormattedAddress(), - Channel: bcChan.ChannelID, - Port: bcChan.PortID, - Next: &next, - }, - } - - memo, err := json.Marshal(firstHopMetadata) - require.NoError(t, err) - - chainAHeight, err := chainA.Height(ctx) - require.NoError(t, err) - - transferTx, err = chainA.SendIBCTransfer(ctx, abChan.ChannelID, userA.KeyName(), transfer, ibc.TransferOptions{Memo: string(memo)}) - require.NoError(t, err) - _, err = testutil.PollForAck(ctx, chainA, chainAHeight, chainAHeight+30, transferTx.Packet) - require.NoError(t, err) - err = testutil.WaitForBlocks(ctx, 1, chainA) - require.NoError(t, err) - - // assert balances for user controlled wallets - chainDBalance, err := chainD.GetBalance(ctx, userD.FormattedAddress(), cdIBCDenom) - require.NoError(t, err) - - chainCBalance, err := chainC.GetBalance(ctx, userC.FormattedAddress(), bcIBCDenom) - require.NoError(t, err) - - chainBBalance, err := chainB.GetBalance(ctx, userB.FormattedAddress(), chainB.Config().Denom) - require.NoError(t, err) - - chainABalance, err = chainA.GetBalance(ctx, userA.FormattedAddress(), baIBCDenom) - require.NoError(t, err) - - require.True(t, chainABalance.Equal(transferAmount)) - require.True(t, chainBBalance.Equal(initBal.Sub(transferAmount))) - require.True(t, chainCBalance.Equal(zeroBal)) - require.True(t, chainDBalance.Equal(zeroBal)) - - // assert balances for IBC escrow accounts - cdEscrowBalance, err := chainC.GetBalance(ctx, transfertypes.GetEscrowAddress(cdChan.PortID, cdChan.ChannelID).String(), bcIBCDenom) - require.NoError(t, err) - - bcEscrowBalance, err := chainB.GetBalance(ctx, transfertypes.GetEscrowAddress(bcChan.PortID, bcChan.ChannelID).String(), chainB.Config().Denom) - require.NoError(t, err) - - baEscrowBalance, err = chainB.GetBalance(ctx, transfertypes.GetEscrowAddress(baChan.PortID, baChan.ChannelID).String(), chainB.Config().Denom) - require.NoError(t, err) - - require.True(t, baEscrowBalance.Equal(transferAmount)) - require.True(t, bcEscrowBalance.Equal(zeroBal)) - require.True(t, cdEscrowBalance.Equal(zeroBal)) - }) - - t.Run("forward a->b->a", func(t *testing.T) { - // Send packet from Chain A->Chain B->Chain A - userABalance, err := chainA.GetBalance(ctx, userA.FormattedAddress(), chainA.Config().Denom) - require.NoError(t, err, "failed to get user a balance") - - userBBalance, err := chainB.GetBalance(ctx, userB.FormattedAddress(), firstHopDenom) - require.NoError(t, err, "failed to get user a balance") - - transfer := ibc.WalletAmount{ - Address: userB.FormattedAddress(), - Denom: chainA.Config().Denom, - Amount: transferAmount, - } - - firstHopMetadata := &PacketMetadata{ - Forward: &ForwardMetadata{ - Receiver: userA.FormattedAddress(), - Channel: baChan.ChannelID, - Port: baChan.PortID, - }, - } - - memo, err := json.Marshal(firstHopMetadata) - require.NoError(t, err) - - chainAHeight, err := chainA.Height(ctx) - require.NoError(t, err) - - transferTx, err := chainA.SendIBCTransfer(ctx, abChan.ChannelID, userA.KeyName(), transfer, ibc.TransferOptions{Memo: string(memo)}) - require.NoError(t, err) - _, err = testutil.PollForAck(ctx, chainA, chainAHeight, chainAHeight+30, transferTx.Packet) - require.NoError(t, err) - err = testutil.WaitForBlocks(ctx, 1, chainA) - require.NoError(t, err) - - chainABalance, err := chainA.GetBalance(ctx, userA.FormattedAddress(), chainA.Config().Denom) - require.NoError(t, err) - - chainBBalance, err := chainB.GetBalance(ctx, userB.FormattedAddress(), firstHopIBCDenom) - require.NoError(t, err) - - require.True(t, chainABalance.Equal(userABalance)) - require.True(t, chainBBalance.Equal(userBBalance)) - }) -}