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 IBC REST endpoints #5310

Merged
merged 33 commits into from
Dec 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
0e834e5
add rest framework
SegueII Nov 12, 2019
15c5fa9
add rest endpoints for ibc connection
chengwenxi Nov 12, 2019
a4585a2
resolve conflicts and fix annotation
chengwenxi Nov 12, 2019
e0bfc98
add rest endpoints for ibc client
SegueII Nov 12, 2019
c10108d
add rest endpoints for ibc channel
SegueII Nov 13, 2019
9bb7d22
Merge branch 'refs/heads/ibc-alpha' into irisnet-ibc-rest-apis
SegueII Nov 13, 2019
8170a2c
modify ibc rest api
SegueII Nov 14, 2019
09425be
add rest endpoints for ibc transfer
SegueII Nov 14, 2019
e36b345
fix query route
SegueII Nov 14, 2019
9c726dd
fix receive packet
SegueII Nov 15, 2019
fa190fd
fix query client state api
SegueII Nov 15, 2019
8329ec9
use sub module name instead of icsxx
SegueII Nov 15, 2019
fc3ae1b
use const for prove judgement
SegueII Nov 15, 2019
ae39605
modify ibc rest api
SegueII Nov 19, 2019
1cb7188
add api docs to swagger
SegueII Nov 19, 2019
e44e287
add ibc config
chengwenxi Nov 20, 2019
bb4ea54
fix proof path in swagger
SegueII Nov 20, 2019
6a8da59
return query result proof
Nov 20, 2019
4cfe60f
Merge pull request #133 from zhiqiang-bianjie/irisnet-ibc-rest-apis
SegueII Nov 20, 2019
5798888
update swagger docs
Nov 20, 2019
2b83694
Merge remote-tracking branch 'origin/irisnet-ibc-rest-apis' into iris…
Nov 20, 2019
ff4085c
Merge remote-tracking branch 'cosmos/ibc-alpha' into irisnet-ibc-rest…
Nov 21, 2019
6d9c62d
parse prove
chengwenxi Nov 21, 2019
03fedca
clean up
chengwenxi Nov 21, 2019
e32395d
fix ibc rest api and swagger docs
SegueII Nov 25, 2019
d877dbd
fix host validate
SegueII Nov 25, 2019
4b9e705
Merge remote-tracking branch 'origin/ibc-alpha' into irisnet-ibc-rest…
chengwenxi Nov 25, 2019
abed335
fix typo
chengwenxi Nov 25, 2019
c2cfd25
add submitMisbehaviour error response in swagger
SegueII Nov 27, 2019
70203f3
Merge remote-tracking branch 'refs/remotes/cosmos/irisnet-ibc-rest-ap…
SegueII Nov 28, 2019
f4bf4e5
fix rest queryRoot and swagger doc
SegueII Nov 28, 2019
8e0ba97
add response comments for each REST functions
SegueII Nov 28, 2019
77a2821
fix rest function comments
SegueII Nov 28, 2019
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
2 changes: 1 addition & 1 deletion client/lcd/statik/statik.go

Large diffs are not rendered by default.

1,199 changes: 1,198 additions & 1 deletion client/lcd/swagger-ui/swagger.yaml

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions types/rest/rest.go
Original file line number Diff line number Diff line change
Expand Up @@ -375,3 +375,14 @@ func ParseHTTPArgsWithLimit(r *http.Request, defaultLimit int) (tags []string, p
func ParseHTTPArgs(r *http.Request) (tags []string, page, limit int, err error) {
return ParseHTTPArgsWithLimit(r, DefaultLimit)
}

// ParseQueryProve sets the prove to execute a query if set by the http request.
// It returns false if there was an error parsing the prove.
func ParseQueryProve(r *http.Request) bool {
proveStr := r.FormValue("prove")
prove := false
if ok, err := strconv.ParseBool(proveStr); err == nil {
prove = ok
}
return prove
}
248 changes: 248 additions & 0 deletions x/ibc/02-client/client/rest/query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
package rest

import (
"fmt"
"net/http"
"strconv"

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

"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/types/rest"
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/client/utils"
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/types"
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint"
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}/consensus-state", RestClientID), queryConsensusStateHandlerFn(cliCtx, queryRoute)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/ibc/clients/{%s}/client-state", RestClientID), queryClientStateHandlerFn(cliCtx, queryRoute)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/ibc/clients/{%s}/roots/{%s}", RestClientID, RestRootHeight), queryRootHandlerFn(cliCtx, queryRoute)).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")
}

// queryConsensusStateHandlerFn implements a consensus state querying route
//
// @Summary Query cliet consensus-state
// @Tags IBC
// @Produce json
// @Param client-id path string true "Client ID"
// @Param prove query boolean false "Proof of result"
// @Success 200 {object} QueryConsensusState "OK"
// @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 {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
clientID := vars[RestClientID]

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

req := abci.RequestQuery{
Path: "store/ibc/key",
Data: types.KeyConsensusState(clientID),
Prove: rest.ParseQueryProve(r),
}

res, err := cliCtx.QueryABCI(req)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}

var cs tendermint.ConsensusState
if err := cliCtx.Codec.UnmarshalBinaryLengthPrefixed(res.Value, &cs); err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
}

cliCtx = cliCtx.WithHeight(res.Height)
rest.PostProcessResponse(w, cliCtx, types.NewConsensusStateResponse(clientID, cs, res.Proof, res.Height))
}
}

// queryHeaderHandlerFn implements a header querying route
//
// @Summary Query header
// @Tags IBC
// @Produce json
// @Success 200 {object} QueryHeader "OK"
// @Failure 500 {object} rest.ErrorResponse "Internal Server Error"
// @Router /ibc/header [get]
func queryHeaderHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
header, err := utils.GetTendermintHeader(cliCtx)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}

rest.PostProcessResponse(w, cliCtx, header)
}
}

// queryClientStateHandlerFn implements a client state querying route
//
// @Summary Query client state
// @Tags IBC
// @Produce json
// @Param client-id path string true "Client ID"
// @Param prove query boolean false "Proof of result"
// @Success 200 {object} QueryClientState "OK"
// @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 {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
clientID := vars[RestClientID]

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

req := abci.RequestQuery{
Path: "store/ibc/key",
Data: types.KeyClientState(clientID),
Prove: rest.ParseQueryProve(r),
}

res, err := cliCtx.QueryABCI(req)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}

var state types.State
if err := cliCtx.Codec.UnmarshalBinaryLengthPrefixed(res.Value, &state); err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
}

cliCtx = cliCtx.WithHeight(res.Height)
rest.PostProcessResponse(w, cliCtx, types.NewClientStateResponse(clientID, state, res.Proof, res.Height))
}
}

// queryRootHandlerFn implements a root querying route
//
// @Summary Query client root
// @Tags IBC
// @Produce json
// @Param client-id path string true "Client ID"
// @Param height path number true "Root height"
// @Success 200 {object} QueryRoot "OK"
// @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 {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
clientID := vars[RestClientID]
height, err := strconv.ParseUint(vars[RestRootHeight], 10, 64)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}

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

req := abci.RequestQuery{
Path: "store/ibc/key",
Data: types.KeyRoot(clientID, height),
Prove: rest.ParseQueryProve(r),
}

res, err := cliCtx.QueryABCI(req)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}

var root commitment.Root
if err := cliCtx.Codec.UnmarshalJSON(res.Value, &root); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}

cliCtx = cliCtx.WithHeight(res.Height)
rest.PostProcessResponse(w, cliCtx, types.NewRootResponse(clientID, height, root, res.Proof, res.Height))
}
}

// queryNodeConsensusStateHandlerFn implements a node consensus state querying route
//
// @Summary Query node consensus-state
// @Tags IBC
// @Produce json
// @Success 200 {object} QueryNodeConsensusState "OK"
// @Failure 500 {object} rest.ErrorResponse "Internal Server Error"
// @Router /ibc/node-state [get]
func queryNodeConsensusStateHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
node, err := cliCtx.GetNode()
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}

info, err := node.ABCIInfo()
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}

height := info.Response.LastBlockHeight
prevHeight := height - 1

commit, err := node.Commit(&height)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}

validators, err := node.Validators(&prevHeight)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}

state := tendermint.ConsensusState{
ChainID: commit.ChainID,
Height: uint64(commit.Height),
Root: commitment.NewRoot(commit.AppHash),
NextValidatorSet: tmtypes.NewValidatorSet(validators.Validators),
}

res := cliCtx.Codec.MustMarshalJSON(state)
cliCtx = cliCtx.WithHeight(height)
rest.PostProcessResponse(w, cliCtx, res)
}
}

// queryPathHandlerFn implements a node consensus path querying route
//
// @Summary Query IBC path
// @Tags IBC
// @Produce json
// @Success 200 {object} QueryPath "OK"
// @Failure 500 {object} rest.ErrorResponse "Internal Server Error"
// @Router /ibc/path [get]
func queryPathHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that this is hardcoded here. We can remove it and move it inside the code where it's needed for a better UX. Same applies to the CLI

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, how about moving to keys.go and providing a Querier method?

return func(w http.ResponseWriter, r *http.Request) {
path := commitment.NewPrefix([]byte("ibc"))
res := cliCtx.Codec.MustMarshalJSON(path)
rest.PostProcessResponse(w, cliCtx, res)
}
}
39 changes: 39 additions & 0 deletions x/ibc/02-client/client/rest/rest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package rest

import (
"github.com/gorilla/mux"

"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/types/rest"
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
)

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)
registerTxRoutes(cliCtx, r)
}

// CreateClientReq defines the properties of a create client request's body.
type CreateClientReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
ClientID string `json:"client_id" yaml:"client_id"`
ConsensusState exported.ConsensusState `json:"consensus_state" yaml:"consensus_state"`
}

// UpdateClientReq defines the properties of a update client request's body.
type UpdateClientReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Header exported.Header `json:"header" yaml:"header"`
}

// SubmitMisbehaviourReq defines the properties of a submit misbehaviour request's body.
type SubmitMisbehaviourReq struct {
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"`
Evidence exported.Evidence `json:"evidence" yaml:"evidence"`
}
61 changes: 61 additions & 0 deletions x/ibc/02-client/client/rest/swagger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package rest

import (
auth "github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/types"
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint"
commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment"
)

type (
QueryConsensusState struct {
Height int64 `json:"height"`
Result types.ConsensusStateResponse `json:"result"`
}

QueryHeader struct {
Height int64 `json:"height"`
Result tendermint.Header `json:"result"`
}

QueryClientState struct {
Height int64 `json:"height"`
Result types.StateResponse `json:"result"`
}

QueryRoot struct {
Height int64 `json:"height"`
Result types.RootResponse `json:"result"`
}

QueryNodeConsensusState struct {
Height int64 `json:"height"`
Result tendermint.ConsensusState `json:"result"`
}

QueryPath struct {
Height int64 `json:"height"`
Result commitment.Prefix `json:"result"`
}

PostCreateClient struct {
Msgs []types.MsgCreateClient `json:"msg" yaml:"msg"`
Fee auth.StdFee `json:"fee" yaml:"fee"`
Signatures []auth.StdSignature `json:"signatures" yaml:"signatures"`
Memo string `json:"memo" yaml:"memo"`
}

PostUpdateClient struct {
Msgs []types.MsgUpdateClient `json:"msg" yaml:"msg"`
Fee auth.StdFee `json:"fee" yaml:"fee"`
Signatures []auth.StdSignature `json:"signatures" yaml:"signatures"`
Memo string `json:"memo" yaml:"memo"`
}

PostSubmitMisbehaviour struct {
Msgs []types.MsgSubmitMisbehaviour `json:"msg" yaml:"msg"`
Fee auth.StdFee `json:"fee" yaml:"fee"`
Signatures []auth.StdSignature `json:"signatures" yaml:"signatures"`
Memo string `json:"memo" yaml:"memo"`
}
)
Loading