Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: e2e upgrade tests for v8 -> v9 #6791

Merged
merged 6 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .github/workflows/e2e-upgrade.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,47 @@ jobs:
test: "TestIBCWasmChainUpgrade"
upload-logs: true
relayer-type: hermes

upgrade-v9-hermes:
uses: ./.github/workflows/e2e-test-workflow-call.yml
with:
chain-image: ghcr.io/cosmos/ibc-go-simd
chain-binary: simd
chain-a-tag: v8.3.2
chain-b-tag: v8.3.2
chain-upgrade-tag: pr-6791 # TODO: Update tag to v9.0.0 once it is (pre)released
upgrade-plan-name: "v9"
test-entry-point: "TestUpgradeTestSuite"
test: "TestV8ToV9ChainUpgrade"
upload-logs: true
relayer-type: hermes

upgrade-v9-rly:
uses: ./.github/workflows/e2e-test-workflow-call.yml
with:
chain-image: ghcr.io/cosmos/ibc-go-simd
chain-binary: simd
chain-a-tag: v8.3.2
chain-b-tag: v8.3.2
chain-upgrade-tag: pr-6791 # TODO: Update tag to v9.0.0 once it is (pre)released
upgrade-plan-name: "v9"
test-entry-point: "TestUpgradeTestSuite"
test: "TestV8ToV9ChainUpgrade"
upload-logs: true
relayer-type: rly
relayer-image: ghcr.io/cosmos/relayer
relayer-tag: latest

upgrade-v9-localhost:
uses: ./.github/workflows/e2e-test-workflow-call.yml
with:
chain-image: ghcr.io/cosmos/ibc-go-simd
chain-binary: simd
chain-a-tag: v8.3.2
chain-b-tag: v8.3.2
chain-upgrade-tag: pr-6791 # TODO: Update tag to v9.0.0 once it is (pre)released
upgrade-plan-name: "v9"
test-entry-point: "TestUpgradeTestSuite"
test: "TestV8ToV9ChainUpgrade_Localhost"
upload-logs: true
relayer-type: hermes
2 changes: 2 additions & 0 deletions e2e/tests/upgrades/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ type GenesisTestSuite struct {
func (s *GenesisTestSuite) TestIBCGenesis() {
t := s.T()

haltHeight := int64(100)

chainA, chainB := s.GetChains()

ctx := context.Background()
Expand Down
266 changes: 256 additions & 10 deletions e2e/tests/upgrades/upgrade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,19 @@ import (
"github.com/cosmos/ibc-go/e2e/testsuite/query"
"github.com/cosmos/ibc-go/e2e/testvalues"
feetypes "github.com/cosmos/ibc-go/v8/modules/apps/29-fee/types"
transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types"
v7migrations "github.com/cosmos/ibc-go/v8/modules/core/02-client/migrations/v7"
clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types"
connectiontypes "github.com/cosmos/ibc-go/v8/modules/core/03-connection/types"
channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types"
"github.com/cosmos/ibc-go/v8/modules/core/exported"
solomachine "github.com/cosmos/ibc-go/v8/modules/light-clients/06-solomachine"
localhost "github.com/cosmos/ibc-go/v8/modules/light-clients/09-localhost"
ibctesting "github.com/cosmos/ibc-go/v8/testing"
)

const (
haltHeight = int64(100)
haltHeightOffset = int64(30)
blocksAfterUpgrade = uint64(10)
)

Expand All @@ -62,6 +64,10 @@ func (s *UpgradeTestSuite) CreateUpgradeTestPath(testName string) (ibc.Relayer,
// UpgradeChain upgrades a chain to a specific version using the planName provided.
// The software upgrade proposal is broadcast by the provided wallet.
func (s *UpgradeTestSuite) UpgradeChain(ctx context.Context, chain *cosmos.CosmosChain, wallet ibc.Wallet, planName, currentVersion, upgradeVersion string) {
height, err := chain.GetNode().Height(ctx)
s.Require().NoError(err, "error fetching height before upgrade")

haltHeight := height + haltHeightOffset
plan := upgradetypes.Plan{
Name: planName,
Height: haltHeight,
Expand All @@ -80,14 +86,14 @@ func (s *UpgradeTestSuite) UpgradeChain(ctx context.Context, chain *cosmos.Cosmo
s.ExecuteAndPassGovV1Beta1Proposal(ctx, chain, wallet, upgradeProposal)
}

height, err := chain.Height(ctx)
s.Require().NoError(err, "error fetching height before upgrade")

timeoutCtx, timeoutCtxCancel := context.WithTimeout(ctx, time.Minute*2)
defer timeoutCtxCancel()

err = test.WaitForBlocks(timeoutCtx, int(haltHeight-height)+1, chain)
s.Require().Error(err, "chain did not halt at halt height")
err = test.WaitForCondition(time.Minute*2, time.Second*2, func() (bool, error) {
status, err := chain.GetNode().Client.Status(ctx)
if err != nil {
return false, err
}
return status.SyncInfo.LatestBlockHeight >= haltHeight, nil
})
s.Require().NoError(err, "failed to wait for chain to halt")

var allNodes []test.ChainHeighter
for _, node := range chain.Nodes() {
Expand All @@ -106,7 +112,7 @@ func (s *UpgradeTestSuite) UpgradeChain(ctx context.Context, chain *cosmos.Cosmo
err = chain.StartAllNodes(ctx)
s.Require().NoError(err, "error starting upgraded node(s)")

timeoutCtx, timeoutCtxCancel = context.WithTimeout(ctx, time.Minute*2)
timeoutCtx, timeoutCtxCancel := context.WithTimeout(ctx, time.Minute*2)
defer timeoutCtxCancel()

err = test.WaitForBlocks(timeoutCtx, int(blocksAfterUpgrade), chain)
Expand Down Expand Up @@ -1031,6 +1037,246 @@ func (s *UpgradeTestSuite) TestV8ToV8_1ChainUpgrade_ChannelUpgrades() {
})
}

func (s *UpgradeTestSuite) TestV8ToV9ChainUpgrade() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not something for today, but it seems like it could be a good idea to parameterize these upgrade tests. It looks like a lot of them are 90% the same.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about that as well. Another thing I was thinking about was this notion of conformance test that exists in interhcaintest today. If we had a reasonable set of conformance tests we could potentially run them before and after an upgrade and ensure many things at once.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one of the things I've been thinking about a bit is basically extracting re-usable fns from our existing E2Es and making the actual test cases super short, something like

   PerformTransferV1(chainA, chainB)  
   UpgradeChain(chainA, newVersion)
   PerformTransferV2(chainA, chainB)

Think it will be possible to reduce a huge amount of duplication in the E2Es.

t := s.T()
testCfg := testsuite.LoadConfig()
ctx := context.Background()

testName := t.Name()

relayer, channelA := s.CreateUpgradeTestPath(testName)

chainA, chainB := s.GetChains()
chainADenom := chainA.Config().Denom
chainBDenom := chainB.Config().Denom

chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount)
chainAAddress := chainAWallet.FormattedAddress()

chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount)
chainBAddress := chainBWallet.FormattedAddress()

chainAIBCToken := testsuite.GetIBCToken(chainBDenom, channelA.PortID, channelA.ChannelID)
chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID)

s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks")

t.Run("start relayer", func(t *testing.T) {
s.StartRelayer(relayer, testName)
})

t.Run("transfer native tokens from chainA to chainB", func(t *testing.T) {
transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferCoins(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "")
s.AssertTxSuccess(transferTxResp)

s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1)

actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom())
s.Require().NoError(err)

expected := testvalues.IBCTransferAmount
s.Require().Equal(expected, actualBalance.Int64())
})

t.Run("transfer native tokens from chainB to chainA", func(t *testing.T) {
transferTxResp := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, testvalues.DefaultTransferCoins(chainBDenom), chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA.(*cosmos.CosmosChain)), 0, "")
s.AssertTxSuccess(transferTxResp)

s.AssertPacketRelayed(ctx, chainA, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, 1)

actualBalance, err := query.Balance(ctx, chainA, chainAAddress, chainAIBCToken.IBCDenom())
s.Require().NoError(err)

expected := testvalues.IBCTransferAmount
s.Require().Equal(expected, actualBalance.Int64())
})

s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA), "failed to wait for blocks")

t.Run("upgrade chain", func(t *testing.T) {
govProposalWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount)
s.UpgradeChain(ctx, chainA.(*cosmos.CosmosChain), govProposalWallet, testCfg.UpgradeConfig.PlanName, testCfg.ChainConfigs[0].Tag, testCfg.UpgradeConfig.Tag)
})

t.Run("query denoms after upgrade", func(t *testing.T) {
resp, err := query.TransferDenoms(ctx, chainA)
s.Require().NoError(err)
s.Require().Len(resp.Denoms, 1)
s.Require().Equal(chainAIBCToken, resp.Denoms[0])
})

t.Run("IBC token transfer from chainA to chainB, to make sure the upgrade did not break the packet flow", func(t *testing.T) {
transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferCoins(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "")
s.AssertTxSuccess(transferTxResp)

s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 2)

actualBalance, err := chainB.GetBalance(ctx, chainBAddress, chainBIBCToken.IBCDenom())
s.Require().NoError(err)

expected := testvalues.IBCTransferAmount * 2
s.Require().Equal(expected, actualBalance.Int64())
})
}

func (s *UpgradeTestSuite) TestV8ToV9ChainUpgrade_Localhost() {
t := s.T()
testCfg := testsuite.LoadConfig()
ctx := context.Background()

chainA, chainB := s.GetChains()
chainADenom := chainA.Config().Denom

rlyWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount)
userAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount)
userBWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount)

var (
srcChannelID string
dstChannelID string
)

s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks")

t.Run("open localhost channel", func(t *testing.T) {
var (
msgChanOpenInitRes channeltypes.MsgChannelOpenInitResponse
msgChanOpenTryRes channeltypes.MsgChannelOpenTryResponse
)

msgChanOpenInit := channeltypes.NewMsgChannelOpenInit(
transfertypes.PortID, transfertypes.V1,
channeltypes.UNORDERED, []string{exported.LocalhostConnectionID},
transfertypes.PortID, rlyWallet.FormattedAddress(),
)
txResp := s.BroadcastMessages(ctx, chainA, rlyWallet, msgChanOpenInit)
s.AssertTxSuccess(txResp)
s.Require().NoError(testsuite.UnmarshalMsgResponses(txResp, &msgChanOpenInitRes))
srcChannelID = msgChanOpenInitRes.ChannelId

msgChanOpenTry := channeltypes.NewMsgChannelOpenTry(
transfertypes.PortID, transfertypes.V1,
channeltypes.UNORDERED, []string{exported.LocalhostConnectionID},
transfertypes.PortID, srcChannelID,
transfertypes.V1, localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress(),
)
txResp = s.BroadcastMessages(ctx, chainA, rlyWallet, msgChanOpenTry)
s.AssertTxSuccess(txResp)
s.Require().NoError(testsuite.UnmarshalMsgResponses(txResp, &msgChanOpenTryRes))
dstChannelID = msgChanOpenTryRes.ChannelId

msgChanOpenAck := channeltypes.NewMsgChannelOpenAck(
transfertypes.PortID, srcChannelID,
dstChannelID, transfertypes.V1,
localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress(),
)
txResp = s.BroadcastMessages(ctx, chainA, rlyWallet, msgChanOpenAck)
s.AssertTxSuccess(txResp)

msgChanOpenConfirm := channeltypes.NewMsgChannelOpenConfirm(
transfertypes.PortID, dstChannelID,
localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress(),
)
txResp = s.BroadcastMessages(ctx, chainA, rlyWallet, msgChanOpenConfirm)
s.AssertTxSuccess(txResp)
})

t.Run("ibc transfer over localhost", func(t *testing.T) {
txResp := s.Transfer(ctx, chainA, userAWallet, transfertypes.PortID, srcChannelID, testvalues.DefaultTransferCoins(chainADenom), userAWallet.FormattedAddress(), userBWallet.FormattedAddress(), s.GetTimeoutHeight(ctx, chainA), 0, "")
s.AssertTxSuccess(txResp)

packet, err := ibctesting.ParsePacketFromEvents(txResp.Events)
s.Require().NoError(err)
s.Require().NotNil(packet)

msgRecvPacket := channeltypes.NewMsgRecvPacket(packet, localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress())

txResp = s.BroadcastMessages(ctx, chainA, rlyWallet, msgRecvPacket)
s.AssertTxSuccess(txResp)

ack, err := ibctesting.ParseAckFromEvents(txResp.Events)
s.Require().NoError(err)
s.Require().NotNil(ack)

msgAcknowledgement := channeltypes.NewMsgAcknowledgement(packet, ack, localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress())
txResp = s.BroadcastMessages(ctx, chainA, rlyWallet, msgAcknowledgement)
s.AssertTxSuccess(txResp)

s.AssertPacketRelayed(ctx, chainA, transfertypes.PortID, srcChannelID, 1)
ibcToken := testsuite.GetIBCToken(chainADenom, transfertypes.PortID, dstChannelID)
actualBalance, err := query.Balance(ctx, chainA, userBWallet.FormattedAddress(), ibcToken.IBCDenom())
s.Require().NoError(err)
s.Require().Equal(testvalues.IBCTransferAmount, actualBalance.Int64())
})

t.Run("localhost exists in state before upgrade", func(t *testing.T) {
status, err := query.ClientStatus(ctx, chainA, exported.LocalhostClientID)
s.Require().NoError(err)
s.Require().Equal(exported.Active.String(), status)

state, err := s.ClientState(ctx, chainA, exported.LocalhostClientID)
s.Require().NoError(err)
s.Require().NotNil(state)
})

t.Run("upgrade chain", func(t *testing.T) {
govProposalWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount)
s.UpgradeChain(ctx, chainA.(*cosmos.CosmosChain), govProposalWallet, testCfg.UpgradeConfig.PlanName, testCfg.ChainConfigs[0].Tag, testCfg.UpgradeConfig.Tag)
})

t.Run("localhost does not exist in state after upgrade", func(t *testing.T) {
status, err := query.ClientStatus(ctx, chainA, exported.LocalhostClientID)
s.Require().NoError(err)
s.Require().Equal(exported.Active.String(), status)

state, err := s.ClientState(ctx, chainA, exported.LocalhostClientID)
s.Require().Error(err)
s.Require().Nil(state)
})

t.Run("query localhost transfer channel ends after upgrade", func(t *testing.T) {
channelEndA, err := query.Channel(ctx, chainA, transfertypes.PortID, srcChannelID)
s.Require().NoError(err)
s.Require().NotNil(channelEndA)

channelEndB, err := query.Channel(ctx, chainA, transfertypes.PortID, dstChannelID)
s.Require().NoError(err)
s.Require().NotNil(channelEndB)

s.Require().Equal(channelEndA.ConnectionHops, channelEndB.ConnectionHops)
})

t.Run("ibc transfer back over localhost after upgrade", func(t *testing.T) {
ibcToken := testsuite.GetIBCToken(chainADenom, transfertypes.PortID, dstChannelID)
transferCoins := testvalues.DefaultTransferCoins(ibcToken.IBCDenom())
txResp := s.Transfer(ctx, chainA, userBWallet, transfertypes.PortID, dstChannelID, transferCoins, userBWallet.FormattedAddress(), userAWallet.FormattedAddress(), s.GetTimeoutHeight(ctx, chainA), 0, "")
s.AssertTxSuccess(txResp)

packet, err := ibctesting.ParsePacketFromEvents(txResp.Events)
s.Require().NoError(err)
s.Require().NotNil(packet)

msgRecvPacket := channeltypes.NewMsgRecvPacket(packet, localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress())

txResp = s.BroadcastMessages(ctx, chainA, rlyWallet, msgRecvPacket)
s.AssertTxSuccess(txResp)

ack, err := ibctesting.ParseAckFromEvents(txResp.Events)
s.Require().NoError(err)
s.Require().NotNil(ack)

msgAcknowledgement := channeltypes.NewMsgAcknowledgement(packet, ack, localhost.SentinelProof, clienttypes.ZeroHeight(), rlyWallet.FormattedAddress())
txResp = s.BroadcastMessages(ctx, chainA, rlyWallet, msgAcknowledgement)
s.AssertTxSuccess(txResp)

s.AssertPacketRelayed(ctx, chainA, transfertypes.PortID, dstChannelID, 1)

actualBalance, err := query.Balance(ctx, chainA, userAWallet.FormattedAddress(), chainADenom)
s.Require().NoError(err)
s.Require().Equal(testvalues.StartingTokenAmount, actualBalance.Int64())
})
}

// ClientState queries the current ClientState by clientID
func (*UpgradeTestSuite) ClientState(ctx context.Context, chain ibc.Chain, clientID string) (*clienttypes.QueryClientStateResponse, error) {
res, err := query.GRPCQuery[clienttypes.QueryClientStateResponse](ctx, chain, &clienttypes.QueryClientStateRequest{ClientId: clientID})
Expand Down
7 changes: 6 additions & 1 deletion e2e/testsuite/query/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ func GRPCQuery[T any](ctx context.Context, chain ibc.Chain, req proto.Message, o
return nil, err
}

return grpcQueryWithMethod[T](ctx, chain, req, path, opts...)
}

// grpcQueryWithMethod queries the chain with a query request with a specific method (grpc path) and deserializes the response to T
func grpcQueryWithMethod[T any](ctx context.Context, chain ibc.Chain, req proto.Message, method string, opts ...grpc.CallOption) (*T, error) {
// Create a connection to the gRPC server.
grpcConn, err := grpc.Dial(
chain.GetHostGRPCAddress(),
Expand All @@ -30,7 +35,7 @@ func GRPCQuery[T any](ctx context.Context, chain ibc.Chain, req proto.Message, o
defer grpcConn.Close()

resp := new(T)
err = grpcConn.Invoke(ctx, path, req, resp, opts...)
err = grpcConn.Invoke(ctx, method, req, resp, opts...)
if err != nil {
return nil, err
}
Expand Down
Loading
Loading