Skip to content

Commit

Permalink
IBC UX improvements (#5364)
Browse files Browse the repository at this point in the history
* ICS02 iterators

* ICS03 iterators

* ICS04 iterators

* ICS02 client updates

* CLI connections

* setup queriers

* clean up queriers

* add tests

* IBC top-level querier tests

* update ICS02 keys

* update ICS03 keys

* make format

* update ICS04 keys

* fix a few tests

* fix ICS20 tests

* update keys
  • Loading branch information
fedekunze authored and jackzampolin committed Jan 15, 2020
1 parent 29b82d9 commit e11aca6
Show file tree
Hide file tree
Showing 40 changed files with 834 additions and 213 deletions.
9 changes: 8 additions & 1 deletion client/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,17 @@ const (
DefaultGasAdjustment = 1.0
DefaultGasLimit = 200000
GasFlagAuto = "auto"
)

// DefaultKeyringBackend
// Keyring constants
const (
DefaultKeyringBackend = KeyringBackendOS
KeyringBackendFile = "file"
KeyringBackendOS = "os"
KeyringBackendTest = "test"
)

const (
// BroadcastBlock defines a tx broadcasting mode where the client waits for
// the tx to be committed in a block.
BroadcastBlock = "block"
Expand All @@ -35,7 +39,10 @@ const (
// BroadcastAsync defines a tx broadcasting mode where the client returns
// immediately.
BroadcastAsync = "async"
)

// List of CLI flags
const (
FlagHome = tmcli.HomeFlag
FlagUseLedger = "ledger"
FlagChainID = "chain-id"
Expand Down
2 changes: 2 additions & 0 deletions x/ibc/02-client/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const (
StoreKey = types.StoreKey
RouterKey = types.RouterKey
QuerierRoute = types.QuerierRoute
QueryAllClients = types.QueryAllClients
QueryClientState = types.QueryClientState
QueryConsensusState = types.QueryConsensusState
QueryVerifiedRoot = types.QueryVerifiedRoot
Expand All @@ -37,6 +38,7 @@ const (
var (
// functions aliases
NewKeeper = keeper.NewKeeper
QuerierClients = keeper.QuerierClients
QuerierClientState = keeper.QuerierClientState
QuerierConsensusState = keeper.QuerierConsensusState
QuerierVerifiedRoot = keeper.QuerierVerifiedRoot
Expand Down
1 change: 1 addition & 0 deletions x/ibc/02-client/client/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
}

ics02ClientQueryCmd.AddCommand(client.GetCommands(
GetCmdQueryClientStates(queryRoute, cdc),
GetCmdQueryClientState(queryRoute, cdc),
GetCmdQueryConsensusState(queryRoute, cdc),
GetCmdQueryRoot(queryRoute, cdc),
Expand Down
32 changes: 32 additions & 0 deletions x/ibc/02-client/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,38 @@ import (
commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment"
)

// GetCmdQueryClientStates defines the command to query all the light clients
// that this chain mantains.
func GetCmdQueryClientStates(queryRoute string, cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "states",
Short: "Query all available light clients",
Long: strings.TrimSpace(
fmt.Sprintf(`Query all available light clients
Example:
$ %s query ibc client states
`, version.ClientName),
),
Example: fmt.Sprintf("%s query ibc client states", version.ClientName),
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx := context.NewCLIContext().WithCodec(cdc)
page := viper.GetInt(flags.FlagPage)
limit := viper.GetInt(flags.FlagLimit)

clientStates, _, err := utils.QueryAllClientStates(cliCtx, page, limit)
if err != nil {
return err
}

return cliCtx.PrintOutput(clientStates)
},
}
cmd.Flags().Int(flags.FlagPage, 1, "pagination page of light clients to to query for")
cmd.Flags().Int(flags.FlagLimit, 100, "pagination limit of light clients to query for")
return cmd
}

// GetCmdQueryClientState defines the command to query the state of a client with
// a given id as defined in https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics#query
func GetCmdQueryClientState(queryRoute string, cdc *codec.Codec) *cobra.Command {
Expand Down
53 changes: 44 additions & 9 deletions x/ibc/02-client/client/rest/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,51 @@ import (
commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment"
)

func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, queryRoute string) {
r.HandleFunc(fmt.Sprintf("/ibc/clients/{%s}/client-state", RestClientID), queryClientStateHandlerFn(cliCtx, queryRoute)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/ibc/clients/{%s}/consensus-state", RestClientID), queryConsensusStateHandlerFn(cliCtx, queryRoute)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/ibc/clients/{%s}/roots/{%s}", RestClientID, RestRootHeight), queryRootHandlerFn(cliCtx, queryRoute)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/ibc/clients/{%s}/committers/{%s}", RestClientID, RestRootHeight), queryCommitterHandlerFn(cliCtx, queryRoute)).Methods("GET")
func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) {
r.HandleFunc("/ibc/clients", queryAllClientStatesFn(cliCtx)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/ibc/clients/{%s}/client-state", RestClientID), queryClientStateHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/ibc/clients/{%s}/consensus-state", RestClientID), queryConsensusStateHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/ibc/clients/{%s}/roots/{%s}", RestClientID, RestRootHeight), queryRootHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/ibc/clients/{%s}/committers/{%s}", RestClientID, RestRootHeight), queryCommitterHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc("/ibc/header", queryHeaderHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc("/ibc/node-state", queryNodeConsensusStateHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc("/ibc/path", queryPathHandlerFn(cliCtx)).Methods("GET")
}

// queryAllClientStatesFn queries all available light clients
//
// @Summary Query client states
// @Tags IBC
// @Produce json
// @Param page query int false "The page number to query" default(1)
// @Param limit query int false "The number of results per page" default(100)
// @Success 200 {object} QueryClientState "OK"
// @Failure 500 {object} rest.ErrorResponse "Internal Server Error"
// @Router /ibc/clients [get]
func queryAllClientStatesFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
_, page, limit, err := rest.ParseHTTPArgsWithLimit(r, 0)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}

cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}

clients, height, err := utils.QueryAllClientStates(cliCtx, page, limit)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}

cliCtx = cliCtx.WithHeight(height)
rest.PostProcessResponse(w, cliCtx, clients)
}
}

// queryClientStateHandlerFn implements a client state querying route
//
// @Summary Query client state
Expand All @@ -34,7 +69,7 @@ func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, queryRoute st
// @Failure 400 {object} rest.ErrorResponse "Invalid client id"
// @Failure 500 {object} rest.ErrorResponse "Internal Server Error"
// @Router /ibc/clients/{client-id}/client-state [get]
func queryClientStateHandlerFn(cliCtx context.CLIContext, queryRoute string) http.HandlerFunc {
func queryClientStateHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
clientID := vars[RestClientID]
Expand Down Expand Up @@ -67,7 +102,7 @@ func queryClientStateHandlerFn(cliCtx context.CLIContext, queryRoute string) htt
// @Failure 400 {object} rest.ErrorResponse "Invalid client id"
// @Failure 500 {object} rest.ErrorResponse "Internal Server Error"
// @Router /ibc/clients/{client-id}/consensus-state [get]
func queryConsensusStateHandlerFn(cliCtx context.CLIContext, queryRoute string) http.HandlerFunc {
func queryConsensusStateHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
clientID := vars[RestClientID]
Expand Down Expand Up @@ -100,7 +135,7 @@ func queryConsensusStateHandlerFn(cliCtx context.CLIContext, queryRoute string)
// @Failure 400 {object} rest.ErrorResponse "Invalid client id or height"
// @Failure 500 {object} rest.ErrorResponse "Internal Server Error"
// @Router /ibc/clients/{client-id}/roots/{height} [get]
func queryRootHandlerFn(cliCtx context.CLIContext, queryRoute string) http.HandlerFunc {
func queryRootHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
clientID := vars[RestClientID]
Expand Down Expand Up @@ -139,7 +174,7 @@ func queryRootHandlerFn(cliCtx context.CLIContext, queryRoute string) http.Handl
// @Failure 400 {object} rest.ErrorResponse "Invalid client id or height"
// @Failure 500 {object} rest.ErrorResponse "Internal Server Error"
// @Router /ibc/clients/{client-id}/committers/{height} [get]
func queryCommitterHandlerFn(cliCtx context.CLIContext, queryRoute string) http.HandlerFunc {
func queryCommitterHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
clientID := vars[RestClientID]
Expand Down
5 changes: 3 additions & 2 deletions x/ibc/02-client/client/rest/rest.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ import (
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
)

// REST client flags
const (
RestClientID = "client-id"
RestRootHeight = "height"
)

// RegisterRoutes - Central function to define routes that get registered by the main application
func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, queryRoute string) {
registerQueryRoutes(cliCtx, r, queryRoute)
registerQueryRoutes(cliCtx, r)
registerTxRoutes(cliCtx, r)
}

Expand All @@ -35,6 +36,6 @@ type UpdateClientReq struct {

// SubmitMisbehaviourReq defines the properties of a submit misbehaviour request's body.
type SubmitMisbehaviourReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Evidence evidenceexported.Evidence `json:"evidence" yaml:"evidence"`
}
1 change: 1 addition & 0 deletions x/ibc/02-client/client/rest/swagger.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment"
)

// nolint
type (
QueryConsensusState struct {
Height int64 `json:"height"`
Expand Down
25 changes: 25 additions & 0 deletions x/ibc/02-client/client/utils/utils.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package utils

import (
"fmt"

abci "github.com/tendermint/tendermint/abci/types"
tmtypes "github.com/tendermint/tendermint/types"

Expand All @@ -11,6 +13,29 @@ import (
commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment"
)

// QueryAllClientStates returns all the light client states. It _does not_ return
// any merkle proof.
func QueryAllClientStates(cliCtx context.CLIContext, page, limit int) ([]types.State, int64, error) {
params := types.NewQueryAllClientsParams(page, limit)
bz, err := cliCtx.Codec.MarshalJSON(params)
if err != nil {
return nil, 0, fmt.Errorf("failed to marshal query params: %w", err)
}

route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryAllClients)
res, height, err := cliCtx.QueryWithData(route, bz)
if err != nil {
return nil, 0, err
}

var clients []types.State
err = cliCtx.Codec.UnmarshalJSON(res, &clients)
if err != nil {
return nil, 0, fmt.Errorf("failed to unmarshal light clients: %w", err)
}
return clients, height, nil
}

// QueryClientState queries the store to get the light client state and a merkle
// proof.
func QueryClientState(
Expand Down
48 changes: 37 additions & 11 deletions x/ibc/02-client/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"github.com/tendermint/tendermint/libs/log"

"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
Expand Down Expand Up @@ -40,7 +39,7 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger {

// GetClientState gets a particular client from the store
func (k Keeper) GetClientState(ctx sdk.Context, clientID string) (types.State, bool) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixClient)
store := ctx.KVStore(k.storeKey)
bz := store.Get(types.KeyClientState(clientID))
if bz == nil {
return types.State{}, false
Expand All @@ -53,14 +52,14 @@ func (k Keeper) GetClientState(ctx sdk.Context, clientID string) (types.State, b

// SetClientState sets a particular Client to the store
func (k Keeper) SetClientState(ctx sdk.Context, clientState types.State) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixClient)
store := ctx.KVStore(k.storeKey)
bz := k.cdc.MustMarshalBinaryLengthPrefixed(clientState)
store.Set(types.KeyClientState(clientState.ID), bz)
}

// GetClientType gets the consensus type for a specific client
func (k Keeper) GetClientType(ctx sdk.Context, clientID string) (exported.ClientType, bool) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixClient)
store := ctx.KVStore(k.storeKey)
bz := store.Get(types.KeyClientType(clientID))
if bz == nil {
return 0, false
Expand All @@ -71,13 +70,13 @@ func (k Keeper) GetClientType(ctx sdk.Context, clientID string) (exported.Client

// SetClientType sets the specific client consensus type to the provable store
func (k Keeper) SetClientType(ctx sdk.Context, clientID string, clientType exported.ClientType) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixClient)
store := ctx.KVStore(k.storeKey)
store.Set(types.KeyClientType(clientID), []byte{byte(clientType)})
}

// GetConsensusState creates a new client state and populates it with a given consensus state
func (k Keeper) GetConsensusState(ctx sdk.Context, clientID string) (exported.ConsensusState, bool) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixClient)
store := ctx.KVStore(k.storeKey)
bz := store.Get(types.KeyConsensusState(clientID))
if bz == nil {
return nil, false
Expand All @@ -90,15 +89,15 @@ func (k Keeper) GetConsensusState(ctx sdk.Context, clientID string) (exported.Co

// SetConsensusState sets a ConsensusState to a particular client
func (k Keeper) SetConsensusState(ctx sdk.Context, clientID string, consensusState exported.ConsensusState) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixClient)
store := ctx.KVStore(k.storeKey)
bz := k.cdc.MustMarshalBinaryLengthPrefixed(consensusState)
store.Set(types.KeyConsensusState(clientID), bz)
}

// GetVerifiedRoot gets a verified commitment Root from a particular height to
// a client
func (k Keeper) GetVerifiedRoot(ctx sdk.Context, clientID string, height uint64) (commitment.RootI, bool) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixClient)
store := ctx.KVStore(k.storeKey)

bz := store.Get(types.KeyRoot(clientID, height))
if bz == nil {
Expand All @@ -113,15 +112,42 @@ func (k Keeper) GetVerifiedRoot(ctx sdk.Context, clientID string, height uint64)
// SetVerifiedRoot sets a verified commitment Root from a particular height to
// a client
func (k Keeper) SetVerifiedRoot(ctx sdk.Context, clientID string, height uint64, root commitment.RootI) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixClient)
store := ctx.KVStore(k.storeKey)
bz := k.cdc.MustMarshalBinaryLengthPrefixed(root)
store.Set(types.KeyRoot(clientID, height), bz)
}

// IterateClients provides an iterator over all stored light client State
// objects. For each State object, cb will be called. If the cb returns true,
// the iterator will close and stop.
func (k Keeper) IterateClients(ctx sdk.Context, cb func(types.State) bool) {
store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, types.GetClientKeysPrefix(ibctypes.KeyClientPrefix))

defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
var clientState types.State
k.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &clientState)

if cb(clientState) {
break
}
}
}

// GetAllClients returns all stored light client State objects.
func (k Keeper) GetAllClients(ctx sdk.Context) (states []types.State) {
k.IterateClients(ctx, func(state types.State) bool {
states = append(states, state)
return false
})
return states
}

// GetCommitter will get the Committer of a particular client at the oldest height
// that is less than or equal to the height passed in
func (k Keeper) GetCommitter(ctx sdk.Context, clientID string, height uint64) (exported.Committer, bool) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixClient)
store := ctx.KVStore(k.storeKey)

var committer exported.Committer

Expand All @@ -139,7 +165,7 @@ func (k Keeper) GetCommitter(ctx sdk.Context, clientID string, height uint64) (e
// SetCommitter sets a committer from a particular height to
// a particular client
func (k Keeper) SetCommitter(ctx sdk.Context, clientID string, height uint64, committer exported.Committer) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixClient)
store := ctx.KVStore(k.storeKey)
bz := k.cdc.MustMarshalBinaryLengthPrefixed(committer)
store.Set(types.KeyCommitter(clientID, height), bz)
}
Expand Down
Loading

0 comments on commit e11aca6

Please sign in to comment.