Add fees and memo to REST, updated CLI to receive coins as fees (#3069)

This commit is contained in:
Federico Kunze
2018-12-19 16:26:33 -08:00
committed by Jack Zampolin
parent c9ef680f67
commit f02f7aa5a0
24 changed files with 461 additions and 397 deletions
+75 -59
View File
@@ -101,30 +101,70 @@ func WriteGenerateStdTxResponse(w http.ResponseWriter, cdc *codec.Codec, txBldr
// BaseReq defines a structure that can be embedded in other request structures
// that all share common "base" fields.
type BaseReq struct {
Name string `json:"name"`
Password string `json:"password"`
ChainID string `json:"chain_id"`
AccountNumber uint64 `json:"account_number"`
Sequence uint64 `json:"sequence"`
Gas string `json:"gas"`
GasAdjustment string `json:"gas_adjustment"`
GenerateOnly bool `json:"generate_only"`
Simulate bool `json:"simulate"`
Name string `json:"name"`
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"`
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(
name, password, memo, chainID string, gas, gasAdjustment string,
accNumber, seq uint64, fees sdk.Coins, genOnly, simulate bool) BaseReq {
return BaseReq{
Name: strings.TrimSpace(name),
Password: password,
Memo: strings.TrimSpace(memo),
ChainID: strings.TrimSpace(chainID),
Fees: fees,
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 BaseReq{
Name: strings.TrimSpace(br.Name),
Password: strings.TrimSpace(br.Password),
ChainID: strings.TrimSpace(br.ChainID),
Gas: strings.TrimSpace(br.Gas),
GasAdjustment: strings.TrimSpace(br.GasAdjustment),
AccountNumber: br.AccountNumber,
Sequence: br.Sequence,
GenerateOnly: br.GenerateOnly,
Simulate: br.Simulate,
newBr := NewBaseReq(
br.Name, br.Password, br.Memo, br.ChainID, br.Gas, br.GasAdjustment,
br.AccountNumber, br.Sequence, br.Fees, br.GenerateOnly, br.Simulate,
)
return newBr
}
// 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.IsValid():
WriteErrorResponse(w, http.StatusPaymentRequired, "invalid or insufficient fees")
return false
}
}
if len(br.Name) == 0 {
WriteErrorResponse(w, http.StatusUnauthorized, "name required but not specified")
return false
}
return true
}
/*
@@ -156,27 +196,6 @@ func ReadRESTReq(w http.ResponseWriter, r *http.Request, cdc *codec.Codec, req i
return nil
}
// 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, cliCtx context.CLIContext) bool {
if !cliCtx.GenerateOnly && !cliCtx.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
}
}
if len(br.Name) == 0 {
WriteErrorResponse(w, http.StatusUnauthorized, "name required but not specified")
return false
}
return true
}
// CompleteAndBroadcastTxREST implements a utility function that facilitates
// sending a series of messages in a signed transaction given a TxBuilder and a
// QueryContext. It ensures that the account exists, has a proper number and
@@ -186,40 +205,37 @@ func (br BaseReq) ValidateBasic(w http.ResponseWriter, cliCtx context.CLIContext
// NOTE: Also see CompleteAndBroadcastTxCli.
// NOTE: Also see x/stake/client/rest/tx.go delegationsRequestHandlerFn.
func CompleteAndBroadcastTxREST(w http.ResponseWriter, r *http.Request, cliCtx context.CLIContext, baseReq BaseReq, msgs []sdk.Msg, cdc *codec.Codec) {
simulateGas, gas, err := client.ReadGasFlag(baseReq.Gas)
gasAdjustment, ok := ParseFloat64OrReturnBadRequest(w, baseReq.GasAdjustment, client.DefaultGasAdjustment)
if !ok {
return
}
simulateAndExecute, gas, err := client.ParseGas(baseReq.Gas)
if err != nil {
WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
adjustment, ok := ParseFloat64OrReturnBadRequest(w, baseReq.GasAdjustment, client.DefaultGasAdjustment)
if !ok {
return
}
txBldr := authtxb.NewTxBuilder(GetTxEncoder(cdc), baseReq.AccountNumber,
baseReq.Sequence, gas, gasAdjustment, baseReq.Simulate,
baseReq.ChainID, baseReq.Memo, baseReq.Fees)
txBldr := authtxb.TxBuilder{
TxEncoder: GetTxEncoder(cdc),
Gas: gas,
GasAdjustment: adjustment,
SimulateGas: simulateGas,
ChainID: baseReq.ChainID,
AccountNumber: baseReq.AccountNumber,
Sequence: baseReq.Sequence,
}
if baseReq.Simulate || simulateAndExecute {
if gasAdjustment < 0 {
WriteErrorResponse(w, http.StatusBadRequest, "gas adjustment must be a positive float")
return
}
if baseReq.Simulate || txBldr.SimulateGas {
newBldr, err := EnrichCtxWithGas(txBldr, cliCtx, baseReq.Name, msgs)
txBldr, err = EnrichCtxWithGas(txBldr, cliCtx, baseReq.Name, msgs)
if err != nil {
WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
if baseReq.Simulate {
WriteSimulationResponse(w, newBldr.Gas)
WriteSimulationResponse(w, txBldr.GetGas())
return
}
txBldr = newBldr
}
if baseReq.GenerateOnly {
+11 -10
View File
@@ -3,10 +3,11 @@ package utils
import (
"bytes"
"fmt"
"github.com/cosmos/cosmos-sdk/codec"
"io"
"os"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/tendermint/go-amino"
"github.com/tendermint/tendermint/libs/common"
@@ -35,12 +36,12 @@ func CompleteAndBroadcastTxCli(txBldr authtxb.TxBuilder, cliCtx context.CLIConte
return err
}
if txBldr.SimulateGas || cliCtx.Simulate {
if txBldr.GetSimulateAndExecute() || cliCtx.Simulate {
txBldr, err = EnrichCtxWithGas(txBldr, cliCtx, name, msgs)
if err != nil {
return err
}
fmt.Fprintf(os.Stderr, "estimated gas = %v\n", txBldr.Gas)
fmt.Fprintf(os.Stderr, "estimated gas = %v\n", txBldr.GetGas())
}
if cliCtx.Simulate {
return nil
@@ -131,7 +132,7 @@ func SignStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string,
"The generated transaction's intended signer does not match the given signer: %q", name)
}
if !offline && txBldr.AccountNumber == 0 {
if !offline && txBldr.GetAccountNumber() == 0 {
accNum, err := cliCtx.GetAccountNumber(addr)
if err != nil {
return signedStdTx, err
@@ -139,7 +140,7 @@ func SignStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string,
txBldr = txBldr.WithAccountNumber(accNum)
}
if !offline && txBldr.Sequence == 0 {
if !offline && txBldr.GetSequence() == 0 {
accSeq, err := cliCtx.GetAccountSequence(addr)
if err != nil {
return signedStdTx, err
@@ -172,7 +173,7 @@ func simulateMsgs(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name stri
if err != nil {
return
}
estimated, adjusted, err = CalculateGas(cliCtx.Query, cliCtx.Codec, txBytes, txBldr.GasAdjustment)
estimated, adjusted, err = CalculateGas(cliCtx.Query, cliCtx.Codec, txBytes, txBldr.GetGasAdjustment())
return
}
@@ -200,7 +201,7 @@ func prepareTxBuilder(txBldr authtxb.TxBuilder, cliCtx context.CLIContext) (auth
// TODO: (ref #1903) Allow for user supplied account number without
// automatically doing a manual lookup.
if txBldr.AccountNumber == 0 {
if txBldr.GetAccountNumber() == 0 {
accNum, err := cliCtx.GetAccountNumber(from)
if err != nil {
return txBldr, err
@@ -210,7 +211,7 @@ func prepareTxBuilder(txBldr authtxb.TxBuilder, cliCtx context.CLIContext) (auth
// TODO: (ref #1903) Allow for user supplied account sequence without
// automatically doing a manual lookup.
if txBldr.Sequence == 0 {
if txBldr.GetSequence() == 0 {
accSeq, err := cliCtx.GetAccountSequence(from)
if err != nil {
return txBldr, err
@@ -231,7 +232,7 @@ func buildUnsignedStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msg
}
func buildUnsignedStdTxOffline(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) (stdTx auth.StdTx, err error) {
if txBldr.SimulateGas {
if txBldr.GetSimulateAndExecute() {
var name string
name, err = cliCtx.GetFromName()
if err != nil {
@@ -242,7 +243,7 @@ func buildUnsignedStdTxOffline(txBldr authtxb.TxBuilder, cliCtx context.CLIConte
if err != nil {
return
}
fmt.Fprintf(os.Stderr, "estimated gas = %v\n", txBldr.Gas)
fmt.Fprintf(os.Stderr, "estimated gas = %v\n", txBldr.GetGas())
}
stdSignMsg, err := txBldr.Build(msgs)
if err != nil {
+2 -1
View File
@@ -3,11 +3,12 @@ package utils
import (
"encoding/json"
"errors"
"testing"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto/ed25519"
"testing"
"github.com/stretchr/testify/assert"
"github.com/tendermint/tendermint/libs/common"