diff --git a/Makefile b/Makefile index 4501452..afc561f 100644 --- a/Makefile +++ b/Makefile @@ -251,7 +251,7 @@ endif ############################################################################### ictest-basic: - @echo "Running basic e2e test" + @echo "Running Basic e2e test" @cd interchaintest && go test -race -v -run TestBasicChain . ictest-ibc: @@ -260,20 +260,16 @@ ictest-ibc: ictest-ics: @echo "Running ICS e2e test" - @cd interchaintest && go test -race -v -run TestICSConnection . + @cd interchaintest && go test -race -v -run TestICSProviderSuite ./integration ictest-wasm: - @echo "Running cosmwasm e2e test" + @echo "Running Cosmwasm e2e test" @cd interchaintest && go test -race -v -run TestCosmWasmIntegration . ictest-packetforward: - @echo "Running packet forward middleware e2e test" + @echo "Running PacketForward e2e test" @cd interchaintest && go test -race -v -run TestPacketForwardMiddleware . -ictest-poa: - @echo "Running proof of authority e2e test" - @cd interchaintest && go test -race -v -run TestPOA . - ictest-tokenfactory: @echo "Running token factory e2e test" @cd interchaintest && go test -race -v -run TestTokenFactory . diff --git a/interchaintest/basic_test.go b/interchaintest/basic_test.go new file mode 100644 index 0000000..9435fad --- /dev/null +++ b/interchaintest/basic_test.go @@ -0,0 +1,58 @@ +package e2e + +import ( + "context" + "testing" + + "cosmossdk.io/math" + + "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v8/testreporter" + "github.com/stretchr/testify/require" + "go.uber.org/zap/zaptest" +) + +func TestBasicChain(t *testing.T) { + ctx := context.Background() + rep := testreporter.NewNopReporter() + eRep := rep.RelayerExecReporter(t) + client, network := interchaintest.DockerSetup(t) + + cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ + &DefaultChainSpec, + }) + + chains, err := cf.Chains(t.Name()) + require.NoError(t, err) + + chain := chains[0].(*cosmos.CosmosChain) + + // Setup Interchain + ic := interchaintest.NewInterchain(). + AddChain(chain) + + require.NoError(t, ic.Build(ctx, eRep, interchaintest.InterchainBuildOptions{ + TestName: t.Name(), + Client: client, + NetworkID: network, + SkipPathCreation: false, + })) + t.Cleanup(func() { + _ = ic.Close() + }) + + amt := math.NewInt(10_000_000) + users := interchaintest.GetAndFundTestUsers(t, ctx, "default", amt, + chain, + ) + user := users[0] + + t.Run("validate funding", func(t *testing.T) { + bal, err := chain.BankQueryBalance(ctx, user.FormattedAddress(), chain.Config().Denom) + require.NoError(t, err) + require.EqualValues(t, amt, bal) + + }) + +} diff --git a/interchaintest/cosmwasm_test.go b/interchaintest/cosmwasm_test.go new file mode 100644 index 0000000..cdfdbfa --- /dev/null +++ b/interchaintest/cosmwasm_test.go @@ -0,0 +1,102 @@ +package e2e + +import ( + "context" + "encoding/json" + "testing" + + "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" +) + +type GetCountResponse struct { + // {"data":{"count":0}} + Data *GetCountObj `json:"data"` +} + +type GetCountObj struct { + Count int64 `json:"count"` +} + +func TestCosmWasmIntegration(t *testing.T) { + t.Parallel() + ctx := context.Background() + rep := testreporter.NewNopReporter() + eRep := rep.RelayerExecReporter(t) + client, network := interchaintest.DockerSetup(t) + + cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ + &DefaultChainSpec, + }) + + chains, err := cf.Chains(t.Name()) + require.NoError(t, err) + + chain := chains[0].(*cosmos.CosmosChain) + + // Setup Interchain + ic := interchaintest.NewInterchain(). + AddChain(chain) + + require.NoError(t, ic.Build(ctx, eRep, interchaintest.InterchainBuildOptions{ + TestName: t.Name(), + Client: client, + NetworkID: network, + SkipPathCreation: false, + })) + t.Cleanup(func() { + _ = ic.Close() + }) + + users := interchaintest.GetAndFundTestUsers(t, ctx, t.Name(), GenesisFundsAmount, chain) + user := users[0] + + StdExecute(t, ctx, chain, user) +} + +func StdExecute(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, user ibc.Wallet) (contractAddr string) { + _, contractAddr = SetupContract(t, ctx, chain, user.KeyName(), "contracts/cw_template.wasm", `{"count":0}`) + chain.ExecuteContract(ctx, user.KeyName(), contractAddr, `{"increment":{}}`, "--fees", "10000"+chain.Config().Denom) + + var res GetCountResponse + err := SmartQueryString(t, ctx, chain, contractAddr, `{"get_count":{}}`, &res) + require.NoError(t, err) + + require.Equal(t, int64(1), res.Data.Count) + + return contractAddr +} + +func SmartQueryString(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, contractAddr, queryMsg string, res interface{}) error { + var jsonMap map[string]interface{} + if err := json.Unmarshal([]byte(queryMsg), &jsonMap); err != nil { + t.Fatal(err) + } + err := chain.QueryContract(ctx, contractAddr, jsonMap, &res) + return err +} + +func SetupContract(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, keyname string, fileLoc string, message string, extraFlags ...string) (codeId, contract string) { + codeId, err := chain.StoreContract(ctx, keyname, fileLoc) + if err != nil { + t.Fatal(err) + } + + needsNoAdminFlag := true + for _, flag := range extraFlags { + if flag == "--admin" { + needsNoAdminFlag = false + } + } + + contractAddr, err := chain.InstantiateContract(ctx, keyname, codeId, message, needsNoAdminFlag, extraFlags...) + if err != nil { + t.Fatal(err) + } + + return codeId, contractAddr +} diff --git a/interchaintest/ibc_rate_limit_test.go b/interchaintest/ibc_rate_limit_test.go new file mode 100644 index 0000000..d32c5e6 --- /dev/null +++ b/interchaintest/ibc_rate_limit_test.go @@ -0,0 +1,102 @@ +package e2e + +import ( + "context" + "fmt" + "testing" + + "cosmossdk.io/math" + "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + interchaintestrelayer "github.com/strangelove-ventures/interchaintest/v8/relayer" + "github.com/strangelove-ventures/interchaintest/v8/testreporter" + "github.com/stretchr/testify/require" + "go.uber.org/zap/zapcore" + "go.uber.org/zap/zaptest" +) + +func TestIBCRateLimit(t *testing.T) { + if testing.Short() { + t.Skip() + } + + t.Parallel() + ctx := context.Background() + rep := testreporter.NewNopReporter() + eRep := rep.RelayerExecReporter(t) + client, network := interchaintest.DockerSetup(t) + + cs := &DefaultChainSpec + cs.ModifyGenesis = cosmos.ModifyGenesis([]cosmos.GenesisKV{cosmos.NewGenesisKV("app_state.ratelimit.blacklisted_denoms", []string{cs.Denom})}) + + cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ + cs, + &SecondDefaultChainSpec, + }) + + chains, err := cf.Chains(t.Name()) + require.NoError(t, err) + + chain := chains[0].(*cosmos.CosmosChain) + secondary := chains[1].(*cosmos.CosmosChain) + + // Relayer Factory + r := interchaintest.NewBuiltinRelayerFactory( + ibc.CosmosRly, + zaptest.NewLogger(t, zaptest.Level(zapcore.DebugLevel)), + interchaintestrelayer.CustomDockerImage(RelayerRepo, RelayerVersion, "100:1000"), + interchaintestrelayer.StartupFlags("--processor", "events", "--block-history", "200"), + ).Build(t, client, network) + + ic := interchaintest.NewInterchain(). + AddChain(chain). + AddChain(secondary). + AddRelayer(r, "relayer") + + ic = ic.AddLink(interchaintest.InterchainLink{ + Chain1: chain, + Chain2: secondary, + Relayer: r, + Path: ibcPath, + }) + + // Build interchain + require.NoError(t, ic.Build(ctx, eRep, interchaintest.InterchainBuildOptions{ + TestName: t.Name(), + Client: client, + NetworkID: network, + SkipPathCreation: false, + })) + + // Create and Fund User Wallets + fundAmount := math.NewInt(10_000_000) + users := interchaintest.GetAndFundTestUsers(t, ctx, "default", fundAmount, chain, secondary) + userA, userB := users[0], users[1] + + userAInitial, err := chain.GetBalance(ctx, userA.FormattedAddress(), chain.Config().Denom) + fmt.Println("userAInitial", userAInitial) + require.NoError(t, err) + require.True(t, userAInitial.Equal(fundAmount)) + + // Get Channel ID + aInfo, err := r.GetChannels(ctx, eRep, chain.Config().ChainID) + require.NoError(t, err) + aChannelID, err := getTransferChannel(aInfo) + require.NoError(t, err) + fmt.Println("aChannelID", aChannelID) + + // Send Transaction + amountToSend := math.NewInt(1_000_000) + dstAddress := userB.FormattedAddress() + transfer := ibc.WalletAmount{ + Address: dstAddress, + Denom: chain.Config().Denom, + Amount: amountToSend, + } + + // Validate transfer error occurs + _, err = chain.SendIBCTransfer(ctx, aChannelID, userA.KeyName(), transfer, ibc.TransferOptions{}) + require.Error(t, err) + require.Contains(t, err.Error(), "denom is blacklisted") +} diff --git a/interchaintest/ibc_test.go b/interchaintest/ibc_test.go new file mode 100644 index 0000000..6ce4dfc --- /dev/null +++ b/interchaintest/ibc_test.go @@ -0,0 +1,125 @@ +package e2e + +import ( + "context" + "testing" + + "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" + interchaintestrelayer "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/zapcore" + "go.uber.org/zap/zaptest" +) + +const ( + ibcPath = "ibc-path" +) + +func TestIBCBasic(t *testing.T) { + t.Parallel() + + ctx := context.Background() + rep := testreporter.NewNopReporter() + eRep := rep.RelayerExecReporter(t) + client, network := interchaintest.DockerSetup(t) + + cs := &DefaultChainSpec + cs.ModifyGenesis = cosmos.ModifyGenesis([]cosmos.GenesisKV{cosmos.NewGenesisKV("app_state.ratelimit.blacklisted_denoms", []string{})}) + + cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ + cs, + &SecondDefaultChainSpec, + }) + + chains, err := cf.Chains(t.Name()) + require.NoError(t, err) + + chainA, chainB := chains[0].(*cosmos.CosmosChain), chains[1].(*cosmos.CosmosChain) + + // Relayer Factory + r := interchaintest.NewBuiltinRelayerFactory( + ibc.CosmosRly, + zaptest.NewLogger(t, zaptest.Level(zapcore.DebugLevel)), + interchaintestrelayer.CustomDockerImage(RelayerRepo, RelayerVersion, "100:1000"), + interchaintestrelayer.StartupFlags("--processor", "events", "--block-history", "200"), + ).Build(t, client, network) + + ic := interchaintest.NewInterchain(). + AddChain(chainA). + AddChain(chainB). + AddRelayer(r, "relayer") + + ic = ic.AddLink(interchaintest.InterchainLink{ + Chain1: chainA, + Chain2: chainB, + Relayer: r, + Path: ibcPath, + }) + + // Build interchain + require.NoError(t, ic.Build(ctx, eRep, interchaintest.InterchainBuildOptions{ + TestName: t.Name(), + Client: client, + NetworkID: network, + SkipPathCreation: false, + })) + + require.NoError(t, testutil.WaitForBlocks(ctx, 5, chainA)) + + // Create and Fund User Wallets + fundAmount := math.NewInt(10_000_000) + users := interchaintest.GetAndFundTestUsers(t, ctx, "default", fundAmount, chainA, chainB) + userA := users[0] + userB := users[1] + + userAInitial, err := chainA.GetBalance(ctx, userA.FormattedAddress(), chainA.Config().Denom) + require.NoError(t, err) + require.True(t, userAInitial.Equal(fundAmount)) + + // Get Channel ID + aInfo, err := r.GetChannels(ctx, eRep, chainA.Config().ChainID) + require.NoError(t, err) + aChannelID, err := getTransferChannel(aInfo) + require.NoError(t, err) + + bInfo, err := r.GetChannels(ctx, eRep, chainB.Config().ChainID) + require.NoError(t, err) + bChannelID, err := getTransferChannel(bInfo) + require.NoError(t, err) + + // Send Transaction + amountToSend := math.NewInt(1_000_000) + dstAddress := userB.FormattedAddress() + transfer := ibc.WalletAmount{ + Address: dstAddress, + Denom: chainA.Config().Denom, + Amount: amountToSend, + } + + _, err = chainA.SendIBCTransfer(ctx, aChannelID, userA.KeyName(), transfer, ibc.TransferOptions{}) + require.NoError(t, err) + + // relay MsgRecvPacket to chainB, then MsgAcknowledgement back to chainA + require.NoError(t, r.Flush(ctx, eRep, ibcPath, aChannelID)) + + // test source wallet has decreased funds + expectedBal := userAInitial.Sub(amountToSend) + aNewBal, err := chainA.GetBalance(ctx, userA.FormattedAddress(), chainA.Config().Denom) + require.NoError(t, err) + require.True(t, aNewBal.Equal(expectedBal)) + + // Trace IBC Denom + srcDenomTrace := transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom("transfer", bChannelID, chainA.Config().Denom)) + dstIbcDenom := srcDenomTrace.IBCDenom() + + // Test destination wallet has increased funds + bNewBal, err := chainB.GetBalance(ctx, userB.FormattedAddress(), dstIbcDenom) + require.NoError(t, err) + require.True(t, bNewBal.Equal(amountToSend)) +} diff --git a/interchaintest/integration/README.md b/interchaintest/integration/README.md new file mode 100644 index 0000000..e73a267 --- /dev/null +++ b/interchaintest/integration/README.md @@ -0,0 +1,3 @@ +# ICS Integration Test + +Prysm Provider E2E Integration Test to verify the ICS implementation with consumer chains. diff --git a/interchaintest/chainsuite/chain.go b/interchaintest/integration/chainsuite/chain.go similarity index 100% rename from interchaintest/chainsuite/chain.go rename to interchaintest/integration/chainsuite/chain.go diff --git a/interchaintest/chainsuite/chain_spec_provider.go b/interchaintest/integration/chainsuite/chain_spec_provider.go similarity index 100% rename from interchaintest/chainsuite/chain_spec_provider.go rename to interchaintest/integration/chainsuite/chain_spec_provider.go diff --git a/interchaintest/chainsuite/config.go b/interchaintest/integration/chainsuite/config.go similarity index 100% rename from interchaintest/chainsuite/config.go rename to interchaintest/integration/chainsuite/config.go diff --git a/interchaintest/chainsuite/context.go b/interchaintest/integration/chainsuite/context.go similarity index 100% rename from interchaintest/chainsuite/context.go rename to interchaintest/integration/chainsuite/context.go diff --git a/interchaintest/chainsuite/query_types.go b/interchaintest/integration/chainsuite/query_types.go similarity index 100% rename from interchaintest/chainsuite/query_types.go rename to interchaintest/integration/chainsuite/query_types.go diff --git a/interchaintest/provider_suite.go b/interchaintest/integration/provider_suite.go similarity index 89% rename from interchaintest/provider_suite.go rename to interchaintest/integration/provider_suite.go index b9b3536..35dd3b0 100644 --- a/interchaintest/provider_suite.go +++ b/interchaintest/integration/provider_suite.go @@ -3,7 +3,7 @@ package e2e import ( "context" - "github.com/lightlabs-dev/prysm/interchaintest/chainsuite" + "github.com/lightlabs-dev/prysm/interchaintest/integration/chainsuite" "github.com/stretchr/testify/suite" ) diff --git a/interchaintest/provider_test.go b/interchaintest/integration/provider_test.go similarity index 99% rename from interchaintest/provider_test.go rename to interchaintest/integration/provider_test.go index 742e779..36a20f3 100644 --- a/interchaintest/provider_test.go +++ b/interchaintest/integration/provider_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "github.com/lightlabs-dev/prysm/interchaintest/chainsuite" + "github.com/lightlabs-dev/prysm/interchaintest/integration/chainsuite" govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" providertypes "github.com/cosmos/interchain-security/v6/x/ccv/provider/types" @@ -14,7 +14,7 @@ import ( "github.com/stretchr/testify/suite" ) -func TestProviderSuite(t *testing.T) { +func TestICSProviderSuite(t *testing.T) { s := &ProviderSuite{} suite.Run(t, s) diff --git a/interchaintest/provider_utils.go b/interchaintest/integration/provider_utils.go similarity index 100% rename from interchaintest/provider_utils.go rename to interchaintest/integration/provider_utils.go diff --git a/interchaintest/old.zip b/interchaintest/old.zip deleted file mode 100644 index ad4740c..0000000 Binary files a/interchaintest/old.zip and /dev/null differ diff --git a/interchaintest/packetforward_test.go b/interchaintest/packetforward_test.go new file mode 100644 index 0000000..a8fe8b4 --- /dev/null +++ b/interchaintest/packetforward_test.go @@ -0,0 +1,232 @@ +package e2e + +import ( + "context" + "encoding/json" + "testing" + "time" + + "cosmossdk.io/math" + "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + interchaintestrelayer "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" + + transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +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() + } + + ctx := context.Background() + rep := testreporter.NewNopReporter() + eRep := rep.RelayerExecReporter(t) + client, network := interchaintest.DockerSetup(t) + + var ( + chainID_A, chainID_B, chainID_C = "chain-a", "chain-b", "chain-c" + chainA, chainB, chainC *cosmos.CosmosChain + ) + + // base config which all networks will use as defaults. + baseCfg := DefaultChainConfig + + // Set specific chain ids for each so they are their own unique networks + baseCfg.ChainID = chainID_A + configA := baseCfg + + baseCfg.ChainID = chainID_B + configB := baseCfg + + baseCfg.ChainID = chainID_C + configC := baseCfg + + // Create chain factory with multiple individual networks. + numVals := 1 + numFullNodes := 0 + + cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ + { + Name: configA.Name, + ChainConfig: configA, + NumValidators: &numVals, + NumFullNodes: &numFullNodes, + }, + { + Name: configA.Name, + ChainConfig: configB, + NumValidators: &numVals, + NumFullNodes: &numFullNodes, + }, + { + Name: configA.Name, + ChainConfig: configC, + NumValidators: &numVals, + NumFullNodes: &numFullNodes, + }, + }) + + // Get chains from the chain factory + chains, err := cf.Chains(t.Name()) + require.NoError(t, err) + + chainA, chainB, chainC = chains[0].(*cosmos.CosmosChain), chains[1].(*cosmos.CosmosChain), chains[2].(*cosmos.CosmosChain) + + r := interchaintest.NewBuiltinRelayerFactory( + ibc.CosmosRly, + zaptest.NewLogger(t), + interchaintestrelayer.CustomDockerImage(RelayerRepo, RelayerVersion, "100:1000"), + interchaintestrelayer.StartupFlags("--processor", "events", "--block-history", "100"), + ).Build(t, client, network) + + const pathAB = "ab" + const pathBC = "bc" + + ic := interchaintest.NewInterchain(). + AddChain(chainA). + AddChain(chainB). + AddChain(chainC). + 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, + }) + + 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() + }) + + users := interchaintest.GetAndFundTestUsers(t, ctx, t.Name(), GenesisFundsAmount, chainA, chainB, chainC) + + 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 + + // Start the relayer on all paths + err = r.StartRelayer(ctx, eRep, pathAB, pathBC) + 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) + } + }, + ) + + // Get original account balances + userA, userB, userC := users[0], users[1], users[2] + + var transferAmount math.Int = math.NewInt(100_000) + + // 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) + + firstHopDenomTrace := transfertypes.ParseDenomTrace(firstHopDenom) + secondHopDenomTrace := transfertypes.ParseDenomTrace(secondHopDenom) + + firstHopIBCDenom := firstHopDenomTrace.IBCDenom() + secondHopIBCDenom := secondHopDenomTrace.IBCDenom() + + firstHopEscrowAccount := sdk.MustBech32ifyAddressBytes(chainA.Config().Bech32Prefix, transfertypes.GetEscrowAddress(abChan.PortID, abChan.ChannelID)) + secondHopEscrowAccount := sdk.MustBech32ifyAddressBytes(chainB.Config().Bech32Prefix, transfertypes.GetEscrowAddress(bcChan.PortID, bcChan.ChannelID)) + + t.Run("multi-hop a->b->c", func(t *testing.T) { + // Send packet from Chain A->Chain B->Chain C + + transfer := ibc.WalletAmount{ + Address: userB.FormattedAddress(), + Denom: chainA.Config().Denom, + Amount: transferAmount, + } + + firstHopMetadata := &PacketMetadata{ + Forward: &ForwardMetadata{ + Receiver: userC.FormattedAddress(), + Channel: bcChan.ChannelID, + Port: bcChan.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) + + chainCBalance, err := chainC.GetBalance(ctx, userC.FormattedAddress(), secondHopIBCDenom) + require.NoError(t, err) + + require.Equal(t, GenesisFundsAmount.Sub(transferAmount).Int64(), chainABalance.Int64()) + require.Equal(t, int64(0), chainBBalance.Int64()) + require.Equal(t, int64(100000), chainCBalance.Int64()) + + 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.Equal(t, transferAmount.Int64(), firstHopEscrowBalance.Int64()) + require.Equal(t, transferAmount.Int64(), secondHopEscrowBalance.Int64()) + }) +} diff --git a/interchaintest/tokenfactory_test.go b/interchaintest/tokenfactory_test.go new file mode 100644 index 0000000..d25cc04 --- /dev/null +++ b/interchaintest/tokenfactory_test.go @@ -0,0 +1,88 @@ +package e2e + +import ( + "context" + "testing" + + "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v8/testreporter" + "github.com/stretchr/testify/require" + "go.uber.org/zap/zaptest" +) + +func TestTokenFactory(t *testing.T) { + if testing.Short() { + t.Skip("skipping in short mode") + } + + ctx := context.Background() + rep := testreporter.NewNopReporter() + eRep := rep.RelayerExecReporter(t) + client, network := interchaintest.DockerSetup(t) + + cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ + &DefaultChainSpec, + }) + + chains, err := cf.Chains(t.Name()) + require.NoError(t, err) + + chain := chains[0].(*cosmos.CosmosChain) + + ic := interchaintest.NewInterchain().AddChain(chain) + + require.NoError(t, ic.Build(ctx, eRep, interchaintest.InterchainBuildOptions{ + TestName: t.Name(), + Client: client, + NetworkID: network, + SkipPathCreation: false, + })) + + users := interchaintest.GetAndFundTestUsers(t, ctx, "default", GenesisFundsAmount, chain, chain) + user := users[0] + user2 := users[1] + + uaddr := user.FormattedAddress() + uaddr2 := user2.FormattedAddress() + + node := chain.GetNode() + + tfDenom, _, err := node.TokenFactoryCreateDenom(ctx, user, "ictestdenom", 5_000_000) + t.Log("TF Denom: ", tfDenom) + require.NoError(t, err) + + t.Run("Mint TF Denom to user", func(t *testing.T) { + node.TokenFactoryMintDenom(ctx, user.FormattedAddress(), tfDenom, 100) + if balance, err := chain.GetBalance(ctx, uaddr, tfDenom); err != nil { + t.Fatal(err) + } else if balance.Int64() != 100 { + t.Fatal("balance not 100") + } + }) + + t.Run("Mint TF Denom to another user", func(t *testing.T) { + node.TokenFactoryMintDenomTo(ctx, user.FormattedAddress(), tfDenom, 70, user2.FormattedAddress()) + if balance, err := chain.GetBalance(ctx, uaddr2, tfDenom); err != nil { + t.Fatal(err) + } else if balance.Int64() != 70 { + t.Fatal("balance not 70") + } + }) + + t.Run("Change admin to uaddr2", func(t *testing.T) { + _, err = node.TokenFactoryChangeAdmin(ctx, user.KeyName(), tfDenom, uaddr2) + require.NoError(t, err) + }) + + t.Run("Validate new admin address", func(t *testing.T) { + res, err := chain.TokenFactoryQueryAdmin(ctx, tfDenom) + require.NoError(t, err) + require.EqualValues(t, res.AuthorityMetadata.Admin, uaddr2, "admin not uaddr2. Did not properly transfer.") + }) + + t.Cleanup(func() { + _ = ic.Close() + }) + +}