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 28 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,192 changes: 1,191 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
}
185 changes: 185 additions & 0 deletions x/ibc/02-client/client/rest/query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
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")
}

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))
}
}

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)
}
}

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))
}
}

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
}

bz, err := cliCtx.Codec.MarshalJSON(types.NewQueryCommitmentRootParams(clientID, height))
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}

res, resHeight, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryVerifiedRoot), bz)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}

cliCtx = cliCtx.WithHeight(resHeight)
rest.PostProcessResponse(w, cliCtx, res)
}
}

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)
}
}

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"`
}
130 changes: 130 additions & 0 deletions x/ibc/02-client/client/rest/tx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package rest

import (
"fmt"
"net/http"

"github.com/gorilla/mux"

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

// RegisterRoutes - Central function to define routes that get registered by the main application
func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router) {
r.HandleFunc("/ibc/clients", createClientHandlerFn(cliCtx)).Methods("POST")
r.HandleFunc(fmt.Sprintf("/ibc/clients/{%s}/update", RestClientID), updateClientHandlerFn(cliCtx)).Methods("POST")
r.HandleFunc(fmt.Sprintf("/ibc/clients/{%s}/misbehaviour", RestClientID), submitMisbehaviourHandlerFn(cliCtx)).Methods("POST")
}

func createClientHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req CreateClientReq
if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) {
return
}

req.BaseReq = req.BaseReq.Sanitize()
if !req.BaseReq.ValidateBasic(w) {
return
}

fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}

// create the message
msg := types.NewMsgCreateClient(
req.ClientID,
req.ConsensusState.ClientType().String(),
req.ConsensusState,
fromAddr,
)

if err := msg.ValidateBasic(); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}

utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg})
}
}

func updateClientHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
clientID := vars[RestClientID]

var req UpdateClientReq
if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) {
return
}

req.BaseReq = req.BaseReq.Sanitize()
if !req.BaseReq.ValidateBasic(w) {
return
}

fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}

// create the message
msg := types.NewMsgUpdateClient(
clientID,
req.Header,
fromAddr,
)

if err := msg.ValidateBasic(); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}

utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg})
}
}

func submitMisbehaviourHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
clientID := vars[RestClientID]

var req SubmitMisbehaviourReq
if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) {
return
}

req.BaseReq = req.BaseReq.Sanitize()
if !req.BaseReq.ValidateBasic(w) {
return
}

fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}

// create the message
msg := types.NewMsgSubmitMisbehaviour(
clientID,
req.Evidence,
fromAddr,
)

if err := msg.ValidateBasic(); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}

utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg})
}
}
5 changes: 3 additions & 2 deletions x/ibc/02-client/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/client/cli"
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/client/rest"
)

// Name returns the IBC client name
Expand All @@ -17,8 +18,8 @@ func Name() string {
}

// RegisterRESTRoutes registers the REST routes for the IBC client
func RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) {
// TODO:
func RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router, queryRoute string) {
rest.RegisterRoutes(ctx, rtr, fmt.Sprintf("%s/%s", queryRoute, SubModuleName))
}

// GetTxCmd returns the root tx command for the IBC client
Expand Down
Loading