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

Add extra client info when querying client expiration #1247

Merged
merged 10 commits into from
Jul 29, 2023
Merged
Show file tree
Hide file tree
Changes from 6 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
35 changes: 24 additions & 11 deletions cmd/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -1055,9 +1055,10 @@ $ %s query unrelayed-acks demo-path channel-0`,

func queryClientsExpiration(a *appState) *cobra.Command {
cmd := &cobra.Command{
Use: "clients-expiration path",
Short: "query for light clients expiration date",
Args: withUsage(cobra.ExactArgs(1)),
Use: "clients-expiration path",
Aliases: []string{"ce"},
Short: "query for light clients expiration date",
Args: withUsage(cobra.ExactArgs(1)),
Example: strings.TrimSpace(fmt.Sprintf(`
$ %s query clients-expiration demo-path`,
appName,
Expand All @@ -1080,17 +1081,29 @@ $ %s query clients-expiration demo-path`,
return err
}

srcExpiration, err := relayer.QueryClientExpiration(cmd.Context(), c[src], c[dst])
if err != nil {
return err
srcExpiration, srcClientInfo, errSrc := relayer.QueryClientExpiration(cmd.Context(), c[src], c[dst])
if errSrc != nil && !strings.Contains(errSrc.Error(), "light client not found") {
return errSrc
}
dstExpiration, err := relayer.QueryClientExpiration(cmd.Context(), c[dst], c[src])
if err != nil {
return err
dstExpiration, dstClientInfo, errDst := relayer.QueryClientExpiration(cmd.Context(), c[dst], c[src])
if errDst != nil && !strings.Contains(errDst.Error(), "light client not found") {
return errDst
}

// if only the src light client is found, just print info for source light client
if errSrc == nil && errDst != nil {
fmt.Fprintln(cmd.OutOrStdout(), relayer.SPrintClientExpiration(c[src], srcExpiration, srcClientInfo))
return nil
}

// if only the dst light client is found, just print info for destination light client
if errDst == nil && errSrc != nil {
fmt.Fprintln(cmd.OutOrStdout(), relayer.SPrintClientExpiration(c[dst], dstExpiration, dstClientInfo))
return nil
}

fmt.Fprintf(cmd.OutOrStdout(), relayer.SPrintClientExpiration(c[src], srcExpiration))
fmt.Fprintf(cmd.OutOrStdout(), relayer.SPrintClientExpiration(c[dst], dstExpiration))
fmt.Fprintln(cmd.OutOrStdout(), relayer.SPrintClientExpiration(c[src], srcExpiration, srcClientInfo))
fmt.Fprintln(cmd.OutOrStdout(), relayer.SPrintClientExpiration(c[dst], dstExpiration, dstClientInfo))

return nil
},
Expand Down
31 changes: 20 additions & 11 deletions relayer/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,39 +272,48 @@ func QueryBalance(ctx context.Context, chain *Chain, address string, showDenoms
return out, nil
}

func QueryClientExpiration(ctx context.Context, src, dst *Chain) (time.Time, error) {
func QueryClientExpiration(ctx context.Context, src, dst *Chain) (time.Time, ClientStateInfo, error) {
latestHeight, err := src.ChainProvider.QueryLatestHeight(ctx)
if err != nil {
return time.Time{}, err
return time.Time{}, ClientStateInfo{}, err
}

clientStateRes, err := src.ChainProvider.QueryClientStateResponse(ctx, latestHeight, src.ClientID())
if err != nil {
return time.Time{}, err
return time.Time{}, ClientStateInfo{}, err
}

clientInfo, err := ClientInfoFromClientState(clientStateRes.ClientState)
if err != nil {
return time.Time{}, err
return time.Time{}, ClientStateInfo{}, err
}

clientTime, err := dst.ChainProvider.BlockTime(ctx, int64(clientInfo.LatestHeight.GetRevisionHeight()))
if err != nil {
return time.Time{}, err
return time.Time{}, ClientStateInfo{}, err
}

return clientTime.Add(clientInfo.TrustingPeriod), nil
return clientTime.Add(clientInfo.TrustingPeriod), clientInfo, nil
}

func SPrintClientExpiration(chain *Chain, expiration time.Time) string {
func SPrintClientExpiration(chain *Chain, expiration time.Time, clientInfo ClientStateInfo) string {
now := time.Now()
remainingTime := expiration.Sub(now)
expirationFormatted := expiration.Format(time.RFC822)

var status string
if remainingTime < 0 {
return fmt.Sprintf("client %s (%s) is already expired (%s)\n",
chain.ClientID(), chain.ChainID(), expirationFormatted)
status = "EXPIRED"
} else {
status = "GOOD"
boojamya marked this conversation as resolved.
Show resolved Hide resolved
}
return fmt.Sprintf("client %s (%s) expires in %s (%s)\n",
chain.ClientID(), chain.ChainID(), remainingTime.Round(time.Second), expirationFormatted)

return fmt.Sprintf(`
client: %s (%s)
HEALTH: %s
TIME: %s (%s)
LAST UPDATE HEIGHT: %d
TRUSTING PERIOD: %s
`,
chain.ClientID(), chain.ChainID(), status, expirationFormatted, remainingTime.Round(time.Second), clientInfo.LatestHeight.GetRevisionHeight(), clientInfo.TrustingPeriod.String())
}
83 changes: 70 additions & 13 deletions relayer/query_test.go
Original file line number Diff line number Diff line change
@@ -1,76 +1,124 @@
package relayer

import (
"github.com/cosmos/relayer/v2/relayer/chains/cosmos"
"testing"
"time"

"github.com/cosmos/relayer/v2/relayer/chains/cosmos"

clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types"
ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported"
"github.com/stretchr/testify/require"
)

func TestSPrintClientExpiration_PrintChainId(t *testing.T) {
previousTime := time.Now().Add(10 * time.Hour)
mockHeight := clienttypes.NewHeight(1, 100)
trustingPeriod := time.Duration(1 * time.Hour)

chain := mockChain("expected-chain-id", "test-client-id")
expiration := SPrintClientExpiration(chain, previousTime)
clientStateInfo := mockClientStateInfo("test-chain-id", trustingPeriod, mockHeight)
expiration := SPrintClientExpiration(chain, previousTime, *clientStateInfo)

require.Contains(t, expiration, "expected-chain-id")
}

func TestSPrintClientExpiration_PrintClientId(t *testing.T) {
previousTime := time.Now().Add(10 * time.Hour)
mockHeight := clienttypes.NewHeight(1, 100)
trustingPeriod := time.Duration(1 * time.Hour)

chain := mockChain("test-chain-id", "expected-client-id")
expiration := SPrintClientExpiration(chain, previousTime)
clientStateInfo := mockClientStateInfo("test-chain-id", trustingPeriod, mockHeight)
expiration := SPrintClientExpiration(chain, previousTime, *clientStateInfo)

require.Contains(t, expiration, "expected-client-id")
}

func TestSPrintClientExpiration_PrintIsAlreadyExpired_WhenTimeIsInPast(t *testing.T) {
func TestSPrintClientExpiration_PrintExpired_WhenTimeIsInPast(t *testing.T) {
previousTime := time.Now().Add(-10 * time.Hour)
mockHeight := clienttypes.NewHeight(1, 100)
trustingPeriod := time.Duration(1 * time.Hour)

chain := mockChain("test-chain-id", "test-client-id")
expiration := SPrintClientExpiration(chain, previousTime)
clientStateInfo := mockClientStateInfo("test-chain-id", trustingPeriod, mockHeight)
expiration := SPrintClientExpiration(chain, previousTime, *clientStateInfo)

require.Contains(t, expiration, "is already expired")
require.Contains(t, expiration, "EXPIRED")
}

func TestSPrintClientExpiration_PrintRFC822FormattedTime_WhenTimeIsInPast(t *testing.T) {
pastTime := time.Now().Add(-10 * time.Hour)
mockHeight := clienttypes.NewHeight(1, 100)
trustingPeriod := time.Duration(1 * time.Hour)

chain := mockChain("test-chain-id", "test-client-id")
expiration := SPrintClientExpiration(chain, pastTime)
chain := mockChain("expected-chain-id", "test-client-id")
clientStateInfo := mockClientStateInfo("test-chain-id", trustingPeriod, mockHeight)
expiration := SPrintClientExpiration(chain, pastTime, *clientStateInfo)

require.Contains(t, expiration, pastTime.Format(time.RFC822))
}

func TestSPrintClientExpiration_PrintExpiresIn_WhenTimeIsInFuture(t *testing.T) {
func TestSPrintClientExpiration_PrintGood_WhenTimeIsInFuture(t *testing.T) {
previousTime := time.Now().Add(10 * time.Hour)
mockHeight := clienttypes.NewHeight(1, 100)
trustingPeriod := time.Duration(1 * time.Hour)

chain := mockChain("test-chain-id", "test-client-id")
expiration := SPrintClientExpiration(chain, previousTime)
clientStateInfo := mockClientStateInfo("test-chain-id", trustingPeriod, mockHeight)
expiration := SPrintClientExpiration(chain, previousTime, *clientStateInfo)

require.Contains(t, expiration, "expires in")
require.Contains(t, expiration, "GOOD")
}

func TestSPrintClientExpiration_PrintRFC822FormattedTime_WhenTimeIsInFuture(t *testing.T) {
futureTime := time.Now().Add(10 * time.Hour)
mockHeight := clienttypes.NewHeight(1, 100)
trustingPeriod := time.Duration(1 * time.Hour)

chain := mockChain("test-chain-id", "test-client-id")
expiration := SPrintClientExpiration(chain, futureTime)
clientStateInfo := mockClientStateInfo("test-chain-id", trustingPeriod, mockHeight)
expiration := SPrintClientExpiration(chain, futureTime, *clientStateInfo)

require.Contains(t, expiration, futureTime.Format(time.RFC822))
}

func TestSPrintClientExpiration_PrintRemainingTime_WhenTimeIsInFuture(t *testing.T) {
futureTime := time.Now().Add(10 * time.Hour)
mockHeight := clienttypes.NewHeight(1, 100)
trustingPeriod := time.Duration(1 * time.Hour)

chain := mockChain("test-chain-id", "test-client-id")
expiration := SPrintClientExpiration(chain, futureTime)
clientStateInfo := mockClientStateInfo("test-chain-id", trustingPeriod, mockHeight)
expiration := SPrintClientExpiration(chain, futureTime, *clientStateInfo)

require.Contains(t, expiration, "10h0m0s")
}

func TestSPrintClientExpiration_TrustingPeriod(t *testing.T) {
previousTime := time.Now().Add(10 * time.Hour)
mockHeight := clienttypes.NewHeight(1, 100)
trustingPeriod := time.Duration(1 * time.Hour)

chain := mockChain("expected-chain-id", "test-client-id")
clientStateInfo := mockClientStateInfo("test-chain-id", trustingPeriod, mockHeight)
expiration := SPrintClientExpiration(chain, previousTime, *clientStateInfo)

require.Contains(t, expiration, "1h0m0s")
}

func TestSPrintClientExpiration_LastUpdateHeight(t *testing.T) {
previousTime := time.Now().Add(10 * time.Hour)
mockHeight := clienttypes.NewHeight(1, 100)
trustingPeriod := time.Duration(1 * time.Hour)

chain := mockChain("expected-chain-id", "test-client-id")
clientStateInfo := mockClientStateInfo("test-chain-id", trustingPeriod, mockHeight)
expiration := SPrintClientExpiration(chain, previousTime, *clientStateInfo)

require.Contains(t, expiration, "100")
}

func mockChain(chainId string, clientId string) *Chain {
return &Chain{
Chainid: chainId,
Expand All @@ -85,3 +133,12 @@ func mockChain(chainId string, clientId string) *Chain {
},
}
}

func mockClientStateInfo(chainID string, trustingPeriod time.Duration, latestHeight ibcexported.Height) *ClientStateInfo {
mockHeight := clienttypes.NewHeight(1, 100)
return &ClientStateInfo{
ChainID: chainID,
TrustingPeriod: time.Duration(1 * time.Hour),
LatestHeight: mockHeight,
}
}