diff --git a/chain/cosmos/config.go b/chain/cosmos/config.go new file mode 100644 index 000000000..01b04b2e9 --- /dev/null +++ b/chain/cosmos/config.go @@ -0,0 +1,23 @@ +package cosmos + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func SetSDKConfig(bech32Prefix string) *sdk.Config { + var ( + bech32MainPrefix = bech32Prefix + bech32PrefixAccAddr = bech32MainPrefix + bech32PrefixAccPub = bech32MainPrefix + sdk.PrefixPublic + bech32PrefixValAddr = bech32MainPrefix + sdk.PrefixValidator + sdk.PrefixOperator + bech32PrefixValPub = bech32MainPrefix + sdk.PrefixValidator + sdk.PrefixOperator + sdk.PrefixPublic + bech32PrefixConsAddr = bech32MainPrefix + sdk.PrefixValidator + sdk.PrefixConsensus + bech32PrefixConsPub = bech32MainPrefix + sdk.PrefixValidator + sdk.PrefixConsensus + sdk.PrefixPublic + ) + + cfg := sdk.GetConfig() + cfg.SetBech32PrefixForAccount(bech32PrefixAccAddr, bech32PrefixAccPub) + cfg.SetBech32PrefixForValidator(bech32PrefixValAddr, bech32PrefixValPub) + cfg.SetBech32PrefixForConsensusNode(bech32PrefixConsAddr, bech32PrefixConsPub) + return cfg +} diff --git a/chain/cosmos/cosmos_chain.go b/chain/cosmos/cosmos_chain.go index 0caf98461..900108768 100644 --- a/chain/cosmos/cosmos_chain.go +++ b/chain/cosmos/cosmos_chain.go @@ -1059,7 +1059,7 @@ func (c *CosmosChain) Height(ctx context.Context) (uint64, error) { // Acknowledgements implements ibc.Chain, returning all acknowledgments in block at height func (c *CosmosChain) Acknowledgements(ctx context.Context, height uint64) ([]ibc.PacketAcknowledgement, error) { var acks []*chanTypes.MsgAcknowledgement - err := rangeBlockMessages(ctx, c.cfg.EncodingConfig.InterfaceRegistry, c.getFullNode().Client, height, func(msg types.Msg) bool { + err := RangeBlockMessages(ctx, c.cfg.EncodingConfig.InterfaceRegistry, c.getFullNode().Client, height, func(msg types.Msg) bool { found, ok := msg.(*chanTypes.MsgAcknowledgement) if ok { acks = append(acks, found) @@ -1092,7 +1092,7 @@ func (c *CosmosChain) Acknowledgements(ctx context.Context, height uint64) ([]ib // Timeouts implements ibc.Chain, returning all timeouts in block at height func (c *CosmosChain) Timeouts(ctx context.Context, height uint64) ([]ibc.PacketTimeout, error) { var timeouts []*chanTypes.MsgTimeout - err := rangeBlockMessages(ctx, c.cfg.EncodingConfig.InterfaceRegistry, c.getFullNode().Client, height, func(msg types.Msg) bool { + err := RangeBlockMessages(ctx, c.cfg.EncodingConfig.InterfaceRegistry, c.getFullNode().Client, height, func(msg types.Msg) bool { found, ok := msg.(*chanTypes.MsgTimeout) if ok { timeouts = append(timeouts, found) diff --git a/chain/cosmos/query.go b/chain/cosmos/query.go index b5a7ceb82..4e5cb4684 100644 --- a/chain/cosmos/query.go +++ b/chain/cosmos/query.go @@ -13,9 +13,9 @@ type blockClient interface { Block(ctx context.Context, height *int64) (*tmtypes.ResultBlock, error) } -// rangeBlockMessages iterates through all a block's transactions and each transaction's messages yielding to f. +// RangeBlockMessages iterates through all a block's transactions and each transaction's messages yielding to f. // Return true from f to stop iteration. -func rangeBlockMessages(ctx context.Context, interfaceRegistry codectypes.InterfaceRegistry, client blockClient, height uint64, done func(sdk.Msg) bool) error { +func RangeBlockMessages(ctx context.Context, interfaceRegistry codectypes.InterfaceRegistry, client blockClient, height uint64, done func(sdk.Msg) bool) error { h := int64(height) block, err := client.Block(ctx, &h) if err != nil { diff --git a/conformance/test.go b/conformance/test.go index fbb56b663..552655fc0 100644 --- a/conformance/test.go +++ b/conformance/test.go @@ -445,6 +445,10 @@ func testPacketRelaySuccess( req.NoError(err, "failed to get acknowledgement on destination chain") req.NoError(dstAck.Validate(), "invalid acknowledgement on destination chain") + // Even though we poll for the ack, there may be timing issues where balances are not fully reconciled yet. + // So we have a small buffer here. + require.NoError(t, testutil.WaitForBlocks(ctx, 5, srcChain, dstChain)) + // get ibc denom for dst denom on src chain dstDenomTrace := transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom(channels[i].PortID, channels[i].ChannelID, dstDenom)) srcIbcDenom := dstDenomTrace.IBCDenom() @@ -496,7 +500,7 @@ func testPacketRelayFail( // Even though we poll for the timeout, there may be timing issues where balances are not fully reconciled yet. // So we have a small buffer here. - require.NoError(t, testutil.WaitForBlocks(ctx, 2, srcChain, dstChain)) + require.NoError(t, testutil.WaitForBlocks(ctx, 5, srcChain, dstChain)) // get ibc denom for src denom on dst chain srcDenomTrace := transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom(channels[i].Counterparty.PortID, channels[i].Counterparty.ChannelID, srcDenom)) @@ -525,6 +529,10 @@ func testPacketRelayFail( req.NoError(err, "failed to get timeout packet on destination chain") req.NoError(timeout.Validate(), "invalid timeout packet on destination chain") + // Even though we poll for the timeout, there may be timing issues where balances are not fully reconciled yet. + // So we have a small buffer here. + require.NoError(t, testutil.WaitForBlocks(ctx, 5, srcChain, dstChain)) + // get ibc denom for dst denom on src chain dstDenomTrace := transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom(channels[i].PortID, channels[i].ChannelID, dstDenom)) srcIbcDenom := dstDenomTrace.IBCDenom() diff --git a/examples/cosmos/README.md b/examples/cosmos/README.md new file mode 100644 index 000000000..cce1e3b0e --- /dev/null +++ b/examples/cosmos/README.md @@ -0,0 +1,3 @@ +# More Examples + +[State Sync](https://github.com/CosmosContracts/juno/blob/reece/add-state-sync-test/interchaintest/state_sync_test.go) diff --git a/examples/cosmos/chain_export_test.go b/examples/cosmos/chain_export_test.go index ea2257084..e95f68f82 100644 --- a/examples/cosmos/chain_export_test.go +++ b/examples/cosmos/chain_export_test.go @@ -14,10 +14,8 @@ import ( ) func TestJunoStateExport(t *testing.T) { - // SDK v45 - CosmosChainStateExportTest(t, "juno", "v15.0.0") // SDK v47 - CosmosChainStateExportTest(t, "juno", "v16.0.0") + CosmosChainStateExportTest(t, "juno", "v17.0.0") } func CosmosChainStateExportTest(t *testing.T, name, version string) { diff --git a/examples/cosmos/chain_miscellaneous_test.go b/examples/cosmos/chain_miscellaneous_test.go index 1377eecb1..e0d53e0ca 100644 --- a/examples/cosmos/chain_miscellaneous_test.go +++ b/examples/cosmos/chain_miscellaneous_test.go @@ -2,9 +2,14 @@ package cosmos_test import ( "context" + "fmt" "testing" "cosmossdk.io/math" + + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/strangelove-ventures/interchaintest/v8" "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" "github.com/strangelove-ventures/interchaintest/v8/ibc" @@ -16,10 +21,6 @@ func TestICTestMiscellaneous(t *testing.T) { CosmosChainTestMiscellaneous(t, "juno", "v16.0.0") } -const ( - initialBalance = 100_000_000 -) - func CosmosChainTestMiscellaneous(t *testing.T, name, version string) { if testing.Short() { t.Skip("skipping in short mode") @@ -28,13 +29,25 @@ func CosmosChainTestMiscellaneous(t *testing.T, name, version string) { numVals := 1 numFullNodes := 0 + cosmos.SetSDKConfig("juno") + + sdk47Genesis := []cosmos.GenesisKV{ + cosmos.NewGenesisKV("app_state.gov.params.voting_period", "15s"), + cosmos.NewGenesisKV("app_state.gov.params.max_deposit_period", "10s"), + cosmos.NewGenesisKV("app_state.gov.params.min_deposit.0.denom", "ujuno"), + cosmos.NewGenesisKV("app_state.gov.params.min_deposit.0.amount", "1"), + } + cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ { Name: name, ChainName: name, Version: version, ChainConfig: ibc.ChainConfig{ - Denom: "ujuno", + Denom: "ujuno", + Bech32Prefix: "juno", + CoinType: "118", + ModifyGenesis: cosmos.ModifyGenesis(sdk47Genesis), }, NumValidators: &numVals, NumFullNodes: &numFullNodes, @@ -62,13 +75,22 @@ func CosmosChainTestMiscellaneous(t *testing.T, name, version string) { _ = ic.Close() }) - users := interchaintest.GetAndFundTestUsers(t, ctx, "default", int64(initialBalance), chain, chain) - - TokenFactory(ctx, t, chain, users) - BuildDependencies(ctx, t, chain) + users := interchaintest.GetAndFundTestUsers(t, ctx, "default", int64(10_000_000_000), chain, chain) + + testBuildDependencies(ctx, t, chain) + testWalletKeys(ctx, t, chain) + testSendingTokens(ctx, t, chain, users) + testFindTxs(ctx, t, chain, users) + testPollForBalance(ctx, t, chain, users) + testRangeBlockMessages(ctx, t, chain, users) + testBroadcaster(ctx, t, chain, users) + testQueryCmd(ctx, t, chain) + testHasCommand(ctx, t, chain) + testTokenFactory(ctx, t, chain, users) + testAddingNode(ctx, t, chain) } -func BuildDependencies(ctx context.Context, t *testing.T, chain *cosmos.CosmosChain) { +func testBuildDependencies(ctx context.Context, t *testing.T, chain *cosmos.CosmosChain) { deps := chain.Validators[0].GetBuildInformation(ctx) sdkVer := "v0.47.3" @@ -106,7 +128,193 @@ func BuildDependencies(ctx context.Context, t *testing.T, chain *cosmos.CosmosCh } } -func TokenFactory(ctx context.Context, t *testing.T, chain *cosmos.CosmosChain, users []ibc.Wallet) { +func testWalletKeys(ctx context.Context, t *testing.T, chain *cosmos.CosmosChain) { + // create a general key + randKey := "randkey123" + err := chain.CreateKey(ctx, randKey) + require.NoError(t, err) + + // verify key was created properly + _, err = chain.GetAddress(ctx, randKey) + require.NoError(t, err) + + // recover a key + // juno1hj5fveer5cjtn4wd6wstzugjfdxzl0xps73ftl + keyName := "key-abc" + testMnemonic := "decorate bright ozone fork gallery riot bus exhaust worth way bone indoor calm squirrel merry zero scheme cotton until shop any excess stage laundry" + wallet, err := chain.BuildWallet(ctx, keyName, testMnemonic) + require.NoError(t, err) + + // verify + addr, err := chain.GetAddress(ctx, keyName) + require.NoError(t, err) + require.Equal(t, wallet.Address(), addr) + + tn := chain.Validators[0] + a, err := tn.KeyBech32(ctx, "key-abc", "val") + require.NoError(t, err) + require.Equal(t, a, "junovaloper1hj5fveer5cjtn4wd6wstzugjfdxzl0xp0r8xsx") + + a, err = tn.KeyBech32(ctx, "key-abc", "acc") + require.NoError(t, err) + require.Equal(t, a, wallet.FormattedAddress()) + + a, err = tn.AccountKeyBech32(ctx, "key-abc") + require.NoError(t, err) + require.Equal(t, a, wallet.FormattedAddress()) +} + +func testSendingTokens(ctx context.Context, t *testing.T, chain *cosmos.CosmosChain, users []ibc.Wallet) { + _, err := chain.GetBalance(ctx, users[0].FormattedAddress(), chain.Config().Denom) + require.NoError(t, err) + b2, err := chain.GetBalance(ctx, users[1].FormattedAddress(), chain.Config().Denom) + require.NoError(t, err) + + sendAmt := int64(1) + _, err = sendTokens(ctx, chain, users[0], users[1], "", sendAmt) + require.NoError(t, err) + + b2New, err := chain.GetBalance(ctx, users[1].FormattedAddress(), chain.Config().Denom) + require.NoError(t, err) + + require.Equal(t, b2.Add(math.NewInt(sendAmt)), b2New) +} + +func testFindTxs(ctx context.Context, t *testing.T, chain *cosmos.CosmosChain, users []ibc.Wallet) { + height, _ := chain.Height(ctx) + + _, err := sendTokens(ctx, chain, users[0], users[1], "", 1) + require.NoError(t, err) + + txs, err := chain.FindTxs(ctx, height+1) + require.NoError(t, err) + require.NotEmpty(t, txs) + require.Equal(t, txs[0].Events[0].Type, "coin_spent") +} + +func testPollForBalance(ctx context.Context, t *testing.T, chain *cosmos.CosmosChain, users []ibc.Wallet) { + bal2, err := chain.GetBalance(ctx, users[1].FormattedAddress(), chain.Config().Denom) + require.NoError(t, err) + + amt := ibc.WalletAmount{ + Address: users[1].FormattedAddress(), + Denom: chain.Config().Denom, + Amount: math.NewInt(1), + } + + delta := uint64(3) + + ch := make(chan error) + go func() { + new := amt + new.Amount = bal2.Add(math.NewInt(1)) + ch <- cosmos.PollForBalance(ctx, chain, delta, new) + }() + + err = chain.SendFunds(ctx, users[0].KeyName(), amt) + require.NoError(t, err) + require.NoError(t, <-ch) +} + +func testRangeBlockMessages(ctx context.Context, t *testing.T, chain *cosmos.CosmosChain, users []ibc.Wallet) { + height, _ := chain.Height(ctx) + + _, err := sendTokens(ctx, chain, users[0], users[1], "", 1) + require.NoError(t, err) + + var bankMsgs []*banktypes.MsgSend + err = cosmos.RangeBlockMessages(ctx, chain.Config().EncodingConfig.InterfaceRegistry, chain.Validators[0].Client, height+1, func(msg sdk.Msg) bool { + found, ok := msg.(*banktypes.MsgSend) + if ok { + bankMsgs = append(bankMsgs, found) + } + return false + }) + require.NoError(t, err) +} + +func testAddingNode(ctx context.Context, t *testing.T, chain *cosmos.CosmosChain) { + // This should be tested last or else Txs will fail on the new full node. + nodesAmt := len(chain.Nodes()) + chain.AddFullNodes(ctx, nil, 1) + require.Equal(t, nodesAmt+1, len(chain.Nodes())) +} + +func testBroadcaster(ctx context.Context, t *testing.T, chain *cosmos.CosmosChain, users []ibc.Wallet) { + from := users[0].FormattedAddress() + addr1 := "juno190g5j8aszqhvtg7cprmev8xcxs6csra7xnk3n3" + addr2 := "juno1a53udazy8ayufvy0s434pfwjcedzqv34q7p7vj" + + c1 := sdk.NewCoins(sdk.NewCoin(chain.Config().Denom, math.NewInt(1))) + c2 := sdk.NewCoins(sdk.NewCoin(chain.Config().Denom, math.NewInt(2))) + + b := cosmos.NewBroadcaster(t, chain) + + in := banktypes.Input{ + Address: from, + Coins: c1.Add(c2[0]), + } + out := []banktypes.Output{ + { + Address: addr1, + Coins: c1, + }, + { + Address: addr2, + Coins: c2, + }, + } + + txResp, err := cosmos.BroadcastTx( + ctx, + b, + users[0], + banktypes.NewMsgMultiSend(in, out), + ) + require.NoError(t, err) + require.NotEmpty(t, txResp.TxHash) + fmt.Printf("txResp: %+v\n", txResp) + + updatedBal1, err := chain.GetBalance(ctx, addr1, chain.Config().Denom) + require.NoError(t, err) + require.Equal(t, math.NewInt(1), updatedBal1) + + updatedBal2, err := chain.GetBalance(ctx, addr2, chain.Config().Denom) + require.NoError(t, err) + require.Equal(t, math.NewInt(2), updatedBal2) +} + +func testQueryCmd(ctx context.Context, t *testing.T, chain *cosmos.CosmosChain) { + tn := chain.Validators[0] + stdout, stderr, err := tn.ExecQuery(ctx, "slashing", "params") + require.NoError(t, err) + require.NotEmpty(t, stdout) + require.Empty(t, stderr) +} + +func testHasCommand(ctx context.Context, t *testing.T, chain *cosmos.CosmosChain) { + tn := chain.Validators[0] + res := tn.HasCommand(ctx, "query") + require.True(t, res) + + if tn.IsAboveSDK47(ctx) { + require.True(t, tn.HasCommand(ctx, "genesis")) + } else { + // 45 does not have this + require.False(t, tn.HasCommand(ctx, "genesis")) + } + + require.True(t, tn.HasCommand(ctx, "tx", "ibc")) + require.True(t, tn.HasCommand(ctx, "q", "ibc")) + require.True(t, tn.HasCommand(ctx, "keys")) + require.True(t, tn.HasCommand(ctx, "help")) + require.True(t, tn.HasCommand(ctx, "tx", "bank", "send")) + + require.False(t, tn.HasCommand(ctx, "tx", "bank", "send2notrealcmd")) + require.False(t, tn.HasCommand(ctx, "incorrectcmd")) +} + +func testTokenFactory(ctx context.Context, t *testing.T, chain *cosmos.CosmosChain, users []ibc.Wallet) { user := users[0] user2 := users[1] @@ -168,6 +376,21 @@ func TokenFactory(ctx context.Context, t *testing.T, chain *cosmos.CosmosChain, } +// helpers +func sendTokens(ctx context.Context, chain *cosmos.CosmosChain, from, to ibc.Wallet, token string, amount int64) (ibc.WalletAmount, error) { + if token == "" { + token = chain.Config().Denom + } + + sendAmt := ibc.WalletAmount{ + Address: to.FormattedAddress(), + Denom: token, + Amount: math.NewInt(amount), + } + err := chain.SendFunds(ctx, from.KeyName(), sendAmt) + return sendAmt, err +} + func validateBalance(ctx context.Context, t *testing.T, chain *cosmos.CosmosChain, user ibc.Wallet, tfDenom string, expected int64) { balance, err := chain.GetBalance(ctx, user.FormattedAddress(), tfDenom) require.NoError(t, err) diff --git a/examples/cosmos/light_client_test.go b/examples/cosmos/light_client_test.go deleted file mode 100644 index 6ca5f7362..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/v8/modules/core/02-client/types" - "github.com/strangelove-ventures/interchaintest/v8" - "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" - "github.com/strangelove-ventures/interchaintest/v8/ibc" - "github.com/strangelove-ventures/interchaintest/v8/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/cosmos/state_sync_test.go b/examples/cosmos/state_sync_test.go deleted file mode 100644 index 0ca69b6b0..000000000 --- a/examples/cosmos/state_sync_test.go +++ /dev/null @@ -1,96 +0,0 @@ -package cosmos_test - -import ( - "context" - "encoding/hex" - "fmt" - "testing" - "time" - - interchaintest "github.com/strangelove-ventures/interchaintest/v8" - "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" - "github.com/strangelove-ventures/interchaintest/v8/ibc" - "github.com/strangelove-ventures/interchaintest/v8/testutil" - "github.com/stretchr/testify/require" -) - -func TestCosmosHubStateSync(t *testing.T) { - CosmosChainStateSyncTest(t, "gaia", gaiaVersion) -} - -const stateSyncSnapshotInterval = 10 - -func CosmosChainStateSyncTest(t *testing.T, chainName, version string) { - if testing.Short() { - t.Skip("skipping in short mode") - } - - t.Parallel() - - nf := 1 - - configFileOverrides := make(map[string]any) - appTomlOverrides := make(testutil.Toml) - - // state sync snapshots every stateSyncSnapshotInterval blocks. - stateSync := make(testutil.Toml) - stateSync["snapshot-interval"] = stateSyncSnapshotInterval - appTomlOverrides["state-sync"] = stateSync - - // state sync snapshot interval must be a multiple of pruning keep every interval. - appTomlOverrides["pruning"] = "custom" - appTomlOverrides["pruning-keep-recent"] = stateSyncSnapshotInterval - appTomlOverrides["pruning-keep-every"] = stateSyncSnapshotInterval - appTomlOverrides["pruning-interval"] = stateSyncSnapshotInterval - - configFileOverrides["config/app.toml"] = appTomlOverrides - - cfg := ibc.ChainConfig{ - ConfigFileOverrides: configFileOverrides, - } - - chains := interchaintest.CreateChainWithConfig(t, 1, nf, chainName, version, cfg) - - chain := chains[0].(*cosmos.CosmosChain) - - enableBlockDB := false - ctx, _, _, _ := interchaintest.BuildInitialChain(t, chains, enableBlockDB) - - // Wait for blocks so that nodes have a few state sync snapshot available - require.NoError(t, testutil.WaitForBlocks(ctx, stateSyncSnapshotInterval*2, chain)) - - latestHeight, err := chain.Height(ctx) - require.NoError(t, err, "failed to fetch latest chain height") - - // Trusted height should be state sync snapshot interval blocks ago. - trustHeight := int64(latestHeight) - stateSyncSnapshotInterval - - firstFullNode := chain.FullNodes[0] - - // Fetch block hash for trusted height. - blockRes, err := firstFullNode.Client.Block(ctx, &trustHeight) - require.NoError(t, err, "failed to fetch trusted block") - trustHash := hex.EncodeToString(blockRes.BlockID.Hash) - - // Construct statesync parameters for new node to get in sync. - configFileOverrides = make(map[string]any) - configTomlOverrides := make(testutil.Toml) - - // Set trusted parameters and rpc servers for verification. - stateSync = make(testutil.Toml) - stateSync["trust_hash"] = trustHash - stateSync["trust_height"] = trustHeight - // State sync requires minimum of two RPC servers for verification. We can provide the same RPC twice though. - stateSync["rpc_servers"] = fmt.Sprintf("tcp://%s:26657,tcp://%s:26657", firstFullNode.HostName(), firstFullNode.HostName()) - configTomlOverrides["statesync"] = stateSync - - configFileOverrides["config/config.toml"] = configTomlOverrides - - // Now that nodes are providing state sync snapshots, state sync a new node. - require.NoError(t, chain.AddFullNodes(ctx, configFileOverrides, 1)) - - // Wait for new node to be in sync. - ctx, cancel := context.WithTimeout(ctx, 30*time.Second) - defer cancel() - require.NoError(t, testutil.WaitForInSync(ctx, chain, chain.FullNodes[len(chain.FullNodes)-1])) -} diff --git a/examples/ibc/README.md b/examples/ibc/README.md new file mode 100644 index 000000000..3c2e45380 --- /dev/null +++ b/examples/ibc/README.md @@ -0,0 +1,23 @@ +# More Examples + +Interchain Accounts + +* [interchain_accounts demo](https://gist.github.com/Reecepbcups/8ec46ad83f6c9c1a152c10ab25774335) +* [ibc-go](https://github.com/cosmos/ibc-go/blob/main/e2e/tests/interchain_accounts/base_test.go) + +Interchain Queries + +* [interchain_queries demo](https://gist.github.com/Reecepbcups/d2a1155aaa3a95f5f6daf672e081e8b1) + +Packet Forward Middleware Test: + +* [Noble](https://github.com/strangelove-ventures/noble/blob/main/interchaintest/packet_forward_test.go) +* [Juno](https://github.com/CosmosContracts/juno/blob/main/interchaintest/module_pfm_test.go) + +IBC Client Update + +* [ibc-go](https://github.com/cosmos/ibc-go/blob/main/e2e/tests/core/02-client/client_test.go) + +IBC Transfers (ICS-20) + +* [ibc-go](https://github.com/cosmos/ibc-go/blob/main/e2e/tests/transfer/base_test.go) diff --git a/examples/ibc/interchain_accounts_test.go b/examples/ibc/interchain_accounts_test.go deleted file mode 100644 index e5943a0bf..000000000 --- a/examples/ibc/interchain_accounts_test.go +++ /dev/null @@ -1,328 +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/v8/modules/core/04-channel/types" - "github.com/strangelove-ventures/interchaintest/v8" - "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" - "github.com/strangelove-ventures/interchaintest/v8/ibc" - "github.com/strangelove-ventures/interchaintest/v8/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", UidGid: "1025:1025"}}, - }, - }, - { - Name: "icad", - ChainConfig: ibc.ChainConfig{ - Images: []ibc.DockerImage{{Repository: "ghcr.io/cosmos/ibc-go-icad", Version: "v0.5.0", UidGid: "1025:1025"}}, - }, - }, - }) - - chain1, chain2 := chains[0].(*cosmos.CosmosChain), chains[1].(*cosmos.CosmosChain) - - // Build chains with a relayer - // Get a relayer instance - // client, network := interchaintest.DockerSetup(t) - // r := interchaintest.NewBuiltinRelayerFactory( - // ibc.CosmosRly, - // zaptest.NewLogger(t), - // relayer.StartupFlags("-p", "events", "-b", "100"), - // ).Build(t, client, network) - - // 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 6b63c6b2b..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/v8" - "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" - "github.com/strangelove-ventures/interchaintest/v8/ibc" - "github.com/strangelove-ventures/interchaintest/v8/internal/dockerutil" - "github.com/strangelove-ventures/interchaintest/v8/relayer" - "github.com/strangelove-ventures/interchaintest/v8/testreporter" - "github.com/strangelove-ventures/interchaintest/v8/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/learn_ibc_test.go b/examples/ibc/learn_ibc_test.go index bae5b7c8c..e8784e727 100644 --- a/examples/ibc/learn_ibc_test.go +++ b/examples/ibc/learn_ibc_test.go @@ -6,9 +6,12 @@ import ( "testing" "time" - "cosmossdk.io/math" transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + + "cosmossdk.io/math" + clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" "github.com/strangelove-ventures/interchaintest/v8/ibc" "github.com/strangelove-ventures/interchaintest/v8/testreporter" "github.com/stretchr/testify/require" @@ -93,6 +96,9 @@ func TestLearn(t *testing.T) { require.NoError(t, err) osmoChannelID := osmoChannelInfo[0].ChannelID + height, err := osmosis.Height(ctx) + require.NoError(t, err) + // Send Transaction amountToSend := math.NewInt(1_000_000) dstAddress := osmosisUser.FormattedAddress() @@ -122,4 +128,13 @@ func TestLearn(t *testing.T) { osmosUserBalNew, err := osmosis.GetBalance(ctx, osmosisUser.FormattedAddress(), dstIbcDenom) require.NoError(t, err) require.True(t, osmosUserBalNew.Equal(amountToSend)) + + // Validate light client + 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) } diff --git a/examples/ibc/packet_forward_test.go b/examples/ibc/packet_forward_test.go deleted file mode 100644 index e315886ab..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/v8/modules/apps/transfer/types" - "github.com/strangelove-ventures/interchaintest/v8" - "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" - "github.com/strangelove-ventures/interchaintest/v8/ibc" - "github.com/strangelove-ventures/interchaintest/v8/testreporter" - "github.com/strangelove-ventures/interchaintest/v8/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)) - }) -} diff --git a/examples/ibc/wasm/wasm_icq_test.go b/examples/ibc/wasm/wasm_icq_test.go index f76c667ec..a6fd3ab71 100644 --- a/examples/ibc/wasm/wasm_icq_test.go +++ b/examples/ibc/wasm/wasm_icq_test.go @@ -110,7 +110,7 @@ func TestInterchainQueriesWASM(t *testing.T) { r := interchaintest.NewBuiltinRelayerFactory( ibc.CosmosRly, logger, - relayer.RelayerOptionExtraStartFlags{Flags: []string{"-p", "events", "-b", "100"}}, + relayer.StartupFlags("-p", "events", "-b", "100"), ).Build(t, client, network) // Build the network; spin up the chains and configure the relayer