package rest import ( "fmt" "io/ioutil" "net/http" "strings" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" ) // GasEstimateResponse defines a response definition for tx gas estimation. type GasEstimateResponse struct { GasEstimate uint64 `json:"gas_estimate"` } // BaseReq defines a structure that can be embedded in other request structures // that all share common "base" fields. type BaseReq struct { From string `json:"from"` Password string `json:"password"` Memo string `json:"memo"` ChainID string `json:"chain_id"` AccountNumber uint64 `json:"account_number"` Sequence uint64 `json:"sequence"` Fees sdk.Coins `json:"fees"` GasPrices sdk.DecCoins `json:"gas_prices"` Gas string `json:"gas"` GasAdjustment string `json:"gas_adjustment"` GenerateOnly bool `json:"generate_only"` Simulate bool `json:"simulate"` } // NewBaseReq creates a new basic request instance and sanitizes its values func NewBaseReq( from, password, memo, chainID string, gas, gasAdjustment string, accNumber, seq uint64, fees sdk.Coins, gasPrices sdk.DecCoins, genOnly, simulate bool, ) BaseReq { return BaseReq{ From: strings.TrimSpace(from), Password: password, Memo: strings.TrimSpace(memo), ChainID: strings.TrimSpace(chainID), Fees: fees, GasPrices: gasPrices, Gas: strings.TrimSpace(gas), GasAdjustment: strings.TrimSpace(gasAdjustment), AccountNumber: accNumber, Sequence: seq, GenerateOnly: genOnly, Simulate: simulate, } } // Sanitize performs basic sanitization on a BaseReq object. func (br BaseReq) Sanitize() BaseReq { return NewBaseReq( br.From, br.Password, br.Memo, br.ChainID, br.Gas, br.GasAdjustment, br.AccountNumber, br.Sequence, br.Fees, br.GasPrices, br.GenerateOnly, br.Simulate, ) } // ValidateBasic performs basic validation of a BaseReq. If custom validation // logic is needed, the implementing request handler should perform those // checks manually. func (br BaseReq) ValidateBasic(w http.ResponseWriter) bool { if !br.GenerateOnly && !br.Simulate { switch { case len(br.Password) == 0: WriteErrorResponse(w, http.StatusUnauthorized, "password required but not specified") return false case len(br.ChainID) == 0: WriteErrorResponse(w, http.StatusUnauthorized, "chain-id required but not specified") return false case !br.Fees.IsZero() && !br.GasPrices.IsZero(): // both fees and gas prices were provided WriteErrorResponse(w, http.StatusBadRequest, "cannot provide both fees and gas prices") return false case !br.Fees.IsValid() && !br.GasPrices.IsValid(): // neither fees or gas prices were provided WriteErrorResponse(w, http.StatusPaymentRequired, "invalid fees or gas prices provided") return false } } if len(br.From) == 0 { WriteErrorResponse(w, http.StatusUnauthorized, "name or address required but not specified") return false } return true } /* ReadRESTReq is a simple convenience wrapper that reads the body and unmarshals to the req interface. Usage: type SomeReq struct { BaseReq `json:"base_req"` CustomField string `json:"custom_field"` } req := new(SomeReq) err := ReadRESTReq(w, r, cdc, req) */ func ReadRESTReq(w http.ResponseWriter, r *http.Request, cdc *codec.Codec, req interface{}) error { body, err := ioutil.ReadAll(r.Body) if err != nil { WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return err } err = cdc.UnmarshalJSON(body, req) if err != nil { WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("failed to decode JSON payload: %s", err)) return err } return nil }