Skip to content

Commit

Permalink
e2e: adding e2e upgrade test for ibc-go v7 (#2902)
Browse files Browse the repository at this point in the history
* add test for automatic migration of solomachine clientstate version

* add clientID generation functions

* update godoc

* update sprintf message
  • Loading branch information
charleenfei committed Dec 12, 2022
1 parent f24f41e commit a3b7581
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 5 deletions.
8 changes: 7 additions & 1 deletion .github/workflows/e2e-upgrade.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@ jobs:
chain-binary: icad
chain-a-tag: v0.3.5
chain-b-tag: v0.3.5
chain-upgrade-tag: v0.4.2
chain-upgrade-tag: v0.4.0
- test: TestV6ToV7ChainUpgrade
chain-image: ghcr.io/cosmos/ibc-go-simd
chain-binary: simd
chain-a-tag: v6.0.0
chain-b-tag: v6.0.0
chain-upgrade-tag: v7.0.0
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
Expand Down
176 changes: 176 additions & 0 deletions e2e/tests/upgrades/upgrade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,13 @@ import (
"github.com/cosmos/ibc-go/e2e/testvalues"
controllertypes "github.com/cosmos/ibc-go/v6/modules/apps/27-interchain-accounts/controller/types"
icatypes "github.com/cosmos/ibc-go/v6/modules/apps/27-interchain-accounts/types"
v7migrations "github.com/cosmos/ibc-go/v6/modules/core/02-client/migrations/v7"
clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types"
"github.com/cosmos/ibc-go/v6/modules/core/exported"
solomachine "github.com/cosmos/ibc-go/v6/modules/light-clients/06-solomachine"
ibctesting "github.com/cosmos/ibc-go/v6/testing"
simappupgrades "github.com/cosmos/ibc-go/v6/testing/simapp/upgrades"
v7upgrades "github.com/cosmos/ibc-go/v6/testing/simapp/upgrades/v7"
)

const (
Expand Down Expand Up @@ -70,6 +75,10 @@ func (s *UpgradeTestSuite) UpgradeChain(ctx context.Context, chain *cosmos.Cosmo
err = chain.StartAllNodes(ctx)
s.Require().NoError(err, "error starting upgraded node(s)")

// we are reinitializing the clients because we need to update the hostGRPCAddress after
// the upgrade and subsequent restarting of nodes
s.InitGRPCClients(chain)

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

Expand Down Expand Up @@ -386,6 +395,160 @@ func (s *UpgradeTestSuite) TestV5ToV6ChainUpgrade() {
})
}

// TestV6ToV7ChainUpgrade will test that an upgrade from a v6 ibc-go binary to a v7 ibc-go binary is successful
// and that the automatic migrations associated with the 02-client module are performed. Namely that the solo machine
// proto definition is migrated in state from the v2 to v3 definition. This is checked by creating a solo machine client
// before the upgrade and asserting that its TypeURL has been changed after the upgrade. The test also ensure packets
// can be sent before and after the upgrade without issue
func (s *UpgradeTestSuite) TestV6ToV7ChainUpgrade() {
t := s.T()
testCfg := testconfig.FromEnv()

ctx := context.Background()
relayer, channelA := s.SetupChainsRelayerAndChannel(ctx)
chainA, chainB := s.GetChains()

var (
chainADenom = chainA.Config().Denom
chainBIBCToken = testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID) // IBC token sent to chainB
)

chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount)
chainAAddress := chainAWallet.Bech32Address(chainA.Config().Bech32Prefix)

chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount)
chainBAddress := chainBWallet.Bech32Address(chainB.Config().Bech32Prefix)

// create second tendermint client
createClientOptions := ibc.CreateClientOptions{
TrustingPeriod: ibctesting.TrustingPeriod.String(),
}

s.SetupClients(ctx, relayer, createClientOptions)

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

t.Run("check that both tendermint clients are active", func(t *testing.T) {
status, err := s.QueryClientStatus(ctx, chainA, testvalues.TendermintClientID(0))
s.Require().NoError(err)
s.Require().Equal(exported.Active.String(), status)

status, err = s.QueryClientStatus(ctx, chainA, testvalues.TendermintClientID(1))
s.Require().NoError(err)
s.Require().Equal(exported.Active.String(), status)
})

// create solo machine client using the solomachine implementation from ibctesting
// TODO: the solomachine clientID should be updated when after fix of this issue: https://github.com/cosmos/ibc-go/issues/2907
solo := ibctesting.NewSolomachine(t, testsuite.Codec(), "solomachine", "testing", 1)

legacyConsensusState := &v7migrations.ConsensusState{
PublicKey: solo.ConsensusState().PublicKey,
Diversifier: solo.ConsensusState().Diversifier,
Timestamp: solo.ConsensusState().Timestamp,
}

legacyClientState := &v7migrations.ClientState{
Sequence: solo.ClientState().Sequence,
IsFrozen: solo.ClientState().IsFrozen,
ConsensusState: legacyConsensusState,
AllowUpdateAfterProposal: true,
}

msgCreateSoloMachineClient, err := clienttypes.NewMsgCreateClient(legacyClientState, legacyConsensusState, chainAAddress)
s.Require().NoError(err)

resp, err := s.BroadcastMessages(
ctx,
chainA,
chainAWallet,
msgCreateSoloMachineClient,
)

s.AssertValidTxResponse(resp)
s.Require().NoError(err)

t.Run("check that the solomachine is now active and that the clientstate is a pre-upgrade v2 solomachine clientstate", func(t *testing.T) {
status, err := s.QueryClientStatus(ctx, chainA, testvalues.SolomachineClientID(2))
s.Require().NoError(err)
s.Require().Equal(exported.Active.String(), status)

res, err := s.ClientState(ctx, chainA, testvalues.SolomachineClientID(2))
s.Require().NoError(err)
s.Require().Equal(fmt.Sprint("/", proto.MessageName(&v7migrations.ClientState{})), res.ClientState.TypeUrl)
})

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

t.Run("IBC token transfer from chainA to chainB, sender is source of tokens", func(t *testing.T) {
transferTxResp, err := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "")
s.Require().NoError(err)
s.AssertValidTxResponse(transferTxResp)
})

t.Run("tokens are escrowed", func(t *testing.T) {
actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet)
s.Require().NoError(err)

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

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

t.Run("packets are relayed", func(t *testing.T) {
s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1)

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

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

// create separate user specifically for the upgrade proposal to more easily verify starting
// and end balances of the chainA users.
chainAUpgradeProposalWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount)

t.Run("upgrade chainA", func(t *testing.T) {
s.UpgradeChain(ctx, chainA, chainAUpgradeProposalWallet, v7upgrades.UpgradeName, testCfg.ChainAConfig.Tag, testCfg.UpgradeTag)
})

t.Run("check that the tendermint clients are active again after upgrade", func(t *testing.T) {
status, err := s.QueryClientStatus(ctx, chainA, testvalues.TendermintClientID(0))
s.Require().NoError(err)
s.Require().Equal(exported.Active.String(), status)

status, err = s.QueryClientStatus(ctx, chainA, testvalues.TendermintClientID(1))
s.Require().NoError(err)
s.Require().Equal(exported.Active.String(), status)
})

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, err := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "")
s.Require().NoError(err)
s.AssertValidTxResponse(transferTxResp)
})

t.Run("packets are relayed", func(t *testing.T) {
s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1)

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

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

t.Run("check that the v2 solo machine clientstate has been updated to the v3 solo machine clientstate", func(t *testing.T) {
res, err := s.ClientState(ctx, chainA, testvalues.SolomachineClientID(2))
s.Require().NoError(err)
s.Require().Equal(fmt.Sprint("/", proto.MessageName(&solomachine.ClientState{})), res.ClientState.TypeUrl)
})
}

// RegisterInterchainAccount will attempt to register an interchain account on the counterparty chain.
func (s *UpgradeTestSuite) RegisterInterchainAccount(ctx context.Context, chain *cosmos.CosmosChain, user *ibc.Wallet, msgRegisterAccount *intertxtypes.MsgRegisterAccount) error {
txResp, err := s.BroadcastMessages(ctx, chain, user, msgRegisterAccount)
Expand All @@ -405,3 +568,16 @@ func getICAVersion(chainAVersion, chainBVersion string) string {
// explicitly set the version string because the host chain might not yet support incentivized channels.
return icatypes.NewDefaultMetadataString(ibctesting.FirstConnectionID, ibctesting.FirstConnectionID)
}

// ClientState queries the current ClientState by clientID
func (s *UpgradeTestSuite) ClientState(ctx context.Context, chain ibc.Chain, clientID string) (*clienttypes.QueryClientStateResponse, error) {
queryClient := s.GetChainGRCPClients(chain).ClientQueryClient
res, err := queryClient.ClientState(ctx, &clienttypes.QueryClientStateRequest{
ClientId: clientID,
})
if err != nil {
return res, err
}

return res, nil
}
13 changes: 13 additions & 0 deletions e2e/testsuite/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@ func (s *E2ETestSuite) QueryClientState(ctx context.Context, chain ibc.Chain, cl
return clientState, nil
}

// QueryClientStatus queries the status of the client by clientID
func (s *E2ETestSuite) QueryClientStatus(ctx context.Context, chain ibc.Chain, clientID string) (string, error) {
queryClient := s.GetChainGRCPClients(chain).ClientQueryClient
res, err := queryClient.ClientStatus(ctx, &clienttypes.QueryClientStatusRequest{
ClientId: clientID,
})
if err != nil {
return "", err
}

return res.Status, nil
}

// QueryChannel queries the channel on a given chain for the provided portID and channelID
func (s *E2ETestSuite) QueryChannel(ctx context.Context, chain ibc.Chain, portID, channelID string) (channeltypes.Channel, error) {
queryClient := s.GetChainGRCPClients(chain).ChannelQueryClient
Expand Down
8 changes: 4 additions & 4 deletions e2e/testsuite/testsuite.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,8 @@ func (s *E2ETestSuite) SetupChainsRelayerAndChannel(ctx context.Context, channel
time.Sleep(time.Second * 10)
}

s.initGRPCClients(chainA)
s.initGRPCClients(chainB)
s.InitGRPCClients(chainA)
s.InitGRPCClients(chainB)

chainAChannels, err := r.GetChannels(ctx, eRep, chainA.Config().ChainID)
s.Require().NoError(err)
Expand Down Expand Up @@ -361,9 +361,9 @@ func (s *E2ETestSuite) GetChainGRCPClients(chain ibc.Chain) GRPCClients {
return cs
}

// initGRPCClients establishes GRPC clients with the given chain.
// InitGRPCClients establishes GRPC clients with the given chain.
// The created GRPCClients can be retrieved with GetChainGRCPClients.
func (s *E2ETestSuite) initGRPCClients(chain *cosmos.CosmosChain) {
func (s *E2ETestSuite) InitGRPCClients(chain *cosmos.CosmosChain) {
// Create a connection to the gRPC server.
grpcConn, err := grpc.Dial(
chain.GetHostGRPCAddress(),
Expand Down
9 changes: 9 additions & 0 deletions e2e/testvalues/values.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package testvalues

import (
"fmt"
"time"

sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -34,3 +35,11 @@ func DefaultFee(denom string) feetypes.Fee {
func DefaultTransferAmount(denom string) sdk.Coin {
return sdk.Coin{Denom: denom, Amount: sdk.NewInt(IBCTransferAmount)}
}

func TendermintClientID(id int) string {
return fmt.Sprintf("07-tendermint-%d", id)
}

func SolomachineClientID(id int) string {
return fmt.Sprint("06-solomachine-%d", id)
}

0 comments on commit a3b7581

Please sign in to comment.