diff --git a/e2e/tests/core/02-client/client_test.go b/e2e/tests/core/02-client/client_test.go index 911b22c912e..db09a386dc7 100644 --- a/e2e/tests/core/02-client/client_test.go +++ b/e2e/tests/core/02-client/client_test.go @@ -153,6 +153,91 @@ func (s *ClientTestSuite) TestClientUpdateProposal_Succeeds() { }) } +// TestRecoverClient_Succeeds tests that a governance proposal to recover a client using a MsgRecoverClient is successful. +func (s *ClientTestSuite) TestRecoverClient_Succeeds() { + t := s.T() + ctx := context.TODO() + + var ( + pathName string + relayer ibc.Relayer + subjectClientID string + substituteClientID string + // set the trusting period to a value which will still be valid upon client creation, but invalid before the first update + badTrustingPeriod = time.Duration(time.Second * 10) + ) + + t.Run("create substitute client with correct trusting period", func(t *testing.T) { + relayer, _ = s.SetupChainsRelayerAndChannel(ctx) + + // TODO: update when client identifier created is accessible + // currently assumes first client is 07-tendermint-0 + substituteClientID = clienttypes.FormatClientIdentifier(ibcexported.Tendermint, 0) + + // TODO: replace with better handling of path names + pathName = fmt.Sprintf("%s-path-%d", s.T().Name(), 0) + pathName = strings.ReplaceAll(pathName, "/", "-") + }) + + chainA, chainB := s.GetChains() + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + + t.Run("create subject client with bad trusting period", func(t *testing.T) { + createClientOptions := ibc.CreateClientOptions{ + TrustingPeriod: badTrustingPeriod.String(), + } + + s.SetupClients(ctx, relayer, createClientOptions) + + // TODO: update when client identifier created is accessible + // currently assumes second client is 07-tendermint-1 + subjectClientID = clienttypes.FormatClientIdentifier(ibcexported.Tendermint, 1) + }) + + time.Sleep(badTrustingPeriod) + + t.Run("update substitute client", func(t *testing.T) { + s.UpdateClients(ctx, relayer, pathName) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + t.Run("check status of each client", func(t *testing.T) { + t.Run("substitute should be active", func(t *testing.T) { + status, err := s.Status(ctx, chainA, substituteClientID) + s.Require().NoError(err) + s.Require().Equal(ibcexported.Active.String(), status) + }) + + t.Run("subject should be expired", func(t *testing.T) { + status, err := s.Status(ctx, chainA, subjectClientID) + s.Require().NoError(err) + s.Require().Equal(ibcexported.Expired.String(), status) + }) + }) + + t.Run("send recover client message", func(t *testing.T) { + authority, err := s.QueryModuleAccountAddress(ctx, govtypes.ModuleName, chainA) + s.Require().NoError(err) + recoverClientMsg := clienttypes.NewMsgRecoverClient(authority.String(), subjectClientID, substituteClientID) + s.ExecuteGovProposalV1(ctx, recoverClientMsg, chainA, chainAWallet, 1) + }) + + t.Run("check status of each client", func(t *testing.T) { + t.Run("substitute should be active", func(t *testing.T) { + status, err := s.Status(ctx, chainA, substituteClientID) + s.Require().NoError(err) + s.Require().Equal(ibcexported.Active.String(), status) + }) + + t.Run("subject should be active", func(t *testing.T) { + status, err := s.Status(ctx, chainA, subjectClientID) + s.Require().NoError(err) + s.Require().Equal(ibcexported.Active.String(), status) + }) + }) +} + func (s *ClientTestSuite) TestClient_Update_Misbehaviour() { t := s.T() ctx := context.TODO()