-
Notifications
You must be signed in to change notification settings - Fork 396
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
Added rest interface #30
Changes from all commits
d33e084
b9e2bc3
6aa15e7
d1c1dcf
386e0f6
0f8ea58
329ee04
2db61ec
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
package rest | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
"github.com/cosmos/cosmos-sdk/types/rest" | ||
"github.com/cosmwasm/wasmd/x/wasm/internal/keeper" | ||
"github.com/cosmwasm/wasmd/x/wasm/internal/types" | ||
"net/http" | ||
"strconv" | ||
|
||
"github.com/cosmos/cosmos-sdk/client/context" | ||
"github.com/gorilla/mux" | ||
) | ||
|
||
func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) { | ||
r.HandleFunc("/wasm/code/", listCodesHandlerFn(cliCtx)).Methods("GET") | ||
r.HandleFunc("/wasm/code/{codeID}", queryCodeHandlerFn(cliCtx)).Methods("GET") | ||
r.HandleFunc("/wasm/contract/", listAllContractsHandlerFn(cliCtx)).Methods("GET") | ||
r.HandleFunc("/wasm/contract/{contractAddr}", queryContractHandlerFn(cliCtx)).Methods("GET") | ||
r.HandleFunc("/wasm/contract/{contractAddr}/state", queryContractStateAllHandlerFn(cliCtx)).Methods("GET") | ||
r.HandleFunc("/wasm/contract/{contractAddr}/smart", queryContractStateSmartHandlerFn(cliCtx)).Methods("GET") | ||
r.HandleFunc("/wasm/contract/{contractAddr}/raw", queryContractStateRawHandlerFn(cliCtx)).Methods("GET") | ||
} | ||
|
||
func listCodesHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { | ||
return func(w http.ResponseWriter, r *http.Request) { | ||
route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, keeper.QueryListCode) | ||
res, _, err := cliCtx.Query(route) | ||
if err != nil { | ||
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) | ||
return | ||
} | ||
rest.PostProcessResponse(w, cliCtx, string(res)) | ||
} | ||
} | ||
|
||
func queryCodeHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { | ||
return func(w http.ResponseWriter, r *http.Request) { | ||
codeID, err := strconv.ParseUint(mux.Vars(r)["codeId"], 10, 64) | ||
if err != nil { | ||
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) | ||
return | ||
} | ||
|
||
route := fmt.Sprintf("custom/%s/%s/%d", types.QuerierRoute, keeper.QueryGetCode, codeID) | ||
res, _, err := cliCtx.Query(route) | ||
if err != nil { | ||
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) | ||
return | ||
} | ||
|
||
if len(res) == 0 { | ||
rest.WriteErrorResponse(w, http.StatusInternalServerError, "contract not found") | ||
return | ||
} | ||
var code keeper.GetCodeResponse | ||
err = json.Unmarshal(res, &code) | ||
if err != nil { | ||
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) | ||
return | ||
} | ||
|
||
if len(code.Code) == 0 { | ||
rest.WriteErrorResponse(w, http.StatusInternalServerError, "contract not found") | ||
return | ||
} | ||
|
||
rest.PostProcessResponse(w, cliCtx, string(code.Code)) | ||
} | ||
} | ||
|
||
func listAllContractsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { | ||
return func(w http.ResponseWriter, r *http.Request) { | ||
route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, keeper.QueryListContracts) | ||
res, _, err := cliCtx.Query(route) | ||
if err != nil { | ||
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) | ||
|
||
return | ||
} | ||
rest.PostProcessResponse(w, cliCtx, string(res)) | ||
} | ||
} | ||
|
||
func queryContractHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { | ||
return func(w http.ResponseWriter, r *http.Request) { | ||
addr, err := sdk.AccAddressFromBech32(mux.Vars(r)["codeId"]) | ||
if err != nil { | ||
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) | ||
return | ||
} | ||
|
||
route := fmt.Sprintf("custom/%s/%s/%s", types.QuerierRoute, keeper.QueryGetContract, addr.String()) | ||
res, _, err := cliCtx.Query(route) | ||
if err != nil { | ||
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) | ||
return | ||
} | ||
rest.PostProcessResponse(w, cliCtx, string(res)) | ||
} | ||
} | ||
|
||
func queryContractStateAllHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { | ||
return func(w http.ResponseWriter, r *http.Request) { | ||
addr, err := sdk.AccAddressFromBech32(mux.Vars(r)["contractAddr"]) | ||
if err != nil { | ||
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) | ||
return | ||
} | ||
|
||
route := fmt.Sprintf("custom/%s/%s/%s/%s", types.QuerierRoute, keeper.QueryGetContractState, addr.String(), keeper.QueryMethodContractStateAll) | ||
res, _, err := cliCtx.Query(route) | ||
if err != nil { | ||
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) | ||
return | ||
} | ||
rest.PostProcessResponse(w, cliCtx, string(res)) | ||
} | ||
} | ||
|
||
func queryContractStateSmartHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These two should be marked TODO, but as long as you know and will follow up shortly, this is fine |
||
return func(w http.ResponseWriter, r *http.Request) { | ||
|
||
} | ||
} | ||
|
||
func queryContractStateRawHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { | ||
return func(w http.ResponseWriter, r *http.Request) { | ||
|
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package rest | ||
|
||
import ( | ||
"github.com/gorilla/mux" | ||
|
||
"github.com/cosmos/cosmos-sdk/client/context" | ||
) | ||
|
||
// RegisterRoutes registers staking-related REST handlers to a router | ||
func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) { | ||
registerQueryRoutes(cliCtx, r) | ||
registerTxRoutes(cliCtx, r) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
package rest | ||
|
||
import ( | ||
"net/http" | ||
"strconv" | ||
|
||
"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/gorilla/mux" | ||
|
||
wasmUtils "github.com/cosmwasm/wasmd/x/wasm/client/utils" | ||
"github.com/cosmwasm/wasmd/x/wasm/internal/types" | ||
) | ||
|
||
func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks good |
||
r.HandleFunc("/wasm/code/", storeCodeHandlerFn(cliCtx)).Methods("POST") | ||
r.HandleFunc("/wasm/code/{codeId}", instantiateContractHandlerFn(cliCtx)).Methods("POST") | ||
r.HandleFunc("/wasm/contract/{contractAddr}", executeContractHandlerFn(cliCtx)).Methods("POST") | ||
} | ||
|
||
// limit max bytes read to prevent gzip bombs | ||
const maxSize = 400 * 1024 | ||
|
||
type storeCodeReq struct { | ||
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was thinking this should be "squash", but see that it is not in the cosmos-sdk either. |
||
WasmBytes []byte `json:"wasm_bytes"` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. missing yaml |
||
} | ||
|
||
type instantiateContractReq struct { | ||
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` | ||
Deposit sdk.Coins `json:"deposit" yaml:"deposit"` | ||
InitMsg []byte `json:"init_msg" yaml:"init_msg"` | ||
} | ||
|
||
type executeContractReq struct { | ||
BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` | ||
ExecMsg []byte `json:"exec_msg" yaml:"exec_msg"` | ||
Amount sdk.Coins `json:"coins" yaml:"coins"` | ||
} | ||
|
||
func storeCodeHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { | ||
return func(w http.ResponseWriter, r *http.Request) { | ||
var req storeCodeReq | ||
if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { | ||
return | ||
} | ||
|
||
req.BaseReq = req.BaseReq.Sanitize() | ||
if !req.BaseReq.ValidateBasic(w) { | ||
return | ||
} | ||
|
||
var err error | ||
wasm := req.WasmBytes | ||
if len(wasm) > maxSize { | ||
rest.WriteErrorResponse(w, http.StatusBadRequest, "Binary size exceeds maximum limit") | ||
return | ||
} | ||
|
||
// gzip the wasm file | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice to auto-gzip it here as well 👍 |
||
if wasmUtils.IsWasm(wasm) { | ||
wasm, err = wasmUtils.GzipIt(wasm) | ||
if err != nil { | ||
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) | ||
return | ||
} | ||
} else if !wasmUtils.IsGzip(wasm) { | ||
rest.WriteErrorResponse(w, http.StatusBadRequest, "Invalid input file, use wasm binary or zip") | ||
return | ||
} | ||
|
||
fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From) | ||
if err != nil { | ||
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) | ||
return | ||
} | ||
// build and sign the transaction, then broadcast to Tendermint | ||
msg := types.MsgStoreCode{ | ||
Sender: fromAddr, | ||
WASMByteCode: wasm, | ||
} | ||
|
||
err = msg.ValidateBasic() | ||
if err != nil { | ||
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) | ||
return | ||
} | ||
|
||
utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg}) | ||
} | ||
} | ||
|
||
func instantiateContractHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { | ||
return func(w http.ResponseWriter, r *http.Request) { | ||
var req instantiateContractReq | ||
if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { | ||
return | ||
} | ||
vars := mux.Vars(r) | ||
codeId := vars["codeId"] | ||
|
||
req.BaseReq = req.BaseReq.Sanitize() | ||
if !req.BaseReq.ValidateBasic(w) { | ||
return | ||
} | ||
|
||
// get the id of the code to instantiate | ||
codeID, err := strconv.ParseUint(codeId, 10, 64) | ||
if err != nil { | ||
return | ||
} | ||
|
||
msg := types.MsgInstantiateContract{ | ||
Sender: cliCtx.GetFromAddress(), | ||
Code: codeID, | ||
InitFunds: req.Deposit, | ||
InitMsg: req.InitMsg, | ||
} | ||
|
||
err = msg.ValidateBasic() | ||
if err != nil { | ||
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) | ||
return | ||
} | ||
|
||
utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg}) | ||
} | ||
} | ||
|
||
func executeContractHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { | ||
return func(w http.ResponseWriter, r *http.Request) { | ||
var req executeContractReq | ||
if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { | ||
return | ||
} | ||
vars := mux.Vars(r) | ||
contractAddr := vars["contractAddr"] | ||
|
||
req.BaseReq = req.BaseReq.Sanitize() | ||
if !req.BaseReq.ValidateBasic(w) { | ||
return | ||
} | ||
|
||
contractAddress, err := sdk.AccAddressFromBech32(contractAddr) | ||
if err != nil { | ||
return | ||
} | ||
|
||
msg := types.MsgExecuteContract{ | ||
Sender: cliCtx.GetFromAddress(), | ||
Contract: contractAddress, | ||
Msg: req.ExecMsg, | ||
SentFunds: req.Amount, | ||
} | ||
|
||
err = msg.ValidateBasic() | ||
if err != nil { | ||
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) | ||
return | ||
} | ||
|
||
utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg}) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file looks good