Skip to content

Commit

Permalink
Add extra client info when querying client expiration (#1247)
Browse files Browse the repository at this point in the history
* extra client info

* cleanup print

* remove extra comments

* add alias

* allow only one client

* spelling

* remainingTime

---------

Co-authored-by: Justin Tieri <37750742+jtieri@users.noreply.github.com>
  • Loading branch information
boojamya and jtieri committed Jul 29, 2023
1 parent f054ac4 commit 1301e1d
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 36 deletions.
35 changes: 24 additions & 11 deletions cmd/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -1070,9 +1070,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 @@ -1095,17 +1096,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
33 changes: 21 additions & 12 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)

if remainingTime < 0 {
return fmt.Sprintf("client %s (%s) is already expired (%s)\n",
chain.ClientID(), chain.ChainID(), expirationFormatted)
var status string
if remainingTime <= 0 {
status = "EXPIRED"
} else {
status = "GOOD"
}
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,
}
}

0 comments on commit 1301e1d

Please sign in to comment.