Finish remaining x/bank REST handlers
This commit is contained in:
parent
0e83e328d0
commit
3e0cf99e81
|
@ -80,31 +80,31 @@ func (f Factory) AccountRetriever() AccountRetriever { return f.accountRetriever
|
|||
// using the gas from the simulation results
|
||||
func (f Factory) SimulateAndExecute() bool { return f.simulateAndExecute }
|
||||
|
||||
// WithTxGenerator returns a copy of the Builder with an updated Generator.
|
||||
// WithTxGenerator returns a copy of the Factory with an updated Generator.
|
||||
func (f Factory) WithTxGenerator(g Generator) Factory {
|
||||
f.txGenerator = g
|
||||
return f
|
||||
}
|
||||
|
||||
// WithAccountRetriever returns a copy of the Builder with an updated AccountRetriever.
|
||||
// WithAccountRetriever returns a copy of the Factory with an updated AccountRetriever.
|
||||
func (f Factory) WithAccountRetriever(ar AccountRetriever) Factory {
|
||||
f.accountRetriever = ar
|
||||
return f
|
||||
}
|
||||
|
||||
// WithChainID returns a copy of the Builder with an updated chainID.
|
||||
// WithChainID returns a copy of the Factory with an updated chainID.
|
||||
func (f Factory) WithChainID(chainID string) Factory {
|
||||
f.chainID = chainID
|
||||
return f
|
||||
}
|
||||
|
||||
// WithGas returns a copy of the Builder with an updated gas value.
|
||||
// WithGas returns a copy of the Factory with an updated gas value.
|
||||
func (f Factory) WithGas(gas uint64) Factory {
|
||||
f.gas = gas
|
||||
return f
|
||||
}
|
||||
|
||||
// WithFees returns a copy of the Builder with an updated fee.
|
||||
// WithFees returns a copy of the Factory with an updated fee.
|
||||
func (f Factory) WithFees(fees string) Factory {
|
||||
parsedFees, err := sdk.ParseCoins(fees)
|
||||
if err != nil {
|
||||
|
@ -115,7 +115,7 @@ func (f Factory) WithFees(fees string) Factory {
|
|||
return f
|
||||
}
|
||||
|
||||
// WithGasPrices returns a copy of the Builder with updated gas prices.
|
||||
// WithGasPrices returns a copy of the Factory with updated gas prices.
|
||||
func (f Factory) WithGasPrices(gasPrices string) Factory {
|
||||
parsedGasPrices, err := sdk.ParseDecCoins(gasPrices)
|
||||
if err != nil {
|
||||
|
@ -126,26 +126,39 @@ func (f Factory) WithGasPrices(gasPrices string) Factory {
|
|||
return f
|
||||
}
|
||||
|
||||
// WithKeybase returns a copy of the Builder with updated Keybase.
|
||||
// WithKeybase returns a copy of the Factory with updated Keybase.
|
||||
func (f Factory) WithKeybase(keybase keys.Keybase) Factory {
|
||||
f.keybase = keybase
|
||||
return f
|
||||
}
|
||||
|
||||
// WithSequence returns a copy of the Builder with an updated sequence number.
|
||||
// WithSequence returns a copy of the Factory with an updated sequence number.
|
||||
func (f Factory) WithSequence(sequence uint64) Factory {
|
||||
f.sequence = sequence
|
||||
return f
|
||||
}
|
||||
|
||||
// WithMemo returns a copy of the Builder with an updated memo.
|
||||
// WithMemo returns a copy of the Factory with an updated memo.
|
||||
func (f Factory) WithMemo(memo string) Factory {
|
||||
f.memo = strings.TrimSpace(memo)
|
||||
return f
|
||||
}
|
||||
|
||||
// WithAccountNumber returns a copy of the Builder with an updated account number.
|
||||
// WithAccountNumber returns a copy of the Factory with an updated account number.
|
||||
func (f Factory) WithAccountNumber(accnum uint64) Factory {
|
||||
f.accountNumber = accnum
|
||||
return f
|
||||
}
|
||||
|
||||
// WithGasAdjustment returns a copy of the Factory with an updated gas adjustment.
|
||||
func (f Factory) WithGasAdjustment(gasAdj float64) Factory {
|
||||
f.gasAdjustment = gasAdj
|
||||
return f
|
||||
}
|
||||
|
||||
// WithSimulateAndExecute returns a copy of the Factory with an updated gas
|
||||
// simulation value.
|
||||
func (f Factory) WithSimulateAndExecute(sim bool) Factory {
|
||||
f.simulateAndExecute = sim
|
||||
return f
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
|
@ -16,7 +17,9 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/rest"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -71,12 +74,7 @@ func GenerateTx(ctx context.CLIContext, txf Factory, msgs ...sdk.Msg) error {
|
|||
return errors.New("cannot estimate gas in offline mode")
|
||||
}
|
||||
|
||||
txBytes, err := BuildSimTx(txf, msgs...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, adjusted, err := CalculateGas(ctx.QueryWithData, txBytes, txf.GasAdjustment())
|
||||
_, adjusted, err := CalculateGas(ctx.QueryWithData, txf, msgs...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -103,12 +101,7 @@ func BroadcastTx(ctx context.CLIContext, txf Factory, msgs ...sdk.Msg) error {
|
|||
}
|
||||
|
||||
if txf.SimulateAndExecute() || ctx.Simulate {
|
||||
txBytes, err := BuildSimTx(txf, msgs...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, adjusted, err := CalculateGas(ctx.QueryWithData, txBytes, txf.GasAdjustment())
|
||||
_, adjusted, err := CalculateGas(ctx.QueryWithData, txf, msgs...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -156,6 +149,70 @@ func BroadcastTx(ctx context.CLIContext, txf Factory, msgs ...sdk.Msg) error {
|
|||
return ctx.Print(res)
|
||||
}
|
||||
|
||||
// WriteGeneratedTxResponse writes a generated unsigned transaction to the
|
||||
// provided http.ResponseWriter. It will simulate gas costs if requested by the
|
||||
// BaseReq. Upon any error, the error will be written to the http.ResponseWriter.
|
||||
func WriteGeneratedTxResponse(
|
||||
ctx context.CLIContext, w http.ResponseWriter, txg Generator, br rest.BaseReq, msgs ...sdk.Msg,
|
||||
) {
|
||||
|
||||
gasAdj, ok := rest.ParseFloat64OrReturnBadRequest(w, br.GasAdjustment, flags.DefaultGasAdjustment)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
simAndExec, gas, err := flags.ParseGas(br.Gas)
|
||||
if err != nil {
|
||||
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
txf := Factory{fees: br.Fees, gasPrices: br.GasPrices}.
|
||||
WithAccountNumber(br.AccountNumber).
|
||||
WithSequence(br.Sequence).
|
||||
WithGas(gas).
|
||||
WithGasAdjustment(gasAdj).
|
||||
WithMemo(br.Memo).
|
||||
WithChainID(br.ChainID).
|
||||
WithSimulateAndExecute(br.Simulate)
|
||||
|
||||
if br.Simulate || simAndExec {
|
||||
if gasAdj < 0 {
|
||||
rest.WriteErrorResponse(w, http.StatusBadRequest, types.ErrorInvalidGasAdjustment.Error())
|
||||
return
|
||||
}
|
||||
|
||||
_, adjusted, err := CalculateGas(ctx.QueryWithData, txf, msgs...)
|
||||
if err != nil {
|
||||
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
txf = txf.WithGas(adjusted)
|
||||
|
||||
if br.Simulate {
|
||||
rest.WriteSimulationResponse(w, ctx.Marshaler, txf.Gas())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
tx, err := BuildUnsignedTx(txf, msgs...)
|
||||
if err != nil {
|
||||
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
output, err := ctx.Marshaler.MarshalJSON(tx)
|
||||
if err != nil {
|
||||
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write(output)
|
||||
}
|
||||
|
||||
// BuildUnsignedTx builds a transaction to be signed given a set of messages. The
|
||||
// transaction is initially created via the provided factory's generator. Once
|
||||
// created, the fee, memo, and messages are set.
|
||||
|
@ -209,9 +266,14 @@ func BuildSimTx(txf Factory, msgs ...sdk.Msg) ([]byte, error) {
|
|||
// CalculateGas simulates the execution of a transaction and returns the
|
||||
// simulation response obtained by the query and the adjusted gas amount.
|
||||
func CalculateGas(
|
||||
queryFunc func(string, []byte) ([]byte, int64, error), txBytes []byte, adjustment float64,
|
||||
queryFunc func(string, []byte) ([]byte, int64, error), txf Factory, msgs ...sdk.Msg,
|
||||
) (sdk.SimulationResponse, uint64, error) {
|
||||
|
||||
txBytes, err := BuildSimTx(txf, msgs...)
|
||||
if err != nil {
|
||||
return sdk.SimulationResponse{}, 0, err
|
||||
}
|
||||
|
||||
bz, _, err := queryFunc("/app/simulate", txBytes)
|
||||
if err != nil {
|
||||
return sdk.SimulationResponse{}, 0, err
|
||||
|
@ -222,7 +284,7 @@ func CalculateGas(
|
|||
return sdk.SimulationResponse{}, 0, err
|
||||
}
|
||||
|
||||
return simRes, uint64(adjustment * float64(simRes.GasUsed)), nil
|
||||
return simRes, uint64(txf.GasAdjustment() * float64(simRes.GasUsed)), nil
|
||||
}
|
||||
|
||||
// PrepareFactory ensures the account defined by ctx.GetFromAddress() exists and
|
||||
|
|
|
@ -53,10 +53,11 @@ func TestCalculateGas(t *testing.T) {
|
|||
|
||||
for _, tc := range testCases {
|
||||
stc := tc
|
||||
txf := tx.Factory{}.WithChainID("test-chain").WithTxGenerator(std.TxGenerator{})
|
||||
|
||||
t.Run(stc.name, func(t *testing.T) {
|
||||
queryFunc := makeQueryFunc(stc.args.queryFuncGasUsed, stc.args.queryFuncWantErr)
|
||||
simRes, gotAdjusted, err := tx.CalculateGas(queryFunc, []byte(""), stc.args.adjustment)
|
||||
simRes, gotAdjusted, err := tx.CalculateGas(queryFunc, txf.WithGasAdjustment(stc.args.adjustment))
|
||||
if stc.expPass {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, simRes.GasInfo.GasUsed, stc.wantEstimate)
|
||||
|
|
|
@ -119,16 +119,16 @@ func (br BaseReq) ValidateBasic(w http.ResponseWriter) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// ReadRESTReq reads and unmarshals a Request's body to the the BaseReq stuct.
|
||||
// ReadRESTReq reads and unmarshals a Request's body to the the BaseReq struct.
|
||||
// Writes an error response to ResponseWriter and returns true if errors occurred.
|
||||
func ReadRESTReq(w http.ResponseWriter, r *http.Request, cdc *codec.Codec, req interface{}) bool {
|
||||
func ReadRESTReq(w http.ResponseWriter, r *http.Request, m codec.JSONMarshaler, req interface{}) bool {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
err = cdc.UnmarshalJSON(body, req)
|
||||
err = m.UnmarshalJSON(body, req)
|
||||
if err != nil {
|
||||
WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("failed to decode JSON payload: %s", err))
|
||||
return false
|
||||
|
@ -158,9 +158,10 @@ func WriteErrorResponse(w http.ResponseWriter, status int, err string) {
|
|||
|
||||
// WriteSimulationResponse prepares and writes an HTTP
|
||||
// response for transactions simulations.
|
||||
func WriteSimulationResponse(w http.ResponseWriter, cdc *codec.Codec, gas uint64) {
|
||||
func WriteSimulationResponse(w http.ResponseWriter, m codec.JSONMarshaler, gas uint64) {
|
||||
gasEst := GasEstimateResponse{GasEstimate: gas}
|
||||
resp, err := cdc.MarshalJSON(gasEst)
|
||||
|
||||
resp, err := m.MarshalJSON(gasEst)
|
||||
if err != nil {
|
||||
WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
|
|
|
@ -4,8 +4,23 @@ import (
|
|||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
"github.com/cosmos/cosmos-sdk/client/tx"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
)
|
||||
|
||||
// RegisterHandlers registers all x/bank transaction and query HTTP REST handlers
|
||||
// on the provided mux router.
|
||||
func RegisterHandlers(ctx context.CLIContext, m codec.Marshaler, txg tx.Generator, r *mux.Router) {
|
||||
r.HandleFunc("/bank/accounts/{address}/transfers", NewSendRequestHandlerFn(ctx, m, txg)).Methods("POST")
|
||||
r.HandleFunc("/bank/balances/{address}", QueryBalancesRequestHandlerFn(ctx)).Methods("GET")
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Deprecated
|
||||
//
|
||||
// TODO: Remove once client-side Protobuf migration has been completed.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// RegisterRoutes - Central function to define routes that get registered by the main application
|
||||
func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) {
|
||||
r.HandleFunc("/bank/accounts/{address}/transfers", SendRequestHandlerFn(cliCtx)).Methods("POST")
|
||||
|
|
|
@ -6,6 +6,8 @@ import (
|
|||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
"github.com/cosmos/cosmos-sdk/client/tx"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/rest"
|
||||
authclient "github.com/cosmos/cosmos-sdk/x/auth/client"
|
||||
|
@ -18,7 +20,52 @@ type SendReq struct {
|
|||
Amount sdk.Coins `json:"amount" yaml:"amount"`
|
||||
}
|
||||
|
||||
// NewSendRequestHandlerFn returns an HTTP REST handler for creating a MsgSend
|
||||
// transaction.
|
||||
func NewSendRequestHandlerFn(ctx context.CLIContext, m codec.Marshaler, txg tx.Generator) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx = ctx.WithMarshaler(m)
|
||||
|
||||
vars := mux.Vars(r)
|
||||
bech32Addr := vars["address"]
|
||||
|
||||
toAddr, err := sdk.AccAddressFromBech32(bech32Addr)
|
||||
if err != nil {
|
||||
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var req SendReq
|
||||
if !rest.ReadRESTReq(w, r, ctx.Marshaler, &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
|
||||
}
|
||||
|
||||
msg := types.NewMsgSend(fromAddr, toAddr, req.Amount)
|
||||
tx.WriteGeneratedTxResponse(ctx, w, txg, req.BaseReq, msg)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Deprecated
|
||||
//
|
||||
// TODO: Remove once client-side Protobuf migration has been completed.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// SendRequestHandlerFn - http request handler to send coins to a address.
|
||||
//
|
||||
// TODO: Remove once client-side Protobuf migration has been completed.
|
||||
// ref: https://github.com/cosmos/cosmos-sdk/issues/5864
|
||||
func SendRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
|
|
Loading…
Reference in New Issue