From f432c0c383af189360302c72d78c25bfa5e91784 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Wed, 22 Aug 2018 12:38:55 +0100 Subject: [PATCH 01/10] Simulate transactions before actual execution * Change --gas=0 semantic in order to enable gas auto estimate. * REST clients have been modified to simulate the execution of the tx first to then populate the context with the estimated gas amount returned by the simulation. * The simulation returns both an unadjusted gas estimate and an adjusted one. The adjustment is required to ensure that the ensuing execution doesn't fail due to state changes that might have occurred. Gas adjustment can be controlled via the CLI's --gas-adjustment flag. * Tiny refactorig of REST endpoints error handling. Closes: #1246 --- baseapp/baseapp.go | 35 +++++++---- client/context/context.go | 4 ++ client/context/query.go | 7 +-- client/flags.go | 6 +- client/lcd/lcd_test.go | 58 ++++++++++------- client/lcd/version.go | 2 +- client/utils/rest.go | 12 ++++ client/utils/utils.go | 61 +++++++++++++++++- client/utils/utils_test.go | 20 ++++++ cmd/gaia/cli_test/cli_test.go | 90 ++++++++++++++++---------- x/auth/ante.go | 6 +- x/auth/ante_test.go | 2 +- x/auth/client/context/context.go | 4 +- x/auth/client/rest/query.go | 13 ++-- x/bank/client/rest/sendtx.go | 33 +++++----- x/gov/client/rest/rest.go | 105 ++++++++++--------------------- x/gov/client/rest/util.go | 37 +++++------ x/ibc/client/rest/transfer.go | 31 ++++----- x/slashing/client/rest/tx.go | 33 +++++----- x/stake/client/rest/tx.go | 75 +++++++++------------- 20 files changed, 367 insertions(+), 267 deletions(-) create mode 100644 client/utils/rest.go create mode 100644 client/utils/utils_test.go diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 365d7b31f249..2669aa9f302f 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -495,13 +495,11 @@ func validateBasicTxMsgs(msgs []sdk.Msg) sdk.Error { func (app *BaseApp) getContextForAnte(mode runTxMode, txBytes []byte) (ctx sdk.Context) { // Get the context - if mode == runTxModeCheck || mode == runTxModeSimulate { - ctx = app.checkState.ctx.WithTxBytes(txBytes) - } else { - ctx = app.deliverState.ctx.WithTxBytes(txBytes) - ctx = ctx.WithSigningValidators(app.signedValidators) + ctx = getState(app, mode).ctx.WithTxBytes(txBytes) + if mode != runTxModeDeliver { + return } - + ctx = ctx.WithSigningValidators(app.signedValidators) return } @@ -567,6 +565,13 @@ func getState(app *BaseApp, mode runTxMode) *state { return app.deliverState } +func (app *BaseApp) applyTxMode(ctx sdk.Context, mode runTxMode) sdk.Context { + if mode != runTxModeSimulate { + return ctx + } + return ctx.WithMultiStore(getState(app, runTxModeSimulate).CacheMultiStore()) +} + // runTx processes a transaction. The transactions is proccessed via an // anteHandler. txBytes may be nil in some cases, eg. in tests. Also, in the // future we may support "internal" transactions. @@ -575,7 +580,9 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk // determined by the GasMeter. We need access to the context to get the gas // meter so we initialize upfront. var gasWanted int64 + var msCache sdk.CacheMultiStore ctx := app.getContextForAnte(mode, txBytes) + ctx = app.applyTxMode(ctx, mode) defer func() { if r := recover(); r != nil { @@ -594,9 +601,7 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk }() var msgs = tx.GetMsgs() - - err := validateBasicTxMsgs(msgs) - if err != nil { + if err := validateBasicTxMsgs(msgs); err != nil { return err.Result() } @@ -613,9 +618,15 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk gasWanted = result.GasWanted } + if mode == runTxModeSimulate { + result = app.runMsgs(ctx, msgs, mode) + result.GasWanted = gasWanted + return + } + // Keep the state in a transient CacheWrap in case processing the messages // fails. - msCache := getState(app, mode).CacheMultiStore() + msCache = getState(app, mode).CacheMultiStore() if msCache.TracingEnabled() { msCache = msCache.WithTracingContext(sdk.TraceContext( map[string]interface{}{"txHash": cmn.HexBytes(tmhash.Sum(txBytes)).String()}, @@ -626,8 +637,8 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk result = app.runMsgs(ctx, msgs, mode) result.GasWanted = gasWanted - // only update state if all messages pass and we're not in a simulation - if result.IsOK() && mode != runTxModeSimulate { + // only update state if all messages pass + if result.IsOK() { msCache.Write() } diff --git a/client/context/context.go b/client/context/context.go index 1b0443b0c7c7..743c923552c8 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -22,6 +22,8 @@ type CLIContext struct { Client rpcclient.Client Logger io.Writer Height int64 + Gas int64 + GasAdjustment float64 NodeURI string FromAddressName string AccountStore string @@ -48,6 +50,8 @@ func NewCLIContext() CLIContext { AccountStore: ctxAccStoreName, FromAddressName: viper.GetString(client.FlagFrom), Height: viper.GetInt64(client.FlagHeight), + Gas: viper.GetInt64(client.FlagGas), + GasAdjustment: viper.GetFloat64(client.FlagGasAdjustment), TrustNode: viper.GetBool(client.FlagTrustNode), UseLedger: viper.GetBool(client.FlagUseLedger), Async: viper.GetBool(client.FlagAsync), diff --git a/client/context/query.go b/client/context/query.go index 68676f7415dc..e526c0abbc90 100644 --- a/client/context/query.go +++ b/client/context/query.go @@ -10,7 +10,6 @@ import ( "github.com/pkg/errors" - "github.com/tendermint/tendermint/libs/common" cmn "github.com/tendermint/tendermint/libs/common" rpcclient "github.com/tendermint/tendermint/rpc/client" ctypes "github.com/tendermint/tendermint/rpc/core/types" @@ -27,8 +26,8 @@ func (ctx CLIContext) GetNode() (rpcclient.Client, error) { } // Query performs a query for information about the connected node. -func (ctx CLIContext) Query(path string) (res []byte, err error) { - return ctx.query(path, nil) +func (ctx CLIContext) Query(path string, data cmn.HexBytes) (res []byte, err error) { + return ctx.query(path, data) } // Query information about the connected node with a data payload @@ -284,7 +283,7 @@ func (ctx CLIContext) ensureBroadcastTx(txBytes []byte) error { // query performs a query from a Tendermint node with the provided store name // and path. -func (ctx CLIContext) query(path string, key common.HexBytes) (res []byte, err error) { +func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, err error) { node, err := ctx.GetNode() if err != nil { return res, err diff --git a/client/flags.go b/client/flags.go index b020789057f6..d128d32f6656 100644 --- a/client/flags.go +++ b/client/flags.go @@ -4,11 +4,14 @@ import "github.com/spf13/cobra" // nolint const ( + DefaultGasAdjustment = 0 + FlagUseLedger = "ledger" FlagChainID = "chain-id" FlagNode = "node" FlagHeight = "height" FlagGas = "gas" + FlagGasAdjustment = "gas-adjustment" FlagTrustNode = "trust-node" FlagFrom = "from" FlagName = "name" @@ -49,7 +52,8 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command { c.Flags().String(FlagChainID, "", "Chain ID of tendermint node") c.Flags().String(FlagNode, "tcp://localhost:26657", ": to tendermint rpc interface for this chain") c.Flags().Bool(FlagUseLedger, false, "Use a connected Ledger device") - c.Flags().Int64(FlagGas, 200000, "gas limit to set per-transaction") + c.Flags().Int64(FlagGas, 0, "gas limit to set per-transaction; set to 0 to calculate required gas automatically") + c.Flags().Float64(FlagGasAdjustment, DefaultGasAdjustment, "gas adjustment to be applied on the estimate returned by the tx simulation") c.Flags().Bool(FlagAsync, false, "broadcast transactions asynchronously") c.Flags().Bool(FlagJson, false, "return output in json format") c.Flags().Bool(FlagPrintResponse, true, "return tx response (only works with async = false)") diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 2e7ab0af7ce1..2db2f6d618d8 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -263,6 +263,10 @@ func TestCoinSend(t *testing.T) { require.Equal(t, "steak", mycoins.Denom) require.Equal(t, int64(1), mycoins.Amount.Int64()) + + // test failure with too little gas + res, body, _ = doSendWithGas(t, port, seed, name, password, addr, 100) + require.Equal(t, http.StatusInternalServerError, res.StatusCode, body) } func TestIBCTransfer(t *testing.T) { @@ -712,7 +716,7 @@ func getAccount(t *testing.T, port string, addr sdk.AccAddress) auth.Account { return acc } -func doSend(t *testing.T, port, seed, name, password string, addr sdk.AccAddress) (receiveAddr sdk.AccAddress, resultTx ctypes.ResultBroadcastTxCommit) { +func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.AccAddress, gas int64) (res *http.Response, body string, receiveAddr sdk.AccAddress) { // create receive address kb := client.MockKeyBase() @@ -730,19 +734,36 @@ func doSend(t *testing.T, port, seed, name, password string, addr sdk.AccAddress panic(err) } - jsonStr := []byte(fmt.Sprintf(`{ - "name":"%s", - "password":"%s", - "account_number":"%d", - "sequence":"%d", - "gas": "10000", - "amount":[%s], - "chain_id":"%s" - }`, name, password, accnum, sequence, coinbz, chainID)) - res, body := Request(t, port, "POST", fmt.Sprintf("/accounts/%s/send", receiveAddr), jsonStr) + jsonStr := func() []byte { + if gas > 0 { + return []byte(fmt.Sprintf(`{ + "name":"%s", + "password":"%s", + "account_number":"%d", + "sequence":"%d", + "amount":[%s], + "chain_id":"%s", + "gas":"%v" + }`, name, password, accnum, sequence, coinbz, chainID, gas)) + } + return []byte(fmt.Sprintf(`{ + "name":"%s", + "password":"%s", + "account_number":"%d", + "sequence":"%d", + "amount":[%s], + "chain_id":"%s" + }`, name, password, accnum, sequence, coinbz, chainID)) + }() + res, body = Request(t, port, "POST", fmt.Sprintf("/accounts/%s/send", receiveAddr), 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) require.Equal(t, http.StatusOK, res.StatusCode, body) - err = cdc.UnmarshalJSON([]byte(body), &resultTx) + err := cdc.UnmarshalJSON([]byte(body), &resultTx) require.Nil(t, err) return receiveAddr, resultTx @@ -768,7 +789,6 @@ func doIBCTransfer(t *testing.T, port, seed, name, password string, addr sdk.Acc "password": "%s", "account_number":"%d", "sequence": "%d", - "gas": "100000", "src_chain_id": "%s", "amount":[ { @@ -887,7 +907,6 @@ func doDelegate(t *testing.T, port, seed, name, password string, delegatorAddr, "password": "%s", "account_number": "%d", "sequence": "%d", - "gas": "10000", "chain_id": "%s", "delegations": [ { @@ -925,7 +944,6 @@ func doBeginUnbonding(t *testing.T, port, seed, name, password string, "password": "%s", "account_number": "%d", "sequence": "%d", - "gas": "20000", "chain_id": "%s", "delegations": [], "begin_unbondings": [ @@ -964,7 +982,6 @@ func doBeginRedelegation(t *testing.T, port, seed, name, password string, "password": "%s", "account_number": "%d", "sequence": "%d", - "gas": "10000", "chain_id": "%s", "delegations": [], "begin_unbondings": [], @@ -1116,8 +1133,7 @@ func doSubmitProposal(t *testing.T, port, seed, name, password string, proposerA "password": "%s", "chain_id": "%s", "account_number":"%d", - "sequence":"%d", - "gas":"100000" + "sequence":"%d" } }`, proposerAddr, name, password, chainID, accnum, sequence)) res, body := Request(t, port, "POST", "/gov/proposals", jsonStr) @@ -1147,8 +1163,7 @@ func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk "password": "%s", "chain_id": "%s", "account_number":"%d", - "sequence": "%d", - "gas":"100000" + "sequence": "%d" } }`, proposerAddr, name, password, chainID, accnum, sequence)) res, body := Request(t, port, "POST", fmt.Sprintf("/gov/proposals/%d/deposits", proposalID), jsonStr) @@ -1178,8 +1193,7 @@ func doVote(t *testing.T, port, seed, name, password string, proposerAddr sdk.Ac "password": "%s", "chain_id": "%s", "account_number": "%d", - "sequence": "%d", - "gas":"100000" + "sequence": "%d" } }`, proposerAddr, name, password, chainID, accnum, sequence)) res, body := Request(t, port, "POST", fmt.Sprintf("/gov/proposals/%d/votes", proposalID), jsonStr) diff --git a/client/lcd/version.go b/client/lcd/version.go index 377d7ca2681f..a124388e6dfc 100644 --- a/client/lcd/version.go +++ b/client/lcd/version.go @@ -17,7 +17,7 @@ func CLIVersionRequestHandler(w http.ResponseWriter, r *http.Request) { // connected node version REST handler endpoint func NodeVersionRequestHandler(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - version, err := cliCtx.Query("/app/version") + version, err := cliCtx.Query("/app/version", nil) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(fmt.Sprintf("Could't query version. Error: %s", err.Error()))) diff --git a/client/utils/rest.go b/client/utils/rest.go new file mode 100644 index 000000000000..0e264031241b --- /dev/null +++ b/client/utils/rest.go @@ -0,0 +1,12 @@ +package utils + +import ( + "net/http" +) + +// WriteErrorResponse prepares and writes a HTTP error +// given a status code and an error message. +func WriteErrorResponse(w *http.ResponseWriter, status int, msg string) { + (*w).WriteHeader(status) + (*w).Write([]byte(msg)) +} diff --git a/client/utils/utils.go b/client/utils/utils.go index 8a058b56fda0..9f06dda1d7b8 100644 --- a/client/utils/utils.go +++ b/client/utils/utils.go @@ -1,12 +1,21 @@ package utils import ( + "fmt" + "os" + "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/keys" sdk "github.com/cosmos/cosmos-sdk/types" authctx "github.com/cosmos/cosmos-sdk/x/auth/client/context" + amino "github.com/tendermint/go-amino" ) +// 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.2 + // 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 @@ -49,12 +58,62 @@ func SendTx(txCtx authctx.TxContext, cliCtx context.CLIContext, msgs []sdk.Msg) return err } + txCtx, err = enrichCtxWithGasIfGasAuto(txCtx, cliCtx, cliCtx.FromAddressName, passphrase, msgs) + if err != nil { + return err + } + // build and sign the transaction txBytes, err := txCtx.BuildAndSign(cliCtx.FromAddressName, passphrase, msgs) if err != nil { return err } - // broadcast to a Tendermint node return cliCtx.EnsureBroadcastTx(txBytes) } + +func enrichCtxWithGasIfGasAuto(txCtx authctx.TxContext, cliCtx context.CLIContext, name, passphrase string, msgs []sdk.Msg) (authctx.TxContext, error) { + if cliCtx.Gas == 0 { + return EnrichTxContextWithGas(txCtx, cliCtx, name, passphrase, msgs) + } + return txCtx, nil +} + +// EnrichTxContextWithGas simulates the execution of a transaction to +// then populate the relevant TxContext.Gas field with the estimate +// obtained by the query. +func EnrichTxContextWithGas(txCtx authctx.TxContext, cliCtx context.CLIContext, name, passphrase string, msgs []sdk.Msg) (authctx.TxContext, error) { + txCtxSimulation := txCtx.WithGas(0) + txBytes, err := txCtxSimulation.BuildAndSign(name, passphrase, msgs) + if err != nil { + return txCtx, err + } + // run a simulation (via /app/simulate query) to + // estimate gas and update TxContext accordingly + rawRes, err := cliCtx.Query("/app/simulate", txBytes) + if err != nil { + return txCtx, err + } + estimate, err := parseQueryResponse(cliCtx.Codec, rawRes) + if err != nil { + return txCtx, err + } + adjusted := adjustGasEstimate(estimate, cliCtx.GasAdjustment) + fmt.Fprintf(os.Stderr, "gas: [estimated = %v] [adjusted = %v]\n", estimate, adjusted) + return txCtx.WithGas(adjusted), nil +} + +func adjustGasEstimate(estimate int64, adjustment float64) int64 { + if adjustment == 0 { + return int64(DefaultGasAdjustment * float64(estimate)) + } + return int64(adjustment * float64(estimate)) +} + +func parseQueryResponse(cdc *amino.Codec, rawRes []byte) (int64, error) { + var simulationResult sdk.Result + if err := cdc.UnmarshalBinary(rawRes, &simulationResult); err != nil { + return 0, err + } + return simulationResult.GasUsed, nil +} diff --git a/client/utils/utils_test.go b/client/utils/utils_test.go new file mode 100644 index 000000000000..d9ea44488180 --- /dev/null +++ b/client/utils/utils_test.go @@ -0,0 +1,20 @@ +package utils + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/assert" +) + +func TestParseQueryResponse(t *testing.T) { + cdc := app.MakeCodec() + sdkResBytes := cdc.MustMarshalBinary(sdk.Result{GasUsed: 10}) + gas, err := parseQueryResponse(cdc, sdkResBytes) + assert.Equal(t, gas, int64(10)) + assert.Nil(t, err) + gas, err = parseQueryResponse(cdc, []byte("fuzzy")) + assert.Equal(t, gas, int64(0)) + assert.NotNil(t, err) +} diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index 47a1949dd26d..f794e5140df7 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -34,16 +34,7 @@ func init() { } func TestGaiaCLISend(t *testing.T) { - tests.ExecuteT(t, fmt.Sprintf("gaiad --home=%s unsafe-reset-all", gaiadHome), "") - executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s foo", gaiacliHome), app.DefaultKeyPass) - executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s bar", gaiacliHome), app.DefaultKeyPass) - - chainID := executeInit(t, fmt.Sprintf("gaiad init -o --name=foo --home=%s --home-client=%s", gaiadHome, gaiacliHome)) - executeWrite(t, fmt.Sprintf("gaiacli keys add --home=%s bar", gaiacliHome), app.DefaultKeyPass) - - // get a free port, also setup some common flags - servAddr, port, err := server.FreeTCPAddr() - require.NoError(t, err) + chainID, servAddr, port := initializeFixtures(t) flags := fmt.Sprintf("--home=%s --node=%v --chain-id=%v", gaiacliHome, servAddr, chainID) // start gaiad server @@ -86,16 +77,42 @@ func TestGaiaCLISend(t *testing.T) { require.Equal(t, int64(20), fooAcc.GetCoins().AmountOf("steak").Int64()) } -func TestGaiaCLICreateValidator(t *testing.T) { - tests.ExecuteT(t, fmt.Sprintf("gaiad --home=%s unsafe-reset-all", gaiadHome), "") - executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s foo", gaiacliHome), app.DefaultKeyPass) - executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s bar", gaiacliHome), app.DefaultKeyPass) - chainID := executeInit(t, fmt.Sprintf("gaiad init -o --name=foo --home=%s --home-client=%s", gaiadHome, gaiacliHome)) - executeWrite(t, fmt.Sprintf("gaiacli keys add --home=%s bar", gaiacliHome), app.DefaultKeyPass) +func TestGaiaCLIGasAuto(t *testing.T) { + chainID, servAddr, port := initializeFixtures(t) + flags := fmt.Sprintf("--home=%s --node=%v --chain-id=%v", gaiacliHome, servAddr, chainID) - // get a free port, also setup some common flags - servAddr, port, err := server.FreeTCPAddr() - require.NoError(t, err) + // start gaiad server + proc := tests.GoExecuteTWithStdout(t, fmt.Sprintf("gaiad start --home=%s --rpc.laddr=%v", gaiadHome, servAddr)) + + defer proc.Stop(false) + tests.WaitForTMStart(port) + tests.WaitForNextNBlocksTM(2, port) + + fooAddr, _ := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show foo --output=json --home=%s", gaiacliHome)) + barAddr, _ := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show bar --output=json --home=%s", gaiacliHome)) + + fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags)) + require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak").Int64()) + + // Test failure with auto gas disabled and very little gas set by hand + success := executeWrite(t, fmt.Sprintf("gaiacli send %v --gas=10 --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass) + require.False(t, success) + tests.WaitForNextNBlocksTM(2, port) + // Check state didn't change + fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags)) + require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak").Int64()) + + // Enable auto gas + success = executeWrite(t, fmt.Sprintf("gaiacli send %v --gas=0 --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass) + require.True(t, success) + tests.WaitForNextNBlocksTM(2, port) + // Check state has changed accordingly + fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags)) + require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf("steak").Int64()) +} + +func TestGaiaCLICreateValidator(t *testing.T) { + chainID, servAddr, port := initializeFixtures(t) flags := fmt.Sprintf("--home=%s --node=%v --chain-id=%v", gaiacliHome, servAddr, chainID) // start gaiad server @@ -168,15 +185,7 @@ func TestGaiaCLICreateValidator(t *testing.T) { } func TestGaiaCLISubmitProposal(t *testing.T) { - tests.ExecuteT(t, fmt.Sprintf("gaiad --home=%s unsafe-reset-all", gaiadHome), "") - executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s foo", gaiacliHome), app.DefaultKeyPass) - executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s bar", gaiacliHome), app.DefaultKeyPass) - chainID := executeInit(t, fmt.Sprintf("gaiad init -o --name=foo --home=%s --home-client=%s", gaiadHome, gaiacliHome)) - executeWrite(t, fmt.Sprintf("gaiacli keys add --home=%s bar", gaiacliHome), app.DefaultKeyPass) - - // get a free port, also setup some common flags - servAddr, port, err := server.FreeTCPAddr() - require.NoError(t, err) + chainID, servAddr, port := initializeFixtures(t) flags := fmt.Sprintf("--home=%s --node=%v --chain-id=%v", gaiacliHome, servAddr, chainID) // start gaiad server @@ -277,10 +286,29 @@ func getTestingHomeDirs() (string, string) { return gaiadHome, gaiacliHome } +func initializeFixtures(t *testing.T) (chainID, servAddr, port string) { + tests.ExecuteT(t, fmt.Sprintf("gaiad --home=%s unsafe-reset-all", gaiadHome), "") + executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s foo", gaiacliHome), app.DefaultKeyPass) + executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s bar", gaiacliHome), app.DefaultKeyPass) + + chainID = executeInit(t, fmt.Sprintf("gaiad init -o --name=foo --home=%s --home-client=%s", gaiadHome, gaiacliHome)) + executeWrite(t, fmt.Sprintf("gaiacli keys add --home=%s bar", gaiacliHome), app.DefaultKeyPass) + + // get a free port, also setup some common flags + servAddr, port, err := server.FreeTCPAddr() + require.NoError(t, err) + return +} + //___________________________________________________________________________________ // executors -func executeWrite(t *testing.T, cmdStr string, writes ...string) bool { +func executeWrite(t *testing.T, cmdStr string, writes ...string) (exitSuccess bool) { + exitSuccess, _, _ = executeWriteRetStdStreams(t, cmdStr, writes...) + return +} + +func executeWriteRetStdStreams(t *testing.T, cmdStr string, writes ...string) (bool, string, string) { proc := tests.GoExecuteT(t, cmdStr) for _, write := range writes { @@ -300,9 +328,7 @@ func executeWrite(t *testing.T, cmdStr string, writes ...string) bool { } proc.Wait() - return proc.ExitState.Success() - // bz := proc.StdoutBuffer.Bytes() - // fmt.Println("EXEC WRITE", string(bz)) + return proc.ExitState.Success(), string(stdout), string(stderr) } func executeInit(t *testing.T, cmdStr string) (chainID string) { diff --git a/x/auth/ante.go b/x/auth/ante.go index 078f376e226b..dd291a4a656a 100644 --- a/x/auth/ante.go +++ b/x/auth/ante.go @@ -35,7 +35,11 @@ func NewAnteHandler(am AccountMapper, fck FeeCollectionKeeper) sdk.AnteHandler { } // set the gas meter - newCtx = ctx.WithGasMeter(sdk.NewGasMeter(stdTx.Fee.Gas)) + if stdTx.Fee.Gas == 0 { + newCtx = ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + } else { + newCtx = ctx.WithGasMeter(sdk.NewGasMeter(stdTx.Fee.Gas)) + } // AnteHandlers must have their own defer/recover in order // for the BaseApp to know how much gas was used! diff --git a/x/auth/ante_test.go b/x/auth/ante_test.go index 5fa04d848d01..25d31b067e91 100644 --- a/x/auth/ante_test.go +++ b/x/auth/ante_test.go @@ -360,7 +360,7 @@ func TestAnteHandlerMemoGas(t *testing.T) { var tx sdk.Tx msg := newTestMsg(addr1) privs, accnums, seqs := []crypto.PrivKey{priv1}, []int64{0}, []int64{0} - fee := NewStdFee(0, sdk.NewInt64Coin("atom", 0)) + fee := NewStdFee(1, sdk.NewInt64Coin("atom", 0)) // tx does not have enough gas tx = newTestTx(ctx, []sdk.Msg{msg}, privs, accnums, seqs, fee) diff --git a/x/auth/client/context/context.go b/x/auth/client/context/context.go index 1cfa435ee516..8d0a94136d89 100644 --- a/x/auth/client/context/context.go +++ b/x/auth/client/context/context.go @@ -110,9 +110,7 @@ func (ctx TxContext) Build(msgs []sdk.Msg) (auth.StdSignMsg, error) { Sequence: ctx.Sequence, Memo: ctx.Memo, Msgs: msgs, - - // TODO: run simulate to estimate gas? - Fee: auth.NewStdFee(ctx.Gas, fee), + Fee: auth.NewStdFee(ctx.Gas, fee), }, nil } diff --git a/x/auth/client/rest/query.go b/x/auth/client/rest/query.go index 431d3d27d53f..07b109d40315 100644 --- a/x/auth/client/rest/query.go +++ b/x/auth/client/rest/query.go @@ -5,6 +5,7 @@ import ( "net/http" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/utils" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" @@ -32,15 +33,13 @@ func QueryAccountRequestHandlerFn( addr, err := sdk.AccAddressFromBech32(bech32addr) if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error()) return } res, err := cliCtx.QueryStore(auth.AddressStoreKey(addr), storeName) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("couldn't query account. Error: %s", err.Error()))) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("couldn't query account. Error: %s", err.Error())) return } @@ -53,16 +52,14 @@ func QueryAccountRequestHandlerFn( // decode the value account, err := decoder(res) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("couldn't parse query result. Result: %s. Error: %s", res, err.Error()))) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("couldn't parse query result. Result: %s. Error: %s", res, err.Error())) return } // print out whole account output, err := cdc.MarshalJSON(account) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("couldn't marshall query result. Error: %s", err.Error()))) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("couldn't marshall query result. Error: %s", err.Error())) return } diff --git a/x/bank/client/rest/sendtx.go b/x/bank/client/rest/sendtx.go index e060a3bb4fd1..d27be38b5731 100644 --- a/x/bank/client/rest/sendtx.go +++ b/x/bank/client/rest/sendtx.go @@ -5,6 +5,7 @@ import ( "net/http" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/crypto/keys" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/wire" @@ -47,37 +48,32 @@ func SendRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLICo to, err := sdk.AccAddressFromBech32(bech32addr) if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return } var m sendBody body, err := ioutil.ReadAll(r.Body) if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return } err = msgCdc.UnmarshalJSON(body, &m) if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return } info, err := kb.Get(m.LocalAccountName) if err != nil { - w.WriteHeader(http.StatusUnauthorized) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusUnauthorized, err.Error()) return } // build message msg := client.BuildMsg(sdk.AccAddress(info.GetPubKey().Address()), to, m.Amount) if err != nil { // XXX rechecking same error ? - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error()) return } @@ -89,24 +85,29 @@ func SendRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLICo Sequence: m.Sequence, } + if m.Gas == 0 { + txCtx, err = utils.EnrichTxContextWithGas(txCtx, cliCtx, m.LocalAccountName, m.Password, []sdk.Msg{msg}) + if err != nil { + utils.WriteErrorResponse(&w, http.StatusUnauthorized, err.Error()) + return + } + } + txBytes, err := txCtx.BuildAndSign(m.LocalAccountName, m.Password, []sdk.Msg{msg}) if err != nil { - w.WriteHeader(http.StatusUnauthorized) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusUnauthorized, err.Error()) return } res, err := cliCtx.BroadcastTx(txBytes) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error()) return } output, err := wire.MarshalJSONIndent(cdc, res) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error()) return } diff --git a/x/gov/client/rest/rest.go b/x/gov/client/rest/rest.go index 5cdc7bda2bfe..d6a9199d496b 100644 --- a/x/gov/client/rest/rest.go +++ b/x/gov/client/rest/rest.go @@ -5,6 +5,7 @@ import ( "net/http" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/utils" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/gov" @@ -76,7 +77,7 @@ func postProposalHandlerFn(cdc *wire.Codec, cliCtx context.CLIContext) http.Hand msg := gov.NewMsgSubmitProposal(req.Title, req.Description, req.ProposalType, req.Proposer, req.InitialDeposit) err = msg.ValidateBasic() if err != nil { - writeErr(&w, http.StatusBadRequest, err.Error()) + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return } @@ -90,10 +91,8 @@ func depositHandlerFn(cdc *wire.Codec, cliCtx context.CLIContext) http.HandlerFu strProposalID := vars[RestProposalID] if len(strProposalID) == 0 { - w.WriteHeader(http.StatusBadRequest) err := errors.New("proposalId required but not specified") - w.Write([]byte(err.Error())) - + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return } @@ -115,7 +114,7 @@ func depositHandlerFn(cdc *wire.Codec, cliCtx context.CLIContext) http.HandlerFu msg := gov.NewMsgDeposit(req.Depositer, proposalID, req.Amount) err = msg.ValidateBasic() if err != nil { - writeErr(&w, http.StatusBadRequest, err.Error()) + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return } @@ -129,10 +128,8 @@ func voteHandlerFn(cdc *wire.Codec, cliCtx context.CLIContext) http.HandlerFunc strProposalID := vars[RestProposalID] if len(strProposalID) == 0 { - w.WriteHeader(http.StatusBadRequest) err := errors.New("proposalId required but not specified") - w.Write([]byte(err.Error())) - + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return } @@ -154,7 +151,7 @@ func voteHandlerFn(cdc *wire.Codec, cliCtx context.CLIContext) http.HandlerFunc msg := gov.NewMsgVote(req.Voter, proposalID, req.Option) err = msg.ValidateBasic() if err != nil { - writeErr(&w, http.StatusBadRequest, err.Error()) + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return } @@ -168,10 +165,8 @@ func queryProposalHandlerFn(cdc *wire.Codec) http.HandlerFunc { strProposalID := vars[RestProposalID] if len(strProposalID) == 0 { - w.WriteHeader(http.StatusBadRequest) err := errors.New("proposalId required but not specified") - w.Write([]byte(err.Error())) - + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return } @@ -188,16 +183,13 @@ func queryProposalHandlerFn(cdc *wire.Codec) http.HandlerFunc { bz, err := cdc.MarshalJSON(params) if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return } res, err := cliCtx.QueryWithData("custom/gov/proposal", bz) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) - + utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error()) return } @@ -212,10 +204,8 @@ func queryDepositHandlerFn(cdc *wire.Codec) http.HandlerFunc { bechDepositerAddr := vars[RestDepositer] if len(strProposalID) == 0 { - w.WriteHeader(http.StatusBadRequest) err := errors.New("proposalId required but not specified") - w.Write([]byte(err.Error())) - + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return } @@ -225,19 +215,15 @@ func queryDepositHandlerFn(cdc *wire.Codec) http.HandlerFunc { } if len(bechDepositerAddr) == 0 { - w.WriteHeader(http.StatusBadRequest) err := errors.New("depositer address required but not specified") - w.Write([]byte(err.Error())) - + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return } depositerAddr, err := sdk.AccAddressFromBech32(bechDepositerAddr) if err != nil { - w.WriteHeader(http.StatusBadRequest) err := errors.Errorf("'%s' needs to be bech32 encoded", RestDepositer) - w.Write([]byte(err.Error())) - + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return } @@ -250,16 +236,13 @@ func queryDepositHandlerFn(cdc *wire.Codec) http.HandlerFunc { bz, err := cdc.MarshalJSON(params) if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return } res, err := cliCtx.QueryWithData("custom/gov/deposit", bz) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) - + utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error()) return } @@ -268,14 +251,12 @@ func queryDepositHandlerFn(cdc *wire.Codec) http.HandlerFunc { if deposit.Empty() { res, err := cliCtx.QueryWithData("custom/gov/proposal", cdc.MustMarshalBinary(gov.QueryProposalParams{params.ProposalID})) if err != nil || len(res) == 0 { - w.WriteHeader(http.StatusNotFound) err := errors.Errorf("proposalID [%d] does not exist", proposalID) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusNotFound, err.Error()) return } - w.WriteHeader(http.StatusNotFound) err = errors.Errorf("depositer [%s] did not deposit on proposalID [%d]", bechDepositerAddr, proposalID) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusNotFound, err.Error()) return } @@ -290,9 +271,8 @@ func queryVoteHandlerFn(cdc *wire.Codec) http.HandlerFunc { bechVoterAddr := vars[RestVoter] if len(strProposalID) == 0 { - w.WriteHeader(http.StatusBadRequest) err := errors.New("proposalId required but not specified") - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return } @@ -302,18 +282,15 @@ func queryVoteHandlerFn(cdc *wire.Codec) http.HandlerFunc { } if len(bechVoterAddr) == 0 { - w.WriteHeader(http.StatusBadRequest) err := errors.New("voter address required but not specified") - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return } voterAddr, err := sdk.AccAddressFromBech32(bechVoterAddr) if err != nil { - w.WriteHeader(http.StatusBadRequest) err := errors.Errorf("'%s' needs to be bech32 encoded", RestVoter) - w.Write([]byte(err.Error())) - + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return } @@ -325,16 +302,13 @@ func queryVoteHandlerFn(cdc *wire.Codec) http.HandlerFunc { } bz, err := cdc.MarshalJSON(params) if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return } res, err := cliCtx.QueryWithData("custom/gov/vote", bz) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) - + utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error()) return } @@ -343,20 +317,17 @@ func queryVoteHandlerFn(cdc *wire.Codec) http.HandlerFunc { if vote.Empty() { bz, err := cdc.MarshalJSON(gov.QueryProposalParams{params.ProposalID}) if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return } res, err := cliCtx.QueryWithData("custom/gov/proposal", bz) if err != nil || len(res) == 0 { - w.WriteHeader(http.StatusNotFound) err := errors.Errorf("proposalID [%d] does not exist", proposalID) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusNotFound, err.Error()) return } - w.WriteHeader(http.StatusNotFound) err = errors.Errorf("voter [%s] did not deposit on proposalID [%d]", bechVoterAddr, proposalID) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusNotFound, err.Error()) return } w.Write(res) @@ -371,10 +342,8 @@ func queryVotesOnProposalHandlerFn(cdc *wire.Codec) http.HandlerFunc { strProposalID := vars[RestProposalID] if len(strProposalID) == 0 { - w.WriteHeader(http.StatusBadRequest) err := errors.New("proposalId required but not specified") - w.Write([]byte(err.Error())) - + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return } @@ -390,15 +359,13 @@ func queryVotesOnProposalHandlerFn(cdc *wire.Codec) http.HandlerFunc { } bz, err := cdc.MarshalJSON(params) if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return } res, err := cliCtx.QueryWithData("custom/gov/votes", bz) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error()) return } @@ -420,9 +387,8 @@ func queryProposalsWithParameterFn(cdc *wire.Codec) http.HandlerFunc { if len(bechVoterAddr) != 0 { voterAddr, err := sdk.AccAddressFromBech32(bechVoterAddr) if err != nil { - w.WriteHeader(http.StatusBadRequest) err := errors.Errorf("'%s' needs to be bech32 encoded", RestVoter) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return } params.Voter = voterAddr @@ -431,10 +397,8 @@ func queryProposalsWithParameterFn(cdc *wire.Codec) http.HandlerFunc { if len(bechDepositerAddr) != 0 { depositerAddr, err := sdk.AccAddressFromBech32(bechDepositerAddr) if err != nil { - w.WriteHeader(http.StatusBadRequest) err := errors.Errorf("'%s' needs to be bech32 encoded", RestDepositer) - w.Write([]byte(err.Error())) - + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return } params.Depositer = depositerAddr @@ -443,10 +407,8 @@ func queryProposalsWithParameterFn(cdc *wire.Codec) http.HandlerFunc { if len(strProposalStatus) != 0 { proposalStatus, err := gov.ProposalStatusFromString(strProposalStatus) if err != nil { - w.WriteHeader(http.StatusBadRequest) err := errors.Errorf("'%s' is not a valid Proposal Status", strProposalStatus) - w.Write([]byte(err.Error())) - + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return } params.ProposalStatus = proposalStatus @@ -461,8 +423,7 @@ func queryProposalsWithParameterFn(cdc *wire.Codec) http.HandlerFunc { bz, err := cdc.MarshalJSON(params) if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return } @@ -470,9 +431,7 @@ func queryProposalsWithParameterFn(cdc *wire.Codec) http.HandlerFunc { res, err := cliCtx.QueryWithData("custom/gov/proposals", bz) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) - + utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error()) return } diff --git a/x/gov/client/rest/util.go b/x/gov/client/rest/util.go index e6aa83cc4b42..c6b360dc801b 100644 --- a/x/gov/client/rest/util.go +++ b/x/gov/client/rest/util.go @@ -7,11 +7,10 @@ import ( "strconv" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/utils" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/wire" authctx "github.com/cosmos/cosmos-sdk/x/auth/client/context" - - "github.com/pkg/errors" ) type baseReq struct { @@ -26,12 +25,12 @@ type baseReq struct { func buildReq(w http.ResponseWriter, r *http.Request, cdc *wire.Codec, req interface{}) error { body, err := ioutil.ReadAll(r.Body) if err != nil { - writeErr(&w, http.StatusBadRequest, err.Error()) + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return err } err = cdc.UnmarshalJSON(body, req) if err != nil { - writeErr(&w, http.StatusBadRequest, err.Error()) + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return err } return nil @@ -39,41 +38,36 @@ func buildReq(w http.ResponseWriter, r *http.Request, cdc *wire.Codec, req inter func (req baseReq) baseReqValidate(w http.ResponseWriter) bool { if len(req.Name) == 0 { - writeErr(&w, http.StatusUnauthorized, "Name required but not specified") + utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Name required but not specified") return false } if len(req.Password) == 0 { - writeErr(&w, http.StatusUnauthorized, "Password required but not specified") + utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Password required but not specified") return false } if len(req.ChainID) == 0 { - writeErr(&w, http.StatusUnauthorized, "ChainID required but not specified") + utils.WriteErrorResponse(&w, http.StatusUnauthorized, "ChainID required but not specified") return false } if req.AccountNumber < 0 { - writeErr(&w, http.StatusUnauthorized, "Account Number required but not specified") + utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Account Number required but not specified") return false } if req.Sequence < 0 { - writeErr(&w, http.StatusUnauthorized, "Sequence required but not specified") + utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Sequence required but not specified") return false } return true } -func writeErr(w *http.ResponseWriter, status int, msg string) { - (*w).WriteHeader(status) - err := errors.New(msg) - (*w).Write([]byte(err.Error())) -} - // TODO: Build this function out into a more generic base-request // (probably should live in client/lcd). func signAndBuild(w http.ResponseWriter, cliCtx context.CLIContext, baseReq baseReq, msg sdk.Msg, cdc *wire.Codec) { + var err error txCtx := authctx.TxContext{ Codec: cdc, AccountNumber: baseReq.AccountNumber, @@ -82,21 +76,28 @@ func signAndBuild(w http.ResponseWriter, cliCtx context.CLIContext, baseReq base Gas: baseReq.Gas, } + if baseReq.Gas == 0 { + txCtx, err = utils.EnrichTxContextWithGas(txCtx, cliCtx, baseReq.Name, baseReq.Password, []sdk.Msg{msg}) + if err != nil { + utils.WriteErrorResponse(&w, http.StatusUnauthorized, err.Error()) + return + } + } txBytes, err := txCtx.BuildAndSign(baseReq.Name, baseReq.Password, []sdk.Msg{msg}) if err != nil { - writeErr(&w, http.StatusUnauthorized, err.Error()) + utils.WriteErrorResponse(&w, http.StatusUnauthorized, err.Error()) return } res, err := cliCtx.BroadcastTx(txBytes) if err != nil { - writeErr(&w, http.StatusInternalServerError, err.Error()) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error()) return } output, err := wire.MarshalJSONIndent(cdc, res) if err != nil { - writeErr(&w, http.StatusInternalServerError, err.Error()) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error()) return } diff --git a/x/ibc/client/rest/transfer.go b/x/ibc/client/rest/transfer.go index 4470f556e087..8c12a4b82655 100644 --- a/x/ibc/client/rest/transfer.go +++ b/x/ibc/client/rest/transfer.go @@ -5,6 +5,7 @@ import ( "net/http" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/crypto/keys" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/wire" @@ -40,30 +41,26 @@ func TransferRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.C to, err := sdk.AccAddressFromBech32(bech32addr) if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return } var m transferBody body, err := ioutil.ReadAll(r.Body) if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return } err = cdc.UnmarshalJSON(body, &m) if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return } info, err := kb.Get(m.LocalAccountName) if err != nil { - w.WriteHeader(http.StatusUnauthorized) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusUnauthorized, err.Error()) return } @@ -79,24 +76,30 @@ func TransferRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.C Gas: m.Gas, } + if m.Gas == 0 { + txCtx, err = utils.EnrichTxContextWithGas(txCtx, cliCtx, m.LocalAccountName, m.Password, []sdk.Msg{msg}) + if err != nil { + w.WriteHeader(http.StatusUnauthorized) + w.Write([]byte(err.Error())) + return + } + } + txBytes, err := txCtx.BuildAndSign(m.LocalAccountName, m.Password, []sdk.Msg{msg}) if err != nil { - w.WriteHeader(http.StatusUnauthorized) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusUnauthorized, err.Error()) return } res, err := cliCtx.BroadcastTx(txBytes) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error()) return } output, err := cdc.MarshalJSON(res) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error()) return } diff --git a/x/slashing/client/rest/tx.go b/x/slashing/client/rest/tx.go index 6e45230b01b8..956e7a571204 100644 --- a/x/slashing/client/rest/tx.go +++ b/x/slashing/client/rest/tx.go @@ -8,6 +8,7 @@ import ( "net/http" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/crypto/keys" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/wire" @@ -40,34 +41,29 @@ func unjailRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLI var m UnjailBody body, err := ioutil.ReadAll(r.Body) if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return } err = json.Unmarshal(body, &m) if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error()) return } info, err := kb.Get(m.LocalAccountName) if err != nil { - w.WriteHeader(http.StatusUnauthorized) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusUnauthorized, err.Error()) return } validatorAddr, err := sdk.AccAddressFromBech32(m.ValidatorAddr) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())) return } if !bytes.Equal(info.GetPubKey().Address(), validatorAddr) { - w.WriteHeader(http.StatusUnauthorized) - w.Write([]byte("Must use own validator address")) + utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Must use own validator address") return } @@ -81,24 +77,29 @@ func unjailRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLI msg := slashing.NewMsgUnjail(validatorAddr) + if m.Gas == 0 { + txCtx, err = utils.EnrichTxContextWithGas(txCtx, cliCtx, m.LocalAccountName, m.Password, []sdk.Msg{msg}) + if err != nil { + utils.WriteErrorResponse(&w, http.StatusUnauthorized, err.Error()) + return + } + } + txBytes, err := txCtx.BuildAndSign(m.LocalAccountName, m.Password, []sdk.Msg{msg}) if err != nil { - w.WriteHeader(http.StatusUnauthorized) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Must use own validator address") return } res, err := cliCtx.BroadcastTx(txBytes) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error()) return } output, err := json.MarshalIndent(res, "", " ") if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error()) return } diff --git a/x/stake/client/rest/tx.go b/x/stake/client/rest/tx.go index d8b9b6011366..e049212d8b0f 100644 --- a/x/stake/client/rest/tx.go +++ b/x/stake/client/rest/tx.go @@ -7,6 +7,7 @@ import ( "net/http" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/crypto/keys" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/wire" @@ -105,21 +106,18 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex for _, msg := range m.Delegations { delegatorAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddr) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error()))) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error())) return } validatorAddr, err := sdk.AccAddressFromBech32(msg.ValidatorAddr) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())) return } if !bytes.Equal(info.GetPubKey().Address(), delegatorAddr) { - w.WriteHeader(http.StatusUnauthorized) - w.Write([]byte("Must use own delegator address")) + utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Must use own delegator address") return } @@ -135,34 +133,29 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex for _, msg := range m.BeginRedelegates { delegatorAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddr) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error()))) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())) return } if !bytes.Equal(info.GetPubKey().Address(), delegatorAddr) { - w.WriteHeader(http.StatusUnauthorized) - w.Write([]byte("Must use own delegator address")) + utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Must use own delegator address") return } validatorSrcAddr, err := sdk.AccAddressFromBech32(msg.ValidatorSrcAddr) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())) return } validatorDstAddr, err := sdk.AccAddressFromBech32(msg.ValidatorDstAddr) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())) return } shares, err := sdk.NewDecFromStr(msg.SharesAmount) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("Couldn't decode shares amount. Error: %s", err.Error()))) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode shares amount. Error: %s", err.Error())) return } @@ -179,27 +172,23 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex for _, msg := range m.CompleteRedelegates { delegatorAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddr) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error()))) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error())) return } validatorSrcAddr, err := sdk.AccAddressFromBech32(msg.ValidatorSrcAddr) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())) return } validatorDstAddr, err := sdk.AccAddressFromBech32(msg.ValidatorDstAddr) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())) return } if !bytes.Equal(info.GetPubKey().Address(), delegatorAddr) { - w.WriteHeader(http.StatusUnauthorized) - w.Write([]byte("Must use own delegator address")) + utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Must use own delegator address") return } @@ -215,28 +204,24 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex for _, msg := range m.BeginUnbondings { delegatorAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddr) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error()))) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error())) return } if !bytes.Equal(info.GetPubKey().Address(), delegatorAddr) { - w.WriteHeader(http.StatusUnauthorized) - w.Write([]byte("Must use own delegator address")) + utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Must use own delegator address") return } validatorAddr, err := sdk.AccAddressFromBech32(msg.ValidatorAddr) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())) return } shares, err := sdk.NewDecFromStr(msg.SharesAmount) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("Couldn't decode shares amount. Error: %s", err.Error()))) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode shares amount. Error: %s", err.Error())) return } @@ -252,21 +237,18 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex for _, msg := range m.CompleteUnbondings { delegatorAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddr) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error()))) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error())) return } validatorAddr, err := sdk.AccAddressFromBech32(msg.ValidatorAddr) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error())) return } if !bytes.Equal(info.GetPubKey().Address(), delegatorAddr) { - w.WriteHeader(http.StatusUnauthorized) - w.Write([]byte("Must use own delegator address")) + utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Must use own delegator address") return } @@ -293,10 +275,17 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex m.Sequence++ + if m.Gas == 0 { + txCtx, err = utils.EnrichTxContextWithGas(txCtx, cliCtx, m.LocalAccountName, m.Password, []sdk.Msg{msg}) + if err != nil { + utils.WriteErrorResponse(&w, http.StatusUnauthorized, err.Error()) + return + } + } + txBytes, err := txCtx.BuildAndSign(m.LocalAccountName, m.Password, []sdk.Msg{msg}) if err != nil { - w.WriteHeader(http.StatusUnauthorized) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusUnauthorized, err.Error()) return } @@ -310,8 +299,7 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex for i, txBytes := range signedTxs { res, err := cliCtx.BroadcastTx(txBytes) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error()) return } @@ -320,8 +308,7 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex output, err := wire.MarshalJSONIndent(cdc, results[:]) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error()) return } From 323fac3bf7791acbaff51fb7a69eb0f9837014cb Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Wed, 22 Aug 2018 12:40:07 +0100 Subject: [PATCH 02/10] Update PENDING.md --- PENDING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/PENDING.md b/PENDING.md index 01bdc2158bed..d1f87da5ab68 100644 --- a/PENDING.md +++ b/PENDING.md @@ -35,6 +35,8 @@ FEATURES * Gaia CLI (`gaiacli`) * [cli] Cmds to query staking pool and params * [gov][cli] #2062 added `--proposal` flag to `submit-proposal` that allows a JSON file containing a proposal to be passed in + * [cli] \#2047 Setting the --gas flag value to 0 triggers a simulation of the tx before the actual execution. The gas estimate obtained via the simulation will be used as gas limit in the actual execution. + * [cli] \#2047 The --gas-adjustment flag can be used to adjust the estimate obtained via the simulation triggered by --gas=0. * Gaia From 47d55bd57217fb2adaedb6ada42b0edeb16d7b71 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Thu, 23 Aug 2018 16:10:33 +0100 Subject: [PATCH 03/10] Add/refresh docs --- docs/sdk/clients.md | 6 ++++++ docs/sdk/core/app1.md | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/sdk/clients.md b/docs/sdk/clients.md index be42a46dbaa9..0a1c2a38d990 100644 --- a/docs/sdk/clients.md +++ b/docs/sdk/clients.md @@ -97,6 +97,12 @@ gaiacli send \ The `--amount` flag accepts the format `--amount=`. ::: +::: tip Note +You may want to cap the maximum gas that can be consumed by the transaction via the `--gas` flag. +If set to 0, the gas limit will be automatically estimated. +Gas estimate might be inaccurate as state changes could occur in between the end of the simulation and the actual execution of a transaction, thus an adjustment is applied on top of the original estimate in order to ensure the transaction is broadcasted successfully. The adjustment can be controlled via the `--gas-adjustment` flag, whose default value is 1.2. +::: + Now, view the updated balances of the origin and destination accounts: ```bash diff --git a/docs/sdk/core/app1.md b/docs/sdk/core/app1.md index a2978ffb088a..9338d30d99b6 100644 --- a/docs/sdk/core/app1.md +++ b/docs/sdk/core/app1.md @@ -208,7 +208,7 @@ type Result struct { // GasWanted is the maximum units of work we allow this tx to perform. GasWanted int64 - // GasUsed is the amount of gas actually consumed. NOTE: unimplemented + // GasUsed is the amount of gas actually consumed. GasUsed int64 // Tx fee amount and denom. From e959478e61697a446762bf7863bb0fff438f6eca Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Fri, 24 Aug 2018 07:34:24 +0100 Subject: [PATCH 04/10] comment getContextForAnte(), rename applyTxMode() --- baseapp/baseapp.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 2669aa9f302f..2a52b7179f7a 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -493,6 +493,8 @@ func validateBasicTxMsgs(msgs []sdk.Msg) sdk.Error { return nil } +// retrieve the context for the ante handler and store the tx bytes; store +// the signing validators if the tx runs within the deliverTx() state. func (app *BaseApp) getContextForAnte(mode runTxMode, txBytes []byte) (ctx sdk.Context) { // Get the context ctx = getState(app, mode).ctx.WithTxBytes(txBytes) @@ -565,7 +567,7 @@ func getState(app *BaseApp, mode runTxMode) *state { return app.deliverState } -func (app *BaseApp) applyTxMode(ctx sdk.Context, mode runTxMode) sdk.Context { +func (app *BaseApp) initializeContext(ctx sdk.Context, mode runTxMode) sdk.Context { if mode != runTxModeSimulate { return ctx } @@ -582,7 +584,7 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk var gasWanted int64 var msCache sdk.CacheMultiStore ctx := app.getContextForAnte(mode, txBytes) - ctx = app.applyTxMode(ctx, mode) + ctx = app.initializeContext(ctx, mode) defer func() { if r := recover(); r != nil { From 7e9ceb452db5d1f0256af66004f6f0a357863f19 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Fri, 24 Aug 2018 07:35:12 +0100 Subject: [PATCH 05/10] clarifyy that adjustment is a multiplicative factor --- client/flags.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/flags.go b/client/flags.go index d128d32f6656..15cc9effa42c 100644 --- a/client/flags.go +++ b/client/flags.go @@ -53,7 +53,7 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command { c.Flags().String(FlagNode, "tcp://localhost:26657", ": to tendermint rpc interface for this chain") c.Flags().Bool(FlagUseLedger, false, "Use a connected Ledger device") c.Flags().Int64(FlagGas, 0, "gas limit to set per-transaction; set to 0 to calculate required gas automatically") - c.Flags().Float64(FlagGasAdjustment, DefaultGasAdjustment, "gas adjustment to be applied on the estimate returned by the tx simulation") + c.Flags().Float64(FlagGasAdjustment, DefaultGasAdjustment, "adjustment factor to be multiplied against the estimate returned by the tx simulation; defaults to an internal value") c.Flags().Bool(FlagAsync, false, "broadcast transactions asynchronously") c.Flags().Bool(FlagJson, false, "return output in json format") c.Flags().Bool(FlagPrintResponse, true, "return tx response (only works with async = false)") From 2dea46779c562d6af21bedd62866aed9c8813569 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Fri, 24 Aug 2018 07:37:41 +0100 Subject: [PATCH 06/10] TestCoinSend: test success case when setting gas by hand --- client/lcd/lcd_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 2db2f6d618d8..67d8708832b3 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -267,6 +267,10 @@ func TestCoinSend(t *testing.T) { // test failure with too little gas res, body, _ = doSendWithGas(t, port, seed, name, password, addr, 100) require.Equal(t, http.StatusInternalServerError, res.StatusCode, body) + + // test success with just enough gas + res, body, _ = doSendWithGas(t, port, seed, name, password, addr, 3000) + require.Equal(t, http.StatusOK, res.StatusCode, body) } func TestIBCTransfer(t *testing.T) { From fb5fe9914d2489f15450a0b9ccde9b5c06a26e16 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Fri, 24 Aug 2018 07:54:25 +0100 Subject: [PATCH 07/10] simplify json handling in LCD tests --- client/lcd/lcd_test.go | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 67d8708832b3..298cf0027e47 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -738,27 +738,22 @@ func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.Acc panic(err) } - jsonStr := func() []byte { - if gas > 0 { - return []byte(fmt.Sprintf(`{ - "name":"%s", - "password":"%s", - "account_number":"%d", - "sequence":"%d", - "amount":[%s], - "chain_id":"%s", - "gas":"%v" - }`, name, password, accnum, sequence, coinbz, chainID, gas)) - } - return []byte(fmt.Sprintf(`{ - "name":"%s", - "password":"%s", - "account_number":"%d", - "sequence":"%d", - "amount":[%s], - "chain_id":"%s" - }`, name, password, accnum, sequence, coinbz, chainID)) - }() + gasStr := "" + if gas > 0 { + gasStr = fmt.Sprintf(` + "gas":"%v", + `, gas) + } + jsonStr := []byte(fmt.Sprintf(`{ + %v + "name":"%s", + "password":"%s", + "account_number":"%d", + "sequence":"%d", + "amount":[%s], + "chain_id":"%s" + }`, gasStr, name, password, accnum, sequence, coinbz, chainID)) + res, body = Request(t, port, "POST", fmt.Sprintf("/accounts/%s/send", receiveAddr), jsonStr) return } From f36f749818e1f2d08cddbe52cc9c3a62a2b8c202 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Fri, 24 Aug 2018 09:48:02 +0100 Subject: [PATCH 08/10] Incorporating @ValarDragon's comments --- client/utils/utils.go | 45 +++++++++++++++++++++-------------- x/bank/client/rest/sendtx.go | 17 +++++++++++-- x/gov/client/rest/util.go | 17 +++++++++++-- x/ibc/client/rest/transfer.go | 18 +++++++++++--- x/slashing/client/rest/tx.go | 17 +++++++++++-- x/stake/client/rest/tx.go | 17 +++++++++++-- 6 files changed, 102 insertions(+), 29 deletions(-) diff --git a/client/utils/utils.go b/client/utils/utils.go index 9f06dda1d7b8..da49ff5e03a5 100644 --- a/client/utils/utils.go +++ b/client/utils/utils.go @@ -9,6 +9,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" authctx "github.com/cosmos/cosmos-sdk/x/auth/client/context" amino "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/libs/common" ) // DefaultGasAdjustment is applied to gas estimates to avoid tx @@ -58,7 +59,7 @@ func SendTx(txCtx authctx.TxContext, cliCtx context.CLIContext, msgs []sdk.Msg) return err } - txCtx, err = enrichCtxWithGasIfGasAuto(txCtx, cliCtx, cliCtx.FromAddressName, passphrase, msgs) + txCtx, err = enrichCtxWithGasIfGasAuto(txCtx, cliCtx, passphrase, msgs) if err != nil { return err } @@ -72,35 +73,43 @@ func SendTx(txCtx authctx.TxContext, cliCtx context.CLIContext, msgs []sdk.Msg) return cliCtx.EnsureBroadcastTx(txBytes) } -func enrichCtxWithGasIfGasAuto(txCtx authctx.TxContext, cliCtx context.CLIContext, name, passphrase string, msgs []sdk.Msg) (authctx.TxContext, error) { +func enrichCtxWithGasIfGasAuto(txCtx authctx.TxContext, cliCtx context.CLIContext, passphrase string, msgs []sdk.Msg) (authctx.TxContext, error) { if cliCtx.Gas == 0 { - return EnrichTxContextWithGas(txCtx, cliCtx, name, passphrase, msgs) + txBytes, err := BuildAndSignTxWithZeroGas(txCtx, cliCtx.FromAddressName, passphrase, msgs) + if err != nil { + return txCtx, err + } + estimate, adjusted, err := CalculateGas(cliCtx.Query, cliCtx.Codec, txBytes, cliCtx.GasAdjustment) + if err != nil { + return txCtx, err + } + fmt.Fprintf(os.Stderr, "gas: [estimated = %v] [adjusted = %v]\n", estimate, adjusted) + return txCtx.WithGas(adjusted), nil } return txCtx, nil } -// EnrichTxContextWithGas simulates the execution of a transaction to -// then populate the relevant TxContext.Gas field with the estimate -// obtained by the query. -func EnrichTxContextWithGas(txCtx authctx.TxContext, cliCtx context.CLIContext, name, passphrase string, msgs []sdk.Msg) (authctx.TxContext, error) { - txCtxSimulation := txCtx.WithGas(0) - txBytes, err := txCtxSimulation.BuildAndSign(name, passphrase, msgs) - if err != nil { - return txCtx, err - } +// BuildAndSignTxWithZeroGas builds transactions with GasWanted set to 0. +func BuildAndSignTxWithZeroGas(txCtx authctx.TxContext, name, passphrase string, msgs []sdk.Msg) ([]byte, error) { + return txCtx.WithGas(0).BuildAndSign(name, passphrase, msgs) +} + +// CalculateGas simulates the execution of a transaction and returns +// both the estimate obtained by the query and the adjusted amount. +func CalculateGas(queryFunc func(string, common.HexBytes) ([]byte, error), cdc *amino.Codec, txBytes []byte, adjustment float64) (estimate, adjusted int64, err error) { // run a simulation (via /app/simulate query) to // estimate gas and update TxContext accordingly - rawRes, err := cliCtx.Query("/app/simulate", txBytes) + rawRes, err := queryFunc("/app/simulate", txBytes) if err != nil { - return txCtx, err + return } - estimate, err := parseQueryResponse(cliCtx.Codec, rawRes) + estimate, err = parseQueryResponse(cdc, rawRes) if err != nil { - return txCtx, err + return } - adjusted := adjustGasEstimate(estimate, cliCtx.GasAdjustment) + adjusted = adjustGasEstimate(estimate, adjustment) fmt.Fprintf(os.Stderr, "gas: [estimated = %v] [adjusted = %v]\n", estimate, adjusted) - return txCtx.WithGas(adjusted), nil + return } func adjustGasEstimate(estimate int64, adjustment float64) int64 { diff --git a/x/bank/client/rest/sendtx.go b/x/bank/client/rest/sendtx.go index d27be38b5731..14bff9a49bb9 100644 --- a/x/bank/client/rest/sendtx.go +++ b/x/bank/client/rest/sendtx.go @@ -86,11 +86,12 @@ func SendRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLICo } if m.Gas == 0 { - txCtx, err = utils.EnrichTxContextWithGas(txCtx, cliCtx, m.LocalAccountName, m.Password, []sdk.Msg{msg}) + newCtx, httperr, err := enrichContextWithGas(txCtx, cliCtx, m.LocalAccountName, m.Password, msg) if err != nil { - utils.WriteErrorResponse(&w, http.StatusUnauthorized, err.Error()) + utils.WriteErrorResponse(&w, httperr, err.Error()) return } + txCtx = newCtx } txBytes, err := txCtx.BuildAndSign(m.LocalAccountName, m.Password, []sdk.Msg{msg}) @@ -114,3 +115,15 @@ func SendRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLICo w.Write(output) } } + +func enrichContextWithGas(txCtx authctx.TxContext, cliCtx context.CLIContext, name, password string, msg sdk.Msg) (authctx.TxContext, int, error) { + txBytes, err := utils.BuildAndSignTxWithZeroGas(txCtx, name, password, []sdk.Msg{msg}) + if err != nil { + return txCtx, http.StatusInternalServerError, err + } + _, adjusted, err := utils.CalculateGas(cliCtx.Query, cliCtx.Codec, txBytes, cliCtx.GasAdjustment) + if err != nil { + return txCtx, http.StatusUnauthorized, err + } + return txCtx.WithGas(adjusted), http.StatusOK, nil +} diff --git a/x/gov/client/rest/util.go b/x/gov/client/rest/util.go index c6b360dc801b..e20402831028 100644 --- a/x/gov/client/rest/util.go +++ b/x/gov/client/rest/util.go @@ -77,11 +77,12 @@ func signAndBuild(w http.ResponseWriter, cliCtx context.CLIContext, baseReq base } if baseReq.Gas == 0 { - txCtx, err = utils.EnrichTxContextWithGas(txCtx, cliCtx, baseReq.Name, baseReq.Password, []sdk.Msg{msg}) + newCtx, httperr, err := enrichContextWithGas(txCtx, cliCtx, baseReq.Name, baseReq.Password, msg) if err != nil { - utils.WriteErrorResponse(&w, http.StatusUnauthorized, err.Error()) + utils.WriteErrorResponse(&w, httperr, err.Error()) return } + txCtx = newCtx } txBytes, err := txCtx.BuildAndSign(baseReq.Name, baseReq.Password, []sdk.Msg{msg}) if err != nil { @@ -115,3 +116,15 @@ func parseInt64OrReturnBadRequest(s string, w http.ResponseWriter) (n int64, ok } return n, true } + +func enrichContextWithGas(txCtx authctx.TxContext, cliCtx context.CLIContext, name, password string, msg sdk.Msg) (authctx.TxContext, int, error) { + txBytes, err := utils.BuildAndSignTxWithZeroGas(txCtx, name, password, []sdk.Msg{msg}) + if err != nil { + return txCtx, http.StatusInternalServerError, err + } + _, adjusted, err := utils.CalculateGas(cliCtx.Query, cliCtx.Codec, txBytes, cliCtx.GasAdjustment) + if err != nil { + return txCtx, http.StatusUnauthorized, err + } + return txCtx.WithGas(adjusted), http.StatusOK, nil +} diff --git a/x/ibc/client/rest/transfer.go b/x/ibc/client/rest/transfer.go index 8c12a4b82655..cf0f24b312e4 100644 --- a/x/ibc/client/rest/transfer.go +++ b/x/ibc/client/rest/transfer.go @@ -77,12 +77,12 @@ func TransferRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.C } if m.Gas == 0 { - txCtx, err = utils.EnrichTxContextWithGas(txCtx, cliCtx, m.LocalAccountName, m.Password, []sdk.Msg{msg}) + newCtx, httperr, err := enrichContextWithGas(txCtx, cliCtx, m.LocalAccountName, m.Password, msg) if err != nil { - w.WriteHeader(http.StatusUnauthorized) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(&w, httperr, err.Error()) return } + txCtx = newCtx } txBytes, err := txCtx.BuildAndSign(m.LocalAccountName, m.Password, []sdk.Msg{msg}) @@ -106,3 +106,15 @@ func TransferRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.C w.Write(output) } } + +func enrichContextWithGas(txCtx authctx.TxContext, cliCtx context.CLIContext, name, password string, msg sdk.Msg) (authctx.TxContext, int, error) { + txBytes, err := utils.BuildAndSignTxWithZeroGas(txCtx, name, password, []sdk.Msg{msg}) + if err != nil { + return txCtx, http.StatusInternalServerError, err + } + _, adjusted, err := utils.CalculateGas(cliCtx.Query, cliCtx.Codec, txBytes, cliCtx.GasAdjustment) + if err != nil { + return txCtx, http.StatusUnauthorized, err + } + return txCtx.WithGas(adjusted), http.StatusOK, nil +} diff --git a/x/slashing/client/rest/tx.go b/x/slashing/client/rest/tx.go index 956e7a571204..414b05cfda9b 100644 --- a/x/slashing/client/rest/tx.go +++ b/x/slashing/client/rest/tx.go @@ -78,11 +78,12 @@ func unjailRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLI msg := slashing.NewMsgUnjail(validatorAddr) if m.Gas == 0 { - txCtx, err = utils.EnrichTxContextWithGas(txCtx, cliCtx, m.LocalAccountName, m.Password, []sdk.Msg{msg}) + newCtx, httperr, err := enrichContextWithGas(txCtx, cliCtx, m.LocalAccountName, m.Password, msg) if err != nil { - utils.WriteErrorResponse(&w, http.StatusUnauthorized, err.Error()) + utils.WriteErrorResponse(&w, httperr, err.Error()) return } + txCtx = newCtx } txBytes, err := txCtx.BuildAndSign(m.LocalAccountName, m.Password, []sdk.Msg{msg}) @@ -106,3 +107,15 @@ func unjailRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLI w.Write(output) } } + +func enrichContextWithGas(txCtx authctx.TxContext, cliCtx context.CLIContext, name, password string, msg sdk.Msg) (authctx.TxContext, int, error) { + txBytes, err := utils.BuildAndSignTxWithZeroGas(txCtx, name, password, []sdk.Msg{msg}) + if err != nil { + return txCtx, http.StatusInternalServerError, err + } + _, adjusted, err := utils.CalculateGas(cliCtx.Query, cliCtx.Codec, txBytes, cliCtx.GasAdjustment) + if err != nil { + return txCtx, http.StatusUnauthorized, err + } + return txCtx.WithGas(adjusted), http.StatusOK, nil +} diff --git a/x/stake/client/rest/tx.go b/x/stake/client/rest/tx.go index e049212d8b0f..729f57895772 100644 --- a/x/stake/client/rest/tx.go +++ b/x/stake/client/rest/tx.go @@ -276,11 +276,12 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex m.Sequence++ if m.Gas == 0 { - txCtx, err = utils.EnrichTxContextWithGas(txCtx, cliCtx, m.LocalAccountName, m.Password, []sdk.Msg{msg}) + newCtx, httperr, err := enrichContextWithGas(txCtx, cliCtx, m.LocalAccountName, m.Password, msg) if err != nil { - utils.WriteErrorResponse(&w, http.StatusUnauthorized, err.Error()) + utils.WriteErrorResponse(&w, httperr, err.Error()) return } + txCtx = newCtx } txBytes, err := txCtx.BuildAndSign(m.LocalAccountName, m.Password, []sdk.Msg{msg}) @@ -315,3 +316,15 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex w.Write(output) } } + +func enrichContextWithGas(txCtx authcliCtx.TxContext, cliCtx context.CLIContext, name, password string, msg sdk.Msg) (authcliCtx.TxContext, int, error) { + txBytes, err := utils.BuildAndSignTxWithZeroGas(txCtx, name, password, []sdk.Msg{msg}) + if err != nil { + return txCtx, http.StatusInternalServerError, err + } + _, adjusted, err := utils.CalculateGas(cliCtx.Query, cliCtx.Codec, txBytes, cliCtx.GasAdjustment) + if err != nil { + return txCtx, http.StatusUnauthorized, err + } + return txCtx.WithGas(adjusted), http.StatusOK, nil +} From 7e8feec73833e3c793f6268b289f82778e48cb33 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Fri, 24 Aug 2018 14:57:45 +0100 Subject: [PATCH 09/10] Incorporating @cwgoes comments --- baseapp/baseapp.go | 11 +++++------ client/flags.go | 4 ++-- client/utils/utils.go | 33 +++++++++++++++++---------------- x/bank/client/rest/sendtx.go | 16 ++-------------- x/gov/client/rest/util.go | 16 ++-------------- x/ibc/client/rest/transfer.go | 16 ++-------------- x/slashing/client/rest/tx.go | 16 ++-------------- x/stake/client/rest/tx.go | 16 ++-------------- 8 files changed, 34 insertions(+), 94 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 2a52b7179f7a..20faf06cd0f5 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -498,10 +498,9 @@ func validateBasicTxMsgs(msgs []sdk.Msg) sdk.Error { func (app *BaseApp) getContextForAnte(mode runTxMode, txBytes []byte) (ctx sdk.Context) { // Get the context ctx = getState(app, mode).ctx.WithTxBytes(txBytes) - if mode != runTxModeDeliver { - return + if mode == runTxModeDeliver { + ctx = ctx.WithSigningValidators(app.signedValidators) } - ctx = ctx.WithSigningValidators(app.signedValidators) return } @@ -568,10 +567,10 @@ func getState(app *BaseApp, mode runTxMode) *state { } func (app *BaseApp) initializeContext(ctx sdk.Context, mode runTxMode) sdk.Context { - if mode != runTxModeSimulate { - return ctx + if mode == runTxModeSimulate { + ctx = ctx.WithMultiStore(getState(app, runTxModeSimulate).CacheMultiStore()) } - return ctx.WithMultiStore(getState(app, runTxModeSimulate).CacheMultiStore()) + return ctx } // runTx processes a transaction. The transactions is proccessed via an diff --git a/client/flags.go b/client/flags.go index 15cc9effa42c..7337e9741149 100644 --- a/client/flags.go +++ b/client/flags.go @@ -4,7 +4,7 @@ import "github.com/spf13/cobra" // nolint const ( - DefaultGasAdjustment = 0 + DefaultGasAdjustment = 1.2 FlagUseLedger = "ledger" FlagChainID = "chain-id" @@ -53,7 +53,7 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command { c.Flags().String(FlagNode, "tcp://localhost:26657", ": to tendermint rpc interface for this chain") c.Flags().Bool(FlagUseLedger, false, "Use a connected Ledger device") c.Flags().Int64(FlagGas, 0, "gas limit to set per-transaction; set to 0 to calculate required gas automatically") - c.Flags().Float64(FlagGasAdjustment, DefaultGasAdjustment, "adjustment factor to be multiplied against the estimate returned by the tx simulation; defaults to an internal value") + c.Flags().Float64(FlagGasAdjustment, DefaultGasAdjustment, "adjustment factor to be multiplied against the estimate returned by the tx simulation") c.Flags().Bool(FlagAsync, false, "broadcast transactions asynchronously") c.Flags().Bool(FlagJson, false, "return output in json format") c.Flags().Bool(FlagPrintResponse, true, "return tx response (only works with async = false)") diff --git a/client/utils/utils.go b/client/utils/utils.go index da49ff5e03a5..fb5d6198871d 100644 --- a/client/utils/utils.go +++ b/client/utils/utils.go @@ -59,9 +59,11 @@ func SendTx(txCtx authctx.TxContext, cliCtx context.CLIContext, msgs []sdk.Msg) return err } - txCtx, err = enrichCtxWithGasIfGasAuto(txCtx, cliCtx, passphrase, msgs) - if err != nil { - return err + if cliCtx.Gas == 0 { + txCtx, err = EnrichCtxWithGas(txCtx, cliCtx, cliCtx.FromAddressName, passphrase, msgs) + if err != nil { + return err + } } // build and sign the transaction @@ -73,20 +75,19 @@ func SendTx(txCtx authctx.TxContext, cliCtx context.CLIContext, msgs []sdk.Msg) return cliCtx.EnsureBroadcastTx(txBytes) } -func enrichCtxWithGasIfGasAuto(txCtx authctx.TxContext, cliCtx context.CLIContext, passphrase string, msgs []sdk.Msg) (authctx.TxContext, error) { - if cliCtx.Gas == 0 { - txBytes, err := BuildAndSignTxWithZeroGas(txCtx, cliCtx.FromAddressName, passphrase, msgs) - if err != nil { - return txCtx, err - } - estimate, adjusted, err := CalculateGas(cliCtx.Query, cliCtx.Codec, txBytes, cliCtx.GasAdjustment) - if err != nil { - return txCtx, err - } - fmt.Fprintf(os.Stderr, "gas: [estimated = %v] [adjusted = %v]\n", estimate, adjusted) - return txCtx.WithGas(adjusted), nil +// EnrichCtxWithGas calculates the gas estimate that would be consumed by the +// transaction and set the transaction's respective value accordingly. +func EnrichCtxWithGas(txCtx authctx.TxContext, cliCtx context.CLIContext, name, passphrase string, msgs []sdk.Msg) (authctx.TxContext, error) { + txBytes, err := BuildAndSignTxWithZeroGas(txCtx, name, passphrase, msgs) + if err != nil { + return txCtx, err } - return txCtx, nil + estimate, adjusted, err := CalculateGas(cliCtx.Query, cliCtx.Codec, txBytes, cliCtx.GasAdjustment) + if err != nil { + return txCtx, err + } + fmt.Fprintf(os.Stderr, "gas: [estimated = %v] [adjusted = %v]\n", estimate, adjusted) + return txCtx.WithGas(adjusted), nil } // BuildAndSignTxWithZeroGas builds transactions with GasWanted set to 0. diff --git a/x/bank/client/rest/sendtx.go b/x/bank/client/rest/sendtx.go index 14bff9a49bb9..c7baa96910d8 100644 --- a/x/bank/client/rest/sendtx.go +++ b/x/bank/client/rest/sendtx.go @@ -86,9 +86,9 @@ func SendRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLICo } if m.Gas == 0 { - newCtx, httperr, err := enrichContextWithGas(txCtx, cliCtx, m.LocalAccountName, m.Password, msg) + newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, m.LocalAccountName, m.Password, []sdk.Msg{msg}) if err != nil { - utils.WriteErrorResponse(&w, httperr, err.Error()) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error()) return } txCtx = newCtx @@ -115,15 +115,3 @@ func SendRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLICo w.Write(output) } } - -func enrichContextWithGas(txCtx authctx.TxContext, cliCtx context.CLIContext, name, password string, msg sdk.Msg) (authctx.TxContext, int, error) { - txBytes, err := utils.BuildAndSignTxWithZeroGas(txCtx, name, password, []sdk.Msg{msg}) - if err != nil { - return txCtx, http.StatusInternalServerError, err - } - _, adjusted, err := utils.CalculateGas(cliCtx.Query, cliCtx.Codec, txBytes, cliCtx.GasAdjustment) - if err != nil { - return txCtx, http.StatusUnauthorized, err - } - return txCtx.WithGas(adjusted), http.StatusOK, nil -} diff --git a/x/gov/client/rest/util.go b/x/gov/client/rest/util.go index e20402831028..f98f7bfa592d 100644 --- a/x/gov/client/rest/util.go +++ b/x/gov/client/rest/util.go @@ -77,9 +77,9 @@ func signAndBuild(w http.ResponseWriter, cliCtx context.CLIContext, baseReq base } if baseReq.Gas == 0 { - newCtx, httperr, err := enrichContextWithGas(txCtx, cliCtx, baseReq.Name, baseReq.Password, msg) + newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, baseReq.Name, baseReq.Password, []sdk.Msg{msg}) if err != nil { - utils.WriteErrorResponse(&w, httperr, err.Error()) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error()) return } txCtx = newCtx @@ -116,15 +116,3 @@ func parseInt64OrReturnBadRequest(s string, w http.ResponseWriter) (n int64, ok } return n, true } - -func enrichContextWithGas(txCtx authctx.TxContext, cliCtx context.CLIContext, name, password string, msg sdk.Msg) (authctx.TxContext, int, error) { - txBytes, err := utils.BuildAndSignTxWithZeroGas(txCtx, name, password, []sdk.Msg{msg}) - if err != nil { - return txCtx, http.StatusInternalServerError, err - } - _, adjusted, err := utils.CalculateGas(cliCtx.Query, cliCtx.Codec, txBytes, cliCtx.GasAdjustment) - if err != nil { - return txCtx, http.StatusUnauthorized, err - } - return txCtx.WithGas(adjusted), http.StatusOK, nil -} diff --git a/x/ibc/client/rest/transfer.go b/x/ibc/client/rest/transfer.go index cf0f24b312e4..765208b05655 100644 --- a/x/ibc/client/rest/transfer.go +++ b/x/ibc/client/rest/transfer.go @@ -77,9 +77,9 @@ func TransferRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.C } if m.Gas == 0 { - newCtx, httperr, err := enrichContextWithGas(txCtx, cliCtx, m.LocalAccountName, m.Password, msg) + newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, m.LocalAccountName, m.Password, []sdk.Msg{msg}) if err != nil { - utils.WriteErrorResponse(&w, httperr, err.Error()) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error()) return } txCtx = newCtx @@ -106,15 +106,3 @@ func TransferRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.C w.Write(output) } } - -func enrichContextWithGas(txCtx authctx.TxContext, cliCtx context.CLIContext, name, password string, msg sdk.Msg) (authctx.TxContext, int, error) { - txBytes, err := utils.BuildAndSignTxWithZeroGas(txCtx, name, password, []sdk.Msg{msg}) - if err != nil { - return txCtx, http.StatusInternalServerError, err - } - _, adjusted, err := utils.CalculateGas(cliCtx.Query, cliCtx.Codec, txBytes, cliCtx.GasAdjustment) - if err != nil { - return txCtx, http.StatusUnauthorized, err - } - return txCtx.WithGas(adjusted), http.StatusOK, nil -} diff --git a/x/slashing/client/rest/tx.go b/x/slashing/client/rest/tx.go index 414b05cfda9b..26412ebc0028 100644 --- a/x/slashing/client/rest/tx.go +++ b/x/slashing/client/rest/tx.go @@ -78,9 +78,9 @@ func unjailRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLI msg := slashing.NewMsgUnjail(validatorAddr) if m.Gas == 0 { - newCtx, httperr, err := enrichContextWithGas(txCtx, cliCtx, m.LocalAccountName, m.Password, msg) + newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, m.LocalAccountName, m.Password, []sdk.Msg{msg}) if err != nil { - utils.WriteErrorResponse(&w, httperr, err.Error()) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error()) return } txCtx = newCtx @@ -107,15 +107,3 @@ func unjailRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLI w.Write(output) } } - -func enrichContextWithGas(txCtx authctx.TxContext, cliCtx context.CLIContext, name, password string, msg sdk.Msg) (authctx.TxContext, int, error) { - txBytes, err := utils.BuildAndSignTxWithZeroGas(txCtx, name, password, []sdk.Msg{msg}) - if err != nil { - return txCtx, http.StatusInternalServerError, err - } - _, adjusted, err := utils.CalculateGas(cliCtx.Query, cliCtx.Codec, txBytes, cliCtx.GasAdjustment) - if err != nil { - return txCtx, http.StatusUnauthorized, err - } - return txCtx.WithGas(adjusted), http.StatusOK, nil -} diff --git a/x/stake/client/rest/tx.go b/x/stake/client/rest/tx.go index 729f57895772..3d7d419a3a64 100644 --- a/x/stake/client/rest/tx.go +++ b/x/stake/client/rest/tx.go @@ -276,9 +276,9 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex m.Sequence++ if m.Gas == 0 { - newCtx, httperr, err := enrichContextWithGas(txCtx, cliCtx, m.LocalAccountName, m.Password, msg) + newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, m.LocalAccountName, m.Password, []sdk.Msg{msg}) if err != nil { - utils.WriteErrorResponse(&w, httperr, err.Error()) + utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error()) return } txCtx = newCtx @@ -316,15 +316,3 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex w.Write(output) } } - -func enrichContextWithGas(txCtx authcliCtx.TxContext, cliCtx context.CLIContext, name, password string, msg sdk.Msg) (authcliCtx.TxContext, int, error) { - txBytes, err := utils.BuildAndSignTxWithZeroGas(txCtx, name, password, []sdk.Msg{msg}) - if err != nil { - return txCtx, http.StatusInternalServerError, err - } - _, adjusted, err := utils.CalculateGas(cliCtx.Query, cliCtx.Codec, txBytes, cliCtx.GasAdjustment) - if err != nil { - return txCtx, http.StatusUnauthorized, err - } - return txCtx.WithGas(adjusted), http.StatusOK, nil -} From 76a16ab288b388eb8a036313f2efcfff410819db Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Sat, 25 Aug 2018 20:12:14 +0100 Subject: [PATCH 10/10] Modify AnteHandler to take a simulate boolean parameter --- baseapp/baseapp.go | 2 +- baseapp/baseapp_test.go | 12 ++--- client/flags.go | 3 +- client/utils/utils_test.go | 38 ++++++++++++++++ docs/sdk/core/examples/app2.go | 2 +- types/handler.go | 2 +- x/auth/ante.go | 4 +- x/auth/ante_test.go | 80 +++++++++++++++++----------------- x/params/msg_status.go | 2 +- 9 files changed, 93 insertions(+), 52 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 20faf06cd0f5..60c694ff766e 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -608,7 +608,7 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk // run the ante handler if app.anteHandler != nil { - newCtx, result, abort := app.anteHandler(ctx, tx) + newCtx, result, abort := app.anteHandler(ctx, tx, (mode == runTxModeSimulate)) if abort { return result } diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 269424e58995..04e47214dd2f 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -365,7 +365,7 @@ func testTxDecoder(cdc *wire.Codec) sdk.TxDecoder { } func anteHandlerTxTest(t *testing.T, capKey *sdk.KVStoreKey, storeKey []byte) sdk.AnteHandler { - return func(ctx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) { + return func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) { store := ctx.KVStore(capKey) msgCounter := tx.(txTest).Counter res = incrementingCounter(t, store, storeKey, msgCounter) @@ -595,7 +595,7 @@ func TestSimulateTx(t *testing.T) { gasConsumed := int64(5) anteOpt := func(bapp *BaseApp) { - bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) { + bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) { newCtx = ctx.WithGasMeter(sdk.NewGasMeter(gasConsumed)) return }) @@ -659,7 +659,9 @@ func TestSimulateTx(t *testing.T) { func TestRunInvalidTransaction(t *testing.T) { anteOpt := func(bapp *BaseApp) { - bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) { return }) + bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) { + return + }) } routerOpt := func(bapp *BaseApp) { bapp.Router().AddRoute(typeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (res sdk.Result) { return }) @@ -734,7 +736,7 @@ func TestRunInvalidTransaction(t *testing.T) { func TestTxGasLimits(t *testing.T) { gasGranted := int64(10) anteOpt := func(bapp *BaseApp) { - bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) { + bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) { newCtx = ctx.WithGasMeter(sdk.NewGasMeter(gasGranted)) // NOTE/TODO/XXX: @@ -825,7 +827,7 @@ func TestTxGasLimits(t *testing.T) { func TestQuery(t *testing.T) { key, value := []byte("hello"), []byte("goodbye") anteOpt := func(bapp *BaseApp) { - bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) { + bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) { store := ctx.KVStore(capKey1) store.Set(key, value) return diff --git a/client/flags.go b/client/flags.go index 7337e9741149..81e06706784a 100644 --- a/client/flags.go +++ b/client/flags.go @@ -4,6 +4,7 @@ import "github.com/spf13/cobra" // nolint const ( + DefaultGasLimit = 200000 DefaultGasAdjustment = 1.2 FlagUseLedger = "ledger" @@ -52,7 +53,7 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command { c.Flags().String(FlagChainID, "", "Chain ID of tendermint node") c.Flags().String(FlagNode, "tcp://localhost:26657", ": to tendermint rpc interface for this chain") c.Flags().Bool(FlagUseLedger, false, "Use a connected Ledger device") - c.Flags().Int64(FlagGas, 0, "gas limit to set per-transaction; set to 0 to calculate required gas automatically") + c.Flags().Int64(FlagGas, DefaultGasLimit, "gas limit to set per-transaction; set to 0 to calculate required gas automatically") c.Flags().Float64(FlagGasAdjustment, DefaultGasAdjustment, "adjustment factor to be multiplied against the estimate returned by the tx simulation") c.Flags().Bool(FlagAsync, false, "broadcast transactions asynchronously") c.Flags().Bool(FlagJson, false, "return output in json format") diff --git a/client/utils/utils_test.go b/client/utils/utils_test.go index d9ea44488180..731ded903ff0 100644 --- a/client/utils/utils_test.go +++ b/client/utils/utils_test.go @@ -1,11 +1,13 @@ package utils import ( + "errors" "testing" "github.com/cosmos/cosmos-sdk/cmd/gaia/app" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/assert" + "github.com/tendermint/tendermint/libs/common" ) func TestParseQueryResponse(t *testing.T) { @@ -18,3 +20,39 @@ func TestParseQueryResponse(t *testing.T) { assert.Equal(t, gas, int64(0)) assert.NotNil(t, err) } + +func TestCalculateGas(t *testing.T) { + cdc := app.MakeCodec() + makeQueryFunc := func(gasUsed int64, wantErr bool) func(string, common.HexBytes) ([]byte, error) { + return func(string, common.HexBytes) ([]byte, error) { + if wantErr { + return nil, errors.New("") + } + return cdc.MustMarshalBinary(sdk.Result{GasUsed: gasUsed}), nil + } + } + type args struct { + queryFuncGasUsed int64 + queryFuncWantErr bool + adjustment float64 + } + tests := []struct { + name string + args args + wantEstimate int64 + wantAdjusted int64 + wantErr bool + }{ + {"error", args{0, true, 1.2}, 0, 0, true}, + {"adjusted gas", args{10, false, 1.2}, 10, 12, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + queryFunc := makeQueryFunc(tt.args.queryFuncGasUsed, tt.args.queryFuncWantErr) + gotEstimate, gotAdjusted, err := CalculateGas(queryFunc, cdc, []byte(""), tt.args.adjustment) + assert.Equal(t, err != nil, tt.wantErr) + assert.Equal(t, gotEstimate, tt.wantEstimate) + assert.Equal(t, gotAdjusted, tt.wantAdjusted) + }) + } +} diff --git a/docs/sdk/core/examples/app2.go b/docs/sdk/core/examples/app2.go index 3c7f71f6d6c6..5f23abe071b9 100644 --- a/docs/sdk/core/examples/app2.go +++ b/docs/sdk/core/examples/app2.go @@ -211,7 +211,7 @@ func tx2Decoder(cdc *wire.Codec) sdk.TxDecoder { // Simple anteHandler that ensures msg signers have signed. // Provides no replay protection. -func antehandler(ctx sdk.Context, tx sdk.Tx) (_ sdk.Context, _ sdk.Result, abort bool) { +func antehandler(ctx sdk.Context, tx sdk.Tx, simulate bool) (_ sdk.Context, _ sdk.Result, abort bool) { appTx, ok := tx.(app2Tx) if !ok { // set abort boolean to true so that we don't continue to process failed tx diff --git a/types/handler.go b/types/handler.go index 3a50e0ce053f..b978e8e51ef4 100644 --- a/types/handler.go +++ b/types/handler.go @@ -5,4 +5,4 @@ type Handler func(ctx Context, msg Msg) Result // AnteHandler authenticates transactions, before their internal messages are handled. // If newCtx.IsZero(), ctx is used instead. -type AnteHandler func(ctx Context, tx Tx) (newCtx Context, result Result, abort bool) +type AnteHandler func(ctx Context, tx Tx, simulate bool) (newCtx Context, result Result, abort bool) diff --git a/x/auth/ante.go b/x/auth/ante.go index dd291a4a656a..c0a129be3eb2 100644 --- a/x/auth/ante.go +++ b/x/auth/ante.go @@ -25,7 +25,7 @@ const ( func NewAnteHandler(am AccountMapper, fck FeeCollectionKeeper) sdk.AnteHandler { return func( - ctx sdk.Context, tx sdk.Tx, + ctx sdk.Context, tx sdk.Tx, simulate bool, ) (newCtx sdk.Context, res sdk.Result, abort bool) { // This AnteHandler requires Txs to be StdTxs @@ -35,7 +35,7 @@ func NewAnteHandler(am AccountMapper, fck FeeCollectionKeeper) sdk.AnteHandler { } // set the gas meter - if stdTx.Fee.Gas == 0 { + if simulate { newCtx = ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) } else { newCtx = ctx.WithGasMeter(sdk.NewGasMeter(stdTx.Fee.Gas)) diff --git a/x/auth/ante_test.go b/x/auth/ante_test.go index 25d31b067e91..a841bc7760aa 100644 --- a/x/auth/ante_test.go +++ b/x/auth/ante_test.go @@ -39,16 +39,16 @@ func privAndAddr() (crypto.PrivKey, sdk.AccAddress) { } // run the tx through the anteHandler and ensure its valid -func checkValidTx(t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, tx sdk.Tx) { - _, result, abort := anteHandler(ctx, tx) +func checkValidTx(t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, tx sdk.Tx, simulate bool) { + _, result, abort := anteHandler(ctx, tx, simulate) require.False(t, abort) require.Equal(t, sdk.ABCICodeOK, result.Code) require.True(t, result.IsOK()) } // run the tx through the anteHandler and ensure it fails with the given code -func checkInvalidTx(t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, tx sdk.Tx, code sdk.CodeType) { - newCtx, result, abort := anteHandler(ctx, tx) +func checkInvalidTx(t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, tx sdk.Tx, simulate bool, code sdk.CodeType) { + newCtx, result, abort := anteHandler(ctx, tx, simulate) require.True(t, abort) require.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, code), result.Code, fmt.Sprintf("Expected %v, got %v", sdk.ToABCICode(sdk.CodespaceRoot, code), result)) @@ -140,23 +140,23 @@ func TestAnteHandlerSigErrors(t *testing.T) { require.Equal(t, expectedSigners, stdTx.GetSigners()) // Check no signatures fails - checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeUnauthorized) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeUnauthorized) // test num sigs dont match GetSigners privs, accNums, seqs = []crypto.PrivKey{priv1}, []int64{0}, []int64{0} tx = newTestTx(ctx, msgs, privs, accNums, seqs, fee) - checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeUnauthorized) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeUnauthorized) // test an unrecognized account privs, accNums, seqs = []crypto.PrivKey{priv1, priv2, priv3}, []int64{0, 1, 2}, []int64{0, 0, 0} tx = newTestTx(ctx, msgs, privs, accNums, seqs, fee) - checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeUnknownAddress) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeUnknownAddress) // save the first account, but second is still unrecognized acc1 := mapper.NewAccountWithAddress(ctx, addr1) acc1.SetCoins(fee.Amount) mapper.SetAccount(ctx, acc1) - checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeUnknownAddress) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeUnknownAddress) } // Test logic around account number checking with one signer and many signers. @@ -192,17 +192,17 @@ func TestAnteHandlerAccountNumbers(t *testing.T) { // test good tx from one signer privs, accnums, seqs := []crypto.PrivKey{priv1}, []int64{0}, []int64{0} tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkValidTx(t, anteHandler, ctx, tx) + checkValidTx(t, anteHandler, ctx, tx, false) // new tx from wrong account number seqs = []int64{1} tx = newTestTx(ctx, msgs, privs, []int64{1}, seqs, fee) - checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInvalidSequence) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidSequence) // from correct account number seqs = []int64{1} tx = newTestTx(ctx, msgs, privs, []int64{0}, seqs, fee) - checkValidTx(t, anteHandler, ctx, tx) + checkValidTx(t, anteHandler, ctx, tx, false) // new tx with another signer and incorrect account numbers msg1 := newTestMsg(addr1, addr2) @@ -210,12 +210,12 @@ func TestAnteHandlerAccountNumbers(t *testing.T) { msgs = []sdk.Msg{msg1, msg2} privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []int64{1, 0}, []int64{2, 0} tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInvalidSequence) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidSequence) // correct account numbers privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []int64{0, 1}, []int64{2, 0} tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkValidTx(t, anteHandler, ctx, tx) + checkValidTx(t, anteHandler, ctx, tx, false) } // Test logic around sequence checking with one signer and many signers. @@ -255,15 +255,15 @@ func TestAnteHandlerSequences(t *testing.T) { // test good tx from one signer privs, accnums, seqs := []crypto.PrivKey{priv1}, []int64{0}, []int64{0} tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkValidTx(t, anteHandler, ctx, tx) + checkValidTx(t, anteHandler, ctx, tx, false) // test sending it again fails (replay protection) - checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInvalidSequence) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidSequence) // fix sequence, should pass seqs = []int64{1} tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkValidTx(t, anteHandler, ctx, tx) + checkValidTx(t, anteHandler, ctx, tx, false) // new tx with another signer and correct sequences msg1 := newTestMsg(addr1, addr2) @@ -272,28 +272,28 @@ func TestAnteHandlerSequences(t *testing.T) { privs, accnums, seqs = []crypto.PrivKey{priv1, priv2, priv3}, []int64{0, 1, 2}, []int64{2, 0, 0} tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkValidTx(t, anteHandler, ctx, tx) + checkValidTx(t, anteHandler, ctx, tx, false) // replay fails - checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInvalidSequence) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidSequence) // tx from just second signer with incorrect sequence fails msg = newTestMsg(addr2) msgs = []sdk.Msg{msg} privs, accnums, seqs = []crypto.PrivKey{priv2}, []int64{1}, []int64{0} tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInvalidSequence) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidSequence) // fix the sequence and it passes tx = newTestTx(ctx, msgs, []crypto.PrivKey{priv2}, []int64{1}, []int64{1}, fee) - checkValidTx(t, anteHandler, ctx, tx) + checkValidTx(t, anteHandler, ctx, tx, false) // another tx from both of them that passes msg = newTestMsg(addr1, addr2) msgs = []sdk.Msg{msg} privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []int64{0, 1}, []int64{3, 2} tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkValidTx(t, anteHandler, ctx, tx) + checkValidTx(t, anteHandler, ctx, tx, false) } // Test logic around fee deduction. @@ -323,17 +323,17 @@ func TestAnteHandlerFees(t *testing.T) { // signer does not have enough funds to pay the fee tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInsufficientFunds) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInsufficientFunds) acc1.SetCoins(sdk.Coins{sdk.NewInt64Coin("atom", 149)}) mapper.SetAccount(ctx, acc1) - checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInsufficientFunds) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInsufficientFunds) require.True(t, feeCollector.GetCollectedFees(ctx).IsEqual(emptyCoins)) acc1.SetCoins(sdk.Coins{sdk.NewInt64Coin("atom", 150)}) mapper.SetAccount(ctx, acc1) - checkValidTx(t, anteHandler, ctx, tx) + checkValidTx(t, anteHandler, ctx, tx, false) require.True(t, feeCollector.GetCollectedFees(ctx).IsEqual(sdk.Coins{sdk.NewInt64Coin("atom", 150)})) } @@ -360,26 +360,26 @@ func TestAnteHandlerMemoGas(t *testing.T) { var tx sdk.Tx msg := newTestMsg(addr1) privs, accnums, seqs := []crypto.PrivKey{priv1}, []int64{0}, []int64{0} - fee := NewStdFee(1, sdk.NewInt64Coin("atom", 0)) + fee := NewStdFee(0, sdk.NewInt64Coin("atom", 0)) // tx does not have enough gas tx = newTestTx(ctx, []sdk.Msg{msg}, privs, accnums, seqs, fee) - checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeOutOfGas) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeOutOfGas) // tx with memo doesn't have enough gas fee = NewStdFee(801, sdk.NewInt64Coin("atom", 0)) tx = newTestTxWithMemo(ctx, []sdk.Msg{msg}, privs, accnums, seqs, fee, "abcininasidniandsinasindiansdiansdinaisndiasndiadninsd") - checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeOutOfGas) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeOutOfGas) // memo too large fee = NewStdFee(2001, sdk.NewInt64Coin("atom", 0)) tx = newTestTxWithMemo(ctx, []sdk.Msg{msg}, privs, accnums, seqs, fee, "abcininasidniandsinasindiansdiansdinaisndiasndiadninsdabcininasidniandsinasindiansdiansdinaisndiasndiadninsdabcininasidniandsinasindiansdiansdinaisndiasndiadninsd") - checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeMemoTooLarge) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeMemoTooLarge) // tx with memo has enough gas fee = NewStdFee(1100, sdk.NewInt64Coin("atom", 0)) tx = newTestTxWithMemo(ctx, []sdk.Msg{msg}, privs, accnums, seqs, fee, "abcininasidniandsinasindiansdiansdinaisndiasndiadninsd") - checkValidTx(t, anteHandler, ctx, tx) + checkValidTx(t, anteHandler, ctx, tx, false) } func TestAnteHandlerMultiSigner(t *testing.T) { @@ -420,17 +420,17 @@ func TestAnteHandlerMultiSigner(t *testing.T) { privs, accnums, seqs := []crypto.PrivKey{priv1, priv2, priv3}, []int64{0, 1, 2}, []int64{0, 0, 0} tx = newTestTxWithMemo(ctx, msgs, privs, accnums, seqs, fee, "Check signers are in expected order and different account numbers works") - checkValidTx(t, anteHandler, ctx, tx) + checkValidTx(t, anteHandler, ctx, tx, false) // change sequence numbers tx = newTestTx(ctx, []sdk.Msg{msg1}, []crypto.PrivKey{priv1, priv2}, []int64{0, 1}, []int64{1, 1}, fee) - checkValidTx(t, anteHandler, ctx, tx) + checkValidTx(t, anteHandler, ctx, tx, false) tx = newTestTx(ctx, []sdk.Msg{msg2}, []crypto.PrivKey{priv3, priv1}, []int64{2, 0}, []int64{1, 2}, fee) - checkValidTx(t, anteHandler, ctx, tx) + checkValidTx(t, anteHandler, ctx, tx, false) // expected seqs = [3, 2, 2] tx = newTestTxWithMemo(ctx, msgs, privs, accnums, []int64{3, 2, 2}, fee, "Check signers are in expected order and different account numbers and sequence numbers works") - checkValidTx(t, anteHandler, ctx, tx) + checkValidTx(t, anteHandler, ctx, tx, false) } func TestAnteHandlerBadSignBytes(t *testing.T) { @@ -467,7 +467,7 @@ func TestAnteHandlerBadSignBytes(t *testing.T) { // test good tx and signBytes privs, accnums, seqs := []crypto.PrivKey{priv1}, []int64{0}, []int64{0} tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkValidTx(t, anteHandler, ctx, tx) + checkValidTx(t, anteHandler, ctx, tx, false) chainID := ctx.ChainID() chainID2 := chainID + "somemorestuff" @@ -497,20 +497,20 @@ func TestAnteHandlerBadSignBytes(t *testing.T) { StdSignBytes(cs.chainID, cs.accnum, cs.seq, cs.fee, cs.msgs, ""), "", ) - checkInvalidTx(t, anteHandler, ctx, tx, cs.code) + checkInvalidTx(t, anteHandler, ctx, tx, false, cs.code) } // test wrong signer if public key exist privs, accnums, seqs = []crypto.PrivKey{priv2}, []int64{0}, []int64{1} tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeUnauthorized) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeUnauthorized) // test wrong signer if public doesn't exist msg = newTestMsg(addr2) msgs = []sdk.Msg{msg} privs, accnums, seqs = []crypto.PrivKey{priv1}, []int64{1}, []int64{0} tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInvalidPubKey) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidPubKey) } @@ -544,7 +544,7 @@ func TestAnteHandlerSetPubKey(t *testing.T) { privs, accnums, seqs := []crypto.PrivKey{priv1}, []int64{0}, []int64{0} fee := newStdFee() tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) - checkValidTx(t, anteHandler, ctx, tx) + checkValidTx(t, anteHandler, ctx, tx, false) acc1 = mapper.GetAccount(ctx, addr1) require.Equal(t, acc1.GetPubKey(), priv1.PubKey()) @@ -555,14 +555,14 @@ func TestAnteHandlerSetPubKey(t *testing.T) { tx = newTestTx(ctx, msgs, privs, []int64{1}, seqs, fee) sigs := tx.(StdTx).GetSignatures() sigs[0].PubKey = nil - checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInvalidPubKey) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidPubKey) acc2 = mapper.GetAccount(ctx, addr2) require.Nil(t, acc2.GetPubKey()) // test invalid signature and public key tx = newTestTx(ctx, msgs, privs, []int64{1}, seqs, fee) - checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInvalidPubKey) + checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidPubKey) acc2 = mapper.GetAccount(ctx, addr2) require.Nil(t, acc2.GetPubKey()) diff --git a/x/params/msg_status.go b/x/params/msg_status.go index 72704e4dc7aa..7f9197c5c189 100644 --- a/x/params/msg_status.go +++ b/x/params/msg_status.go @@ -24,7 +24,7 @@ func InitGenesis(ctx sdk.Context, k Keeper, data GenesisState) { // NewAnteHandler returns an AnteHandler that checks // whether msg type is activate or not func NewAnteHandler(k Keeper) sdk.AnteHandler { - return func(ctx sdk.Context, tx sdk.Tx) (sdk.Context, sdk.Result, bool) { + return func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, sdk.Result, bool) { for _, msg := range tx.GetMsgs() { ok := k.Getter().GetBoolWithDefault(ctx, ActivatedParamKey(msg.Type()), false) if !ok {