Skip to content

Commit

Permalink
e2e: timeout transfer with grandpa light client (#5018)
Browse files Browse the repository at this point in the history
  • Loading branch information
charleenfei authored and damiannolan committed Dec 5, 2023
1 parent 147f976 commit e784660
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 54 deletions.
2 changes: 1 addition & 1 deletion e2e/sample.config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ relayers:
tag: "latest"
- id: hyperspace
image: ghcr.io/misko9/hyperspace
tag: "local"
tag: "timeout"

cometbft:
logLevel: info
Expand Down
223 changes: 171 additions & 52 deletions e2e/tests/wasm/grandpa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ type GrandpaTestSuite struct {
// * send transfer over ibc
func (s *GrandpaTestSuite) TestMsgTransfer_Succeeds_GrandpaContract() {
ctx := context.Background()
t := s.T()

chainA, chainB := s.GetGrandpaTestChains()

Expand Down Expand Up @@ -109,6 +110,15 @@ func (s *GrandpaTestSuite) TestMsgTransfer_Succeeds_GrandpaContract() {
fundAmount := int64(12_333_000_000_000)
polkadotUser, cosmosUser := s.fundUsers(ctx, fundAmount, polkadotChain, cosmosChain)

// TODO: this can be refactored to broadcast a MsgTransfer instead of CLI.
// https://github.com/cosmos/ibc-go/issues/4963
amountToSend := int64(1_770_000)
transfer := ibc.WalletAmount{
Address: polkadotUser.FormattedAddress(),
Denom: cosmosChain.Config().Denom,
Amount: math.NewInt(amountToSend),
}

pathName := s.GetPathName(0)

err = r.GeneratePath(ctx, eRep, cosmosChain.Config().ChainID, polkadotChain.Config().ChainID, pathName)
Expand All @@ -135,79 +145,188 @@ func (s *GrandpaTestSuite) TestMsgTransfer_Succeeds_GrandpaContract() {
// Start relayer
s.Require().NoError(r.StartRelayer(ctx, eRep, pathName))

t.Run("send successful IBC transfer from Cosmos to Polkadot parachain", func(t *testing.T) {
// Send 1.77 stake from cosmosUser to parachainUser
tx, err := cosmosChain.SendIBCTransfer(ctx, "channel-0", cosmosUser.KeyName(), transfer, ibc.TransferOptions{})
s.Require().NoError(tx.Validate(), "source ibc transfer tx is invalid")
s.Require().NoError(err)
// verify token balance for cosmos user has decreased
balance, err := cosmosChain.GetBalance(ctx, cosmosUser.FormattedAddress(), cosmosChain.Config().Denom)
s.Require().NoError(err)
s.Require().Equal(balance, math.NewInt(fundAmount-amountToSend), "unexpected cosmos user balance after first tx")
err = testutil.WaitForBlocks(ctx, 15, cosmosChain, polkadotChain)
s.Require().NoError(err)

// Verify tokens arrived on parachain user
parachainUserStake, err := polkadotChain.GetIbcBalance(ctx, string(polkadotUser.Address()), 2)
s.Require().NoError(err)
s.Require().Equal(amountToSend, parachainUserStake.Amount.Int64(), "unexpected parachain user balance after first tx")
})

t.Run("send two successful IBC transfers from Polkadot parachain to Cosmos, first with ibc denom, second with parachain denom", func(t *testing.T) {
// Send 1.16 stake from parachainUser to cosmosUser
amountToReflect := int64(1_160_000)
reflectTransfer := ibc.WalletAmount{
Address: cosmosUser.FormattedAddress(),
Denom: "2", // stake
Amount: math.NewInt(amountToReflect),
}
_, err := polkadotChain.SendIBCTransfer(ctx, "channel-0", polkadotUser.KeyName(), reflectTransfer, ibc.TransferOptions{})
s.Require().NoError(err)

// Send 1.88 "UNIT" from Alice to cosmosUser
amountUnits := math.NewInt(1_880_000_000_000)
unitTransfer := ibc.WalletAmount{
Address: cosmosUser.FormattedAddress(),
Denom: "1", // UNIT
Amount: amountUnits,
}
_, err = polkadotChain.SendIBCTransfer(ctx, "channel-0", "alice", unitTransfer, ibc.TransferOptions{})
s.Require().NoError(err)

// Wait for MsgRecvPacket on cosmos chain
finalStakeBal := math.NewInt(fundAmount - amountToSend + amountToReflect)
err = cosmos.PollForBalance(ctx, cosmosChain, 20, ibc.WalletAmount{
Address: cosmosUser.FormattedAddress(),
Denom: cosmosChain.Config().Denom,
Amount: finalStakeBal,
})
s.Require().NoError(err)

// Wait for a new update state
err = testutil.WaitForBlocks(ctx, 5, cosmosChain, polkadotChain)
s.Require().NoError(err)

// Verify cosmos user's final "stake" balance
cosmosUserStakeBal, err := cosmosChain.GetBalance(ctx, cosmosUser.FormattedAddress(), cosmosChain.Config().Denom)
s.Require().NoError(err)
s.Require().True(cosmosUserStakeBal.Equal(finalStakeBal))

// Verify cosmos user's final "unit" balance
unitDenomTrace := transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom("transfer", "channel-0", "UNIT"))
cosmosUserUnitBal, err := cosmosChain.GetBalance(ctx, cosmosUser.FormattedAddress(), unitDenomTrace.IBCDenom())
s.Require().NoError(err)
s.Require().True(cosmosUserUnitBal.Equal(amountUnits))

// Verify parachain user's final "unit" balance (will be less than expected due gas costs for stake tx)
parachainUserUnits, err := polkadotChain.GetIbcBalance(ctx, string(polkadotUser.Address()), 1)
s.Require().NoError(err)
s.Require().True(parachainUserUnits.Amount.LTE(math.NewInt(fundAmount)), "parachain user's final unit amount not expected")

// Verify parachain user's final "stake" balance
parachainUserStake, err := polkadotChain.GetIbcBalance(ctx, string(polkadotUser.Address()), 2)
s.Require().NoError(err)
s.Require().True(parachainUserStake.Amount.Equal(math.NewInt(amountToSend-amountToReflect)), "parachain user's final stake amount not expected")
})
}

// TestMsgTransfer_TimesOut_GrandpaContract
// sets up cosmos and polkadot chains, hyperspace relayer, and funds users on both chains
// * sends transfer over ibc channel, this transfer should timeout
func (s *GrandpaTestSuite) TestMsgTransfer_TimesOut_GrandpaContract() {
ctx := context.Background()
t := s.T()

chainA, chainB := s.GetGrandpaTestChains()

polkadotChain := chainA.(*polkadot.PolkadotChain)
cosmosChain := chainB.(*cosmos.CosmosChain)

// we explicitly skip path creation as the contract needs to be uploaded before we can create clients.
r := s.ConfigureRelayer(ctx, polkadotChain, cosmosChain, nil, func(options *interchaintest.InterchainBuildOptions) {
options.SkipPathCreation = true
})

s.InitGRPCClients(cosmosChain)

cosmosWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount)

file, err := os.Open("contracts/ics10_grandpa_cw.wasm")
s.Require().NoError(err)

checksum := s.PushNewWasmClientProposal(ctx, cosmosChain, cosmosWallet, file)

s.Require().NotEmpty(checksum, "checksum was empty but should not have been")

eRep := s.GetRelayerExecReporter()

// Set client contract hash in cosmos chain config
err = r.SetClientContractHash(ctx, eRep, cosmosChain.Config(), checksum)
s.Require().NoError(err)

// Ensure parachain has started (starts 1 session/epoch after relay chain)
err = testutil.WaitForBlocks(ctx, 1, polkadotChain)
s.Require().NoError(err, "polkadot chain failed to make blocks")

// Fund users on both cosmos and parachain, mints Asset 1 for Alice
fundAmount := int64(12_333_000_000_000)
polkadotUser, cosmosUser := s.fundUsers(ctx, fundAmount, polkadotChain, cosmosChain)

// TODO: this can be refactored to broadcast a MsgTransfer instead of CLI.
// https://github.com/cosmos/ibc-go/issues/4963
// Send 1.77 stake from cosmosUser to parachainUser
amountToSend := int64(1_770_000)
transfer := ibc.WalletAmount{
Address: polkadotUser.FormattedAddress(),
Denom: cosmosChain.Config().Denom,
Amount: math.NewInt(amountToSend),
}
tx, err := cosmosChain.SendIBCTransfer(ctx, "channel-0", cosmosUser.KeyName(), transfer, ibc.TransferOptions{})
s.Require().NoError(err)
s.Require().NoError(tx.Validate()) // test source wallet has decreased funds
err = testutil.WaitForBlocks(ctx, 15, cosmosChain, polkadotChain)
s.Require().NoError(err)

// Verify tokens arrived on parachain user
parachainUserStake, err := polkadotChain.GetIbcBalance(ctx, string(polkadotUser.Address()), 2)
s.Require().NoError(err)
s.Require().Equal(amountToSend, parachainUserStake.Amount.Int64(), "parachain user's stake amount not expected after first tx")
pathName := s.GetPathName(0)

// Send 1.16 stake from parachainUser to cosmosUser
amountToReflect := int64(1_160_000)
reflectTransfer := ibc.WalletAmount{
Address: cosmosUser.FormattedAddress(),
Denom: "2", // stake
Amount: math.NewInt(amountToReflect),
}
_, err = polkadotChain.SendIBCTransfer(ctx, "channel-0", polkadotUser.KeyName(), reflectTransfer, ibc.TransferOptions{})
err = r.GeneratePath(ctx, eRep, cosmosChain.Config().ChainID, polkadotChain.Config().ChainID, pathName)
s.Require().NoError(err)

// Send 1.88 "UNIT" from Alice to cosmosUser
amountUnits := math.NewInt(1_880_000_000_000)
unitTransfer := ibc.WalletAmount{
Address: cosmosUser.FormattedAddress(),
Denom: "1", // UNIT
Amount: amountUnits,
}
_, err = polkadotChain.SendIBCTransfer(ctx, "channel-0", "alice", unitTransfer, ibc.TransferOptions{})
// Create new clients
err = r.CreateClients(ctx, eRep, pathName, ibc.DefaultClientOpts())
s.Require().NoError(err)

// Wait for MsgRecvPacket on cosmos chain
finalStakeBal := math.NewInt(fundAmount - amountToSend + amountToReflect)
err = cosmos.PollForBalance(ctx, cosmosChain, 20, ibc.WalletAmount{
Address: cosmosUser.FormattedAddress(),
Denom: cosmosChain.Config().Denom,
Amount: finalStakeBal,
})
err = testutil.WaitForBlocks(ctx, 1, cosmosChain, polkadotChain) // these 1 block waits seem to be needed to reduce flakiness
s.Require().NoError(err)

// Wait for a new update state
err = testutil.WaitForBlocks(ctx, 5, cosmosChain, polkadotChain)
// Create a new connection
err = r.CreateConnections(ctx, eRep, pathName)
s.Require().NoError(err)

// Verify cosmos user's final "stake" balance
cosmosUserStakeBal, err := cosmosChain.GetBalance(ctx, cosmosUser.FormattedAddress(), cosmosChain.Config().Denom)
err = testutil.WaitForBlocks(ctx, 1, cosmosChain, polkadotChain)
s.Require().NoError(err)
s.Require().True(cosmosUserStakeBal.Equal(finalStakeBal))

// Verify cosmos user's final "unit" balance
unitDenomTrace := transfertypes.ParseDenomTrace(transfertypes.GetPrefixedDenom("transfer", "channel-0", "UNIT"))
cosmosUserUnitBal, err := cosmosChain.GetBalance(ctx, cosmosUser.FormattedAddress(), unitDenomTrace.IBCDenom())
// Create a new channel & get channels from each chain
err = r.CreateChannel(ctx, eRep, pathName, ibc.DefaultChannelOpts())
s.Require().NoError(err)
s.Require().True(cosmosUserUnitBal.Equal(amountUnits))

// Verify parachain user's final "unit" balance (will be less than expected due gas costs for stake tx)
parachainUserUnits, err := polkadotChain.GetIbcBalance(ctx, string(polkadotUser.Address()), 1)
err = testutil.WaitForBlocks(ctx, 1, cosmosChain, polkadotChain)
s.Require().NoError(err)
s.Require().True(parachainUserUnits.Amount.LTE(math.NewInt(fundAmount)), "parachain user's final unit amount not expected")

// Verify parachain user's final "stake" balance
parachainUserStake, err = polkadotChain.GetIbcBalance(ctx, string(polkadotUser.Address()), 2)
s.Require().NoError(err)
s.Require().True(parachainUserStake.Amount.Equal(math.NewInt(amountToSend-amountToReflect)), "parachain user's final stake amount not expected")
// Start relayer
s.Require().NoError(r.StartRelayer(ctx, eRep, pathName))

t.Run("IBC transfer from Cosmos chain to Polkadot parachain times out", func(t *testing.T) {
// Stop relayer
s.Require().NoError(r.StopRelayer(ctx, s.GetRelayerExecReporter()))

tx, err := cosmosChain.SendIBCTransfer(ctx, "channel-0", cosmosUser.KeyName(), transfer, ibc.TransferOptions{Timeout: testvalues.ImmediatelyTimeout()})
s.Require().NoError(err)
s.Require().NoError(tx.Validate(), "source ibc transfer tx is invalid")
time.Sleep(time.Nanosecond * 1) // want it to timeout immediately

// check that tokens are escrowed
actualBalance, err := cosmosChain.GetBalance(ctx, cosmosUser.FormattedAddress(), cosmosChain.Config().Denom)
s.Require().NoError(err)
expected := fundAmount - amountToSend
s.Require().Equal(expected, actualBalance.Int64())

// start relayer
s.Require().NoError(r.StartRelayer(ctx, s.GetRelayerExecReporter(), s.GetPathName(0)))
err = testutil.WaitForBlocks(ctx, 15, polkadotChain, cosmosChain)
s.Require().NoError(err)

// ensure that receiver on parachain did not receive any tokens
receiverBalance, err := polkadotChain.GetIbcBalance(ctx, polkadotUser.FormattedAddress(), 2)
s.Require().NoError(err)
s.Require().Equal(int64(0), receiverBalance.Amount.Int64())

// check that tokens have been refunded to sender address
senderBalance, err := cosmosChain.GetBalance(ctx, cosmosUser.FormattedAddress(), cosmosChain.Config().Denom)
s.Require().NoError(err)
s.Require().Equal(fundAmount, senderBalance.Int64())
})
}

// TestMsgMigrateContract_Success_GrandpaContract features
Expand Down
2 changes: 1 addition & 1 deletion e2e/testsuite/testconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const (
defaultRlyTag = "latest"

// TODO: https://github.com/cosmos/ibc-go/issues/4965
defaultHyperspaceTag = "local"
defaultHyperspaceTag = "timeout"
// defaultHermesTag is the tag that will be used if no relayer tag is specified for hermes.
defaultHermesTag = "v1.7.0"
// defaultChainTag is the tag that will be used for the chains if none is specified.
Expand Down

0 comments on commit e784660

Please sign in to comment.