diff --git a/e2e/tests/core/02-client/client_test.go b/e2e/tests/core/02-client/client_test.go index 34e6d8d9adc..aa93b5df3ed 100644 --- a/e2e/tests/core/02-client/client_test.go +++ b/e2e/tests/core/02-client/client_test.go @@ -14,6 +14,8 @@ import ( test "github.com/strangelove-ventures/interchaintest/v7/testutil" testifysuite "github.com/stretchr/testify/suite" + "cosmossdk.io/x/upgrade/types" + "github.com/cosmos/cosmos-sdk/client/grpc/cmtservice" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" @@ -71,6 +73,64 @@ func (s *ClientTestSuite) QueryAllowedClients(ctx context.Context, chain ibc.Cha return res.Params.AllowedClients } +// TestScheduleIBCUpgrade_Succeeds tests that a governance proposal to schedule an IBC software upgrade is successful. +func (s *ClientTestSuite) TestScheduleIBCUpgrade_Succeeds() { + t := s.T() + ctx := context.TODO() + + _, _ = s.SetupChainsRelayerAndChannel(ctx) + chainA, chainB := s.GetChains() + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + + const planHeight = int64(75) + var newChainID string + + t.Run("send schedule IBC upgrade message", func(t *testing.T) { + authority, err := s.QueryModuleAccountAddress(ctx, govtypes.ModuleName, chainA) + s.Require().NoError(err) + s.Assert().NotNil(authority) + + clientState, err := s.QueryClientState(ctx, chainB, ibctesting.FirstClientID) + s.Require().NoError(err) + + originalChainID := clientState.(*ibctm.ClientState).ChainId + revisionNumber := clienttypes.ParseChainID(fmt.Sprintf("%s-%d", originalChainID, 1)) + // increment revision number even with new chain ID to prevent loss of misbehaviour detection support + newChainID, err = clienttypes.SetRevisionNumber(fmt.Sprintf("%s-%d", originalChainID, 1), revisionNumber+1) + s.Require().NoError(err) + s.Assert().NotEqual(originalChainID, newChainID) + + upgradedClientState := clientState.(*ibctm.ClientState) + upgradedClientState.ChainId = newChainID + + scheduleUpgradeMsg, err := clienttypes.NewMsgIBCSoftwareUpgrade( + authority.String(), + types.Plan{ + Name: "upgrade-client", + Height: planHeight, + }, + upgradedClientState, + ) + s.Require().NoError(err) + s.ExecuteGovProposalV1(ctx, scheduleUpgradeMsg, chainA, chainAWallet, 1) + }) + + t.Run("check that IBC software upgrade has been scheduled successfully on chainA", func(t *testing.T) { + // checks there is an upgraded client state stored + cs, err := s.QueryUpgradedClientState(ctx, chainA, ibctesting.FirstClientID) + s.Require().NoError(err) + + upgradedClientState := cs.(*ibctm.ClientState) + s.Assert().Equal(upgradedClientState.ChainId, newChainID) + + plan, err := s.QueryCurrentPlan(ctx, chainA) + s.Require().NoError(err) + + s.Assert().Equal("upgrade-client", plan.Name) + s.Assert().Equal(planHeight, plan.Height) + }) +} + // TestRecoverClient_Succeeds tests that a governance proposal to recover a client using a MsgRecoverClient is successful. func (s *ClientTestSuite) TestRecoverClient_Succeeds() { t := s.T() diff --git a/e2e/testsuite/grpc_query.go b/e2e/testsuite/grpc_query.go index 03155feb408..eb7f62db1a2 100644 --- a/e2e/testsuite/grpc_query.go +++ b/e2e/testsuite/grpc_query.go @@ -12,6 +12,8 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" + upgradetypes "cosmossdk.io/x/upgrade/types" + "github.com/cosmos/cosmos-sdk/client/grpc/cmtservice" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -45,12 +47,13 @@ type GRPCClients struct { InterTxQueryClient intertxtypes.QueryClient // SDK query clients - GovQueryClient govtypesv1beta1.QueryClient - GovQueryClientV1 govtypesv1.QueryClient - GroupsQueryClient grouptypes.QueryClient - ParamsQueryClient paramsproposaltypes.QueryClient - AuthQueryClient authtypes.QueryClient - AuthZQueryClient authz.QueryClient + GovQueryClient govtypesv1beta1.QueryClient + GovQueryClientV1 govtypesv1.QueryClient + GroupsQueryClient grouptypes.QueryClient + ParamsQueryClient paramsproposaltypes.QueryClient + AuthQueryClient authtypes.QueryClient + AuthZQueryClient authz.QueryClient + UpgradeQueryClient upgradetypes.QueryClient ConsensusServiceClient cmtservice.ServiceClient } @@ -90,6 +93,7 @@ func (s *E2ETestSuite) InitGRPCClients(chain *cosmos.CosmosChain) { AuthQueryClient: authtypes.NewQueryClient(grpcConn), AuthZQueryClient: authz.NewQueryClient(grpcConn), ConsensusServiceClient: cmtservice.NewServiceClient(grpcConn), + UpgradeQueryClient: upgradetypes.NewQueryClient(grpcConn), } } @@ -119,6 +123,23 @@ func (s *E2ETestSuite) QueryClientState(ctx context.Context, chain ibc.Chain, cl return clientState, nil } +// QueryUpgradedClientState queries the upgraded client state on the given chain for the provided clientID. +func (s *E2ETestSuite) QueryUpgradedClientState(ctx context.Context, chain ibc.Chain, clientID string) (ibcexported.ClientState, error) { + queryClient := s.GetChainGRCPClients(chain).ClientQueryClient + res, err := queryClient.UpgradedClientState(ctx, &clienttypes.QueryUpgradedClientStateRequest{}) + if err != nil { + return nil, err + } + + cfg := EncodingConfig() + var clientState ibcexported.ClientState + if err := cfg.InterfaceRegistry.UnpackAny(res.UpgradedClientState, &clientState); err != nil { + return nil, err + } + + 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 @@ -132,6 +153,17 @@ func (s *E2ETestSuite) QueryClientStatus(ctx context.Context, chain ibc.Chain, c return res.Status, nil } +// QueryCurrentPlan queries the currently scheduled plans, if any +func (s *E2ETestSuite) QueryCurrentPlan(ctx context.Context, chain ibc.Chain) (upgradetypes.Plan, error) { + queryClient := s.GetChainGRCPClients(chain).UpgradeQueryClient + res, err := queryClient.CurrentPlan(ctx, &upgradetypes.QueryCurrentPlanRequest{}) + if err != nil { + return upgradetypes.Plan{}, err + } + + return *res.Plan, nil +} + // QueryConnection queries the connection end using the given chain and connection id. func (s *E2ETestSuite) QueryConnection(ctx context.Context, chain ibc.Chain, connectionID string) (connectiontypes.ConnectionEnd, error) { queryClient := s.GetChainGRCPClients(chain).ConnectionQueryClient diff --git a/modules/core/02-client/keeper/events.go b/modules/core/02-client/keeper/events.go index 608a121edae..4b8a875bddd 100644 --- a/modules/core/02-client/keeper/events.go +++ b/modules/core/02-client/keeper/events.go @@ -97,21 +97,6 @@ func emitRecoverClientEvent(ctx sdk.Context, clientID, clientType string) { }) } -// emitUpgradeClientProposalEvent emits an upgrade client proposal event -func emitUpgradeClientProposalEvent(ctx sdk.Context, title string, height int64) { - ctx.EventManager().EmitEvents(sdk.Events{ - sdk.NewEvent( - types.EventTypeUpgradeClientProposal, - sdk.NewAttribute(types.AttributeKeyUpgradePlanTitle, title), - sdk.NewAttribute(types.AttributeKeyUpgradePlanHeight, fmt.Sprintf("%d", height)), - ), - sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), - ), - }) -} - // emitScheduleIBCSoftwareUpgradeEvent emits a schedule IBC software upgrade event func emitScheduleIBCSoftwareUpgradeEvent(ctx sdk.Context, title string, height int64, upgradeClientState exported.ClientState) { ctx.EventManager().EmitEvents(sdk.Events{