-
Notifications
You must be signed in to change notification settings - Fork 411
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #30 from cosmwasm/vw/add-rest-server
Added rest interface
- Loading branch information
Showing
3 changed files
with
312 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 { | ||
return func(w http.ResponseWriter, r *http.Request) { | ||
|
||
} | ||
} | ||
|
||
func queryContractStateRawHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { | ||
return func(w http.ResponseWriter, r *http.Request) { | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) { | ||
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"` | ||
WasmBytes []byte `json:"wasm_bytes"` | ||
} | ||
|
||
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 | ||
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}) | ||
} | ||
} |