Created
October 4, 2023 15:11
-
-
Save Reecepbcups/8ec46ad83f6c9c1a152c10ab25774335 to your computer and use it in GitHub Desktop.
Deprecated intertx module interchaintest
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package ibc | |
import ( | |
"context" | |
"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/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" | |
) | |
// 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() | |
client, network := interchaintest.DockerSetup(t) | |
rep := testreporter.NewNopReporter() | |
eRep := rep.RelayerExecReporter(t) | |
ctx := context.Background() | |
// Get both chains | |
cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(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"}}, | |
}, | |
}, | |
}) | |
chains, err := cf.Chains(t.Name()) | |
require.NoError(t, err) | |
chain1, chain2 := chains[0].(*cosmos.CosmosChain), chains[1].(*cosmos.CosmosChain) | |
// Get a relayer instance | |
r := interchaintest.NewBuiltinRelayerFactory( | |
ibc.CosmosRly, | |
zaptest.NewLogger(t), | |
relayer.RelayerOptionExtraStartFlags{Flags: []string{"-p", "events", "-b", "100"}}, | |
).Build(t, client, network) | |
// Build the network; spin up the chains and configure the relayer | |
const pathName = "test-path" | |
const relayerName = "relayer" | |
ic := interchaintest.NewInterchain(). | |
AddChain(chain1). | |
AddChain(chain2). | |
AddRelayer(r, relayerName). | |
AddLink(interchaintest.InterchainLink{ | |
Chain1: chain1, | |
Chain2: chain2, | |
Relayer: r, | |
Path: pathName, | |
}) | |
require.NoError(t, ic.Build(ctx, eRep, interchaintest.InterchainBuildOptions{ | |
TestName: t.Name(), | |
Client: client, | |
NetworkID: network, | |
SkipPathCreation: true, | |
})) | |
// 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 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment