diff --git a/client/context/context.go b/client/context/context.go index 64d2375d09..2444c56d13 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -159,5 +159,9 @@ func (ctx CLIContext) WithUseLedger(useLedger bool) CLIContext { // WithCertifier - return a copy of the context with an updated Certifier func (ctx CLIContext) WithCertifier(certifier tmlite.Certifier) CLIContext { ctx.Certifier = certifier + +// WithGasAdjustment returns a copy of the context with an updated GasAdjustment flag. +func (ctx CLIContext) WithGasAdjustment(adjustment float64) CLIContext { + ctx.GasAdjustment = adjustment return ctx } diff --git a/client/flags.go b/client/flags.go index 3bb4d7bc33..a1d3c6e178 100644 --- a/client/flags.go +++ b/client/flags.go @@ -4,8 +4,11 @@ import "github.com/spf13/cobra" // nolint const ( - DefaultGasLimit = 200000 + // DefaultGasAdjustment is applied to gas estimates to avoid tx + // execution failures due to state changes that might + // occur between the tx simulation and the actual run. DefaultGasAdjustment = 1.0 + DefaultGasLimit = 200000 FlagUseLedger = "ledger" FlagChainID = "chain-id" diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 5a0694976b..707cc21f7a 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -266,17 +266,21 @@ func TestCoinSend(t *testing.T) { require.Equal(t, int64(1), mycoins.Amount.Int64()) // test failure with too little gas - res, body, _ = doSendWithGas(t, port, seed, name, password, addr, 100, "") + res, body, _ = doSendWithGas(t, port, seed, name, password, addr, 100, 0, "") + require.Equal(t, http.StatusInternalServerError, res.StatusCode, body) + + // test failure with wrong adjustment + res, body, _ = doSendWithGas(t, port, seed, name, password, addr, 0, 0.1, "") require.Equal(t, http.StatusInternalServerError, res.StatusCode, body) // run simulation and test success with estimated gas - res, body, _ = doSendWithGas(t, port, seed, name, password, addr, 0, "?simulate=true") + res, body, _ = doSendWithGas(t, port, seed, name, password, addr, 0, 0, "?simulate=true") require.Equal(t, http.StatusOK, res.StatusCode, body) var responseBody struct { GasEstimate int64 `json:"gas_estimate"` } require.Nil(t, json.Unmarshal([]byte(body), &responseBody)) - res, body, _ = doSendWithGas(t, port, seed, name, password, addr, responseBody.GasEstimate, "") + res, body, _ = doSendWithGas(t, port, seed, name, password, addr, responseBody.GasEstimate, 0, "") require.Equal(t, http.StatusOK, res.StatusCode, body) } @@ -727,7 +731,7 @@ func getAccount(t *testing.T, port string, addr sdk.AccAddress) auth.Account { return acc } -func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.AccAddress, gas int64, queryStr string) (res *http.Response, body string, receiveAddr sdk.AccAddress) { +func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.AccAddress, gas int64, gasAdjustment float64, queryStr string) (res *http.Response, body string, receiveAddr sdk.AccAddress) { // create receive address kb := client.MockKeyBase() @@ -751,22 +755,28 @@ func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.Acc "gas":"%v", `, gas) } + gasAdjustmentStr := "" + if gasAdjustment > 0 { + gasStr = fmt.Sprintf(` + "gas_adjustment":"%v", + `, gasAdjustment) + } jsonStr := []byte(fmt.Sprintf(`{ - %v + %v%v "name":"%s", "password":"%s", "account_number":"%d", "sequence":"%d", "amount":[%s], "chain_id":"%s" - }`, gasStr, name, password, accnum, sequence, coinbz, chainID)) + }`, gasStr, gasAdjustmentStr, name, password, accnum, sequence, coinbz, chainID)) res, body = Request(t, port, "POST", fmt.Sprintf("/accounts/%s/send%v", receiveAddr, queryStr), jsonStr) return } func doSend(t *testing.T, port, seed, name, password string, addr sdk.AccAddress) (receiveAddr sdk.AccAddress, resultTx ctypes.ResultBroadcastTxCommit) { - res, body, receiveAddr := doSendWithGas(t, port, seed, name, password, addr, 0, "") + res, body, receiveAddr := doSendWithGas(t, port, seed, name, password, addr, 0, 0, "") require.Equal(t, http.StatusOK, res.StatusCode, body) err := cdc.UnmarshalJSON([]byte(body), &resultTx) diff --git a/client/utils/rest.go b/client/utils/rest.go index 4bbc10b328..b369a81274 100644 --- a/client/utils/rest.go +++ b/client/utils/rest.go @@ -3,6 +3,7 @@ package utils import ( "fmt" "net/http" + "strconv" ) const ( @@ -28,3 +29,17 @@ func WriteSimulationResponse(w *http.ResponseWriter, gas int64) { func HasDryRunArg(r *http.Request) bool { return r.URL.Query().Get(queryArgDryRun) == "true" } + +// ParseFloat64OrReturnBadRequest converts s to a float64 value. It returns a default +// value if the string is empty. Write +func ParseFloat64OrReturnBadRequest(w *http.ResponseWriter, s string, defaultIfEmpty float64) (n float64, ok bool) { + if len(s) == 0 { + return defaultIfEmpty, true + } + n, err := strconv.ParseFloat(s, 64) + if err != nil { + WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return n, false + } + return n, true +} diff --git a/client/utils/utils.go b/client/utils/utils.go index acf90efaa0..20b1c6bc54 100644 --- a/client/utils/utils.go +++ b/client/utils/utils.go @@ -12,11 +12,6 @@ import ( "github.com/tendermint/tendermint/libs/common" ) -// DefaultGasAdjustment is applied to gas estimates to avoid tx -// execution failures due to state changes that might -// occur between the tx simulation and the actual run. -const DefaultGasAdjustment = 1.0 - // SendTx implements a auxiliary handler that facilitates sending a series of // messages in a signed transaction given a TxContext and a QueryContext. It // ensures that the account exists, has a proper number and sequence set. In @@ -91,9 +86,6 @@ func CalculateGas(queryFunc func(string, common.HexBytes) ([]byte, error), cdc * } func adjustGasEstimate(estimate int64, adjustment float64) int64 { - if adjustment == 0 { - adjustment = DefaultGasAdjustment - } return int64(adjustment * float64(estimate)) } diff --git a/x/bank/client/rest/sendtx.go b/x/bank/client/rest/sendtx.go index 07b0b87862..41635d28a8 100644 --- a/x/bank/client/rest/sendtx.go +++ b/x/bank/client/rest/sendtx.go @@ -4,6 +4,7 @@ import ( "io/ioutil" "net/http" + cliclient "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/crypto/keys" @@ -31,6 +32,7 @@ type sendBody struct { AccountNumber int64 `json:"account_number"` Sequence int64 `json:"sequence"` Gas int64 `json:"gas"` + GasAdjustment string `json:"gas_adjustment"` } var msgCdc = wire.NewCodec() @@ -86,6 +88,12 @@ func SendRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLICo Sequence: m.Sequence, } + adjustment, ok := utils.ParseFloat64OrReturnBadRequest(&w, m.GasAdjustment, cliclient.DefaultGasAdjustment) + if !ok { + return + } + cliCtx = cliCtx.WithGasAdjustment(adjustment) + if utils.HasDryRunArg(r) || m.Gas == 0 { newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, m.LocalAccountName, []sdk.Msg{msg}) if err != nil { diff --git a/x/gov/client/rest/util.go b/x/gov/client/rest/util.go index d664be5ad0..f563b4ec20 100644 --- a/x/gov/client/rest/util.go +++ b/x/gov/client/rest/util.go @@ -6,6 +6,7 @@ import ( "net/http" "strconv" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" sdk "github.com/cosmos/cosmos-sdk/types" @@ -20,6 +21,7 @@ type baseReq struct { AccountNumber int64 `json:"account_number"` Sequence int64 `json:"sequence"` Gas int64 `json:"gas"` + GasAdjustment string `json:"gas_adjustment"` } func buildReq(w http.ResponseWriter, r *http.Request, cdc *wire.Codec, req interface{}) error { @@ -76,6 +78,12 @@ func signAndBuild(w http.ResponseWriter, r *http.Request, cliCtx context.CLICont Gas: baseReq.Gas, } + adjustment, ok := utils.ParseFloat64OrReturnBadRequest(&w, baseReq.GasAdjustment, client.DefaultGasAdjustment) + if !ok { + return + } + cliCtx = cliCtx.WithGasAdjustment(adjustment) + if utils.HasDryRunArg(r) || baseReq.Gas == 0 { newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, baseReq.Name, []sdk.Msg{msg}) if err != nil { diff --git a/x/ibc/client/rest/transfer.go b/x/ibc/client/rest/transfer.go index ba0a845c29..111f9086d0 100644 --- a/x/ibc/client/rest/transfer.go +++ b/x/ibc/client/rest/transfer.go @@ -4,6 +4,7 @@ import ( "io/ioutil" "net/http" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/crypto/keys" @@ -29,6 +30,7 @@ type transferBody struct { AccountNumber int64 `json:"account_number"` Sequence int64 `json:"sequence"` Gas int64 `json:"gas"` + GasAdjustment string `json:"gas_adjustment"` } // TransferRequestHandler - http request handler to transfer coins to a address @@ -77,6 +79,12 @@ func TransferRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.C Gas: m.Gas, } + adjustment, ok := utils.ParseFloat64OrReturnBadRequest(&w, m.GasAdjustment, client.DefaultGasAdjustment) + if !ok { + return + } + cliCtx = cliCtx.WithGasAdjustment(adjustment) + if utils.HasDryRunArg(r) || m.Gas == 0 { newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, m.LocalAccountName, []sdk.Msg{msg}) if err != nil { diff --git a/x/slashing/client/rest/tx.go b/x/slashing/client/rest/tx.go index 3728558839..322a4aa083 100644 --- a/x/slashing/client/rest/tx.go +++ b/x/slashing/client/rest/tx.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "net/http" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/crypto/keys" @@ -33,6 +34,7 @@ type UnjailBody struct { AccountNumber int64 `json:"account_number"` Sequence int64 `json:"sequence"` Gas int64 `json:"gas"` + GasAdjustment string `json:"gas_adjustment"` ValidatorAddr string `json:"validator_addr"` } @@ -78,6 +80,12 @@ func unjailRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLI msg := slashing.NewMsgUnjail(valAddr) + adjustment, ok := utils.ParseFloat64OrReturnBadRequest(&w, m.GasAdjustment, client.DefaultGasAdjustment) + if !ok { + return + } + cliCtx = cliCtx.WithGasAdjustment(adjustment) + if utils.HasDryRunArg(r) || m.Gas == 0 { newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, m.LocalAccountName, []sdk.Msg{msg}) if err != nil { diff --git a/x/stake/client/rest/tx.go b/x/stake/client/rest/tx.go index 320205dd71..5e6226dcc7 100644 --- a/x/stake/client/rest/tx.go +++ b/x/stake/client/rest/tx.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "net/http" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/crypto/keys" @@ -60,6 +61,7 @@ type EditDelegationsBody struct { AccountNumber int64 `json:"account_number"` Sequence int64 `json:"sequence"` Gas int64 `json:"gas"` + GasAdjustment string `json:"gas_adjustment"` Delegations []msgDelegationsInput `json:"delegations"` BeginUnbondings []msgBeginUnbondingInput `json:"begin_unbondings"` CompleteUnbondings []msgCompleteUnbondingInput `json:"complete_unbondings"` @@ -276,6 +278,12 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex m.Sequence++ + adjustment, ok := utils.ParseFloat64OrReturnBadRequest(&w, m.GasAdjustment, client.DefaultGasAdjustment) + if !ok { + return + } + cliCtx = cliCtx.WithGasAdjustment(adjustment) + if utils.HasDryRunArg(r) || m.Gas == 0 { newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, m.LocalAccountName, []sdk.Msg{msg}) if err != nil {